Skip to content

Comments

7.x#1

Open
jarlungoodoo73 wants to merge 3 commits intojarlungoodoo73:mainfrom
FluentValidation:7.x
Open

7.x#1
jarlungoodoo73 wants to merge 3 commits intojarlungoodoo73:mainfrom
FluentValidation:7.x

Conversation

@jarlungoodoo73
Copy link
Owner

@jarlungoodoo73 jarlungoodoo73 commented Dec 15, 2025

Create a new pull request by comparing changes across two branches. If you need to, you can also

Summary by Sourcery

Add Traditional Chinese localization support, refine validation test failure matching behavior, and bump the build version.

New Features:

  • Introduce a Traditional Chinese (zh-TW) language resource for validation messages.
  • Register the Traditional Chinese language in the default language manager so it can be selected at runtime.

Bug Fixes:

  • Ensure validator test helper failure matching only requires at least one matching failure instead of validating all failures, allowing independent checks for multiple error codes.

Build:

  • Increment the build script default version to 7.6.105.

Tests:

  • Add a regression test verifying that multiple validation failures can be asserted independently using the test helper.

@jarlungoodoo73 jarlungoodoo73 added bug Something isn't working documentation Improvements or additions to documentation labels Dec 15, 2025
@sourcery-ai
Copy link

sourcery-ai bot commented Dec 15, 2025

Reviewer's Guide

Updates test helper validation error filtering to only require at least one matching failure, adds a regression test for multiple error matches, introduces Traditional Chinese (zh-TW) localization support, bumps the build script version, and wires the new language into the language manager.

Class diagram for new ChineseTraditionalLanguage and LanguageManager update

classDiagram

class Language {
}

class ChineseTraditionalLanguage {
  +string Name
  +ChineseTraditionalLanguage()
}

class LanguageManager {
  +LanguageManager()
  -Language[] languages
}

ChineseTraditionalLanguage --|> Language
LanguageManager o--> Language
Loading

File-Level Changes

Change Details Files
Change ValidationFailure.When extension to only assert that at least one failure matches the predicate instead of requiring all failures to match.
  • Compute whether any validation failure matches the provided predicate using LINQ Any.
  • If no failures match, build an error message (optionally using the provided template) from the first failure and throw ValidationTestException.
  • Return the original failures collection unchanged when at least one failure matches.
src/FluentValidation/TestHelper/ValidatorTestExtensions.cs
Add a regression test ensuring multiple failures can be asserted independently using ShouldHaveError.
  • Define a validator with two distinct rules on the Surname property, each with different expected error codes.
  • Validate a Person whose Surname violates both rules and assert each error separately via ShouldHaveError().WithErrorCode(...).
src/FluentValidation.Tests/ValidatorTesterTester.cs
Introduce Traditional Chinese (zh-TW) language support and register it with the language manager.
  • Add a ChineseTraditionalLanguage implementation providing translations for core validators with zh-TW messages.
  • Register the new ChineseTraditionalLanguage in the LanguageManager’s language list so it becomes available at runtime.
src/FluentValidation/Resources/Languages/ChineseTraditionalLanguage.cs
src/FluentValidation/Resources/LanguageManager.cs
Bump build script version number.
  • Update default version argument in build.ps1 from 7.6.104 to 7.6.105.
build.ps1

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey there - I've reviewed your changes - here's some feedback:

  • The new implementation of When now calls Any and then First, which will enumerate the IEnumerable<ValidationFailure> multiple times; consider materializing to a list or using a single pass to avoid unexpected behavior with non-replayable enumerables.
  • The updated When logic assumes there is at least one failure (using First()); previously an empty failures collection would simply return without throwing, so you may want to either handle the empty case explicitly or confirm this behavior change is intentional.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The new implementation of `When` now calls `Any` and then `First`, which will enumerate the `IEnumerable<ValidationFailure>` multiple times; consider materializing to a list or using a single pass to avoid unexpected behavior with non-replayable enumerables.
- The updated `When` logic assumes there is at least one failure (using `First()`); previously an empty `failures` collection would simply return without throwing, so you may want to either handle the empty case explicitly or confirm this behavior change is intentional.

## Individual Comments

### Comment 1
<location> `src/FluentValidation/TestHelper/ValidatorTestExtensions.cs:124-127` </location>
<code_context>
-			foreach (var failure in failures) {
-				if (!failurePredicate(failure)) {
-					string message = "Expected validation error was not found";
+			bool anyMatched = failures.Any(failurePredicate);

-					if (exceptionMessage != null) {
-						message = exceptionMessage.Replace("{Code}", failure.ErrorCode)
-							.Replace("{Message}", failure.ErrorMessage)
-							.Replace("{State}", failure.CustomState?.ToString() ?? "");
-					}
+			if (!anyMatched) {
+				var failure = failures.First();
+				
+				string message = "Expected validation error was not found";
</code_context>

<issue_to_address>
**issue (bug_risk):** Handling empty failure sequences will throw an unexpected InvalidOperationException instead of the intended ValidationTestException.

When `failures` is empty, `Any` returns `false`, so the `if (!anyMatched)` branch executes, and `failures.First()` throws `InvalidOperationException` before the intended `ValidationTestException`. Previously this method returned quietly in the no-failures case (with earlier helpers asserting on failure presence). To preserve the expected behavior and exception type, add an explicit empty-sequence guard (e.g., use `FirstOrDefault()` and handle `null`, or materialize to a list and check `Count == 0` before accessing the first element).
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +124 to +127
bool anyMatched = failures.Any(failurePredicate);

if (exceptionMessage != null) {
message = exceptionMessage.Replace("{Code}", failure.ErrorCode)
.Replace("{Message}", failure.ErrorMessage)
.Replace("{State}", failure.CustomState?.ToString() ?? "");
}
if (!anyMatched) {
var failure = failures.First();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Handling empty failure sequences will throw an unexpected InvalidOperationException instead of the intended ValidationTestException.

When failures is empty, Any returns false, so the if (!anyMatched) branch executes, and failures.First() throws InvalidOperationException before the intended ValidationTestException. Previously this method returned quietly in the no-failures case (with earlier helpers asserting on failure presence). To preserve the expected behavior and exception type, add an explicit empty-sequence guard (e.g., use FirstOrDefault() and handle null, or materialize to a list and check Count == 0 before accessing the first element).

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR includes a bug fix for the validation test helper extension method When() and adds Chinese Traditional language translation support. The changes prepare version 7.6.105 for release.

  • Fixes issue FluentValidation#858 where ValidationTestExtension.When() incorrectly checked each failure individually instead of checking if any failure matches the predicate
  • Adds complete Chinese Traditional (zh-TW) language translation support
  • Updates version number from 7.6.104 to 7.6.105

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/FluentValidation/TestHelper/ValidatorTestExtensions.cs Refactored When() method to use Any() for checking if any failure matches the predicate, rather than iterating through each failure individually
src/FluentValidation/Resources/Languages/ChineseTraditionalLanguage.cs Added new language file with Traditional Chinese translations for all validator error messages
src/FluentValidation/Resources/LanguageManager.cs Registered the new ChineseTraditionalLanguage in the language manager's constructor
src/FluentValidation.Tests/ValidatorTesterTester.cs Added test case to verify that multiple validation errors can be independently matched using the fluent API
build.ps1 Bumped version number from 7.6.104 to 7.6.105
Changelog.txt Added release date and documented the new features and bug fix

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


var person = new Person() { Surname = "c" };
var result = validator.TestValidate(person);

Copy link

Copilot AI Dec 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent whitespace: there are multiple spaces before the statement end. This should be a single space or no space before the semicolon for consistency with the rest of the codebase.

Suggested change

Copilot uses AI. Check for mistakes.
Comment on lines +375 to +381
var validator = new InlineValidator<Person> {
v => v.RuleFor(x => x.Surname).Equal("a").WithErrorCode("nota"),
v => v.RuleFor(x => x.Surname).Equal("b").WithErrorCode("notb")
};

var person = new Person() { Surname = "c" };
var result = validator.TestValidate(person);
Copy link

Copilot AI Dec 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Local scope variable 'validator' shadows ValidatorTesterTester.validator.

Suggested change
var validator = new InlineValidator<Person> {
v => v.RuleFor(x => x.Surname).Equal("a").WithErrorCode("nota"),
v => v.RuleFor(x => x.Surname).Equal("b").WithErrorCode("notb")
};
var person = new Person() { Surname = "c" };
var result = validator.TestValidate(person);
var inlineValidator = new InlineValidator<Person> {
v => v.RuleFor(x => x.Surname).Equal("a").WithErrorCode("nota"),
v => v.RuleFor(x => x.Surname).Equal("b").WithErrorCode("notb")
};
var person = new Person() { Surname = "c" };
var result = inlineValidator.TestValidate(person);

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working documentation Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants