diff --git a/src/HomeInventory/HomeInventory.Tests.Framework/GivenContext.cs b/src/HomeInventory/HomeInventory.Tests.Framework/GivenContext.cs index cda09d7c..e292da41 100644 --- a/src/HomeInventory/HomeInventory.Tests.Framework/GivenContext.cs +++ b/src/HomeInventory/HomeInventory.Tests.Framework/GivenContext.cs @@ -23,6 +23,24 @@ public TContext New(out IVariable variable, Func create, int count = 1, where T : notnull => New(out variable, _ => create(), count, name); + public TContext New(out IVariable variable, IVariable arg, Func create, int count = 1, [CallerArgumentExpression(nameof(variable))] string? name = null) + where T : notnull + where TArg : notnull => + New(out variable, _ => create(GetValue(arg)), count, name); + + public TContext New(out IVariable variable, IVariable arg1, IVariable arg2, Func create, int count = 1, [CallerArgumentExpression(nameof(variable))] string? name = null) + where T : notnull + where TArg1 : notnull + where TArg2 : notnull => + New(out variable, _ => create(GetValue(arg1), GetValue(arg2)), count, name); + + public TContext New(out IVariable variable, IVariable arg1, IVariable arg2, IVariable arg3, Func create, int count = 1, [CallerArgumentExpression(nameof(variable))] string? name = null) + where T : notnull + where TArg1 : notnull + where TArg2 : notnull + where TArg3 : notnull => + New(out variable, _ => create(GetValue(arg1), GetValue(arg2), GetValue(arg3)), count, name); + public TContext EmptyHashCode(out IVariable emptyHash) => New(out emptyHash, static () => new()); diff --git a/src/HomeInventory/HomeInventory.Tests.Framework/ThenContext.cs b/src/HomeInventory/HomeInventory.Tests.Framework/ThenContext.cs index 75fea3b0..e47ef232 100644 --- a/src/HomeInventory/HomeInventory.Tests.Framework/ThenContext.cs +++ b/src/HomeInventory/HomeInventory.Tests.Framework/ThenContext.cs @@ -2,7 +2,7 @@ namespace HomeInventory.Tests.Framework; -public class ThenContext(VariablesContainer variables, IVariable resultVariable) : BaseContext(variables) +public class ThenContext(VariablesContainer variables, IVariable resultVariable) : ThenContext(variables) where TResult : notnull { private readonly IVariable _resultVariable = resultVariable; diff --git a/src/HomeInventory/HomeInventory.Tests.Framework/WhenContext.cs b/src/HomeInventory/HomeInventory.Tests.Framework/WhenContext.cs index fdabc02c..755fb179 100644 --- a/src/HomeInventory/HomeInventory.Tests.Framework/WhenContext.cs +++ b/src/HomeInventory/HomeInventory.Tests.Framework/WhenContext.cs @@ -70,6 +70,20 @@ public ThenContext Invoked(IIndexedVariable where TResult : notnull => Invoked(sut, sutValue => invoke(sutValue, Variables.Get(arg).Value)); + internal ThenContext Invoked(IVariable sut, IVariable arg1, IVariable arg2, Func invoke) + where TSut : notnull + where TArg1 : notnull + where TArg2 : notnull + where TResult : notnull => + Invoked(sut[0], arg1[0], arg2[0], invoke); + + public ThenContext Invoked(IIndexedVariable sut, IIndexedVariable arg1, IIndexedVariable arg2, Func invoke) + where TSut : notnull + where TArg1 : notnull + where TArg2 : notnull + where TResult : notnull => + Invoked(sut, sutValue => invoke(sutValue, Variables.Get(arg1).Value, Variables.Get(arg2).Value)); + public ThenContext Invoked(IIndexedVariable sut, Func invoke) where TSut : notnull where TResult : notnull diff --git a/src/HomeInventory/HomeInventory.Tests/Presentation/Web/FluentOptionsValidatorTests.cs b/src/HomeInventory/HomeInventory.Tests/Presentation/Web/FluentOptionsValidatorTests.cs new file mode 100644 index 00000000..e3bbaacd --- /dev/null +++ b/src/HomeInventory/HomeInventory.Tests/Presentation/Web/FluentOptionsValidatorTests.cs @@ -0,0 +1,58 @@ +using FluentValidation; +using HomeInventory.Web.Framework; +using Microsoft.Extensions.Options; + +namespace HomeInventory.Tests.Presentation.Web; + +[UnitTest] +public sealed class FluentOptionsValidatorTests() : BaseTest(static t => new(t)) +{ + [Fact] + public void Create_Should_ReturnValidator() + { + Given + .New(out var nameVar) + .SubstituteFor(out var validatorVar); + + var then = When + .Invoked(nameVar, validatorVar, static (name, validator) => FluentOptionsValidator.Create(name, validator)); + + then.Result(actual => actual.Should().BeAssignableTo>()); + } + + [Fact] + public void Validate_Should_Skip_When_NameIsDifferent() + { + Given + .New(out var nameVar) + .New(out var differentNameVar) + .New(out var optionsVar, () => new()) + .SubstituteFor(out var validatorVar) + .New(out var sutVar, nameVar, validatorVar, static (name, validator) => FluentOptionsValidator.Create(name, validator)); + + var then = When + .Invoked(sutVar, differentNameVar, optionsVar, (sut, name, options) => sut.Validate(name, options)); + + then.Result(actual => actual.Should().BeSameAs(ValidateOptionsResult.Skip)); + } + + [Fact] + public void Validate_Should_CallValidator() + { + Given + .New(out var nameVar) + .New(out var optionsVar, () => new()) + .New(out var resultVar, () => new()) + .SubstituteFor(out var validationContextVar) + .SubstituteFor(out IVariable> factoryVar, optionsVar, validationContextVar, static (f, o, ctx) => f.CreateContext(o).Returns(ctx)) + .SubstituteFor(out IVariable validatorVar, validationContextVar, resultVar, static (v, ctx, r) => v.Validate(ctx).Returns(r)) + .New(out var sutVar, nameVar, validatorVar, factoryVar, static (name, validator, factory) => FluentOptionsValidator.Create(name, validator, factory)); + + var then = When + .Invoked(sutVar, nameVar, optionsVar, (sut, name, options) => sut.Validate(name, options)); + + then + .Result(actual => actual.Should().BeSameAs(ValidateOptionsResult.Success)) + .Ensure(validatorVar, validationContextVar, static (validator, context) => validator.Received(1).Validate(context)); + } +} diff --git a/src/HomeInventory/HomeInventory.Tests/Presentation/Web/FluentOptionsValidatorTestsGivenContext.cs b/src/HomeInventory/HomeInventory.Tests/Presentation/Web/FluentOptionsValidatorTestsGivenContext.cs new file mode 100644 index 00000000..7bcda7aa --- /dev/null +++ b/src/HomeInventory/HomeInventory.Tests/Presentation/Web/FluentOptionsValidatorTestsGivenContext.cs @@ -0,0 +1,5 @@ +namespace HomeInventory.Tests.Presentation.Web; + +public sealed class FluentOptionsValidatorTestsGivenContext(BaseTest test) : GivenContext(test) +{ +} diff --git a/src/HomeInventory/HomeInventory.Tests/Presentation/Web/SubjectOptions.cs b/src/HomeInventory/HomeInventory.Tests/Presentation/Web/SubjectOptions.cs new file mode 100644 index 00000000..1494375b --- /dev/null +++ b/src/HomeInventory/HomeInventory.Tests/Presentation/Web/SubjectOptions.cs @@ -0,0 +1,8 @@ +using HomeInventory.Web.Framework; + +namespace HomeInventory.Tests.Presentation.Web; + +public sealed class SubjectOptions : IOptions +{ + public static SectionPath Section => nameof(Section); +} diff --git a/src/HomeInventory/HomeInventory.Web.Framework/FluentOptionsValidator.cs b/src/HomeInventory/HomeInventory.Web.Framework/FluentOptionsValidator.cs index e13e065c..fbceb90d 100644 --- a/src/HomeInventory/HomeInventory.Web.Framework/FluentOptionsValidator.cs +++ b/src/HomeInventory/HomeInventory.Web.Framework/FluentOptionsValidator.cs @@ -8,11 +8,12 @@ namespace HomeInventory.Web.Framework; internal static class FluentOptionsValidator { public static IValidateOptions Create(string name, IValidator validator, Action>? validationOptions = null) - where TOptions : class - { - var factory = new ValidationContextFactory(validationOptions); - return new FluentOptionsValidator(name, validator, factory); - } + where TOptions : class => + Create(name, validator, new ValidationContextFactory(validationOptions)); + + public static IValidateOptions Create(string name, IValidator validator, IValidationContextFactory factory) + where TOptions : class => + new FluentOptionsValidator(name, validator, factory); } internal sealed class FluentOptionsValidator(string name, IValidator validator, IValidationContextFactory validationContextFactory) : IValidateOptions @@ -35,11 +36,7 @@ public ValidateOptionsResult Validate(string? name, TOptions options) return ToValidateOptionsResult(result); } - private bool ShouldSkip(string? name) - { - var validateAll = name is null; - return !validateAll && name != _name; - } + private bool ShouldSkip(string? name) => name is not null && name != _name; private static ValidateOptionsResult ToValidateOptionsResult(ValidationResult result) => result.IsValid