// Copyright (c) The Geekeey Authors // SPDX-License-Identifier: EUPL-1.2 using Microsoft.Extensions.DependencyInjection; namespace Geekeey.Request.Tests; internal sealed class ScalarBehaviourTests { [Test] public async Task I_can_execute_the_closed_behaviour() { var sc = new ServiceCollection(); sc.AddSingleton(); sc.AddRequestDispatcher(builder => builder .Add(typeof(ScalarTestHandler)) .Add(typeof(ScalarTestBehavior))); var provider = sc.BuildServiceProvider(); var dispatcher = provider.GetRequiredService(); var tracker = provider.GetRequiredService(); var request = new ScalarTestRequest { Value = "Hello" }; var result = await dispatcher.DispatchAsync(request); await Assert.That(result).IsEquivalentTo("Hello-Handled"); await Assert.That(tracker.Executed).IsTrue(); } [Test] public async Task I_can_execute_the_open_behaviour() { var sc = new ServiceCollection(); sc.AddSingleton(); sc.AddRequestDispatcher(builder => builder .Add(typeof(ScalarTestHandler)) .Add(typeof(ScalarOpenBehavior<,>))); var provider = sc.BuildServiceProvider(); var dispatcher = provider.GetRequiredService(); var tracker = provider.GetRequiredService(); var request = new ScalarTestRequest { Value = "Hello" }; var result = await dispatcher.DispatchAsync(request); await Assert.That(result).IsEquivalentTo("Hello-Handled"); await Assert.That(tracker.Executed).IsTrue(); } [Test] public async Task I_can_chain_the_behaviours_in_order() { var sc = new ServiceCollection(); sc.AddSingleton(); sc.AddRequestDispatcher(builder => builder .Add(typeof(ScalarTestHandler)) .Add(typeof(ScalarChainedBehaviour1)) .Add(typeof(ScalarChainedBehaviour2))); var provider = sc.BuildServiceProvider(); var dispatcher = provider.GetRequiredService(); var tracker = provider.GetRequiredService(); var request = new ScalarTestRequest { Value = "Hello" }; await dispatcher.DispatchAsync(request); // They are discovered in the order they appear in the assembly. // In this file: ChainedBehaviour1, ChainedBehaviour2 await Assert.That(tracker.Log).Count().IsEqualTo(2); await Assert.That(tracker.Log[0]).IsEquivalentTo("Behaviour1"); await Assert.That(tracker.Log[1]).IsEquivalentTo("Behaviour2"); } [Test] public async Task I_can_work_with_a_generic_wrapper_request_and_the_open_behaviour() { var sc = new ServiceCollection(); sc.AddSingleton(); sc.AddRequestDispatcher(builder => builder .Add(typeof(ScalarTestWrapperHandler<>)) .Add(typeof(ScalarWrapperBehavior<>))); var provider = sc.BuildServiceProvider(); var dispatcher = provider.GetRequiredService(); var tracker = provider.GetRequiredService(); var request = new ScalarTestWrapperRequest { Item = 42 }; var result = await dispatcher.DispatchAsync(request); await Assert.That(result).IsEquivalentTo("Handled-42"); await Assert.That(tracker.Executed).IsTrue(); } [Test] public async Task I_can_maintain_the_ordering_between_open_and_closed_behaviours() { var sc = new ServiceCollection(); sc.AddSingleton(); sc.AddRequestDispatcher(builder => builder .Add(typeof(ScalarTestHandler)) .Add(typeof(ScalarOrderingOpenBehavior<,>)) .Add(typeof(ScalarOrderingClosedBehavior))); var provider = sc.BuildServiceProvider(); var dispatcher = provider.GetRequiredService(); var tracker = provider.GetRequiredService(); var request = new ScalarTestRequest { Value = "Order" }; await dispatcher.DispatchAsync(request); await Assert.That(tracker.Log).Contains("OrderingOpen"); await Assert.That(tracker.Log).Contains("OrderingClosed"); } } public class ScalarTestTracker { public List Log { get; } = []; public bool Executed { get; set; } } public class ScalarOrderingOpenBehavior(ScalarTestTracker tracker) : IScalarRequestBehavior where TRequest : IScalarRequest { public Task HandleAsync(TRequest request, ScalarHandlerDelegate next, CancellationToken cancellationToken) { tracker.Log.Add("OrderingOpen"); return next(request, cancellationToken); } } public class ScalarOrderingClosedBehavior(ScalarTestTracker tracker) : IScalarRequestBehavior { public Task HandleAsync(ScalarTestRequest request, ScalarHandlerDelegate next, CancellationToken cancellationToken) { tracker.Log.Add("OrderingClosed"); return next(request, cancellationToken); } } public class ScalarTestRequest : IScalarRequest { public string Value { get; set; } = string.Empty; } public class ScalarTestHandler : IScalarRequestHandler { public Task HandleAsync(ScalarTestRequest request, CancellationToken cancellationToken) { return Task.FromResult($"{request.Value}-Handled"); } } public class ScalarTestBehavior(ScalarTestTracker tracker) : IScalarRequestBehavior { public async Task HandleAsync(ScalarTestRequest request, ScalarHandlerDelegate next, CancellationToken cancellationToken) { tracker.Executed = true; return await next(request, cancellationToken); } } public class ScalarOpenBehavior(ScalarTestTracker tracker) : IScalarRequestBehavior where TRequest : IScalarRequest { public async Task HandleAsync(TRequest request, ScalarHandlerDelegate next, CancellationToken cancellationToken) { tracker.Executed = true; return await next(request, cancellationToken); } } public class ScalarChainedBehaviour1(ScalarTestTracker tracker) : IScalarRequestBehavior { public async Task HandleAsync(ScalarTestRequest request, ScalarHandlerDelegate next, CancellationToken cancellationToken) { tracker.Log.Add("Behaviour1"); return await next(request, cancellationToken); } } public class ScalarChainedBehaviour2(ScalarTestTracker tracker) : IScalarRequestBehavior { public async Task HandleAsync(ScalarTestRequest request, ScalarHandlerDelegate next, CancellationToken cancellationToken) { tracker.Log.Add("Behaviour2"); return await next(request, cancellationToken); } } public class ScalarTestWrapperRequest : IScalarRequest { public T Item { get; set; } = default!; } public class ScalarTestWrapperHandler : IScalarRequestHandler, string> { public Task HandleAsync(ScalarTestWrapperRequest request, CancellationToken cancellationToken) { return Task.FromResult($"Handled-{request.Item}"); } } public class ScalarWrapperBehavior(ScalarTestTracker tracker) : IScalarRequestBehavior, string> { public async Task HandleAsync(ScalarTestWrapperRequest request, ScalarHandlerDelegate next, CancellationToken cancellationToken) { tracker.Executed = true; return await next(request, cancellationToken); } }