// Copyright (c) 2025-2026 GeWuYou // SPDX-License-Identifier: Apache-2.0 using GFramework.Core.Architectures; using GFramework.Core.Abstractions.Logging; using GFramework.Core.Ioc; using GFramework.Core.Logging; using GFramework.Cqrs.Abstractions.Cqrs; using GFramework.Cqrs.Extensions; using GFramework.Cqrs.Notification; namespace GFramework.Cqrs.Tests.Cqrs; /// /// 验证 notification publisher 组合根注册扩展的关键行为。 /// [TestFixture] internal sealed class NotificationPublisherRegistrationExtensionsTests { /// /// 验证显式注册内置 后, /// 标准 runtime 基础设施会复用该策略并继续调度所有处理器。 /// [Test] public async Task UseTaskWhenAllNotificationPublisher_Should_Be_Used_By_Default_Runtime_Infrastructure() { LoggerFactoryResolver.Provider = new ConsoleLoggerFactoryProvider(); var trailingHandler = new RecordingNotificationHandler(); var container = new MicrosoftDiContainer(); container.UseTaskWhenAllNotificationPublisher(); container.Register>(new ThrowingNotificationHandler()); container.Register>(trailingHandler); CqrsTestRuntime.RegisterInfrastructure(container); container.Freeze(); var context = new ArchitectureContext(container); var publishTask = context.PublishAsync(new TestNotification()).AsTask(); try { await publishTask.ConfigureAwait(false); } catch (Exception) { // `TaskWhenAll` 策略会在所有处理器都结束后聚合失败;这里仅消费异常并继续断言第二个处理器已执行。 } Assert.That(trailingHandler.WasInvoked, Is.True); Assert.That(publishTask.Exception, Is.Not.Null); } /// /// 验证显式注册内置 后, /// 默认 runtime 基础设施会保留“首个失败立即停止后续处理器”的顺序语义。 /// [Test] public void UseSequentialNotificationPublisher_Should_Preserve_Stop_On_First_Failure_Semantics() { LoggerFactoryResolver.Provider = new ConsoleLoggerFactoryProvider(); var trailingHandler = new RecordingNotificationHandler(); var container = new MicrosoftDiContainer(); container.UseSequentialNotificationPublisher(); container.Register>(new ThrowingNotificationHandler()); container.Register>(trailingHandler); CqrsTestRuntime.RegisterInfrastructure(container); container.Freeze(); var context = new ArchitectureContext(container); Assert.That( async () => await context.PublishAsync(new TestNotification()).ConfigureAwait(false), Throws.InvalidOperationException.With.Message.EqualTo("boom")); Assert.That(trailingHandler.WasInvoked, Is.False); Assert.That(container.GetRequired(), Is.TypeOf()); } /// /// 验证显式传入实例的组合根注册入口会把同一个 publisher 实例绑定到容器。 /// [Test] public void UseNotificationPublisher_Instance_Overload_Should_Register_Same_Instance() { var container = new MicrosoftDiContainer(); var publisher = new TrackingNotificationPublisher(); var returnedContainer = container.UseNotificationPublisher(publisher); Assert.That(returnedContainer, Is.SameAs(container)); Assert.That(container.Get(), Is.SameAs(publisher)); } /// /// 验证组合根扩展会阻止重复 notification publisher 注册,避免 runtime 创建阶段才暴露歧义。 /// [Test] public void UseNotificationPublisher_Should_Throw_When_NotificationPublisher_Already_Registered() { var container = new MicrosoftDiContainer(); container.UseTaskWhenAllNotificationPublisher(); Assert.That( () => container.UseNotificationPublisher(new TrackingNotificationPublisher()), Throws.InvalidOperationException.With.Message.Contains(nameof(INotificationPublisher))); } /// /// 为本组测试提供最小 notification 类型。 /// private sealed record TestNotification : INotification; /// /// 记录自己是否被执行的测试处理器。 /// private sealed class RecordingNotificationHandler : INotificationHandler { /// /// 获取当前处理器是否至少执行过一次。 /// public bool WasInvoked { get; private set; } /// /// 记录执行痕迹并立刻完成。 /// public ValueTask Handle(TestNotification notification, CancellationToken cancellationToken) { ArgumentNullException.ThrowIfNull(notification); cancellationToken.ThrowIfCancellationRequested(); WasInvoked = true; return ValueTask.CompletedTask; } } /// /// 始终抛出异常的测试处理器,用于验证并行策略不会因为首个失败而停止其余处理器。 /// private sealed class ThrowingNotificationHandler : INotificationHandler { /// /// 始终抛出测试异常。 /// public ValueTask Handle(TestNotification notification, CancellationToken cancellationToken) { ArgumentNullException.ThrowIfNull(notification); cancellationToken.ThrowIfCancellationRequested(); throw new InvalidOperationException("boom"); } } /// /// 用于验证实例注册重载是否保留原对象身份的测试发布器。 /// private sealed class TrackingNotificationPublisher : INotificationPublisher { /// /// 直接完成当前 publish 调用。 /// public ValueTask PublishAsync( NotificationPublishContext context, CancellationToken cancellationToken = default) where TNotification : INotification { ArgumentNullException.ThrowIfNull(context); cancellationToken.ThrowIfCancellationRequested(); return ValueTask.CompletedTask; } } }