Skip to content

Commit

Permalink
Add FluentOptionsValidatorTests
Browse files Browse the repository at this point in the history
Signed-off-by: Serhii A. Hrytsenko <[email protected]>
  • Loading branch information
gritcsenko committed Jan 25, 2025
1 parent a08f68d commit 0101f8d
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 11 deletions.
18 changes: 18 additions & 0 deletions src/HomeInventory/HomeInventory.Tests.Framework/GivenContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,24 @@ public TContext New<T>(out IVariable<T> variable, Func<T> create, int count = 1,
where T : notnull =>
New(out variable, _ => create(), count, name);

public TContext New<T, TArg>(out IVariable<T> variable, IVariable<TArg> arg, Func<TArg, T> 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<T, TArg1, TArg2>(out IVariable<T> variable, IVariable<TArg1> arg1, IVariable<TArg2> arg2, Func<TArg1, TArg2, T> 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<T, TArg1, TArg2, TArg3>(out IVariable<T> variable, IVariable<TArg1> arg1, IVariable<TArg2> arg2, IVariable<TArg3> arg3, Func<TArg1, TArg2, TArg3, T> 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<HashCode> emptyHash) =>
New(out emptyHash, static () => new());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace HomeInventory.Tests.Framework;

public class ThenContext<TResult>(VariablesContainer variables, IVariable<TResult> resultVariable) : BaseContext(variables)
public class ThenContext<TResult>(VariablesContainer variables, IVariable<TResult> resultVariable) : ThenContext(variables)
where TResult : notnull
{
private readonly IVariable<TResult> _resultVariable = resultVariable;
Expand Down
14 changes: 14 additions & 0 deletions src/HomeInventory/HomeInventory.Tests.Framework/WhenContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,20 @@ public ThenContext<TResult> Invoked<TSut, TArg, TResult>(IIndexedVariable<TSut>
where TResult : notnull =>
Invoked(sut, sutValue => invoke(sutValue, Variables.Get(arg).Value));

internal ThenContext<TResult> Invoked<TSut, TArg1, TArg2, TResult>(IVariable<TSut> sut, IVariable<TArg1> arg1, IVariable<TArg2> arg2, Func<TSut, TArg1, TArg2, TResult> invoke)
where TSut : notnull
where TArg1 : notnull
where TArg2 : notnull
where TResult : notnull =>
Invoked(sut[0], arg1[0], arg2[0], invoke);

public ThenContext<TResult> Invoked<TSut, TArg1, TArg2, TResult>(IIndexedVariable<TSut> sut, IIndexedVariable<TArg1> arg1, IIndexedVariable<TArg2> arg2, Func<TSut, TArg1, TArg2, TResult> 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<TResult> Invoked<TSut, TResult>(IIndexedVariable<TSut> sut, Func<TSut, TResult> invoke)
where TSut : notnull
where TResult : notnull
Expand Down
Original file line number Diff line number Diff line change
@@ -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<FluentOptionsValidatorTestsGivenContext>(static t => new(t))
{
[Fact]
public void Create_Should_ReturnValidator()
{
Given
.New<string>(out var nameVar)
.SubstituteFor<IValidator>(out var validatorVar);

var then = When
.Invoked(nameVar, validatorVar, static (name, validator) => FluentOptionsValidator.Create<SubjectOptions>(name, validator));

then.Result(actual => actual.Should().BeAssignableTo<FluentOptionsValidator<SubjectOptions>>());
}

[Fact]
public void Validate_Should_Skip_When_NameIsDifferent()
{
Given
.New<string>(out var nameVar)
.New<string>(out var differentNameVar)
.New<SubjectOptions>(out var optionsVar, () => new())
.SubstituteFor<IValidator>(out var validatorVar)
.New(out var sutVar, nameVar, validatorVar, static (name, validator) => FluentOptionsValidator.Create<SubjectOptions>(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<string>(out var nameVar)
.New<SubjectOptions>(out var optionsVar, () => new())
.New<FluentValidation.Results.ValidationResult>(out var resultVar, () => new())
.SubstituteFor<IValidationContext>(out var validationContextVar)
.SubstituteFor(out IVariable<IValidationContextFactory<SubjectOptions>> factoryVar, optionsVar, validationContextVar, static (f, o, ctx) => f.CreateContext(o).Returns(ctx))
.SubstituteFor(out IVariable<IValidator> 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));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
namespace HomeInventory.Tests.Presentation.Web;

public sealed class FluentOptionsValidatorTestsGivenContext(BaseTest test) : GivenContext<FluentOptionsValidatorTestsGivenContext>(test)
{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using HomeInventory.Web.Framework;

namespace HomeInventory.Tests.Presentation.Web;

public sealed class SubjectOptions : IOptions
{
public static SectionPath Section => nameof(Section);
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ namespace HomeInventory.Web.Framework;
internal static class FluentOptionsValidator
{
public static IValidateOptions<TOptions> Create<TOptions>(string name, IValidator validator, Action<ValidationStrategy<TOptions>>? validationOptions = null)
where TOptions : class
{
var factory = new ValidationContextFactory<TOptions>(validationOptions);
return new FluentOptionsValidator<TOptions>(name, validator, factory);
}
where TOptions : class =>
Create(name, validator, new ValidationContextFactory<TOptions>(validationOptions));

public static IValidateOptions<TOptions> Create<TOptions>(string name, IValidator validator, IValidationContextFactory<TOptions> factory)
where TOptions : class =>
new FluentOptionsValidator<TOptions>(name, validator, factory);
}

internal sealed class FluentOptionsValidator<TOptions>(string name, IValidator validator, IValidationContextFactory<TOptions> validationContextFactory) : IValidateOptions<TOptions>
Expand All @@ -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
Expand Down

0 comments on commit 0101f8d

Please sign in to comment.