feat: create request projects for basic CQRS
This commit is contained in:
commit
d614788e06
190 changed files with 12236 additions and 0 deletions
251
src/request.dispatcher.tests/ScalarDispatcherTests.cs
Normal file
251
src/request.dispatcher.tests/ScalarDispatcherTests.cs
Normal file
|
|
@ -0,0 +1,251 @@
|
|||
// Copyright (c) The Geekeey Authors
|
||||
// SPDX-License-Identifier: EUPL-1.2
|
||||
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Geekeey.Request.Dispatcher.Tests;
|
||||
|
||||
internal sealed class ScalarDispatcherTests
|
||||
{
|
||||
[Test]
|
||||
public async Task I_can_dispatch_a_request_async_with_an_open_generic_handler()
|
||||
{
|
||||
var sc = new ServiceCollection();
|
||||
sc.AddRequestDispatcher(builder => builder
|
||||
.Add(typeof(OpenScalarHandler<>)));
|
||||
var provider = sc.BuildServiceProvider();
|
||||
var dispatcher = provider.GetRequiredService<IRequestDispatcher>();
|
||||
|
||||
var request = new OpenScalarRequest { Data = "Hello" };
|
||||
var result = await dispatcher.DispatchAsync(request);
|
||||
|
||||
await Assert.That(result).IsEquivalentTo("Hello-Handled");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task I_can_dispatch_a_request_async_with_an_open_generic_handler_that_has_constraints()
|
||||
{
|
||||
var sc = new ServiceCollection();
|
||||
sc.AddRequestDispatcher(builder => builder
|
||||
.Add(typeof(ConstrainedScalarHandler<>)));
|
||||
var provider = sc.BuildServiceProvider();
|
||||
var dispatcher = provider.GetRequiredService<IRequestDispatcher>();
|
||||
|
||||
var request = new ConstrainedScalarRequest { Value = 123 };
|
||||
var result = await dispatcher.DispatchAsync(request);
|
||||
|
||||
await Assert.That(result).IsEquivalentTo("123-Constrained");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task I_can_see_it_fail_if_no_handler_is_found_even_with_an_open_generic_available()
|
||||
{
|
||||
var sc = new ServiceCollection();
|
||||
sc.AddRequestDispatcher(builder => builder
|
||||
.Add(typeof(OpenScalarHandler<>)));
|
||||
var provider = sc.BuildServiceProvider();
|
||||
var dispatcher = provider.GetRequiredService<IRequestDispatcher>();
|
||||
|
||||
var request = new UnhandledScalarRequest();
|
||||
await Assert.ThrowsAsync<InvalidOperationException>(async () => await dispatcher.DispatchAsync(request));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task I_can_dispatch_a_request_async_with_an_inherited_request()
|
||||
{
|
||||
var sc = new ServiceCollection();
|
||||
sc.AddRequestDispatcher(builder => builder
|
||||
.Add(typeof(OpenScalarHandler<>)));
|
||||
var provider = sc.BuildServiceProvider();
|
||||
var dispatcher = provider.GetRequiredService<IRequestDispatcher>();
|
||||
|
||||
// InheritedScalarRequest : OpenScalarRequest.
|
||||
// There is no Handler<InheritedScalarRequest> but there is OpenScalarHandler<TRequest> where TRequest : OpenScalarRequest.
|
||||
// It should be able to handle InheritedScalarRequest.
|
||||
var request = new InheritedScalarRequest { Data = "Sub" };
|
||||
var result = await dispatcher.DispatchAsync(request);
|
||||
|
||||
await Assert.That(result).IsEquivalentTo("Sub-Handled");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task I_can_dispatch_a_request_async_with_an_inherited_handler()
|
||||
{
|
||||
var sc = new ServiceCollection();
|
||||
sc.AddRequestDispatcher(builder => builder
|
||||
.Add(typeof(DerivedScalarHandler)));
|
||||
var provider = sc.BuildServiceProvider();
|
||||
var dispatcher = provider.GetRequiredService<IRequestDispatcher>();
|
||||
|
||||
var request = new DerivedScalarRequest { Value = 42 };
|
||||
var result = await dispatcher.DispatchAsync(request);
|
||||
|
||||
await Assert.That(result).IsEquivalentTo("Derived: 42");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task I_can_dispatch_a_request_async_with_an_interface_inherited_request()
|
||||
{
|
||||
var sc = new ServiceCollection();
|
||||
sc.AddRequestDispatcher(builder => builder
|
||||
.Add(typeof(InterfaceInheritedScalarHandler)));
|
||||
var provider = sc.BuildServiceProvider();
|
||||
var dispatcher = provider.GetRequiredService<IRequestDispatcher>();
|
||||
|
||||
var request = new InterfaceInheritedScalarRequest { Name = "InterfaceTest" };
|
||||
var result = await dispatcher.DispatchAsync(request);
|
||||
|
||||
await Assert.That(result).IsEquivalentTo("InterfaceTest-InterfaceHandled");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task I_can_dispatch_a_request_async_with_deep_inheritance_in_the_request()
|
||||
{
|
||||
var sc = new ServiceCollection();
|
||||
sc.AddRequestDispatcher(builder => builder
|
||||
.Add(typeof(OpenScalarHandler<>)));
|
||||
var provider = sc.BuildServiceProvider();
|
||||
var dispatcher = provider.GetRequiredService<IRequestDispatcher>();
|
||||
|
||||
var request = new DeepDerivedScalarRequest { Data = "Deep", DeepValue = 99 };
|
||||
var result = await dispatcher.DispatchAsync(request);
|
||||
|
||||
// OpenScalarHandler<TRequest> where TRequest : OpenScalarRequest should handle this
|
||||
await Assert.That(result).IsEquivalentTo("Deep-Handled");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task I_can_dispatch_a_request_async_with_an_interface_constrained_handler()
|
||||
{
|
||||
var sc = new ServiceCollection();
|
||||
sc.AddRequestDispatcher(builder => builder
|
||||
.Add(typeof(InterfaceInheritedScalarHandler))
|
||||
.Add(typeof(InterfaceConstrainedScalarHandler<>)));
|
||||
var provider = sc.BuildServiceProvider();
|
||||
var dispatcher = provider.GetRequiredService<IRequestDispatcher>();
|
||||
|
||||
var request = new InterfaceInheritedScalarRequest { Name = "Constrained" };
|
||||
var result = await dispatcher.DispatchAsync(request);
|
||||
|
||||
// Both InterfaceInheritedScalarHandler and InterfaceConstrainedScalarHandler could match.
|
||||
// InterfaceInheritedScalarHandler is a concrete match for InterfaceInheritedScalarRequest.
|
||||
// InterfaceConstrainedScalarHandler is an open generic match.
|
||||
// Currently Dispatcher.SendAsync checks concrete handlers first.
|
||||
await Assert.That(result).IsEquivalentTo("Constrained-InterfaceHandled");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task I_can_dispatch_a_request_async_with_an_interface_only_match()
|
||||
{
|
||||
var sc = new ServiceCollection();
|
||||
sc.AddRequestDispatcher(builder => builder
|
||||
.Add(typeof(InterfaceConstrainedScalarHandler<>)));
|
||||
var provider = sc.BuildServiceProvider();
|
||||
var dispatcher = provider.GetRequiredService<IRequestDispatcher>();
|
||||
|
||||
var request = new AnotherNamedScalarRequest { Name = "InterfaceOnly" };
|
||||
var result = await dispatcher.DispatchAsync(request);
|
||||
|
||||
// No concrete handler for AnotherNamedScalarRequest, but InterfaceConstrainedScalarHandler<T> where T : INamedScalarRequest matches.
|
||||
await Assert.That(result).IsEquivalentTo("InterfaceOnly-ConstrainedByInterface");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task I_can_dispatch_a_request_async_with_a_nested_generic_request()
|
||||
{
|
||||
var sc = new ServiceCollection();
|
||||
sc.AddRequestDispatcher(builder => builder
|
||||
.Add(typeof(WrapperScalarHandler<>)));
|
||||
var provider = sc.BuildServiceProvider();
|
||||
var dispatcher = provider.GetRequiredService<IRequestDispatcher>();
|
||||
|
||||
var request = new WrapperScalarRequest<int> { Item = 42 };
|
||||
var result = await dispatcher.DispatchAsync(request);
|
||||
|
||||
await Assert.That(result).IsEquivalentTo("Handled-42");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task I_can_handle_multiple_interface_implementations()
|
||||
{
|
||||
var sc = new ServiceCollection();
|
||||
sc.AddRequestDispatcher(builder => builder
|
||||
.Add(typeof(MultiInterfaceScalarHandler)));
|
||||
var provider = sc.BuildServiceProvider();
|
||||
var dispatcher = provider.GetRequiredService<IRequestDispatcher>();
|
||||
|
||||
var request = new MultiInterfaceScalarRequest();
|
||||
var result1 = await dispatcher.DispatchAsync<int>(request);
|
||||
var result2 = await dispatcher.DispatchAsync<string>(request);
|
||||
|
||||
await Assert.That(result1).IsEqualTo(1);
|
||||
await Assert.That(result2).IsEquivalentTo("One");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task I_can_see_it_fail_if_there_are_ambiguous_handle_methods()
|
||||
{
|
||||
var sc = new ServiceCollection();
|
||||
sc.AddRequestDispatcher(builder => builder
|
||||
.Add(typeof(AmbiguousScalarHandler)));
|
||||
var provider = sc.BuildServiceProvider();
|
||||
var dispatcher = provider.GetRequiredService<IRequestDispatcher>();
|
||||
|
||||
var request = new AmbiguousScalarRequest();
|
||||
var result = await dispatcher.DispatchAsync(request);
|
||||
|
||||
await Assert.That(result).IsEquivalentTo("Interface-Handled");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task I_can_dispatch_a_request_async_with_a_generic_interface_explicit_implementation()
|
||||
{
|
||||
var sc = new ServiceCollection();
|
||||
sc.AddRequestDispatcher(builder => builder
|
||||
.Add(typeof(ExplicitGenericScalarHandler<>)));
|
||||
var provider = sc.BuildServiceProvider();
|
||||
var dispatcher = provider.GetRequiredService<IRequestDispatcher>();
|
||||
|
||||
var request = new ExplicitGenericScalarRequest { Value = "Explicit" };
|
||||
var result = await dispatcher.DispatchAsync(request);
|
||||
|
||||
await Assert.That(result).IsEquivalentTo("Explicit-ExplicitHandled");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task I_can_see_it_throw_the_original_exception()
|
||||
{
|
||||
var sc = new ServiceCollection();
|
||||
sc.AddRequestDispatcher(builder => builder
|
||||
.Add(typeof(FailingScalarHandler)));
|
||||
var provider = sc.BuildServiceProvider();
|
||||
var dispatcher = provider.GetRequiredService<IRequestDispatcher>();
|
||||
|
||||
var request = new FailingScalarRequest();
|
||||
var ex = await Assert.That(async () => await dispatcher.DispatchAsync(request)).Throws<InvalidOperationException>();
|
||||
|
||||
using (Assert.Multiple())
|
||||
{
|
||||
await Assert.That(ex?.Message).IsEquivalentTo("Handler failed");
|
||||
// Assert that the stack trace contains the handler's method name,
|
||||
// which proves the exception was rethrown while preserving its origin.
|
||||
await Assert.That(ex?.StackTrace).Contains(nameof(FailingScalarHandler.HandleAsync));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task I_can_see_it_throw_if_dispatcher_options_are_modified_after_build()
|
||||
{
|
||||
var sc = new ServiceCollection();
|
||||
sc.AddRequestDispatcher(builder => builder
|
||||
.Add(typeof(FailingScalarHandler)));
|
||||
var provider = sc.BuildServiceProvider();
|
||||
var options = provider.GetRequiredService<IOptions<RequestDispatcherOptions>>().Value;
|
||||
options.GetRequestBehaviors<IScalarRequestHandler<FailingScalarRequest, string>>(default!);
|
||||
|
||||
await Assert.That(() => options.Inspect([])).Throws<InvalidOperationException>();
|
||||
}
|
||||
}
|
||||
|
||||
// Moved to _fixtures
|
||||
Loading…
Add table
Add a link
Reference in a new issue