diff --git a/CHANGELOG.md b/CHANGELOG.md index 491f224..3de75fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,6 +53,7 @@ To have a consistent experience across all packages, some public interfaces have - **request.result:** Fold `IsSuccess` into `Result.GetHashCode` to avoid collisions between a failure and a success value whose hash is `0` - **request.validation:** Guard null comparison bounds in `GreaterThan`/`LessThan`/`Between` etc. so a `null` bound no longer throws (`NullReferenceException`) - **request.validation:** Return no problems when validating a `null` instance of a reference type instead of dereferencing it (`NullReferenceException`) +- **request.validation:** Make `Severity` meaningful — `Validation.IsValid` now only fails on `Error` problems; `Warning`/`Info` no longer invalidate. Added `Validation.IsValidFor(Severity)` to set the failing-severity threshold (see PLAN.md Inconsistencies#B) ### Removed diff --git a/src/request.validation.tests/ValidationSeverityTests.cs b/src/request.validation.tests/ValidationSeverityTests.cs new file mode 100644 index 0000000..6f24c8d --- /dev/null +++ b/src/request.validation.tests/ValidationSeverityTests.cs @@ -0,0 +1,49 @@ +// Copyright (c) The Geekeey Authors +// SPDX-License-Identifier: EUPL-1.2 + +namespace Geekeey.Request.Validation.Tests; + +internal sealed class ValidationSeverityTests +{ + [Test] + public async Task I_can_have_warning_problems_not_invalidate_by_default() + { + var validator = new PropertyValidator(person + => person.Name, rule => rule.Must(value => !string.IsNullOrWhiteSpace(value), "Name is required.") + .WithSeverity(Severity.Warning)); + + var result = validator.Validate(new Person { Name = "" }); + + await Assert.That(result.Problems).Count().IsEqualTo(1); + await Assert.That(result.IsValid).IsTrue(); + await Assert.That(result.IsValidFor(Severity.Warning)).IsFalse(); + await Assert.That(result.IsValidFor(Severity.Info)).IsFalse(); + } + + [Test] + public async Task I_can_have_info_problems_only_invalidate_at_info_threshold() + { + var validator = new PropertyValidator(person + => person.Name, rule => rule.Must(value => !string.IsNullOrWhiteSpace(value), "Name is required.") + .WithSeverity(Severity.Info)); + + var result = validator.Validate(new Person { Name = "" }); + + await Assert.That(result.Problems).Count().IsEqualTo(1); + await Assert.That(result.IsValid).IsTrue(); + await Assert.That(result.IsValidFor(Severity.Warning)).IsTrue(); + await Assert.That(result.IsValidFor(Severity.Info)).IsFalse(); + } + + [Test] + public async Task I_can_have_error_problems_invalidate_by_default() + { + var validator = new PropertyValidator(person + => person.Name, rule => rule.Must(value => !string.IsNullOrWhiteSpace(value), "Name is required.")); + + var result = validator.Validate(new Person { Name = "" }); + + await Assert.That(result.IsValid).IsFalse(); + await Assert.That(result.IsValidFor(Severity.Error)).IsFalse(); + } +} diff --git a/src/request.validation.tests/ValidatorTests.cs b/src/request.validation.tests/ValidatorTests.cs index 6613720..820edc4 100644 --- a/src/request.validation.tests/ValidatorTests.cs +++ b/src/request.validation.tests/ValidatorTests.cs @@ -14,7 +14,8 @@ internal sealed class ValidatorTests var result = validator.Validate(new Person { Name = "" }); - await Assert.That(result.IsValid).IsFalse(); + await Assert.That(result.IsValid).IsTrue(); + await Assert.That(result.IsValidFor(Severity.Warning)).IsFalse(); await Assert.That(result.Problems).Count().IsEqualTo(1); var problem = result.Problems.Single(); @@ -39,7 +40,8 @@ internal sealed class ValidatorTests using (Assert.Multiple()) { - await Assert.That(invalid.IsValid).IsFalse(); + await Assert.That(invalid.IsValid).IsTrue(); + await Assert.That(invalid.IsValidFor(Severity.Warning)).IsFalse(); await Assert.That(valid.IsValid).IsTrue(); await Assert.That(valid.Problems).IsEmpty(); } diff --git a/src/request.validation/Validation.cs b/src/request.validation/Validation.cs index fadadff..8e9c7c7 100644 --- a/src/request.validation/Validation.cs +++ b/src/request.validation/Validation.cs @@ -23,7 +23,27 @@ public sealed class Validation /// /// Whether the validation was successful. /// - public bool IsValid => Problems.Count is 0; + /// + /// A validation is considered successful unless it contains at least one problem with + /// . Problems of lower severity ( or + /// ) do not invalidate the result. Use + /// to control which severities are treated as failures. + /// + public bool IsValid => IsValidFor(Severity.Error); + + /// + /// Whether the validation was successful, treating problems at or above the given severity as failures. + /// + /// The minimum severity that should be considered a failure. + /// + /// A problem invalidates the validation when its is at least as severe as + /// . For example, IsValidFor(Severity.Warning) fails on errors and + /// warnings but not on informational problems. + /// + public bool IsValidFor(Severity minimum) + { + return !Problems.Any(problem => problem.Severity <= minimum); + } /// /// The problems that were found during validation.