diff --git a/src/Core/Resources/ResponseMessages.Designer.cs b/src/Core/Resources/ResponseMessages.Designer.cs index e300dc1..b8d79e4 100644 --- a/src/Core/Resources/ResponseMessages.Designer.cs +++ b/src/Core/Resources/ResponseMessages.Designer.cs @@ -168,6 +168,15 @@ internal static string ObtainedResources { } } + /// + /// Looks up a localized string similar to '{0}' property failed validation. Error was: {1}. + /// + internal static string PropertyFailedValidation { + get { + return ResourceManager.GetString("PropertyFailedValidation", resourceCulture); + } + } + /// /// Looks up a localized string similar to Operation successfully executed. /// diff --git a/src/Core/Resources/ResponseMessages.es.resx b/src/Core/Resources/ResponseMessages.es.resx index 362c513..1b1cf83 100644 --- a/src/Core/Resources/ResponseMessages.es.resx +++ b/src/Core/Resources/ResponseMessages.es.resx @@ -153,6 +153,9 @@ Recursos obtenidos con éxito + + Propiedad '{0}' falló en la validación. El error fue: {1} + Operación ejecutada con éxito diff --git a/src/Core/Resources/ResponseMessages.resx b/src/Core/Resources/ResponseMessages.resx index c02d1dd..1eeb07a 100644 --- a/src/Core/Resources/ResponseMessages.resx +++ b/src/Core/Resources/ResponseMessages.resx @@ -153,6 +153,9 @@ Resources successfully obtained + + '{0}' property failed validation. Error was: {1} + Operation successfully executed diff --git a/src/Core/SimpleResults.csproj b/src/Core/SimpleResults.csproj index b8acb05..8b99d11 100644 --- a/src/Core/SimpleResults.csproj +++ b/src/Core/SimpleResults.csproj @@ -40,6 +40,7 @@ + diff --git a/src/FluentValidation/SRFluentValidationResultExtensions.cs b/src/FluentValidation/SRFluentValidationResultExtensions.cs index 45f6f26..e811b35 100644 --- a/src/FluentValidation/SRFluentValidationResultExtensions.cs +++ b/src/FluentValidation/SRFluentValidationResultExtensions.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using FluentValidation.Results; +using SimpleResults.Resources; namespace SimpleResults; @@ -15,7 +16,7 @@ public static class SRFluentValidationResultExtensions /// /// true if the validation result is failed; otherwise false. /// - public static bool IsFailed(this ValidationResult result) + public static bool IsFailed(this ValidationResult result) => !result.IsValid; /// @@ -24,7 +25,13 @@ public static bool IsFailed(this ValidationResult result) /// The result of running a validator. /// A collection that contains error messages. public static IEnumerable AsErrors(this ValidationResult result) - => result.Errors.Select(failure => failure.ErrorMessage); + => result.Errors.Select(GetErrorMessage); + + private static string GetErrorMessage(ValidationFailure failure) + => string.Format( + ResponseMessages.PropertyFailedValidation, + failure.PropertyName, + failure.ErrorMessage); /// /// Represents a validation error that prevents the underlying service from completing. diff --git a/tests/FluentValidation/FailedValidationTestCases.cs b/tests/FluentValidation/FailedValidationTestCases.cs new file mode 100644 index 0000000..3f54a07 --- /dev/null +++ b/tests/FluentValidation/FailedValidationTestCases.cs @@ -0,0 +1,69 @@ +namespace SimpleResults.Tests.FluentValidation; + +public class FailedValidationTestCases : IEnumerable +{ + public IEnumerator GetEnumerator() + { + yield return new object[] + { + new Order + { + Customer = string.Empty, + Description = string.Empty, + DeliveryAddress = default, + Details = default + }, + new[] + { + "'Customer' property failed validation. Error was: 'Customer' must not be empty.", + "'Description' property failed validation. Error was: 'Description' must not be empty.", + "'DeliveryAddress' property failed validation. Error was: 'Delivery Address' must not be empty.", + "'Details' property failed validation. Error was: 'Details' must not be empty." + } + }; + + yield return new object[] + { + new Order + { + Customer = string.Empty, + Description = string.Empty, + DeliveryAddress = new Address + { + Description = string.Empty, + Postcode = string.Empty, + Country = string.Empty + }, + Details = new List + { + new() + { + Product = string.Empty, + Price = 0, + Amount = 0 + }, + new() + { + Product = string.Empty, + Price = -1, + Amount = -1 + } + } + }, + new[] + { + "'Customer' property failed validation. Error was: 'Customer' must not be empty.", + "'Description' property failed validation. Error was: 'Description' must not be empty.", + "'DeliveryAddress.Description' property failed validation. Error was: 'Description' must not be empty.", + "'DeliveryAddress.Postcode' property failed validation. Error was: 'Postcode' must not be empty.", + "'DeliveryAddress.Country' property failed validation. Error was: 'Country' must not be empty.", + "'Details[0].Product' property failed validation. Error was: 'Product' must not be empty.", + "'Details[0].Price' property failed validation. Error was: 'Price' must be greater than '0'.", + "'Details[0].Amount' property failed validation. Error was: 'Amount' must be greater than '0'.", + "'Details[1].Product' property failed validation. Error was: 'Product' must not be empty.", + "'Details[1].Price' property failed validation. Error was: 'Price' must be greater than '0'.", + "'Details[1].Amount' property failed validation. Error was: 'Amount' must be greater than '0'." + } + }; + } +} diff --git a/tests/FluentValidation/ValidationResultExtensionsTests.cs b/tests/FluentValidation/ValidationResultExtensionsTests.cs index 47ba214..cd7f577 100644 --- a/tests/FluentValidation/ValidationResultExtensionsTests.cs +++ b/tests/FluentValidation/ValidationResultExtensionsTests.cs @@ -32,32 +32,43 @@ public void IsFailed_WhenValidationResultIsSuccess_ShouldReturnsFalse() actual.Should().BeFalse(); } - [Test] - public void AsErrors_WhenValidationResultIsReceived_ShouldReturnsCollectionOfErrorMessages() + [TestCaseSource(typeof(FailedValidationTestCases))] + public void AsErrors_WhenValidationResultIsReceived_ShouldReturnsCollectionOfErrorMessages( + Order order, + string[] expectedErrors) { // Arrange - var person = new Person { Name = string.Empty }; - var validator = new PersonValidator(); - ValidationResult result = validator.Validate(person); - var expectedCollection = new[] - { - "'Name' must not be empty." - }; + var validator = new OrderValidator(); + ValidationResult result = validator.Validate(order); // Act IEnumerable actual = result.AsErrors(); // Assert - actual.Should().BeEquivalentTo(expectedCollection); + actual.Should().BeEquivalentTo(expectedErrors); } [Test] public void AsErrors_WhenThereAreNoErrors_ShouldReturnsEmptyCollection() { // Arrange - var person = new Person { Name = "Alice" }; - var validator = new PersonValidator(); - ValidationResult result = validator.Validate(person); + var order = new Order + { + Customer = "Bob", + Description = "Test", + DeliveryAddress = new Address { Description = "D", Postcode = "P", Country = "C" }, + Details = new List + { + new() + { + Product = "P", + Price = 5000, + Amount = 2 + } + } + }; + var validator = new OrderValidator(); + ValidationResult result = validator.Validate(order); // Act IEnumerable actual = result.AsErrors(); @@ -76,7 +87,7 @@ public void Invalid_WhenResultIsInvalidWithoutMessage_ShouldReturnsResultObject( var expectedMessage = ResponseMessages.ValidationErrors; var expectedErrors = new[] { - "'Name' must not be empty." + "'Name' property failed validation. Error was: 'Name' must not be empty." }; // Act @@ -100,7 +111,7 @@ public void Invalid_WhenResultIsInvalidWithMessage_ShouldReturnsResultObject() var expectedMessage = "Error"; var expectedErrors = new[] { - "'Name' must not be empty." + "'Name' property failed validation. Error was: 'Name' must not be empty." }; // Act diff --git a/tests/FluentValidation/Validators/DeliveryAddressValidator.cs b/tests/FluentValidation/Validators/DeliveryAddressValidator.cs new file mode 100644 index 0000000..db4940a --- /dev/null +++ b/tests/FluentValidation/Validators/DeliveryAddressValidator.cs @@ -0,0 +1,18 @@ +namespace SimpleResults.Tests.FluentValidation.Validators; + +public class Address +{ + public string Description { get; init; } + public string Country { get; init; } + public string Postcode { get; init; } +} + +public class DeliveryAddressValidator : AbstractValidator
+{ + public DeliveryAddressValidator() + { + RuleFor(d => d.Description).NotEmpty(); + RuleFor(d => d.Country).NotEmpty(); + RuleFor(d => d.Postcode).NotEmpty(); + } +} diff --git a/tests/FluentValidation/Validators/OrderDetailValidator.cs b/tests/FluentValidation/Validators/OrderDetailValidator.cs new file mode 100644 index 0000000..2f0b7b4 --- /dev/null +++ b/tests/FluentValidation/Validators/OrderDetailValidator.cs @@ -0,0 +1,18 @@ +namespace SimpleResults.Tests.FluentValidation.Validators; + +public class OrderDetail +{ + public string Product { get; init; } + public int Amount { get; init; } + public double Price { get; init; } +} + +public class OrderDetailValidator : AbstractValidator +{ + public OrderDetailValidator() + { + RuleFor(o => o.Product).NotEmpty(); + RuleFor(o => o.Amount).GreaterThan(0); + RuleFor(o => o.Price).GreaterThan(0); + } +} diff --git a/tests/FluentValidation/Validators/OrderValidator.cs b/tests/FluentValidation/Validators/OrderValidator.cs new file mode 100644 index 0000000..6ccb9ee --- /dev/null +++ b/tests/FluentValidation/Validators/OrderValidator.cs @@ -0,0 +1,23 @@ +namespace SimpleResults.Tests.FluentValidation.Validators; + +public class Order +{ + public string Customer { get; init; } + public string Description { get; init; } + public Address DeliveryAddress { get; init; } + public IEnumerable Details { get; init; } +} + +public class OrderValidator : AbstractValidator +{ + public OrderValidator() + { + RuleFor(o => o.Customer).NotEmpty(); + RuleFor(o => o.Description).NotEmpty(); + RuleFor(o => o.DeliveryAddress) + .NotEmpty() + .SetValidator(new DeliveryAddressValidator()); + RuleFor(o => o.Details).NotEmpty(); + RuleForEach(o => o.Details).SetValidator(new OrderDetailValidator()); + } +} diff --git a/tests/FluentValidation/PersonValidator.cs b/tests/FluentValidation/Validators/PersonValidator.cs similarity index 78% rename from tests/FluentValidation/PersonValidator.cs rename to tests/FluentValidation/Validators/PersonValidator.cs index 0f05dbb..de80230 100644 --- a/tests/FluentValidation/PersonValidator.cs +++ b/tests/FluentValidation/Validators/PersonValidator.cs @@ -1,4 +1,4 @@ -namespace SimpleResults.Tests.FluentValidation; +namespace SimpleResults.Tests.FluentValidation.Validators; public class PersonValidator : AbstractValidator { diff --git a/tests/GlobalUsings.cs b/tests/GlobalUsings.cs index b080ef2..14b6883 100644 --- a/tests/GlobalUsings.cs +++ b/tests/GlobalUsings.cs @@ -1,5 +1,6 @@ global using System.Globalization; global using System.Text.Json; +global using System.Collections; global using NUnit.Framework; global using FluentAssertions; global using FluentValidation; @@ -8,4 +9,5 @@ global using Microsoft.AspNetCore.Http; global using Microsoft.AspNetCore.Http.HttpResults; global using Microsoft.AspNetCore.Mvc.Filters; -global using SimpleResults.Resources; \ No newline at end of file +global using SimpleResults.Resources; +global using SimpleResults.Tests.FluentValidation.Validators; \ No newline at end of file diff --git a/tests/Utils/TestFixtureProjectSetup.cs b/tests/Utils/TestFixtureProjectSetup.cs index 80dad29..f0057c8 100644 --- a/tests/Utils/TestFixtureProjectSetup.cs +++ b/tests/Utils/TestFixtureProjectSetup.cs @@ -7,6 +7,6 @@ public class TestFixtureProjectSetup public void RunBeforeAllTestFixtures() { // Allows to load the default resource in English. - Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("en-US"); + Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("en"); } }