From 3c43b40a66faaa4e21156474b9ac0d8315097259 Mon Sep 17 00:00:00 2001 From: Stuart Turner Date: Thu, 30 May 2024 10:31:58 -0500 Subject: [PATCH] Add `Length` validator (#44) --- .../Validators/LengthAttribute.cs | 35 +++++++++++ .../Validators/MatchAttribute.cs | 9 --- .../Validators/MaxLengthAttribute.cs | 4 +- .../Validators/MinLengthAttribute.cs | 4 +- .../Validators/LengthTests.cs | 62 +++++++++++++++++++ 5 files changed, 101 insertions(+), 13 deletions(-) create mode 100644 src/Immediate.Validations.Shared/Validators/LengthAttribute.cs create mode 100644 tests/Immediate.Validations.FunctionalTests/Validators/LengthTests.cs diff --git a/src/Immediate.Validations.Shared/Validators/LengthAttribute.cs b/src/Immediate.Validations.Shared/Validators/LengthAttribute.cs new file mode 100644 index 0000000..db099b1 --- /dev/null +++ b/src/Immediate.Validations.Shared/Validators/LengthAttribute.cs @@ -0,0 +1,35 @@ +using System.Diagnostics.CodeAnalysis; + +namespace Immediate.Validations.Shared; + +/// +/// Applied to a property to indicate that the value should have a length of exactly . +/// +/// +/// The maximum length of the . +/// +public sealed class LengthAttribute( + [TargetType] + object length +) : ValidatorAttribute +{ + /// + /// Validates that the has a length of exactly . + /// + /// + /// The value to validate. + /// + /// + /// The valid length for the string . + /// + /// + /// A indicating whether the property is valid or not, along with an error + /// message if the property is not valid. + /// + [SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "Will already by validated by IV first.")] + public static (bool Invalid, string? Message) ValidateProperty(string target, int length) => + target.Length == length + ? default + : (true, $"String is of length '{target.Length}', which is {(target.Length < length ? "shorter" : "longer")} than the allowed length of '{length}'."); +} diff --git a/src/Immediate.Validations.Shared/Validators/MatchAttribute.cs b/src/Immediate.Validations.Shared/Validators/MatchAttribute.cs index 8a56e50..1f6c6ab 100644 --- a/src/Immediate.Validations.Shared/Validators/MatchAttribute.cs +++ b/src/Immediate.Validations.Shared/Validators/MatchAttribute.cs @@ -20,15 +20,6 @@ public sealed class MatchAttribute( object? regex = null ) : ValidatorAttribute { - /// - /// - /// - public object? Expr { get; } = expr; - /// - /// - /// - public object? Regex { get; } = regex; - /// /// Validates that the matches a particular pattern. /// diff --git a/src/Immediate.Validations.Shared/Validators/MaxLengthAttribute.cs b/src/Immediate.Validations.Shared/Validators/MaxLengthAttribute.cs index eaf87bc..fdb07b7 100644 --- a/src/Immediate.Validations.Shared/Validators/MaxLengthAttribute.cs +++ b/src/Immediate.Validations.Shared/Validators/MaxLengthAttribute.cs @@ -15,13 +15,13 @@ object length ) : ValidatorAttribute { /// - /// Validates that the is less than . + /// Validates that the value should have a length at most . /// /// /// The value to validate. /// /// - /// The maximum valid length for the string . + /// The maximum valid length for the string . /// /// /// A indicating whether the property is valid or not, along with an error diff --git a/src/Immediate.Validations.Shared/Validators/MinLengthAttribute.cs b/src/Immediate.Validations.Shared/Validators/MinLengthAttribute.cs index 087798e..13be4e2 100644 --- a/src/Immediate.Validations.Shared/Validators/MinLengthAttribute.cs +++ b/src/Immediate.Validations.Shared/Validators/MinLengthAttribute.cs @@ -16,13 +16,13 @@ object length { /// - /// Validates that the is less than . + /// Validates that the value should have a length at least . /// /// /// The value to validate. /// /// - /// The minimum valid length for the string . + /// The minimum valid length for the string . /// /// /// A indicating whether the property is valid or not, along with an error diff --git a/tests/Immediate.Validations.FunctionalTests/Validators/LengthTests.cs b/tests/Immediate.Validations.FunctionalTests/Validators/LengthTests.cs new file mode 100644 index 0000000..91f25b1 --- /dev/null +++ b/tests/Immediate.Validations.FunctionalTests/Validators/LengthTests.cs @@ -0,0 +1,62 @@ +using Immediate.Validations.Shared; +using Xunit; + +namespace Immediate.Validations.FunctionalTests.Validators; + +public sealed partial class LengthTests +{ + [Validate] + public partial record StringRecord : IValidationTarget + { + [Length(12)] + public required string StringValue { get; init; } + } + + [Fact] + public void LengthWhenShort() + { + var instance = new StringRecord { StringValue = "Hello" }; + + var errors = StringRecord.Validate(instance); + + Assert.Equal( + [ + new() + { + PropertyName = nameof(StringRecord.StringValue), + ErrorMessage = "String is of length '5', which is shorter than the allowed length of '12'.", + } + ], + errors + ); + } + + [Fact] + public void LengthWhenEqual() + { + var instance = new StringRecord { StringValue = "Hello World!" }; + + var errors = StringRecord.Validate(instance); + + Assert.Empty(errors); + } + + [Fact] + public void LengthWhenLong() + { + var instance = new StringRecord { StringValue = "Hello World! Hello World! Hello World!" }; + + var errors = StringRecord.Validate(instance); + + Assert.Equal( + [ + new() + { + PropertyName = nameof(StringRecord.StringValue), + ErrorMessage = "String is of length '38', which is longer than the allowed length of '12'.", + } + ], + errors + ); + } +}