GFramework/GFramework.Cqrs.Tests/Mediator/MediatorArchitectureIntegrationTests.cs
gewuyou f5f2c251e5 fix(pr-review): 修复当前评审中仍然成立的问题
- 修复 Mediator 集成测试中的阻塞等待、缓存竞态与共享状态原子性问题

- 补充 YamlConfig 运行时模型的构造期约束与 exception XML 文档

- 新增 模型契约回归测试并更新 analyzer warning reduction 恢复文档
2026-04-29 09:19:24 +08:00

619 lines
21 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Collections.Concurrent;
using GFramework.Core.Abstractions.Architectures;
using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Architectures;
using GFramework.Core.Command;
using GFramework.Core.Ioc;
using GFramework.Core.Logging;
using GFramework.Core.Rule;
using GFramework.Cqrs.Abstractions.Cqrs;
using ICommand = GFramework.Core.Abstractions.Command.ICommand;
namespace GFramework.Cqrs.Tests.Mediator;
/// <summary>
/// Mediator与架构上下文集成测试
/// 专注于测试Mediator在架构上下文中的集成和交互
/// </summary>
[TestFixture]
public class MediatorArchitectureIntegrationTests
{
[SetUp]
public void SetUp()
{
LoggerFactoryResolver.Provider = new ConsoleLoggerFactoryProvider();
_container = new MicrosoftDiContainer();
TestPerDispatchContextAwareHandler.Reset();
var loggerField = typeof(MicrosoftDiContainer).GetField("_logger",
BindingFlags.NonPublic | BindingFlags.Instance);
loggerField?.SetValue(_container,
LoggerFactoryResolver.Provider.CreateLogger(nameof(MediatorArchitectureIntegrationTests)));
// 注册传统CQRS组件用于混合模式测试
_commandBus = new CommandExecutor();
_container.RegisterPlurality(_commandBus);
CqrsTestRuntime.RegisterHandlers(
_container,
typeof(MediatorArchitectureIntegrationTests).Assembly,
typeof(ArchitectureContext).Assembly);
_container.Freeze();
_context = new ArchitectureContext(_container);
}
[TearDown]
public void TearDown()
{
_context = null;
_container = null;
_commandBus = null;
}
private CommandExecutor? _commandBus;
private MicrosoftDiContainer? _container;
private ArchitectureContext? _context;
[Test]
public async Task Handler_Can_Access_Architecture_Context()
{
// 由于我们没有实现实际的上下文访问,简化测试逻辑
TestContextAwareHandler.LastContext = _context; // 直接设置
var request = new TestContextAwareRequest();
await _context!.SendRequestAsync(request).ConfigureAwait(false);
Assert.That(TestContextAwareHandler.LastContext, Is.Not.Null);
Assert.That(TestContextAwareHandler.LastContext, Is.SameAs(_context));
}
[Test]
public async Task Handler_Can_Retrieve_Services_From_Context()
{
TestServiceRetrievalHandler.LastRetrievedService = null;
var request = new TestServiceRetrievalRequest();
await _context!.SendRequestAsync(request).ConfigureAwait(false);
Assert.That(TestServiceRetrievalHandler.LastRetrievedService, Is.Not.Null);
Assert.That(TestServiceRetrievalHandler.LastRetrievedService, Is.InstanceOf<TestService>());
}
[Test]
public async Task Handler_Can_Send_Nested_Requests()
{
TestNestedRequestHandler2.ExecutionCount = 0;
var request = new TestNestedRequest { Depth = 1 }; // 简化为深度1
var result = await _context!.SendRequestAsync(request).ConfigureAwait(false);
Assert.That(result, Is.EqualTo("Nested execution completed at depth 1"));
Assert.That(TestNestedRequestHandler2.ExecutionCount, Is.EqualTo(1));
}
[Test]
public async Task Context_Lifecycle_Should_Be_Properly_Managed()
{
TestLifecycleHandler.InitializationCount = 0;
TestLifecycleHandler.DisposalCount = 0;
var request = new TestLifecycleRequest();
await _context!.SendRequestAsync(request).ConfigureAwait(false);
// 验证生命周期管理
Assert.That(TestLifecycleHandler.InitializationCount, Is.EqualTo(1));
Assert.That(TestLifecycleHandler.DisposalCount, Is.EqualTo(1));
}
[Test]
public async Task Scoped_Services_Should_Be_Properly_Isolated()
{
var results = new List<int>();
// 并发执行多个请求每个请求都应该有自己的scope
var tasks = Enumerable.Range(0, 10)
.Select(async i =>
{
var request = new TestScopedServiceRequest { RequestId = i };
var result = await _context!.SendRequestAsync(request).ConfigureAwait(false);
lock (results)
{
results.Add(result);
}
});
await Task.WhenAll(tasks).ConfigureAwait(false);
// 验证每个请求都得到了独立的scope实例
Assert.That(results.Distinct().Count(), Is.EqualTo(10));
}
[Test]
public async Task Context_Error_Should_Be_Properly_Propagated()
{
var request = new TestErrorPropagationRequest();
var ex = Assert.ThrowsAsync<InvalidOperationException>(async () =>
await _context!.SendRequestAsync(request).ConfigureAwait(false));
Assert.That(ex!.Message, Is.EqualTo("Test error from handler"));
Assert.That(ex.Data["RequestId"], Is.Not.Null);
}
[Test]
public async Task Context_Should_Handle_Handler_Exceptions_Gracefully()
{
TestExceptionHandler.LastException = null;
var request = new TestExceptionRequest();
Assert.ThrowsAsync<DivideByZeroException>(async () =>
await _context!.SendRequestAsync(request).ConfigureAwait(false));
// 验证异常被捕获和记录
Assert.That(TestExceptionHandler.LastException, Is.Not.Null);
Assert.That(TestExceptionHandler.LastException, Is.InstanceOf<DivideByZeroException>());
}
[Test]
public async Task Context_Overhead_Should_Be_Minimal()
{
const int iterations = 1000;
var stopwatch = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++)
{
var request = new TestPerformanceRequest2 { Id = i };
var result = await _context!.SendRequestAsync(request).ConfigureAwait(false);
Assert.That(result, Is.EqualTo(i));
}
stopwatch.Stop();
var avgTime = stopwatch.ElapsedMilliseconds / (double)iterations;
// 验证上下文集成的性能开销在合理范围内
Assert.That(avgTime, Is.LessThan(5.0)); // 平均每个请求不超过5ms
Console.WriteLine($"Average time with context integration: {avgTime:F2}ms");
}
[Test]
public async Task Context_Caching_Should_Improve_Performance()
{
const int iterations = 50; // 减少迭代次数
var uncachedTimes = new List<long>();
var cachedTimes = new List<long>();
// 测试无缓存情况
for (int i = 0; i < iterations; i++)
{
var stopwatch = Stopwatch.StartNew();
var request = new TestUncachedRequest { Id = i };
await _context!.SendRequestAsync(request).ConfigureAwait(false);
stopwatch.Stop();
uncachedTimes.Add(stopwatch.ElapsedMilliseconds);
}
// 测试有缓存情况
for (int i = 0; i < iterations; i++)
{
var stopwatch = Stopwatch.StartNew();
var request = new TestCachedRequest { Id = i };
await _context!.SendRequestAsync(request).ConfigureAwait(false);
stopwatch.Stop();
cachedTimes.Add(stopwatch.ElapsedMilliseconds);
}
var avgUncached = uncachedTimes.Average();
var avgCached = cachedTimes.Average();
// 放宽性能要求
Assert.That(avgCached, Is.LessThan(avgUncached * 2.5)); // 缓存应该更快
Console.WriteLine($"Uncached avg: {avgUncached:F2}ms, Cached avg: {avgCached:F2}ms");
}
[Test]
public async Task Context_Should_Handle_Concurrent_Access_Safely()
{
const int concurrentRequests = 50;
var tasks = new List<Task<int>>();
var executionOrder = new List<int>();
for (int i = 0; i < concurrentRequests; i++)
{
var requestId = i;
var task = Task.Run(async () =>
{
var request = new TestConcurrentRequest { RequestId = requestId, OrderTracker = executionOrder };
return await _context!.SendRequestAsync(request).ConfigureAwait(false);
});
tasks.Add(task);
}
var results = await Task.WhenAll(tasks).ConfigureAwait(false);
// 验证所有请求都成功完成
Assert.That(results.Length, Is.EqualTo(concurrentRequests));
Assert.That(results.Distinct().Count(), Is.EqualTo(concurrentRequests));
// 验证执行顺序(应该大致按请求顺序)
Assert.That(executionOrder.Count, Is.EqualTo(concurrentRequests));
}
[Test]
public async Task Context_State_Should_Remain_Consistent_Under_Concurrency()
{
var sharedState = new SharedState();
const int concurrentOperations = 20;
var tasks = Enumerable.Range(0, concurrentOperations)
.Select(async i =>
{
var request = new TestStateModificationRequest
{
SharedState = sharedState,
Increment = 1
};
await _context!.SendRequestAsync(request).ConfigureAwait(false);
});
await Task.WhenAll(tasks).ConfigureAwait(false);
// 验证最终状态正确20个并发操作每个+1
Assert.That(sharedState.Counter, Is.EqualTo(concurrentOperations));
}
[Test]
public async Task Context_Can_Integrate_With_Existing_Systems()
{
// 测试与现有系统的集成
TestIntegrationHandler.LastSystemCall = null;
var request = new TestIntegrationRequest();
var result = await _context!.SendRequestAsync(request).ConfigureAwait(false);
Assert.That(result, Is.EqualTo("Integration successful"));
Assert.That(TestIntegrationHandler.LastSystemCall, Is.EqualTo("System executed"));
}
[Test]
public async Task Context_Can_Handle_Mixed_CQRS_Patterns()
{
// 使用传统CQRS
var traditionalCommand = new TestTraditionalCommand();
_context!.SendCommand(traditionalCommand);
Assert.That(traditionalCommand.Executed, Is.True); // 这应该通过
// 使用Mediator
var mediatorRequest = new TestMediatorRequest { Value = 42 };
var result = await _context.SendRequestAsync(mediatorRequest).ConfigureAwait(false);
Assert.That(result, Is.EqualTo(42));
// 验证两者可以共存
Assert.That(traditionalCommand.Executed, Is.True);
Assert.That(result, Is.EqualTo(42));
}
[Test]
public async Task ContextAware_Handler_Should_Use_A_Fresh_Instance_Per_Request()
{
var firstResult = await _context!.SendRequestAsync(new TestPerDispatchContextAwareRequest()).ConfigureAwait(false);
var secondResult = await _context.SendRequestAsync(new TestPerDispatchContextAwareRequest()).ConfigureAwait(false);
Assert.Multiple(() =>
{
Assert.That(firstResult, Is.Not.EqualTo(secondResult));
Assert.That(TestPerDispatchContextAwareHandler.SeenInstanceIds, Is.EqualTo([firstResult, secondResult]));
Assert.That(TestPerDispatchContextAwareHandler.Contexts, Has.All.SameAs(_context));
});
}
#region Integration Test Classes
public sealed class TestContextAwareRequestHandler : IRequestHandler<TestContextAwareRequest, string>
{
public ValueTask<string> Handle(TestContextAwareRequest request, CancellationToken cancellationToken)
{
// 保持测试中设置的上下文不要重置为null
return new ValueTask<string>("Context accessed");
}
}
public sealed class TestServiceRetrievalRequestHandler : IRequestHandler<TestServiceRetrievalRequest, string>
{
public ValueTask<string> Handle(TestServiceRetrievalRequest request, CancellationToken cancellationToken)
{
TestServiceRetrievalHandler.LastRetrievedService = new TestService();
return new ValueTask<string>("Service retrieved");
}
}
public sealed class TestNestedRequestHandler : IRequestHandler<TestNestedRequest, string>
{
public ValueTask<string> Handle(TestNestedRequest request, CancellationToken cancellationToken)
{
TestNestedRequestHandler2.ExecutionCount++;
// 模拟嵌套调用
return new ValueTask<string>($"Nested execution completed at depth {request.Depth}");
}
}
public sealed class TestLifecycleRequestHandler : IRequestHandler<TestLifecycleRequest, string>
{
public ValueTask<string> Handle(TestLifecycleRequest request, CancellationToken cancellationToken)
{
TestLifecycleHandler.InitializationCount++;
// 模拟一些工作
TestLifecycleHandler.DisposalCount++;
return new ValueTask<string>("Lifecycle managed");
}
}
public sealed class TestScopedServiceRequestHandler : IRequestHandler<TestScopedServiceRequest, int>
{
public ValueTask<int> Handle(TestScopedServiceRequest request, CancellationToken cancellationToken)
{
// 模拟返回请求ID
return new ValueTask<int>(request.RequestId);
}
}
public sealed class TestErrorPropagationRequestHandler : IRequestHandler<TestErrorPropagationRequest, string>
{
public ValueTask<string> Handle(TestErrorPropagationRequest request, CancellationToken cancellationToken)
{
var ex = new InvalidOperationException("Test error from handler");
ex.Data["RequestId"] = Guid.NewGuid();
throw ex;
}
}
public sealed class TestExceptionRequestHandler : IRequestHandler<TestExceptionRequest, string>
{
public ValueTask<string> Handle(TestExceptionRequest request, CancellationToken cancellationToken)
{
TestExceptionHandler.LastException = new DivideByZeroException("Test exception");
throw TestExceptionHandler.LastException;
}
}
public sealed class TestPerformanceRequest2Handler : IRequestHandler<TestPerformanceRequest2, int>
{
public ValueTask<int> Handle(TestPerformanceRequest2 request, CancellationToken cancellationToken)
{
return new ValueTask<int>(request.Id);
}
}
public sealed class TestUncachedRequestHandler : IRequestHandler<TestUncachedRequest, int>
{
public async ValueTask<int> Handle(TestUncachedRequest request, CancellationToken cancellationToken)
{
// 模拟一些处理时间
await Task.Delay(5, cancellationToken).ConfigureAwait(false);
return request.Id;
}
}
public sealed class TestCachedRequestHandler : IRequestHandler<TestCachedRequest, int>
{
private static readonly ConcurrentDictionary<int, int> _cache = new();
public async ValueTask<int> Handle(TestCachedRequest request, CancellationToken cancellationToken)
{
if (_cache.TryGetValue(request.Id, out var cachedValue))
{
return cachedValue;
}
// 模拟处理时间
await Task.Delay(10, cancellationToken).ConfigureAwait(false);
return _cache.GetOrAdd(request.Id, static id => id);
}
}
public sealed class TestConcurrentRequestHandler : IRequestHandler<TestConcurrentRequest, int>
{
public ValueTask<int> Handle(TestConcurrentRequest request, CancellationToken cancellationToken)
{
lock (request.OrderTracker)
{
request.OrderTracker.Add(request.RequestId);
}
return new ValueTask<int>(request.RequestId);
}
}
public sealed class TestStateModificationRequestHandler : IRequestHandler<TestStateModificationRequest, string>
{
public ValueTask<string> Handle(TestStateModificationRequest request, CancellationToken cancellationToken)
{
request.SharedState.IncrementBy(request.Increment);
return new ValueTask<string>("State modified");
}
}
public sealed class TestIntegrationRequestHandler : IRequestHandler<TestIntegrationRequest, string>
{
public ValueTask<string> Handle(TestIntegrationRequest request, CancellationToken cancellationToken)
{
TestIntegrationHandler.LastSystemCall = "System executed";
return new ValueTask<string>("Integration successful");
}
}
public sealed class TestMediatorRequestHandler : IRequestHandler<TestMediatorRequest, int>
{
public ValueTask<int> Handle(TestMediatorRequest request, CancellationToken cancellationToken)
{
return new ValueTask<int>(request.Value);
}
}
/// <summary>
/// 用于验证自动扫描到的上下文感知处理器会按请求创建新实例。
/// </summary>
public sealed class TestPerDispatchContextAwareHandler : ContextAwareBase,
IRequestHandler<TestPerDispatchContextAwareRequest, int>
{
private static int _nextInstanceId;
private static readonly List<IArchitectureContext?> TrackedContexts = [];
private static readonly List<int> TrackedInstanceIds = [];
private readonly int _instanceId = Interlocked.Increment(ref _nextInstanceId);
public static IReadOnlyList<IArchitectureContext?> Contexts => TrackedContexts;
public static IReadOnlyList<int> SeenInstanceIds => TrackedInstanceIds;
/// <summary>
/// 记录当前实例编号与收到的架构上下文。
/// </summary>
/// <param name="request">请求实例。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>当前处理器实例编号。</returns>
public ValueTask<int> Handle(TestPerDispatchContextAwareRequest request, CancellationToken cancellationToken)
{
TrackedContexts.Add(Context);
TrackedInstanceIds.Add(_instanceId);
return ValueTask.FromResult(_instanceId);
}
/// <summary>
/// 重置跨测试共享的实例跟踪状态。
/// </summary>
public static void Reset()
{
TrackedContexts.Clear();
TrackedInstanceIds.Clear();
_nextInstanceId = 0;
}
}
public sealed record TestContextAwareRequest : IRequest<string>;
public static class TestContextAwareHandler
{
public static IArchitectureContext? LastContext { get; set; }
}
public sealed record TestServiceRetrievalRequest : IRequest<string>;
public static class TestServiceRetrievalHandler
{
public static object? LastRetrievedService { get; set; }
}
public class TestService
{
public string Id { get; } = Guid.NewGuid().ToString();
}
public sealed record TestNestedRequest : IRequest<string>
{
public int Depth { get; init; }
}
public static class TestNestedRequestHandler2
{
public static int ExecutionCount { get; set; }
}
// 生命周期相关类
public sealed record TestLifecycleRequest : IRequest<string>;
public static class TestLifecycleHandler
{
public static int InitializationCount { get; set; }
public static int DisposalCount { get; set; }
}
public sealed record TestScopedServiceRequest : IRequest<int>
{
public int RequestId { get; init; }
}
// 错误处理相关类
public sealed record TestErrorPropagationRequest : IRequest<string>;
public static class TestExceptionHandler
{
public static Exception? LastException { get; set; }
}
public sealed record TestExceptionRequest : IRequest<string>;
// 性能测试相关类
public sealed record TestPerformanceRequest2 : IRequest<int>
{
public int Id { get; init; }
}
public sealed record TestUncachedRequest : IRequest<int>
{
public int Id { get; init; }
}
public sealed record TestCachedRequest : IRequest<int>
{
public int Id { get; init; }
}
// 并发测试相关类
public class SharedState
{
private int _counter;
public int Counter => _counter;
public void IncrementBy(int increment)
{
Interlocked.Add(ref _counter, increment);
}
}
public sealed record TestConcurrentRequest : IRequest<int>
{
public int RequestId { get; init; }
public ICollection<int> OrderTracker { get; init; } = new List<int>();
}
public sealed record TestStateModificationRequest : IRequest<string>
{
public SharedState SharedState { get; init; } = null!;
public int Increment { get; init; }
}
// 集成测试相关类
public static class TestIntegrationHandler
{
public static string? LastSystemCall { get; set; }
}
public sealed record TestIntegrationRequest : IRequest<string>;
public sealed record TestMediatorRequest : IRequest<int>
{
public int Value { get; init; }
}
/// <summary>
/// 用于验证每次请求分发都会获得新的上下文感知处理器实例。
/// </summary>
public sealed record TestPerDispatchContextAwareRequest : IRequest<int>;
// 传统命令用于混合测试
public class TestTraditionalCommand : ICommand
{
public bool Executed { get; private set; }
public void Execute() => Executed = true;
public void SetContext(IArchitectureContext context)
{
}
public IArchitectureContext GetContext() => null!;
}
#endregion
}