Improve ValidateLength error message consistency and refactor validation tests#25806
Improve ValidateLength error message consistency and refactor validation tests#25806iSazonov merged 9 commits intoPowerShell:masterfrom jorgeasaurus:master
Conversation
(#25566) ## Problem The ValidateLengthMaxLengthFailure error message was displaying incorrectly, showing the length value where the parameter name should appear: - Actual: "The character length of the 5 argument is too long..." - Expected: "The character length of the "argument" argument is too long..." This occurred because the resource string was updated to expect three parameters (MaxLength, actual length, and parameter name) but the code was only passing two parameters, causing the format string to use the length value in place of the parameter name. ## Root Cause The resource string format was changed to include a parameter name placeholder: - Old format: "The character length of the {1} argument..." - New format: "The character length of the "{2}" argument is too long at {1} characters..." But the corresponding code change to pass the parameter name was not implemented. ## Solution 1. Updated ValidateLengthAttribute.ValidateElement() to pass "argument" as the third parameter when throwing ValidationMetadataException for max length validation failures. 2. Updated the ValidateLengthMaxLengthFailure resource string in Metadata.resx to properly format the error message with all three placeholders. While the current architecture doesn't support passing actual parameter names to validation attributes (which would require significant architectural changes), using "argument" as a generic placeholder prevents the confusing formatting error and provides a clear, consistent error message. ## Changes - Modified src/System.Management.Automation/engine/Attributes.cs to pass "argument" as third parameter in ValidateLength max length validation - Updated src/System.Management.Automation/resources/ Metadata.resx to include proper formatting for parameter name placeholder - Added comprehensive tests in test/powershell/engine /Basic/ValidateAttributes.Tests.ps1 to verify the fix ## Testing Added tests to verify: - Maximum length validation error formatting shows "argument" placeholder - Minimum length validation remains unchanged (different format) - Non-string parameter validation works correctly - Valid string lengths pass validation - Error message contains actual parameter name in outer exception The fix ensures error messages are properly formatted and readable while maintaining backward compatibility. Fixes #25566
|
Thank you @iSazonov for the review feedback. I've updated the PR to make Changes:1. Error Message Format (Metadata.resx:178) Changed from: To: 2. Code Update (Attributes.cs:872) Removed the third parameter to match MinLength's two-parameter format: // Before
throw new ValidationMetadataException(
"ValidateLengthMaxLengthFailure",
null,
Metadata.ValidateLengthMaxLengthFailure,
MaxLength, len, "argument");
// After
throw new ValidationMetadataException(
"ValidateLengthMaxLengthFailure",
null,
Metadata.ValidateLengthMaxLengthFailure,
MaxLength, len);3. Test Update (ValidateAttributes.Tests.ps1:319-320) Updated test to verify the new consistent format: # Check the inner exception message is properly formatted and consistent with MinLength format
$error[0].Exception.InnerException.Message | Should -Match 'The character length \(5\) of the argument is too long\.'
$error[0].Exception.InnerException.Message | Should -Match 'shorter than or equal to "2"'Result:Both error messages now follow the same consistent pattern: MinLength: MaxLength: |
| BeforeAll { | ||
| $testCases = @( | ||
| @{ | ||
| ScriptBlock = { function foo { param([ValidateLength(2, 5)] [string] $bar) }; foo "a" } |
There was a problem hiding this comment.
Please replace foo/bar with more informative names.
There was a problem hiding this comment.
Please address the comment.
Please replace foo/bar with more informative names.
|
|
||
| { foo "11111" } | Should -Throw -ErrorId "ParameterArgumentValidationError,foo" | ||
|
|
||
| # Check the inner exception message is properly formatted and consistent with MinLength format |
There was a problem hiding this comment.
Please remove obvious comment.
Below too.
| { foo "11111" } | Should -Throw -ErrorId "ParameterArgumentValidationError,foo" | ||
|
|
||
| # Check the inner exception message is properly formatted and consistent with MinLength format | ||
| $error[0].Exception.InnerException.Message | Should -Match 'The character length \(5\) of the argument is too long\.' |
There was a problem hiding this comment.
We never use localized messages in tests. Please use more formal check like ".+($ExpectedRealLength).+($ExpectedMaxLength)". Also you can remove \n in the string before check.
|
Thank you @iSazonov Changes:Test Update (ValidateAttributes.Tests.ps1:313-323)
It 'ValidateLength error message should be properly formatted' {
function Test-ValidateLength { param([ValidateLength(0,2)] [string] $Value) $Value }
$ExpectedMaxLength = 2
$ExpectedRealLength = 5
{ Test-ValidateLength "11111" } | Should -Throw -ErrorId "ParameterArgumentValidationError,Test-ValidateLength"
$error[0].Exception.InnerException.Message | Should -Match ".+\($ExpectedRealLength\).+\`"$ExpectedMaxLength\`""
$error[0].Exception.Message | Should -Match 'Value'
}This approach avoids relying on localized message text and provides a more robust test that verifies the error message contains the expected length values in the correct format. |
|
|
||
| { Test-ValidateLength "11111" } | Should -Throw -ErrorId "ParameterArgumentValidationError,Test-ValidateLength" | ||
|
|
||
| $error[0].Exception.InnerException.Message | Should -Match ".+\($ExpectedRealLength\).+\`"$ExpectedMaxLength\`"" |
There was a problem hiding this comment.
If we check a message for max we should check a message for min too.
| function Test-ValidateLength { param([ValidateLength(0,2)] [string] $Value) $Value } | ||
|
|
||
| $ExpectedMaxLength = 2 | ||
| $ExpectedRealLength = 5 |
There was a problem hiding this comment.
If it comes from "11111" it is better to evaluate the value from the string.
|
Thank you @iSazonov. I've made the following changes: Changes:
It 'ValidateLength error message should be properly formatted' {
function Test-ValidateLengthMax { param([ValidateLength(0,2)] [string] $Value) $Value }
function Test-ValidateLengthMin { param([ValidateLength(5,10)] [string] $Value) $Value }
$TestStringTooLong = "11111"
$TestStringTooShort = "123"
$ExpectedMaxLength = 2
$ExpectedMinLength = 5
{ Test-ValidateLengthMax $TestStringTooLong } | Should -Throw -ErrorId "ParameterArgumentValidationError,Test-ValidateLengthMax"
$error[0].Exception.InnerException.Message | Should -Match ".+\($($TestStringTooLong.Length)\).+\`"$ExpectedMaxLength\`""
$error[0].Exception.Message | Should -Match 'Value'
{ Test-ValidateLengthMin $TestStringTooShort } | Should -Throw -ErrorId "ParameterArgumentValidationError,Test-ValidateLengthMin"
$error[0].Exception.InnerException.Message | Should -Match ".+\($($TestStringTooShort.Length)\).+\`"$ExpectedMinLength\`""
$error[0].Exception.Message | Should -Match 'Value'
} |
|
/azp run PowerShell-Windows-Packaging-CI, PowerShell-CI-linux-packaging |
|
Azure Pipelines could not run because the pipeline triggers exclude this branch/path. |
@iSazonov this command appears to have failed. Tagging for awareness. |
| It 'Exception: <FullyQualifiedErrorId>:<InnerErrorId>' -TestCases $testCases { | ||
| param($ScriptBlock, $FullyQualifiedErrorId, $InnerErrorId) | ||
|
|
||
| $ScriptBlock | Should -Throw -ErrorId $FullyQualifiedErrorId |
There was a problem hiding this comment.
Using $error[0] is not reliable. We can use:
$err = $ScriptBlock | Should -Throw -ErrorId $FullyQualifiedErrorId -PassThru| BeforeAll { | ||
| $testCases = @( | ||
| @{ | ||
| ScriptBlock = { function foo { param([ValidateLength(2, 5)] [string] $bar) }; foo "a" } |
There was a problem hiding this comment.
Please address the comment.
Please replace foo/bar with more informative names.
|
|
||
| { Test-ValidateLengthMax $TestStringTooLong } | Should -Throw -ErrorId "ParameterArgumentValidationError,Test-ValidateLengthMax" | ||
| $error[0].Exception.InnerException.Message | Should -Match ".+\($($TestStringTooLong.Length)\).+\`"$ExpectedMaxLength\`"" | ||
| $error[0].Exception.Message | Should -Match 'Value' |
There was a problem hiding this comment.
I wonder why we check this.
|
Thank you @iSazonov. I've made the following changes: Changes Made1. Replaced
|
|
/azp run PowerShell-Windows-Packaging-CI, PowerShell-CI-linux-packaging |
|
Azure Pipelines could not run because the pipeline triggers exclude this branch/path. |
|
@jorgeasaurus Please update the PR title and description to reflect the PR changes. |
|
@iSazonov PR title and description have been updated. |
|
📣 Hey @@jorgeasaurus, how did we do? We would love to hear your feedback with the link below! 🗣️ 🔗 https://aka.ms/PSRepoFeedback |
|
@jorgeasaurus Thanks for your contribution! |
PR Summary
This PR improves the consistency of ValidateLength error messages and significantly enhances the quality of validation attribute tests. The changes improve both user experience (clearer error messages) and code maintainability (more reliable, readable tests).
Key improvements include:
Error Message Consistency
ValidateLengthMaxLengthFailureresource string in Metadata.resx to match the format ofValidateLengthMinLengthFailure"The character length of the {1} argument is too long..."to"The character length ({1}) of the argument is too long..."Test Quality Improvements
foo) with descriptive PowerShell-compliant names (Test-ArrayCount,Test-NumericRange,Test-StringLength)bar) with meaningful names ($Items,$Number,$InputString)-PassThruwith captured error variables instead of relying on$error[0]PR Context
The ValidateLength error messages were inconsistent in format:
The updated format makes both messages consistent:
Additionally, the test refactoring addresses best practices by using
-PassThruto capture exceptions directly rather than reading from the global$errorarray, making tests more reliable and less prone to interference from other test runs.PR Checklist
Breaking changes
OR
User-facing changes
OR
Testing - New and feature
OR