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 { /// /// 在每个用例前重置 registrar / dispatcher 的静态缓存,避免跨用例共享状态影响断言。 /// [SetUp] public void SetUp() { LoggerFactoryResolver.Provider = new ConsoleLoggerFactoryProvider(); ClearRegistrarCaches(); ClearDispatcherCaches(); } /// /// 在每个用例后清理静态缓存。 /// [TearDown] public void TearDown() { 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)])); } /// /// 验证 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")); var requestBindings = GetDispatcherCacheField("RequestDispatchBindings"); var binding = GetRequestDispatchBindingValue( requestBindings, typeof(GeneratedRequestInvokerRequest), typeof(string)); Assert.Multiple(() => { Assert.That(response, Is.EqualTo("generated:payload")); Assert.That(binding, Is.Not.Null); }); } /// /// 创建带有 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; } /// /// 清空 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")); } /// /// 通过反射读取 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()); } /// /// 读取指定请求/响应类型对当前缓存的 request dispatch binding。 /// private static object? GetRequestDispatchBindingValue(object requestBindings, Type requestType, Type responseType) { var bindingBox = requestBindings.GetType() .GetMethod("GetValueOrDefaultForTesting", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)! .Invoke(requestBindings, [requestType, responseType]); if (bindingBox is null) { return null; } return bindingBox.GetType() .GetMethod("Get", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)! .MakeGenericMethod(responseType) .Invoke(bindingBox, Array.Empty()); } }