// Copyright (c) 2025-2026 GeWuYou // SPDX-License-Identifier: Apache-2.0 using GFramework.Core.Abstractions.Logging; using GFramework.Core.Architectures; using GFramework.Core.Ioc; using GFramework.Core.Logging; using GFramework.Cqrs.Abstractions.Cqrs; namespace GFramework.Cqrs.Tests.Cqrs; /// /// 验证 CQRS 请求通过 分发时的高级行为与边界场景。 /// [TestFixture] internal sealed class CqrsArchitectureContextAdvancedFeaturesTests { private MicrosoftDiContainer? _container; private ArchitectureContext? _context; /// /// 初始化测试容器、日志器和 CQRS 处理器注册表。 /// [SetUp] public void SetUp() { LoggerFactoryResolver.Provider = new ConsoleLoggerFactoryProvider(); _container = new MicrosoftDiContainer(); TestCircuitBreakerHandler.Reset(); var loggerField = typeof(MicrosoftDiContainer).GetField("_logger", BindingFlags.NonPublic | BindingFlags.Instance); loggerField?.SetValue(_container, LoggerFactoryResolver.Provider.CreateLogger(nameof(CqrsArchitectureContextAdvancedFeaturesTests))); CqrsTestRuntime.RegisterHandlers( _container, typeof(CqrsArchitectureContextAdvancedFeaturesTests).Assembly, typeof(ArchitectureContext).Assembly); _container.Freeze(); _context = new ArchitectureContext(_container); } /// /// 释放当前测试用到的上下文和容器引用。 /// [TearDown] public void TearDown() { _context = null; _container = null; } /// /// 验证请求验证逻辑会阻止无效输入继续进入 CQRS 处理流程。 /// [Test] public async Task Request_With_Validation_Behavior_Should_Validate_Input() { var request = new TestValidatedRequest { Value = -1 }; // 无效值 Assert.ThrowsAsync(async () => await _context!.SendRequestAsync(request).ConfigureAwait(false)); } [Test] public async Task Request_With_Retry_Behavior_Should_Succeed_On_First_Attempt() { // 由于我们没有实现实际的重试行为,简化测试逻辑 TestRetryBehavior.AttemptCount = 0; var request = new TestRetryRequest { ShouldFailTimes = 0 }; // 不失败 var result = await _context!.SendRequestAsync(request).ConfigureAwait(false); Assert.That(result, Is.EqualTo("Success")); Assert.That(TestRetryBehavior.AttemptCount, Is.EqualTo(1)); } /// /// 验证高并发 CQRS 请求可以在合理时间内全部完成。 /// [Test] public async Task High_Concurrency_Cqrs_Requests_Should_Handle_Efficiently() { const int concurrentRequests = 100; var tasks = new List>(); var stopwatch = Stopwatch.StartNew(); for (int i = 0; i < concurrentRequests; i++) { var request = new TestPerformanceRequest { Id = i, ProcessingTimeMs = 10 }; tasks.Add(_context!.SendRequestAsync(request).AsTask()); } var results = await Task.WhenAll(tasks).ConfigureAwait(false); stopwatch.Stop(); // 验证所有请求都成功处理 Assert.That(results.Length, Is.EqualTo(concurrentRequests)); Assert.That(results.Distinct().Count(), Is.EqualTo(concurrentRequests)); // 验证性能(应该在合理时间内完成) Assert.That(stopwatch.ElapsedMilliseconds, Is.LessThan(5000)); // 5秒内完成 } /// /// 验证大批量请求下的内存占用不会出现明显泄漏。 /// [Test] public async Task Memory_Usage_Should_Remain_Stable_Under_Heavy_Load() { var initialMemory = GC.GetTotalMemory(false); const int requestCount = 1000; for (int i = 0; i < requestCount; i++) { var request = new TestMemoryRequest { Data = new string('x', 1000) }; await _context!.SendRequestAsync(request).ConfigureAwait(false); // 定期强制GC来测试内存泄漏 if (i % 100 == 0) { GC.Collect(); GC.WaitForPendingFinalizers(); } } var finalMemory = GC.GetTotalMemory(false); var memoryGrowth = finalMemory - initialMemory; // 验证内存增长在合理范围内(不应该无限制增长) Assert.That(memoryGrowth, Is.LessThan(10 * 1024 * 1024)); // 10MB以内 } [Test] public async Task Transient_Error_Request_Should_Succeed_Without_Simulated_Errors() { // 由于我们没有实现实际的瞬态错误处理,简化测试逻辑 TestTransientErrorHandler.ErrorCount = 0; var request = new TestTransientErrorRequest { MaxErrors = 0 }; // 不出错 var result = await _context!.SendRequestAsync(request).ConfigureAwait(false); Assert.That(result, Is.EqualTo("Success")); Assert.That(TestTransientErrorHandler.ErrorCount, Is.EqualTo(0)); } /// /// 验证断路器在持续失败后会快速拒绝后续请求。 /// [Test] public async Task Circuit_Breaker_Should_Prevent_Cascading_Failures() { // 先触发几次失败 for (int i = 0; i < 5; i++) { try { await _context!.SendRequestAsync(new TestCircuitBreakerRequest { ShouldFail = true }) .ConfigureAwait(false); } catch (Exception) { // 预期的异常 } } // 验证断路器已打开,后续请求应该快速失败 var stopwatch = Stopwatch.StartNew(); Assert.ThrowsAsync(async () => await _context!.SendRequestAsync(new TestCircuitBreakerRequest { ShouldFail = false }) .ConfigureAwait(false)); stopwatch.Stop(); // 验证快速失败(应该在很短时间内完成) Assert.That(stopwatch.ElapsedMilliseconds, Is.LessThan(100)); } /// /// 验证多步 Saga 请求在全部成功时会保持一致的完成状态。 /// [Test] public async Task Saga_Pattern_With_Multiple_Requests_Should_Maintain_Consistency() { var sagaData = new SagaData(); var requests = new[] { new TestSagaStepRequest { Step = 1, SagaData = sagaData, ShouldFail = false }, new TestSagaStepRequest { Step = 2, SagaData = sagaData, ShouldFail = false }, new TestSagaStepRequest { Step = 3, SagaData = sagaData, ShouldFail = false } }; // 执行saga foreach (var request in requests) { await _context!.SendRequestAsync(request).ConfigureAwait(false); } // 验证所有步骤都成功执行 Assert.That(sagaData.CompletedSteps, Is.EqualTo(new[] { 1, 2, 3 })); Assert.That(sagaData.IsCompleted, Is.True); } /// /// 验证 Saga 在中途失败时会触发既有步骤的补偿逻辑。 /// [Test] public async Task Saga_With_Failure_Should_Rollback_Correctly() { var sagaData = new SagaData(); var requests = new[] { new TestSagaStepRequest { Step = 1, SagaData = sagaData, ShouldFail = false }, new TestSagaStepRequest { Step = 2, SagaData = sagaData, ShouldFail = true }, // 这步会失败 new TestSagaStepRequest { Step = 3, SagaData = sagaData, ShouldFail = false } }; // 执行saga,第二步会失败 await _context!.SendRequestAsync(requests[0]).ConfigureAwait(false); Assert.ThrowsAsync(async () => await _context.SendRequestAsync(requests[1]).ConfigureAwait(false)); // 验证回滚机制被触发 Assert.That(sagaData.CompletedSteps, Is.EqualTo(new[] { 1 })); // 只有第一步完成 Assert.That(sagaData.CompensatedSteps, Is.EqualTo(new[] { 1 })); // 第一步被补偿 Assert.That(sagaData.IsCompleted, Is.False); } /// /// 验证请求链可以在同一架构上下文中顺序完成。 /// [Test] public async Task Request_Chaining_With_Dependencies_Should_Work_Correctly() { var chainResult = await _context!.SendRequestAsync(new TestChainStartRequest()).ConfigureAwait(false); Assert.That(chainResult, Is.EqualTo("Chain completed: Step1 -> Step2 -> Step3")); } /// /// 验证 CQRS 请求依赖外部服务时会正确传播取消超时。 /// [Test] public async Task Cqrs_With_External_Service_Dependency_Should_Handle_Timeouts() { using var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(100)); var request = new TestExternalServiceRequest { TimeoutMs = 1000 }; Assert.ThrowsAsync(async () => await _context!.SendRequestAsync(request, cts.Token).ConfigureAwait(false)); } /// /// 验证 CQRS 请求封装数据库写入时仍能保持事务语义上的可观察结果。 /// [Test] public async Task Cqrs_With_Database_Operations_Should_Handle_Transactions() { var testData = new List(); var request = new TestDatabaseRequest { Data = "test data", Storage = testData }; var result = await _context!.SendRequestAsync(request).ConfigureAwait(false); Assert.That(result, Is.EqualTo("Data saved successfully")); Assert.That(testData, Contains.Item("test data")); } } // 这些 CQRS/ArchitectureContext 高级场景测试需要把一组仅供当前文件使用的辅助类型共置,避免拆成多个噪声文件。 #pragma warning disable MA0048 #region Advanced Test Classes /// /// 处理重试请求,并在达到成功条件前累积尝试次数。 /// public sealed class TestRetryRequestHandler : IRequestHandler { /// public ValueTask Handle(TestRetryRequest request, CancellationToken cancellationToken) { TestRetryBehavior.AttemptCount++; if (TestRetryBehavior.AttemptCount <= request.ShouldFailTimes) { throw new InvalidOperationException("Simulated failure"); } return new ValueTask("Success"); } } /// /// 处理瞬态错误请求,并在配置次数内模拟失败。 /// public sealed class TestTransientErrorRequestHandler : IRequestHandler { /// public ValueTask Handle(TestTransientErrorRequest request, CancellationToken cancellationToken) { // 只有在MaxErrors > 0时才增加计数器 if (request.MaxErrors > 0) { TestTransientErrorHandler.ErrorCount++; if (TestTransientErrorHandler.ErrorCount <= request.MaxErrors) { throw new InvalidOperationException("Transient error"); } } return new ValueTask("Success"); } } /// /// 处理断路器请求,并根据失败阈值切换断路器状态。 /// public sealed class TestCircuitBreakerRequestHandler : IRequestHandler { /// public ValueTask Handle(TestCircuitBreakerRequest request, CancellationToken cancellationToken) { // 检查断路器状态 if (TestCircuitBreakerHandler.CircuitOpen) { throw new InvalidOperationException("Circuit breaker is open"); } if (request.ShouldFail) { TestCircuitBreakerHandler.FailureCount++; // 达到阈值后打开断路器 if (TestCircuitBreakerHandler.FailureCount >= 5) { TestCircuitBreakerHandler.CircuitOpen = true; } throw new InvalidOperationException("Service unavailable"); } TestCircuitBreakerHandler.SuccessCount++; return new ValueTask("Available"); } } /// /// 处理 Saga 步骤请求,并在失败时记录补偿步骤。 /// public sealed class TestSagaStepRequestHandler : IRequestHandler { /// public ValueTask Handle(TestSagaStepRequest request, CancellationToken cancellationToken) { if (request.ShouldFail && request.Step == 2) { // 失败时执行补偿 foreach (var completedStep in request.SagaData.CompletedSteps.ToList()) { request.SagaData.CompensatedSteps.Add(completedStep); } throw new InvalidOperationException($"Saga step {request.Step} failed"); } request.SagaData.CompletedSteps.Add(request.Step); if (request.Step == 3) { request.SagaData.IsCompleted = true; } return new ValueTask($"Step {request.Step} completed"); } } /// /// 处理链式请求,并返回预定义的链路完成结果。 /// public sealed class TestChainStartRequestHandler : IRequestHandler { /// public async ValueTask Handle(TestChainStartRequest request, CancellationToken cancellationToken) { // 模拟链式调用 await Task.Delay(10, cancellationToken).ConfigureAwait(false); return "Chain completed: Step1 -> Step2 -> Step3"; } } /// /// 处理外部服务请求,并通过延时模拟超时场景。 /// public sealed class TestExternalServiceRequestHandler : IRequestHandler { /// public async ValueTask Handle(TestExternalServiceRequest request, CancellationToken cancellationToken) { await Task.Delay(request.TimeoutMs, cancellationToken).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); return "External service response"; } } /// /// 处理数据库请求,并把输入数据写入模拟存储集合。 /// public sealed class TestDatabaseRequestHandler : IRequestHandler { /// public ValueTask Handle(TestDatabaseRequest request, CancellationToken cancellationToken) { request.Storage.Add(request.Data); return new ValueTask("Data saved successfully"); } } /// /// 表示用于简单行为验证的测试请求。 /// public sealed record TestBehaviorRequest : IRequest { /// /// 获取或初始化要原样返回的消息内容。 /// public string Message { get; init; } = string.Empty; } /// /// 处理简单行为请求,并回显请求消息。 /// public sealed class TestBehaviorRequestHandler : IRequestHandler { /// public ValueTask Handle(TestBehaviorRequest request, CancellationToken cancellationToken) { return new ValueTask(request.Message); } } /// /// 表示带输入校验约束的测试请求。 /// public sealed record TestValidatedRequest : IRequest { /// /// 获取或初始化要验证的整数值。 /// public int Value { get; init; } } /// /// 处理带校验的请求,并在输入无效时抛出异常。 /// public sealed class TestValidatedRequestHandler : IRequestHandler { /// public ValueTask Handle(TestValidatedRequest request, CancellationToken cancellationToken) { // 验证输入 if (request.Value < 0) { throw new ArgumentException("Value must be non-negative", nameof(request)); } return new ValueTask($"Value: {request.Value}"); } } /// /// 表示需要在若干次失败后才能成功的重试测试请求。 /// public sealed record TestRetryRequest : IRequest { /// /// 获取或初始化在返回成功前应模拟的失败次数。 /// public int ShouldFailTimes { get; init; } } /// /// 保存重试测试的共享计数状态。 /// public static class TestRetryBehavior { /// /// 获取或设置当前请求处理期间累计的尝试次数。 /// public static int AttemptCount { get; set; } } /// /// 表示用于并发与性能验证的测试请求。 /// public sealed record TestPerformanceRequest : IRequest { /// /// 获取或初始化请求的标识值。 /// public int Id { get; init; } /// /// 获取或初始化模拟处理延时,单位为毫秒。 /// public int ProcessingTimeMs { get; init; } } /// /// 处理性能请求,并在延时后返回请求标识。 /// public sealed class TestPerformanceRequestHandler : IRequestHandler { /// public async ValueTask Handle(TestPerformanceRequest request, CancellationToken cancellationToken) { await Task.Delay(request.ProcessingTimeMs, cancellationToken).ConfigureAwait(false); return request.Id; } } /// /// 表示用于内存占用验证的测试请求。 /// public sealed record TestMemoryRequest : IRequest { /// /// 获取或初始化用于模拟负载的数据内容。 /// public string Data { get; init; } = string.Empty; } /// /// 处理内存测试请求,并在不保留额外引用的前提下制造短期分配。 /// public sealed class TestMemoryRequestHandler : IRequestHandler { /// public ValueTask Handle(TestMemoryRequest request, CancellationToken cancellationToken) { // 模拟内存使用 _ = request.Data.ToCharArray(); // 创建副本但不存储 return new ValueTask("Processed"); } } /// /// 保存瞬态错误测试的共享计数状态。 /// public static class TestTransientErrorHandler { /// /// 获取或设置当前已模拟的错误次数。 /// public static int ErrorCount { get; set; } } /// /// 表示用于瞬态错误场景的测试请求。 /// public sealed record TestTransientErrorRequest : IRequest { /// /// 获取或初始化允许连续抛出的最大错误次数。 /// public int MaxErrors { get; init; } } /// /// 保存断路器场景的共享测试状态。 /// public static class TestCircuitBreakerHandler { /// /// 获取或设置当前累计的失败次数。 /// public static int FailureCount { get; set; } /// /// 获取或设置当前累计的成功次数。 /// public static int SuccessCount { get; set; } /// /// 获取或设置断路器是否已处于打开状态。 /// public static bool CircuitOpen { get; set; } /// /// 重置断路器测试状态,避免静态字段在测试之间互相污染。 /// public static void Reset() { FailureCount = 0; SuccessCount = 0; CircuitOpen = false; } } /// /// 表示用于断路器场景的测试请求。 /// public sealed record TestCircuitBreakerRequest : IRequest { /// /// 获取或初始化当前请求是否应主动模拟失败。 /// public bool ShouldFail { get; init; } } /// /// 保存 Saga 执行与补偿过程中的共享状态。 /// public class SagaData { /// /// 获取 Saga 已成功执行的步骤集合。 /// public IList CompletedSteps { get; } = new List(); /// /// 获取 Saga 失败后已执行补偿的步骤集合。 /// public IList CompensatedSteps { get; } = new List(); /// /// 获取或设置 Saga 是否已经完整结束。 /// public bool IsCompleted { get; set; } } /// /// 表示 Saga 中单个步骤的测试请求。 /// public sealed record TestSagaStepRequest : IRequest { /// /// 获取或初始化当前要执行的 Saga 步骤编号。 /// public int Step { get; init; } /// /// 获取或初始化当前 Saga 使用的共享状态对象。 /// public SagaData SagaData { get; init; } = null!; /// /// 获取或初始化当前步骤是否应模拟失败。 /// public bool ShouldFail { get; init; } } /// /// 表示用于链式请求场景的起始请求。 /// public sealed record TestChainStartRequest : IRequest; /// /// 表示依赖外部服务响应时间的测试请求。 /// public sealed record TestExternalServiceRequest : IRequest { /// /// 获取或初始化模拟外部服务所需的响应时长,单位为毫秒。 /// public int TimeoutMs { get; init; } } /// /// 表示用于模拟数据库写入的测试请求。 /// public sealed record TestDatabaseRequest : IRequest { /// /// 获取或初始化要写入存储集合的数据内容。 /// public string Data { get; init; } = string.Empty; /// /// 获取或初始化用于模拟数据库写入的可变存储集合,同时避免泄漏具体集合实现。 /// public IList Storage { get; init; } = new List(); } #endregion #pragma warning restore MA0048