diff --git a/GFramework.Cqrs.Tests/Cqrs/AlphaDeterministicNotificationHandler.cs b/GFramework.Cqrs.Tests/Cqrs/AlphaDeterministicNotificationHandler.cs new file mode 100644 index 00000000..5142d5b7 --- /dev/null +++ b/GFramework.Cqrs.Tests/Cqrs/AlphaDeterministicNotificationHandler.cs @@ -0,0 +1,23 @@ +using System.Threading; +using System.Threading.Tasks; +using GFramework.Cqrs.Abstractions.Cqrs; + +namespace GFramework.Cqrs.Tests.Cqrs; + +/// +/// 名称排序上应先于 Zeta 处理器执行的通知处理器。 +/// +internal sealed class AlphaDeterministicNotificationHandler : INotificationHandler +{ + /// + /// 记录当前处理器已执行。 + /// + /// 通知实例。 + /// 取消令牌。 + /// 已完成任务。 + public ValueTask Handle(DeterministicOrderNotification notification, CancellationToken cancellationToken) + { + DeterministicNotificationHandlerState.InvocationOrder.Add(nameof(AlphaDeterministicNotificationHandler)); + return ValueTask.CompletedTask; + } +} diff --git a/GFramework.Cqrs.Tests/Cqrs/CapturingLoggerFactoryProvider.cs b/GFramework.Cqrs.Tests/Cqrs/CapturingLoggerFactoryProvider.cs new file mode 100644 index 00000000..00f3df65 --- /dev/null +++ b/GFramework.Cqrs.Tests/Cqrs/CapturingLoggerFactoryProvider.cs @@ -0,0 +1,49 @@ +using System.Collections.Generic; +using GFramework.Core.Abstractions.Logging; +using GFramework.Core.Logging; +using GFramework.Cqrs.Tests.Logging; + +namespace GFramework.Cqrs.Tests.Cqrs; + +/// +/// 为 CQRS 注册测试捕获真实启动路径中创建的日志记录器。 +/// +/// +/// 处理器注册入口会分别为测试运行时、容器和注册器创建日志器。 +/// 该提供程序统一保留这些测试日志器,以便断言警告是否经由公开入口真正发出。 +/// +internal sealed class CapturingLoggerFactoryProvider : ILoggerFactoryProvider +{ + private readonly List _loggers = []; + + /// + /// 使用指定的最小日志级别初始化一个新的捕获型日志工厂提供程序。 + /// + /// 要应用到新建测试日志器的最小日志级别。 + public CapturingLoggerFactoryProvider(LogLevel minLevel = LogLevel.Info) + { + MinLevel = minLevel; + } + + /// + /// 获取通过当前提供程序创建的全部测试日志器。 + /// + public IReadOnlyList Loggers => _loggers; + + /// + /// 获取或设置新建测试日志器的最小日志级别。 + /// + public LogLevel MinLevel { get; set; } + + /// + /// 创建一个测试日志器并将其纳入捕获集合。 + /// + /// 日志记录器名称。 + /// 用于后续断言的测试日志器。 + public ILogger CreateLogger(string name) + { + var logger = new TestLogger(name, MinLevel); + _loggers.Add(logger); + return logger; + } +} diff --git a/GFramework.Cqrs.Tests/Cqrs/CqrsHandlerRegistrarTests.cs b/GFramework.Cqrs.Tests/Cqrs/CqrsHandlerRegistrarTests.cs index dcbfbd2a..76a2b922 100644 --- a/GFramework.Cqrs.Tests/Cqrs/CqrsHandlerRegistrarTests.cs +++ b/GFramework.Cqrs.Tests/Cqrs/CqrsHandlerRegistrarTests.cs @@ -614,230 +614,3 @@ internal sealed class CqrsHandlerRegistrarTests .GetType("GFramework.Cqrs.Internal.CqrsHandlerRegistrar", throwOnError: true)!; } } - -/// -/// 记录确定性通知处理器的实际执行顺序。 -/// -internal static class DeterministicNotificationHandlerState -{ - /// - /// 获取当前测试中的通知处理器执行顺序。 - /// - public static List InvocationOrder { get; } = []; - - /// - /// 重置共享的执行顺序状态。 - /// - public static void Reset() - { - InvocationOrder.Clear(); - } -} - -/// -/// 用于验证同一通知的多个处理器是否按稳定顺序执行。 -/// -internal sealed record DeterministicOrderNotification : INotification; - -/// -/// 故意放在 Alpha 之前声明,用于验证注册器不会依赖源码声明顺序。 -/// -internal sealed class ZetaDeterministicNotificationHandler : INotificationHandler -{ - /// - /// 记录当前处理器已执行。 - /// - /// 通知实例。 - /// 取消令牌。 - /// 已完成任务。 - public ValueTask Handle(DeterministicOrderNotification notification, CancellationToken cancellationToken) - { - DeterministicNotificationHandlerState.InvocationOrder.Add(nameof(ZetaDeterministicNotificationHandler)); - return ValueTask.CompletedTask; - } -} - -/// -/// 名称排序上应先于 Zeta 处理器执行的通知处理器。 -/// -internal sealed class AlphaDeterministicNotificationHandler : INotificationHandler -{ - /// - /// 记录当前处理器已执行。 - /// - /// 通知实例。 - /// 取消令牌。 - /// 已完成任务。 - public ValueTask Handle(DeterministicOrderNotification notification, CancellationToken cancellationToken) - { - DeterministicNotificationHandlerState.InvocationOrder.Add(nameof(AlphaDeterministicNotificationHandler)); - return ValueTask.CompletedTask; - } -} - -/// -/// 为 CQRS 注册测试捕获真实启动路径中创建的日志记录器。 -/// -/// -/// 处理器注册入口会分别为测试运行时、容器和注册器创建日志器。 -/// 该提供程序统一保留这些测试日志器,以便断言警告是否经由公开入口真正发出。 -/// -internal sealed class CapturingLoggerFactoryProvider : ILoggerFactoryProvider -{ - private readonly List _loggers = []; - - /// - /// 使用指定的最小日志级别初始化一个新的捕获型日志工厂提供程序。 - /// - /// 要应用到新建测试日志器的最小日志级别。 - public CapturingLoggerFactoryProvider(LogLevel minLevel = LogLevel.Info) - { - MinLevel = minLevel; - } - - /// - /// 获取通过当前提供程序创建的全部测试日志器。 - /// - public IReadOnlyList Loggers => _loggers; - - /// - /// 获取或设置新建测试日志器的最小日志级别。 - /// - public LogLevel MinLevel { get; set; } - - /// - /// 创建一个测试日志器并将其纳入捕获集合。 - /// - /// 日志记录器名称。 - /// 用于后续断言的测试日志器。 - public ILogger CreateLogger(string name) - { - var logger = new TestLogger(name, MinLevel); - _loggers.Add(logger); - return logger; - } -} - -/// -/// 用于验证生成注册器路径的通知消息。 -/// -internal sealed record GeneratedRegistryNotification : INotification; - -/// -/// 由模拟的源码生成注册器显式注册的通知处理器。 -/// -internal sealed class GeneratedRegistryNotificationHandler : INotificationHandler -{ - /// - /// 处理生成注册器测试中的通知。 - /// - /// 通知实例。 - /// 取消令牌。 - /// 已完成任务。 - public ValueTask Handle(GeneratedRegistryNotification notification, CancellationToken cancellationToken) - { - return ValueTask.CompletedTask; - } -} - -/// -/// 模拟源码生成器为某个程序集生成的 CQRS 处理器注册器。 -/// -internal sealed class GeneratedNotificationHandlerRegistry : ICqrsHandlerRegistry -{ - /// - /// 将测试通知处理器注册到目标服务集合。 - /// - /// 承载处理器映射的服务集合。 - /// 用于记录注册诊断的日志器。 - public void Register(IServiceCollection services, ILogger logger) - { - ArgumentNullException.ThrowIfNull(services); - ArgumentNullException.ThrowIfNull(logger); - - services.AddTransient( - typeof(INotificationHandler), - typeof(GeneratedRegistryNotificationHandler)); - logger.Debug( - $"Registered CQRS handler {typeof(GeneratedRegistryNotificationHandler).FullName} as {typeof(INotificationHandler).FullName}."); - } -} - -/// -/// 用于验证“生成注册器 + reflection fallback”组合路径的私有嵌套处理器容器。 -/// -internal sealed class ReflectionFallbackNotificationContainer -{ - /// - /// 获取仅能通过反射补扫接入的私有嵌套处理器类型。 - /// - public static Type ReflectionOnlyHandlerType => typeof(ReflectionOnlyGeneratedRegistryNotificationHandler); - - private sealed class ReflectionOnlyGeneratedRegistryNotificationHandler - : INotificationHandler - { - /// - /// 处理测试通知。 - /// - /// 通知实例。 - /// 取消令牌。 - /// 已完成任务。 - public ValueTask Handle(GeneratedRegistryNotification notification, CancellationToken cancellationToken) - { - return ValueTask.CompletedTask; - } - } -} - -/// -/// 模拟局部生成注册器场景中,仅注册“可由生成代码直接引用”的那部分 handlers。 -/// -internal sealed class PartialGeneratedNotificationHandlerRegistry : ICqrsHandlerRegistry -{ - /// - /// 将生成路径可见的通知处理器注册到目标服务集合。 - /// - /// 承载处理器映射的服务集合。 - /// 用于记录注册诊断的日志器。 - public void Register(IServiceCollection services, ILogger logger) - { - ArgumentNullException.ThrowIfNull(services); - ArgumentNullException.ThrowIfNull(logger); - - services.AddTransient( - typeof(INotificationHandler), - typeof(GeneratedRegistryNotificationHandler)); - logger.Debug( - $"Registered CQRS handler {typeof(GeneratedRegistryNotificationHandler).FullName} as {typeof(INotificationHandler).FullName}."); - } -} - -/// -/// 模拟生成注册器使用私有无参构造器的场景,验证运行时仍可通过缓存工厂激活它。 -/// -internal sealed class PrivateConstructorNotificationHandlerRegistry : ICqrsHandlerRegistry -{ - /// - /// 初始化一个新的私有生成注册器实例。 - /// - private PrivateConstructorNotificationHandlerRegistry() - { - } - - /// - /// 将测试通知处理器注册到目标服务集合。 - /// - /// 承载处理器映射的服务集合。 - /// 用于记录注册诊断的日志器。 - public void Register(IServiceCollection services, ILogger logger) - { - ArgumentNullException.ThrowIfNull(services); - ArgumentNullException.ThrowIfNull(logger); - - services.AddTransient( - typeof(INotificationHandler), - typeof(GeneratedRegistryNotificationHandler)); - logger.Debug( - $"Registered CQRS handler {typeof(GeneratedRegistryNotificationHandler).FullName} as {typeof(INotificationHandler).FullName}."); - } -} diff --git a/GFramework.Cqrs.Tests/Cqrs/DeterministicNotificationHandlerState.cs b/GFramework.Cqrs.Tests/Cqrs/DeterministicNotificationHandlerState.cs new file mode 100644 index 00000000..957eda1d --- /dev/null +++ b/GFramework.Cqrs.Tests/Cqrs/DeterministicNotificationHandlerState.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; + +namespace GFramework.Cqrs.Tests.Cqrs; + +/// +/// 记录确定性通知处理器的实际执行顺序。 +/// +internal static class DeterministicNotificationHandlerState +{ + /// + /// 获取当前测试中的通知处理器执行顺序。 + /// + public static List InvocationOrder { get; } = []; + + /// + /// 重置共享的执行顺序状态。 + /// + public static void Reset() + { + InvocationOrder.Clear(); + } +} diff --git a/GFramework.Cqrs.Tests/Cqrs/DeterministicOrderNotification.cs b/GFramework.Cqrs.Tests/Cqrs/DeterministicOrderNotification.cs new file mode 100644 index 00000000..9bc0e059 --- /dev/null +++ b/GFramework.Cqrs.Tests/Cqrs/DeterministicOrderNotification.cs @@ -0,0 +1,8 @@ +using GFramework.Cqrs.Abstractions.Cqrs; + +namespace GFramework.Cqrs.Tests.Cqrs; + +/// +/// 用于验证同一通知的多个处理器是否按稳定顺序执行。 +/// +internal sealed record DeterministicOrderNotification : INotification; diff --git a/GFramework.Cqrs.Tests/Cqrs/GeneratedNotificationHandlerRegistry.cs b/GFramework.Cqrs.Tests/Cqrs/GeneratedNotificationHandlerRegistry.cs new file mode 100644 index 00000000..ab2c344f --- /dev/null +++ b/GFramework.Cqrs.Tests/Cqrs/GeneratedNotificationHandlerRegistry.cs @@ -0,0 +1,30 @@ +using System; +using GFramework.Core.Abstractions.Logging; +using GFramework.Core.Ioc; +using GFramework.Core.Logging; +using GFramework.Cqrs.Abstractions.Cqrs; + +namespace GFramework.Cqrs.Tests.Cqrs; + +/// +/// 模拟源码生成器为某个程序集生成的 CQRS 处理器注册器。 +/// +internal sealed class GeneratedNotificationHandlerRegistry : ICqrsHandlerRegistry +{ + /// + /// 将测试通知处理器注册到目标服务集合。 + /// + /// 承载处理器映射的服务集合。 + /// 用于记录注册诊断的日志器。 + public void Register(IServiceCollection services, ILogger logger) + { + ArgumentNullException.ThrowIfNull(services); + ArgumentNullException.ThrowIfNull(logger); + + services.AddTransient( + typeof(INotificationHandler), + typeof(GeneratedRegistryNotificationHandler)); + logger.Debug( + $"Registered CQRS handler {typeof(GeneratedRegistryNotificationHandler).FullName} as {typeof(INotificationHandler).FullName}."); + } +} diff --git a/GFramework.Cqrs.Tests/Cqrs/GeneratedRegistryNotification.cs b/GFramework.Cqrs.Tests/Cqrs/GeneratedRegistryNotification.cs new file mode 100644 index 00000000..98f79991 --- /dev/null +++ b/GFramework.Cqrs.Tests/Cqrs/GeneratedRegistryNotification.cs @@ -0,0 +1,8 @@ +using GFramework.Cqrs.Abstractions.Cqrs; + +namespace GFramework.Cqrs.Tests.Cqrs; + +/// +/// 用于验证生成注册器路径的通知消息。 +/// +internal sealed record GeneratedRegistryNotification : INotification; diff --git a/GFramework.Cqrs.Tests/Cqrs/GeneratedRegistryNotificationHandler.cs b/GFramework.Cqrs.Tests/Cqrs/GeneratedRegistryNotificationHandler.cs new file mode 100644 index 00000000..082b16f0 --- /dev/null +++ b/GFramework.Cqrs.Tests/Cqrs/GeneratedRegistryNotificationHandler.cs @@ -0,0 +1,22 @@ +using System.Threading; +using System.Threading.Tasks; +using GFramework.Cqrs.Abstractions.Cqrs; + +namespace GFramework.Cqrs.Tests.Cqrs; + +/// +/// 由模拟的源码生成注册器显式注册的通知处理器。 +/// +internal sealed class GeneratedRegistryNotificationHandler : INotificationHandler +{ + /// + /// 处理生成注册器测试中的通知。 + /// + /// 通知实例。 + /// 取消令牌。 + /// 已完成任务。 + public ValueTask Handle(GeneratedRegistryNotification notification, CancellationToken cancellationToken) + { + return ValueTask.CompletedTask; + } +} diff --git a/GFramework.Cqrs.Tests/Cqrs/PartialGeneratedNotificationHandlerRegistry.cs b/GFramework.Cqrs.Tests/Cqrs/PartialGeneratedNotificationHandlerRegistry.cs new file mode 100644 index 00000000..6bbcfb36 --- /dev/null +++ b/GFramework.Cqrs.Tests/Cqrs/PartialGeneratedNotificationHandlerRegistry.cs @@ -0,0 +1,30 @@ +using System; +using GFramework.Core.Abstractions.Logging; +using GFramework.Core.Ioc; +using GFramework.Core.Logging; +using GFramework.Cqrs.Abstractions.Cqrs; + +namespace GFramework.Cqrs.Tests.Cqrs; + +/// +/// 模拟局部生成注册器场景中,仅注册“可由生成代码直接引用”的那部分 handlers。 +/// +internal sealed class PartialGeneratedNotificationHandlerRegistry : ICqrsHandlerRegistry +{ + /// + /// 将生成路径可见的通知处理器注册到目标服务集合。 + /// + /// 承载处理器映射的服务集合。 + /// 用于记录注册诊断的日志器。 + public void Register(IServiceCollection services, ILogger logger) + { + ArgumentNullException.ThrowIfNull(services); + ArgumentNullException.ThrowIfNull(logger); + + services.AddTransient( + typeof(INotificationHandler), + typeof(GeneratedRegistryNotificationHandler)); + logger.Debug( + $"Registered CQRS handler {typeof(GeneratedRegistryNotificationHandler).FullName} as {typeof(INotificationHandler).FullName}."); + } +} diff --git a/GFramework.Cqrs.Tests/Cqrs/PrivateConstructorNotificationHandlerRegistry.cs b/GFramework.Cqrs.Tests/Cqrs/PrivateConstructorNotificationHandlerRegistry.cs new file mode 100644 index 00000000..b9bb1b06 --- /dev/null +++ b/GFramework.Cqrs.Tests/Cqrs/PrivateConstructorNotificationHandlerRegistry.cs @@ -0,0 +1,37 @@ +using System; +using GFramework.Core.Abstractions.Logging; +using GFramework.Core.Ioc; +using GFramework.Core.Logging; +using GFramework.Cqrs.Abstractions.Cqrs; + +namespace GFramework.Cqrs.Tests.Cqrs; + +/// +/// 模拟生成注册器使用私有无参构造器的场景,验证运行时仍可通过缓存工厂激活它。 +/// +internal sealed class PrivateConstructorNotificationHandlerRegistry : ICqrsHandlerRegistry +{ + /// + /// 初始化一个新的私有生成注册器实例。 + /// + private PrivateConstructorNotificationHandlerRegistry() + { + } + + /// + /// 将测试通知处理器注册到目标服务集合。 + /// + /// 承载处理器映射的服务集合。 + /// 用于记录注册诊断的日志器。 + public void Register(IServiceCollection services, ILogger logger) + { + ArgumentNullException.ThrowIfNull(services); + ArgumentNullException.ThrowIfNull(logger); + + services.AddTransient( + typeof(INotificationHandler), + typeof(GeneratedRegistryNotificationHandler)); + logger.Debug( + $"Registered CQRS handler {typeof(GeneratedRegistryNotificationHandler).FullName} as {typeof(INotificationHandler).FullName}."); + } +} diff --git a/GFramework.Cqrs.Tests/Cqrs/ReflectionFallbackNotificationContainer.cs b/GFramework.Cqrs.Tests/Cqrs/ReflectionFallbackNotificationContainer.cs new file mode 100644 index 00000000..b0475e01 --- /dev/null +++ b/GFramework.Cqrs.Tests/Cqrs/ReflectionFallbackNotificationContainer.cs @@ -0,0 +1,32 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using GFramework.Cqrs.Abstractions.Cqrs; + +namespace GFramework.Cqrs.Tests.Cqrs; + +/// +/// 用于验证“生成注册器 + reflection fallback”组合路径的私有嵌套处理器容器。 +/// +internal sealed class ReflectionFallbackNotificationContainer +{ + /// + /// 获取仅能通过反射补扫接入的私有嵌套处理器类型。 + /// + public static Type ReflectionOnlyHandlerType => typeof(ReflectionOnlyGeneratedRegistryNotificationHandler); + + private sealed class ReflectionOnlyGeneratedRegistryNotificationHandler + : INotificationHandler + { + /// + /// 处理测试通知。 + /// + /// 通知实例。 + /// 取消令牌。 + /// 已完成任务。 + public ValueTask Handle(GeneratedRegistryNotification notification, CancellationToken cancellationToken) + { + return ValueTask.CompletedTask; + } + } +} diff --git a/GFramework.Cqrs.Tests/Cqrs/ZetaDeterministicNotificationHandler.cs b/GFramework.Cqrs.Tests/Cqrs/ZetaDeterministicNotificationHandler.cs new file mode 100644 index 00000000..10ff18e0 --- /dev/null +++ b/GFramework.Cqrs.Tests/Cqrs/ZetaDeterministicNotificationHandler.cs @@ -0,0 +1,23 @@ +using System.Threading; +using System.Threading.Tasks; +using GFramework.Cqrs.Abstractions.Cqrs; + +namespace GFramework.Cqrs.Tests.Cqrs; + +/// +/// 故意放在 Alpha 之前声明,用于验证注册器不会依赖源码声明顺序。 +/// +internal sealed class ZetaDeterministicNotificationHandler : INotificationHandler +{ + /// + /// 记录当前处理器已执行。 + /// + /// 通知实例。 + /// 取消令牌。 + /// 已完成任务。 + public ValueTask Handle(DeterministicOrderNotification notification, CancellationToken cancellationToken) + { + DeterministicNotificationHandlerState.InvocationOrder.Add(nameof(ZetaDeterministicNotificationHandler)); + return ValueTask.CompletedTask; + } +}