diff --git a/GFramework.Cqrs.Tests/Mediator/MediatorComprehensiveTests.cs b/GFramework.Cqrs.Tests/Cqrs/ArchitectureContextComprehensiveTests.cs similarity index 61% rename from GFramework.Cqrs.Tests/Mediator/MediatorComprehensiveTests.cs rename to GFramework.Cqrs.Tests/Cqrs/ArchitectureContextComprehensiveTests.cs index 0871144b..be08d405 100644 --- a/GFramework.Cqrs.Tests/Mediator/MediatorComprehensiveTests.cs +++ b/GFramework.Cqrs.Tests/Cqrs/ArchitectureContextComprehensiveTests.cs @@ -11,10 +11,13 @@ using GFramework.Core.Query; using GFramework.Cqrs.Abstractions.Cqrs; using ICommand = GFramework.Core.Abstractions.Command.ICommand; -namespace GFramework.Cqrs.Tests.Mediator; +namespace GFramework.Cqrs.Tests.Cqrs; +/// +/// 覆盖 在 CQRS 请求、通知、流式处理和传统总线共存场景下的综合行为。 +/// [TestFixture] -public class MediatorComprehensiveTests +public sealed class ArchitectureContextComprehensiveTests { /// /// 测试初始化方法,在每个测试方法执行前运行。 @@ -29,9 +32,9 @@ public class MediatorComprehensiveTests var loggerField = typeof(MicrosoftDiContainer).GetField("_logger", BindingFlags.NonPublic | BindingFlags.Instance); loggerField?.SetValue(_container, - LoggerFactoryResolver.Provider.CreateLogger(nameof(MediatorComprehensiveTests))); + LoggerFactoryResolver.Provider.CreateLogger(nameof(ArchitectureContextComprehensiveTests))); - // 注册基础服务(Legacy CQRS) + // 注册传统 CQRS 基础服务,验证其与 ArchitectureContext 请求管线可以共存。 _eventBus = new EventBus(); _commandBus = new CommandExecutor(); _queryBus = new QueryExecutor(); @@ -46,7 +49,7 @@ public class MediatorComprehensiveTests CqrsTestRuntime.RegisterHandlers( _container, - typeof(MediatorComprehensiveTests).Assembly, + typeof(ArchitectureContextComprehensiveTests).Assembly, typeof(ArchitectureContext).Assembly); _container.Freeze(); @@ -416,306 +419,304 @@ public class MediatorComprehensiveTests Assert.That(legacyCommand.Executed, Is.True); // 使用自有 CQRS 方式 - var mediatorCommand = new TestCommandWithResult { ResultValue = 999 }; - var result = await _context.SendAsync(mediatorCommand).ConfigureAwait(false); + var cqrsCommand = new TestCommandWithResult { ResultValue = 999 }; + var result = await _context.SendAsync(cqrsCommand).ConfigureAwait(false); Assert.That(result, Is.EqualTo(999)); // 验证两者可以同时工作 Assert.That(legacyCommand.Executed, Is.True); Assert.That(result, Is.EqualTo(999)); } -#region Advanced Test Classes for CQRS Features + #region ArchitectureContext CQRS Test Helpers -public sealed record TestLongRunningRequest : IRequest -{ - public int DelayMs { get; init; } -} - -public sealed class TestLongRunningRequestHandler : IRequestHandler -{ - public async ValueTask Handle(TestLongRunningRequest request, CancellationToken cancellationToken) + private sealed record TestLongRunningRequest : IRequest { - await Task.Delay(request.DelayMs, cancellationToken).ConfigureAwait(false); - cancellationToken.ThrowIfCancellationRequested(); - return "Completed"; + public int DelayMs { get; init; } } -} -public sealed record TestLongStreamRequest : IStreamRequest -{ - public int ItemCount { get; init; } -} - -public sealed class TestLongStreamRequestHandler : IStreamRequestHandler -{ - public async IAsyncEnumerable Handle( - TestLongStreamRequest request, - [EnumeratorCancellation] CancellationToken cancellationToken) + private sealed class TestLongRunningRequestHandler : IRequestHandler { - for (int i = 0; i < request.ItemCount; i++) + public async ValueTask Handle(TestLongRunningRequest request, CancellationToken cancellationToken) { + await Task.Delay(request.DelayMs, cancellationToken).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); - yield return i; - await Task.Delay(10, cancellationToken).ConfigureAwait(false); // 模拟处理延迟 + return "Completed"; } } -} -public sealed record TestFaultyRequest : IRequest; - -public sealed class TestFaultyRequestHandler : IRequestHandler -{ - public ValueTask Handle(TestFaultyRequest request, CancellationToken cancellationToken) + private sealed record TestLongStreamRequest : IStreamRequest { - throw new InvalidOperationException("Handler failed intentionally"); + public int ItemCount { get; init; } } -} -public class SharedData -{ - public int Value { get; set; } -} - -public sealed record TestModifyDataCommand : IRequest -{ - public SharedData Data { get; init; } = null!; - public int Value { get; init; } -} - -public sealed class TestModifyDataCommandHandler : IRequestHandler -{ - public ValueTask Handle(TestModifyDataCommand request, CancellationToken cancellationToken) + private sealed class TestLongStreamRequestHandler : IStreamRequestHandler { - request.Data.Value += request.Value; - return ValueTask.FromResult(Unit.Value); - } -} - -public sealed record TestCachingQuery : IRequest -{ - public string Key { get; init; } = string.Empty; - public IDictionary Cache { get; init; } = new Dictionary(StringComparer.Ordinal); -} - -public sealed class TestCachingQueryHandler : IRequestHandler -{ - public ValueTask Handle(TestCachingQuery request, CancellationToken cancellationToken) - { - if (request.Cache.TryGetValue(request.Key, out var cachedValue)) + public async IAsyncEnumerable Handle( + TestLongStreamRequest request, + [EnumeratorCancellation] CancellationToken cancellationToken) { - return new ValueTask(cachedValue); + for (int i = 0; i < request.ItemCount; i++) + { + cancellationToken.ThrowIfCancellationRequested(); + yield return i; + await Task.Delay(10, cancellationToken).ConfigureAwait(false); // 模拟处理延迟 + } } - - var newValue = $"Value_for_{request.Key}"; - request.Cache[request.Key] = newValue; - return new ValueTask(newValue); } -} -public sealed record TestOrderedNotification : INotification -{ - public int Order { get; init; } - public string Message { get; init; } = string.Empty; -} + private sealed record TestFaultyRequest : IRequest; -public sealed class TestOrderedNotificationHandler : INotificationHandler -{ - public static ICollection ReceivedMessages { get; set; } = new List(); - - public ValueTask Handle(TestOrderedNotification notification, CancellationToken cancellationToken) + private sealed class TestFaultyRequestHandler : IRequestHandler { - ReceivedMessages.Add(notification.Message); - return ValueTask.CompletedTask; - } -} - -// 额外的通知处理器来测试多处理器场景 -public sealed class TestNotificationHandler2 : INotificationHandler -{ - public static string? LastReceivedMessage { get; set; } - - public ValueTask Handle(TestNotification notification, CancellationToken cancellationToken) - { - LastReceivedMessage = notification.Message; - return ValueTask.CompletedTask; - } -} - -public sealed class TestNotificationHandler3 : INotificationHandler -{ - public static string? LastReceivedMessage { get; set; } - - public ValueTask Handle(TestNotification notification, CancellationToken cancellationToken) - { - LastReceivedMessage = notification.Message; - return ValueTask.CompletedTask; - } -} - -public sealed record TestFilterStreamRequest : IStreamRequest -{ - public int[] Values { get; init; } = []; - public bool FilterEven { get; init; } -} - -public sealed class TestFilterStreamRequestHandler : IStreamRequestHandler -{ - public async IAsyncEnumerable Handle( - TestFilterStreamRequest request, - [EnumeratorCancellation] CancellationToken cancellationToken) - { - foreach (var value in request.Values) + public ValueTask Handle(TestFaultyRequest request, CancellationToken cancellationToken) { - cancellationToken.ThrowIfCancellationRequested(); - - if (request.FilterEven && value % 2 != 0) - continue; - - yield return value; - await Task.Yield(); + throw new InvalidOperationException("Handler failed intentionally"); } } -} -public sealed record TestValidatedCommand : IRequest -{ - public string Name { get; init; } = string.Empty; -} - -public sealed class TestValidatedCommandHandler : IRequestHandler -{ - public ValueTask Handle(TestValidatedCommand request, CancellationToken cancellationToken) + private sealed class SharedData { - if (string.IsNullOrWhiteSpace(request.Name)) + public int Value { get; set; } + } + + private sealed record TestModifyDataCommand : IRequest + { + public SharedData Data { get; init; } = null!; + public int Value { get; init; } + } + + private sealed class TestModifyDataCommandHandler : IRequestHandler + { + public ValueTask Handle(TestModifyDataCommand request, CancellationToken cancellationToken) { - throw new ArgumentException("Name cannot be empty.", nameof(request)); + request.Data.Value += request.Value; + return ValueTask.FromResult(Unit.Value); } - - return ValueTask.FromResult(Unit.Value); - } -} - -// 传统命令用于共存测试 -public class TestLegacyCommand : ICommand -{ - public bool Executed { get; private set; } - - public void Execute() - { - Executed = true; } - public void SetContext(IArchitectureContext context) + private sealed record TestCachingQuery : IRequest { - // 不需要实现 + public string Key { get; init; } = string.Empty; + public IDictionary Cache { get; init; } = new Dictionary(StringComparer.Ordinal); } - public IArchitectureContext GetContext() + private sealed class TestCachingQueryHandler : IRequestHandler { - return null!; - } -} - -#endregion - -#region Test Classes - CQRS Runtime - -// ✅ 这些类使用自有 CQRS IRequest -public sealed record TestRequest : IRequest -{ - public int Value { get; init; } -} - -public sealed record TestCommand : IRequest -{ - public bool ShouldExecute { get; init; } - public bool Executed { get; set; } -} - -public sealed record TestCommandWithResult : IRequest -{ - public int ResultValue { get; init; } -} - -public sealed record TestQuery : IRequest -{ - public string QueryResult { get; init; } = string.Empty; -} - -public sealed record TestNotification : INotification -{ - public string Message { get; init; } = string.Empty; -} - -public sealed record TestStreamRequest : IStreamRequest -{ - public int[] Values { get; init; } = []; -} - -// ✅ 这些 Handler 使用自有 CQRS IRequestHandler -public sealed class TestRequestHandler : IRequestHandler -{ - public ValueTask Handle(TestRequest request, CancellationToken cancellationToken) - { - return new ValueTask(request.Value); - } -} - -public sealed class TestCommandHandler : IRequestHandler -{ - public ValueTask Handle(TestCommand request, CancellationToken cancellationToken) - { - if (request.ShouldExecute) + public ValueTask Handle(TestCachingQuery request, CancellationToken cancellationToken) { - request.Executed = true; + if (request.Cache.TryGetValue(request.Key, out var cachedValue)) + { + return new ValueTask(cachedValue); + } + + var newValue = $"Value_for_{request.Key}"; + request.Cache[request.Key] = newValue; + return new ValueTask(newValue); } - - return ValueTask.FromResult(Unit.Value); } -} -public sealed class TestCommandWithResultHandler : IRequestHandler -{ - public ValueTask Handle(TestCommandWithResult request, CancellationToken cancellationToken) + private sealed record TestOrderedNotification : INotification { - return new ValueTask(request.ResultValue); + public int Order { get; init; } + public string Message { get; init; } = string.Empty; } -} -public sealed class TestQueryHandler : IRequestHandler -{ - public ValueTask Handle(TestQuery request, CancellationToken cancellationToken) + private sealed class TestOrderedNotificationHandler : INotificationHandler { - return new ValueTask(request.QueryResult); - } -} + public static ICollection ReceivedMessages { get; set; } = new List(); -public sealed class TestNotificationHandler : INotificationHandler -{ - public static string? LastReceivedMessage { get; set; } - - public ValueTask Handle(TestNotification notification, CancellationToken cancellationToken) - { - LastReceivedMessage = notification.Message; - return ValueTask.CompletedTask; - } -} - -public sealed class TestStreamRequestHandler : IStreamRequestHandler -{ - public async IAsyncEnumerable Handle( - TestStreamRequest request, - [EnumeratorCancellation] CancellationToken cancellationToken) - { - foreach (var value in request.Values) + public ValueTask Handle(TestOrderedNotification notification, CancellationToken cancellationToken) { - cancellationToken.ThrowIfCancellationRequested(); - yield return value; - await Task.Yield(); + ReceivedMessages.Add(notification.Message); + return ValueTask.CompletedTask; } } -} -#endregion + // 额外的通知处理器用于验证多处理器通知分发场景。 + private sealed class TestNotificationHandler2 : INotificationHandler + { + public static string? LastReceivedMessage { get; set; } + + public ValueTask Handle(TestNotification notification, CancellationToken cancellationToken) + { + LastReceivedMessage = notification.Message; + return ValueTask.CompletedTask; + } + } + + private sealed class TestNotificationHandler3 : INotificationHandler + { + public static string? LastReceivedMessage { get; set; } + + public ValueTask Handle(TestNotification notification, CancellationToken cancellationToken) + { + LastReceivedMessage = notification.Message; + return ValueTask.CompletedTask; + } + } + + private sealed record TestFilterStreamRequest : IStreamRequest + { + public int[] Values { get; init; } = []; + public bool FilterEven { get; init; } + } + + private sealed class TestFilterStreamRequestHandler : IStreamRequestHandler + { + public async IAsyncEnumerable Handle( + TestFilterStreamRequest request, + [EnumeratorCancellation] CancellationToken cancellationToken) + { + foreach (var value in request.Values) + { + cancellationToken.ThrowIfCancellationRequested(); + + if (request.FilterEven && value % 2 != 0) + continue; + + yield return value; + await Task.Yield(); + } + } + } + + private sealed record TestValidatedCommand : IRequest + { + public string Name { get; init; } = string.Empty; + } + + private sealed class TestValidatedCommandHandler : IRequestHandler + { + public ValueTask Handle(TestValidatedCommand request, CancellationToken cancellationToken) + { + if (string.IsNullOrWhiteSpace(request.Name)) + { + throw new ArgumentException("Name cannot be empty.", nameof(request)); + } + + return ValueTask.FromResult(Unit.Value); + } + } + + // 传统命令用于验证旧命令总线与 ArchitectureContext 的 CQRS API 可以同时工作。 + private sealed class TestLegacyCommand : ICommand + { + public bool Executed { get; private set; } + + public void Execute() + { + Executed = true; + } + + public void SetContext(IArchitectureContext context) + { + // 该测试只验证传统命令是否被执行,不依赖上下文回填。 + } + + public IArchitectureContext GetContext() + { + return null!; + } + } + + #endregion + + #region ArchitectureContext CQRS Runtime Types + + private sealed record TestRequest : IRequest + { + public int Value { get; init; } + } + + private sealed record TestCommand : IRequest + { + public bool ShouldExecute { get; init; } + public bool Executed { get; set; } + } + + private sealed record TestCommandWithResult : IRequest + { + public int ResultValue { get; init; } + } + + private sealed record TestQuery : IRequest + { + public string QueryResult { get; init; } = string.Empty; + } + + private sealed record TestNotification : INotification + { + public string Message { get; init; } = string.Empty; + } + + private sealed record TestStreamRequest : IStreamRequest + { + public int[] Values { get; init; } = []; + } + + private sealed class TestRequestHandler : IRequestHandler + { + public ValueTask Handle(TestRequest request, CancellationToken cancellationToken) + { + return new ValueTask(request.Value); + } + } + + private sealed class TestCommandHandler : IRequestHandler + { + public ValueTask Handle(TestCommand request, CancellationToken cancellationToken) + { + if (request.ShouldExecute) + { + request.Executed = true; + } + + return ValueTask.FromResult(Unit.Value); + } + } + + private sealed class TestCommandWithResultHandler : IRequestHandler + { + public ValueTask Handle(TestCommandWithResult request, CancellationToken cancellationToken) + { + return new ValueTask(request.ResultValue); + } + } + + private sealed class TestQueryHandler : IRequestHandler + { + public ValueTask Handle(TestQuery request, CancellationToken cancellationToken) + { + return new ValueTask(request.QueryResult); + } + } + + private sealed class TestNotificationHandler : INotificationHandler + { + public static string? LastReceivedMessage { get; set; } + + public ValueTask Handle(TestNotification notification, CancellationToken cancellationToken) + { + LastReceivedMessage = notification.Message; + return ValueTask.CompletedTask; + } + } + + private sealed class TestStreamRequestHandler : IStreamRequestHandler + { + public async IAsyncEnumerable Handle( + TestStreamRequest request, + [EnumeratorCancellation] CancellationToken cancellationToken) + { + foreach (var value in request.Values) + { + cancellationToken.ThrowIfCancellationRequested(); + yield return value; + await Task.Yield(); + } + } + } + + #endregion }