Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<RootNamespace>Cratis.Arc.CodeAnalysis</RootNamespace>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
<IsAotCompatible>false</IsAotCompatible>
<NoWarn>$(NoWarn);MA0101;RCS1266</NoWarn>
</PropertyGroup>
<ItemGroup>
Expand All @@ -21,4 +22,3 @@
<ProjectReference Include="../Arc.Core/Arc.Core.csproj" />
</ItemGroup>
</Project>

Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<TargetFramework>netstandard2.0</TargetFramework>
<TargetFrameworks></TargetFrameworks>
<LangVersion>latest</LangVersion>
<IsAotCompatible>false</IsAotCompatible>
<NoWarn>$(NoWarn);NU5128;NU1507</NoWarn>
</PropertyGroup>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
<RootNamespace>Cratis.Arc.Core.Generators.Integration.Specs</RootNamespace>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
<IsAotCompatible>false</IsAotCompatible>
<NoWarn>$(NoWarn);MA0101;RCS1266;MA0136</NoWarn>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="../../Arc.Core/Arc.Core.csproj" />
</ItemGroup>
</Project>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<RootNamespace>Cratis.Arc.Generators</RootNamespace>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
<IsAotCompatible>false</IsAotCompatible>
<NoWarn>$(NoWarn);MA0101;RCS1266;MA0136</NoWarn>
</PropertyGroup>
<ItemGroup>
Expand Down
3 changes: 2 additions & 1 deletion Source/DotNET/Arc.Core.Generators/Arc.Core.Generators.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<TargetFramework>netstandard2.0</TargetFramework>
<TargetFrameworks></TargetFrameworks>
<LangVersion>latest</LangVersion>
<IsAotCompatible>false</IsAotCompatible>
<NoWarn>$(NoWarn);NU5128;NU1507</NoWarn>
</PropertyGroup>

Expand All @@ -21,4 +22,4 @@
<ItemGroup>
<None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
</ItemGroup>
</Project>
</Project>
3 changes: 2 additions & 1 deletion Source/DotNET/Arc.Core.Specs/Arc.Core.Specs.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@
<RootNamespace>Cratis.Arc</RootNamespace>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
<IsAotCompatible>false</IsAotCompatible>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Telemetry" />
<ProjectReference Include="../Arc.Core/Arc.Core.csproj" />
</ItemGroup>
</Project>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ void Establish()
_command = new ComplexCommand("ValidName", nestedObject);
_context = new CommandContext(_correlationId, typeof(ComplexCommand), _command, [], new());

_commandValidator = Substitute.For<IValidator>();
_commandValidator = Substitute.For<IValidator, IObjectValidator>();
_commandValidationResult = new FluentValidation.Results.ValidationResult();

_nestedValidator = Substitute.For<IValidator>();
_nestedValidator = Substitute.For<IValidator, IObjectValidator>();
_nestedValidationResult = new FluentValidation.Results.ValidationResult([
new ValidationFailure("Value", "Nested value is invalid")
]);
Expand All @@ -44,8 +44,8 @@ void Establish()
return true;
});

_commandValidator.ValidateAsync(Arg.Any<IValidationContext>(), Arg.Any<CancellationToken>()).Returns(_commandValidationResult);
_nestedValidator.ValidateAsync(Arg.Any<IValidationContext>(), Arg.Any<CancellationToken>()).Returns(_nestedValidationResult);
((IObjectValidator)_commandValidator).ValidateObjectAsync(Arg.Any<object>(), Arg.Any<CancellationToken>()).Returns(_commandValidationResult);
((IObjectValidator)_nestedValidator).ValidateObjectAsync(Arg.Any<object>(), Arg.Any<CancellationToken>()).Returns(_nestedValidationResult);
}

async Task Because() => _result = await _filter.OnExecution(_context);
Expand All @@ -59,8 +59,8 @@ void Establish()
[Fact] void should_have_validation_result_with_error_severity() => _result.ValidationResults.First().Severity.ShouldEqual(ValidationResultSeverity.Error);
[Fact] void should_have_validation_result_with_correct_message() => _result.ValidationResults.First().Message.ShouldEqual("Nested value is invalid");
[Fact] void should_have_validation_result_with_correct_member() => _result.ValidationResults.First().Members.ShouldContain("Value");
[Fact] void should_call_command_validator() => _commandValidator.Received(1).ValidateAsync(Arg.Any<IValidationContext>(), Arg.Any<CancellationToken>());
[Fact] void should_call_nested_validator() => _nestedValidator.Received(1).ValidateAsync(Arg.Any<IValidationContext>(), Arg.Any<CancellationToken>());
[Fact] void should_call_command_validator() => ((IObjectValidator)_commandValidator).Received(1).ValidateObjectAsync(Arg.Any<object>(), Arg.Any<CancellationToken>());
[Fact] void should_call_nested_validator() => ((IObjectValidator)_nestedValidator).Received(1).ValidateObjectAsync(Arg.Any<object>(), Arg.Any<CancellationToken>());

record ComplexCommand(string Name, NestedObject Nested);
record NestedObject(string Value);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Cratis. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using Cratis.Arc.Validation;
using FluentValidation;
using FluentValidationResult = FluentValidation.Results.ValidationResult;

Expand All @@ -17,7 +18,7 @@ void Establish()
const int command = 42;
_context = new CommandContext(_correlationId, typeof(int), command, [], new());

_validator = Substitute.For<IValidator>();
_validator = Substitute.For<IValidator, IObjectValidator>();
_validationResult = new FluentValidationResult();

_discoverableValidators.TryGet(typeof(int), out Arg.Any<IValidator>())
Expand All @@ -27,7 +28,7 @@ void Establish()
return true;
});

_validator.ValidateAsync(Arg.Any<IValidationContext>(), Arg.Any<CancellationToken>()).Returns(_validationResult);
((IObjectValidator)_validator).ValidateObjectAsync(Arg.Any<object>(), Arg.Any<CancellationToken>()).Returns(_validationResult);
}

async Task Because() => _result = await _filter.OnExecution(_context);
Expand All @@ -38,5 +39,5 @@ void Establish()
[Fact] void should_be_valid() => _result.IsValid.ShouldBeTrue();
[Fact] void should_not_have_exceptions() => _result.HasExceptions.ShouldBeFalse();
[Fact] void should_not_have_validation_results() => _result.ValidationResults.ShouldBeEmpty();
[Fact] void should_call_validator_once() => _validator.Received(1).ValidateAsync(Arg.Any<IValidationContext>(), Arg.Any<CancellationToken>());
[Fact] void should_call_validator_once() => ((IObjectValidator)_validator).Received(1).ValidateObjectAsync(Arg.Any<object>(), Arg.Any<CancellationToken>());
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ void Establish()
_command = new TestCommand("InvalidName");
_context = new CommandContext(_correlationId, typeof(TestCommand), _command, [], new());

_validator = Substitute.For<IValidator>();
_validator = Substitute.For<IValidator, IObjectValidator>();
_validationResult = new FluentValidationResult([
new ValidationFailure("Name", "Name is required")
]);
Expand All @@ -32,7 +32,7 @@ void Establish()
return true;
});

_validator.ValidateAsync(Arg.Any<IValidationContext>(), Arg.Any<CancellationToken>()).Returns(_validationResult);
((IObjectValidator)_validator).ValidateObjectAsync(Arg.Any<object>(), Arg.Any<CancellationToken>()).Returns(_validationResult);
}

async Task Because() => _result = await _filter.OnExecution(_context);
Expand All @@ -46,7 +46,7 @@ void Establish()
[Fact] void should_have_validation_result_with_error_severity() => _result.ValidationResults.First().Severity.ShouldEqual(ValidationResultSeverity.Error);
[Fact] void should_have_validation_result_with_correct_message() => _result.ValidationResults.First().Message.ShouldEqual("Name is required");
[Fact] void should_have_validation_result_with_correct_member() => _result.ValidationResults.First().Members.ShouldContain("Name");
[Fact] void should_call_validator() => _validator.Received(1).ValidateAsync(Arg.Any<IValidationContext>(), Arg.Any<CancellationToken>());
[Fact] void should_call_validator() => ((IObjectValidator)_validator).Received(1).ValidateObjectAsync(Arg.Any<object>(), Arg.Any<CancellationToken>());

record TestCommand(string Name);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Cratis. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using Cratis.Arc.Validation;
using FluentValidation;
using FluentValidationResult = FluentValidation.Results.ValidationResult;

Expand All @@ -18,7 +19,7 @@ void Establish()
_command = new SimpleCommand();
_context = new CommandContext(_correlationId, typeof(SimpleCommand), _command, [], new());

_validator = Substitute.For<IValidator>();
_validator = Substitute.For<IValidator, IObjectValidator>();
_validationResult = new FluentValidationResult();

_discoverableValidators.TryGet(typeof(SimpleCommand), out Arg.Any<IValidator>())
Expand All @@ -28,7 +29,7 @@ void Establish()
return true;
});

_validator.ValidateAsync(Arg.Any<IValidationContext>(), Arg.Any<CancellationToken>()).Returns(_validationResult);
((IObjectValidator)_validator).ValidateObjectAsync(Arg.Any<object>(), Arg.Any<CancellationToken>()).Returns(_validationResult);
}

async Task Because() => _result = await _filter.OnExecution(_context);
Expand All @@ -39,7 +40,7 @@ void Establish()
[Fact] void should_be_valid() => _result.IsValid.ShouldBeTrue();
[Fact] void should_not_have_exceptions() => _result.HasExceptions.ShouldBeFalse();
[Fact] void should_not_have_validation_results() => _result.ValidationResults.ShouldBeEmpty();
[Fact] void should_call_validator() => _validator.Received(1).ValidateAsync(Arg.Any<IValidationContext>(), Arg.Any<CancellationToken>());
[Fact] void should_call_validator() => ((IObjectValidator)_validator).Received(1).ValidateObjectAsync(Arg.Any<object>(), Arg.Any<CancellationToken>());

public class SimpleCommand;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Cratis. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using Cratis.Arc.Validation;
using FluentValidation;
using FluentValidation.Results;
using ValidationResult = FluentValidation.Results.ValidationResult;
Expand All @@ -22,12 +23,12 @@ void Establish()
_command = new ComplexCommand("", nestedObject);
_context = new CommandContext(_correlationId, typeof(ComplexCommand), _command, [], new());

_commandValidator = Substitute.For<IValidator>();
_commandValidator = Substitute.For<IValidator, IObjectValidator>();
_commandValidationResult = new ValidationResult([
new ValidationFailure("Name", "Name is required")
]);

_nestedValidator = Substitute.For<IValidator>();
_nestedValidator = Substitute.For<IValidator, IObjectValidator>();
_nestedValidationResult = new ValidationResult([
new ValidationFailure("Value", "Nested value is invalid")
]);
Expand All @@ -46,8 +47,8 @@ void Establish()
return true;
});

_commandValidator.ValidateAsync(Arg.Any<IValidationContext>(), Arg.Any<CancellationToken>()).Returns(_commandValidationResult);
_nestedValidator.ValidateAsync(Arg.Any<IValidationContext>(), Arg.Any<CancellationToken>()).Returns(_nestedValidationResult);
((IObjectValidator)_commandValidator).ValidateObjectAsync(Arg.Any<object>(), Arg.Any<CancellationToken>()).Returns(_commandValidationResult);
((IObjectValidator)_nestedValidator).ValidateObjectAsync(Arg.Any<object>(), Arg.Any<CancellationToken>()).Returns(_nestedValidationResult);
}

async Task Because() => _result = await _filter.OnExecution(_context);
Expand All @@ -60,8 +61,8 @@ void Establish()
[Fact] void should_have_two_validation_results() => _result.ValidationResults.Count().ShouldEqual(2);
[Fact] void should_have_command_validation_error() => _result.ValidationResults.Any(vr => vr.Message == "Name is required").ShouldBeTrue();
[Fact] void should_have_nested_validation_error() => _result.ValidationResults.Any(vr => vr.Message == "Nested value is invalid").ShouldBeTrue();
[Fact] void should_call_command_validator() => _commandValidator.Received(1).ValidateAsync(Arg.Any<IValidationContext>(), Arg.Any<CancellationToken>());
[Fact] void should_call_nested_validator() => _nestedValidator.Received(1).ValidateAsync(Arg.Any<IValidationContext>(), Arg.Any<CancellationToken>());
[Fact] void should_call_command_validator() => ((IObjectValidator)_commandValidator).Received(1).ValidateObjectAsync(Arg.Any<object>(), Arg.Any<CancellationToken>());
[Fact] void should_call_nested_validator() => ((IObjectValidator)_nestedValidator).Received(1).ValidateObjectAsync(Arg.Any<object>(), Arg.Any<CancellationToken>());

record ComplexCommand(string Name, NestedObject Nested);
record NestedObject(string Value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ void Establish()
_command = new CommandWithNullProperty("ValidName", null);
_context = new CommandContext(_correlationId, typeof(CommandWithNullProperty), _command, [], new());

_validator = Substitute.For<IValidator>();
_validator = Substitute.For<IValidator, IObjectValidator>();
_validationResult = new FluentValidation.Results.ValidationResult([
new ValidationFailure("NullProperty", "Property cannot be null")
]);
Expand All @@ -31,7 +31,7 @@ void Establish()
return true;
});

_validator.ValidateAsync(Arg.Any<IValidationContext>(), Arg.Any<CancellationToken>()).Returns(_validationResult);
((IObjectValidator)_validator).ValidateObjectAsync(Arg.Any<object>(), Arg.Any<CancellationToken>()).Returns(_validationResult);
}

async Task Because() => _result = await _filter.OnExecution(_context);
Expand All @@ -45,7 +45,7 @@ void Establish()
[Fact] void should_have_validation_result_with_error_severity() => _result.ValidationResults.First().Severity.ShouldEqual(ValidationResultSeverity.Error);
[Fact] void should_have_validation_result_with_correct_message() => _result.ValidationResults.First().Message.ShouldEqual("Property cannot be null");
[Fact] void should_have_validation_result_with_correct_member() => _result.ValidationResults.First().Members.ShouldContain("NullProperty");
[Fact] void should_call_validator() => _validator.Received(1).ValidateAsync(Arg.Any<IValidationContext>(), Arg.Any<CancellationToken>());
[Fact] void should_call_validator() => ((IObjectValidator)_validator).Received(1).ValidateObjectAsync(Arg.Any<object>(), Arg.Any<CancellationToken>());
[Fact] void should_not_attempt_to_validate_null_property() => _discoverableValidators.DidNotReceive().TryGet(typeof(NestedObject), out Arg.Any<IValidator>());

record CommandWithNullProperty(string Name, NestedObject? NullProperty);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
// Copyright (c) Cratis. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using Cratis.Arc.Validation;
using FluentValidation;
using FluentValidation.Results;

namespace Cratis.Arc.Commands.Filters.for_FluentValidationFilter.when_validating;

public class with_validation_context_setup : given.a_fluent_validation_filter
{
CommandResult _result;
IValidator _validator;
ValidationResult _validationResult;
FluentValidation.Results.ValidationResult _validationResult;
TestCommand _command;
IValidationContext _capturedContext;
object _capturedInstance;

void Establish()
{
_command = new TestCommand("TestName");
_context = new CommandContext(_correlationId, typeof(TestCommand), _command, [], new());

_validator = Substitute.For<IValidator>();
_validationResult = new ValidationResult();
_validator = Substitute.For<IValidator, IObjectValidator>();
_validationResult = new FluentValidation.Results.ValidationResult();

_discoverableValidators.TryGet(typeof(TestCommand), out Arg.Any<IValidator>())
.Returns(x =>
Expand All @@ -29,19 +29,19 @@ void Establish()
return true;
});

_validator.ValidateAsync(Arg.Any<IValidationContext>(), Arg.Any<CancellationToken>())
((IObjectValidator)_validator).ValidateObjectAsync(Arg.Any<object>(), Arg.Any<CancellationToken>())
.Returns(call =>
{
_capturedContext = call.Arg<IValidationContext>();
_capturedInstance = call.Arg<object>();
return _validationResult;
});
}

async Task Because() => _result = await _filter.OnExecution(_context);

[Fact] void should_return_successful_result() => _result.IsSuccess.ShouldBeTrue();
[Fact] void should_call_validator_with_validation_context() => _validator.Received(1).ValidateAsync(Arg.Any<IValidationContext>(), Arg.Any<CancellationToken>());
[Fact] void should_create_validation_context_with_correct_instance() => _capturedContext.InstanceToValidate.ShouldEqual(_command);
[Fact] void should_call_validator_with_instance() => ((IObjectValidator)_validator).Received(1).ValidateObjectAsync(Arg.Any<object>(), Arg.Any<CancellationToken>());
[Fact] void should_call_validator_with_correct_instance() => _capturedInstance.ShouldEqual(_command);

record TestCommand(string Name);
}
4 changes: 4 additions & 0 deletions Source/DotNET/Arc.Core/ArcApplicationBuilderExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Cratis. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Diagnostics.CodeAnalysis;
using Cratis.Arc.Queries;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
Expand All @@ -20,6 +21,9 @@ public static class ArcApplicationBuilderExtensions
/// <param name="configureBuilder">Optional callback for configuring the <see cref="IArcBuilder"/>.</param>
/// <param name="configSectionPath">The optional configuration section path.</param>
/// <returns>The <see cref="ArcApplicationBuilder"/> for continuation.</returns>
[UnconditionalSuppressMessage("AOT", "IL2026", Justification = "IServiceCollection.Configure<ArcOptions> uses reflection-based configuration binding. Source-generated configuration binders are the long-term fix (tracked in GitHub issue #2204).")]
[UnconditionalSuppressMessage("AOT", "IL3050", Justification = "IServiceCollection.Configure<ArcOptions> uses reflection-based configuration binding. Source-generated configuration binders are the long-term fix (tracked in GitHub issue #2204).")]
[UnconditionalSuppressMessage("AOT", "IL2111", Justification = "ArcOptions.IdentityDetailsProvider setter has DynamicallyAccessedMembers but is accessed via reflection in Configure<ArcOptions>. Source-generated configuration binders are the long-term fix (tracked in GitHub issue #2204).")]
public static ArcApplicationBuilder AddCratisArc(
this ArcApplicationBuilder builder,
Action<ArcOptions>? configureOptions = null,
Expand Down
2 changes: 2 additions & 0 deletions Source/DotNET/Arc.Core/ArcOptions.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Cratis. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Diagnostics.CodeAnalysis;
using System.Text.Json;
using Cratis.Arc.Execution;
using Cratis.Arc.Queries;
Expand Down Expand Up @@ -39,6 +40,7 @@ public ArcOptions()
/// <summary>
/// Gets or sets what type of identity details provider to use. If none is specified it will use type discovery to try to find one.
/// </summary>
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
public Type? IdentityDetailsProvider { get; set; }

/// <summary>
Expand Down
Loading