using System.Reflection; 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; /// /// 验证 generated request invoker provider 的 registrar 接线与 dispatcher 消费语义。 /// [TestFixture] [NonParallelizable] internal sealed class CqrsGeneratedRequestInvokerProviderTests { private ILoggerFactoryProvider? _previousLoggerFactoryProvider; /// /// 在每个用例前重置 registrar / dispatcher 的静态缓存,避免跨用例共享状态影响断言。 /// [SetUp] public void SetUp() { _previousLoggerFactoryProvider = LoggerFactoryResolver.Provider; LoggerFactoryResolver.Provider = new ConsoleLoggerFactoryProvider(); ClearRegistrarCaches(); ClearDispatcherCaches(); } /// /// 在每个用例后清理静态缓存。 /// [TearDown] public void TearDown() { LoggerFactoryResolver.Provider = _previousLoggerFactoryProvider ?? new ConsoleLoggerFactoryProvider(); ClearRegistrarCaches(); ClearDispatcherCaches(); } /// /// 验证 registrar 激活 generated registry 后,会把 request invoker provider 注册到容器中。 /// [Test] public void RegisterHandlers_Should_Register_Generated_Request_Invoker_Provider() { var generatedAssembly = CreateGeneratedRequestInvokerAssembly(); var container = new MicrosoftDiContainer(); CqrsTestRuntime.RegisterHandlers(container, generatedAssembly.Object); var providers = container.GetAll(); Assert.That( providers.Select(static provider => provider.GetType()), Is.EqualTo([typeof(GeneratedRequestInvokerProviderRegistry)])); } /// /// 验证当实现类型隐藏、但 request handler interface 仍可直接表达时, /// registrar 仍会把 generated request invoker provider 注册到容器中。 /// [Test] public void RegisterHandlers_Should_Register_Generated_Request_Invoker_Provider_For_Hidden_Implementation() { var generatedAssembly = CreateHiddenImplementationGeneratedRequestInvokerAssembly(); var container = new MicrosoftDiContainer(); CqrsTestRuntime.RegisterHandlers(container, generatedAssembly.Object); var providers = container.GetAll(); Assert.That( providers.Select(static provider => provider.GetType()), Is.EqualTo([typeof(HiddenImplementationGeneratedRequestInvokerProviderRegistry)])); } /// /// 验证 registrar 激活 generated registry 后,会把 stream invoker provider 注册到容器中。 /// [Test] public void RegisterHandlers_Should_Register_Generated_Stream_Invoker_Provider() { var generatedAssembly = CreateGeneratedStreamInvokerAssembly(); var container = new MicrosoftDiContainer(); CqrsTestRuntime.RegisterHandlers(container, generatedAssembly.Object); var providers = container.GetAll(); Assert.That( providers.Select(static provider => provider.GetType()), Is.EqualTo([typeof(GeneratedStreamInvokerProviderRegistry)])); } /// /// 验证当实现类型隐藏、但 stream handler interface 仍可直接表达时, /// registrar 仍会把 generated stream invoker provider 注册到容器中。 /// [Test] public void RegisterHandlers_Should_Register_Generated_Stream_Invoker_Provider_For_Hidden_Implementation() { var generatedAssembly = CreateHiddenImplementationGeneratedStreamInvokerAssembly(); var container = new MicrosoftDiContainer(); CqrsTestRuntime.RegisterHandlers(container, generatedAssembly.Object); var providers = container.GetAll(); Assert.That( providers.Select(static provider => provider.GetType()), Is.EqualTo([typeof(HiddenImplementationGeneratedStreamInvokerProviderRegistry)])); } /// /// 验证 dispatcher 在首次创建 request binding 时,会优先消费 generated request invoker provider。 /// [Test] public async Task SendAsync_Should_Use_Generated_Request_Invoker_When_Provider_Is_Registered() { var generatedAssembly = CreateGeneratedRequestInvokerAssembly(); var container = new MicrosoftDiContainer(); CqrsTestRuntime.RegisterHandlers(container, generatedAssembly.Object); container.Freeze(); var context = new ArchitectureContext(container); var response = await context.SendRequestAsync(new GeneratedRequestInvokerRequest("payload")); Assert.That(response, Is.EqualTo("generated:payload")); } /// /// 验证当实现类型隐藏、但 request handler interface 仍可直接表达时, /// dispatcher 仍会消费 generated request invoker descriptor。 /// [Test] public async Task SendAsync_Should_Use_Generated_Request_Invoker_For_Hidden_Implementation_When_Provider_Is_Registered() { var generatedAssembly = CreateHiddenImplementationGeneratedRequestInvokerAssembly(); var container = new MicrosoftDiContainer(); CqrsTestRuntime.RegisterHandlers(container, generatedAssembly.Object); container.Freeze(); var context = new ArchitectureContext(container); var response = await context.SendRequestAsync( new HiddenImplementationRequestInvokerContainer.VisibleRequest("payload")); Assert.That(response, Is.EqualTo("generated-hidden:payload")); } /// /// 验证 dispatcher 在首次创建 stream binding 时,会优先消费 generated stream invoker provider。 /// [Test] public async Task CreateStream_Should_Use_Generated_Stream_Invoker_When_Provider_Is_Registered() { var generatedAssembly = CreateGeneratedStreamInvokerAssembly(); var container = new MicrosoftDiContainer(); CqrsTestRuntime.RegisterHandlers(container, generatedAssembly.Object); container.Freeze(); var context = new ArchitectureContext(container); var results = await DrainAsync(context.CreateStream(new GeneratedStreamInvokerRequest(3))); Assert.That(results, Is.EqualTo([30, 31])); } /// /// 验证当实现类型隐藏、但 stream handler interface 仍可直接表达时, /// dispatcher 仍会消费 generated stream invoker descriptor。 /// [Test] public async Task CreateStream_Should_Use_Generated_Stream_Invoker_For_Hidden_Implementation_When_Provider_Is_Registered() { var generatedAssembly = CreateHiddenImplementationGeneratedStreamInvokerAssembly(); var container = new MicrosoftDiContainer(); CqrsTestRuntime.RegisterHandlers(container, generatedAssembly.Object); container.Freeze(); var context = new ArchitectureContext(container); var results = await DrainAsync( context.CreateStream(new HiddenImplementationStreamInvokerContainer.VisibleStreamRequest(3))); Assert.That(results, Is.EqualTo([300, 301])); } /// /// 创建带有 generated request invoker registry 元数据的程序集替身。 /// private static Mock CreateGeneratedRequestInvokerAssembly() { var generatedAssembly = new Mock(); generatedAssembly .SetupGet(static assembly => assembly.FullName) .Returns("GFramework.Cqrs.Tests.Cqrs.GeneratedRequestInvokerAssembly, Version=1.0.0.0"); generatedAssembly .Setup(static assembly => assembly.GetCustomAttributes(typeof(CqrsHandlerRegistryAttribute), false)) .Returns([new CqrsHandlerRegistryAttribute(typeof(GeneratedRequestInvokerProviderRegistry))]); return generatedAssembly; } /// /// 创建带有 generated stream invoker registry 元数据的程序集替身。 /// private static Mock CreateGeneratedStreamInvokerAssembly() { var generatedAssembly = new Mock(); generatedAssembly .SetupGet(static assembly => assembly.FullName) .Returns("GFramework.Cqrs.Tests.Cqrs.GeneratedStreamInvokerAssembly, Version=1.0.0.0"); generatedAssembly .Setup(static assembly => assembly.GetCustomAttributes(typeof(CqrsHandlerRegistryAttribute), false)) .Returns([new CqrsHandlerRegistryAttribute(typeof(GeneratedStreamInvokerProviderRegistry))]); return generatedAssembly; } /// /// 创建带有 hidden implementation request invoker registry 元数据的程序集替身。 /// private static Mock CreateHiddenImplementationGeneratedRequestInvokerAssembly() { var generatedAssembly = new Mock(); generatedAssembly .SetupGet(static assembly => assembly.FullName) .Returns("GFramework.Cqrs.Tests.Cqrs.HiddenGeneratedRequestInvokerAssembly, Version=1.0.0.0"); generatedAssembly .Setup(static assembly => assembly.GetCustomAttributes(typeof(CqrsHandlerRegistryAttribute), false)) .Returns([new CqrsHandlerRegistryAttribute(typeof(HiddenImplementationGeneratedRequestInvokerProviderRegistry))]); return generatedAssembly; } /// /// 创建带有 hidden implementation stream invoker registry 元数据的程序集替身。 /// private static Mock CreateHiddenImplementationGeneratedStreamInvokerAssembly() { var generatedAssembly = new Mock(); generatedAssembly .SetupGet(static assembly => assembly.FullName) .Returns("GFramework.Cqrs.Tests.Cqrs.HiddenGeneratedStreamInvokerAssembly, Version=1.0.0.0"); generatedAssembly .Setup(static assembly => assembly.GetCustomAttributes(typeof(CqrsHandlerRegistryAttribute), false)) .Returns([new CqrsHandlerRegistryAttribute(typeof(HiddenImplementationGeneratedStreamInvokerProviderRegistry))]); return generatedAssembly; } /// /// 清空 registrar 静态缓存。 /// private static void ClearRegistrarCaches() { ClearCache(GetRegistrarCacheField("AssemblyMetadataCache")); ClearCache(GetRegistrarCacheField("RegistryActivationMetadataCache")); ClearCache(GetRegistrarCacheField("LoadableTypesCache")); ClearCache(GetRegistrarCacheField("SupportedHandlerInterfacesCache")); } /// /// 清空 dispatcher 静态缓存。 /// private static void ClearDispatcherCaches() { ClearCache(GetDispatcherCacheField("NotificationDispatchBindings")); ClearCache(GetDispatcherCacheField("RequestDispatchBindings")); ClearCache(GetDispatcherCacheField("StreamDispatchBindings")); ClearCache(GetDispatcherCacheField("GeneratedRequestInvokers")); ClearCache(GetDispatcherCacheField("GeneratedStreamInvokers")); } /// /// 通过反射读取 registrar 的静态缓存字段。 /// private static object GetRegistrarCacheField(string fieldName) { var field = typeof(CqrsReflectionFallbackAttribute).Assembly .GetType("GFramework.Cqrs.Internal.CqrsHandlerRegistrar", throwOnError: true)! .GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Static); Assert.That(field, Is.Not.Null, $"Missing registrar cache field {fieldName}."); return field!.GetValue(null) ?? throw new InvalidOperationException($"Registrar cache field {fieldName} returned null."); } /// /// 通过反射读取 dispatcher 的静态缓存字段。 /// private static object GetDispatcherCacheField(string fieldName) { var field = typeof(CqrsReflectionFallbackAttribute).Assembly .GetType("GFramework.Cqrs.Internal.CqrsDispatcher", throwOnError: true)! .GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Static); Assert.That(field, Is.Not.Null, $"Missing dispatcher cache field {fieldName}."); return field!.GetValue(null) ?? throw new InvalidOperationException($"Dispatcher cache field {fieldName} returned null."); } /// /// 清空目标缓存实例。 /// private static void ClearCache(object cache) { _ = cache.GetType() .GetMethod("Clear", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)! .Invoke(cache, Array.Empty()); } /// /// 枚举并收集当前异步流中的全部元素,便于断言 generated stream invoker 的输出。 /// /// 流元素类型。 /// 待消耗的异步流。 /// 按产出顺序收集得到的元素列表。 private static async Task> DrainAsync(IAsyncEnumerable stream) { ArgumentNullException.ThrowIfNull(stream); var items = new List(); await foreach (var item in stream.ConfigureAwait(false)) { items.Add(item); } return items; } }