From 337ffbd5808e39099aab613573937e88f40950d6 Mon Sep 17 00:00:00 2001 From: gewuyou <95328647+GeWuYou@users.noreply.github.com> Date: Mon, 11 May 2026 12:37:55 +0800 Subject: [PATCH] =?UTF-8?q?test(cqrs):=20=E8=A1=A5=E9=BD=90=E9=80=9A?= =?UTF-8?q?=E7=9F=A5=E5=8F=91=E5=B8=83=E5=99=A8=E8=A7=A3=E6=9E=90=E7=BC=93?= =?UTF-8?q?=E5=AD=98=E5=9B=9E=E5=BD=92=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 notification publisher 多实例冲突时抛错的回归测试 - 补充首次解析后复用同一 publisher 且不重复查容器的缓存测试 - 更新测试发布器计数以验证缓存命中的发布调用次数 --- .../Cqrs/CqrsNotificationPublisherTests.cs | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/GFramework.Cqrs.Tests/Cqrs/CqrsNotificationPublisherTests.cs b/GFramework.Cqrs.Tests/Cqrs/CqrsNotificationPublisherTests.cs index d862652d..485c11e7 100644 --- a/GFramework.Cqrs.Tests/Cqrs/CqrsNotificationPublisherTests.cs +++ b/GFramework.Cqrs.Tests/Cqrs/CqrsNotificationPublisherTests.cs @@ -91,6 +91,70 @@ internal sealed class CqrsNotificationPublisherTests Assert.That(handler.ObservedContext, Is.SameAs(architectureContext.Object)); } + /// + /// 验证当容器里可见多个通知发布策略时,dispatcher 会拒绝在歧义状态下继续发布。 + /// + [Test] + public void PublishAsync_Should_Throw_When_Multiple_NotificationPublishers_Are_Registered() + { + var runtime = CreateRuntime( + container => + { + container + .Setup(currentContainer => currentContainer.GetAll(typeof(INotificationHandler))) + .Returns([new RecordingNotificationHandler("only", [])]); + container + .Setup(currentContainer => currentContainer.GetAll(typeof(INotificationPublisher))) + .Returns( + [ + new TrackingNotificationPublisher(), + new TrackingNotificationPublisher() + ]); + }); + + Assert.That( + async () => await runtime.PublishAsync(new FakeCqrsContext(), new PublisherNotification()).ConfigureAwait(false), + Throws.InvalidOperationException.With.Message.EqualTo( + $"Multiple {typeof(INotificationPublisher).FullName} instances are registered. Remove duplicate notification publisher strategies before publishing notifications.")); + } + + /// + /// 验证 dispatcher 在首次发布时解析通知发布器后,会复用同一实例并停止继续查询容器。 + /// + [Test] + public async Task PublishAsync_Should_Cache_Resolved_NotificationPublisher_After_First_Publish() + { + var firstPublisher = new TrackingNotificationPublisher(); + var secondPublisher = new TrackingNotificationPublisher(); + var notificationPublisherLookupCount = 0; + var runtime = CreateRuntime( + container => + { + container + .Setup(currentContainer => currentContainer.GetAll(typeof(INotificationHandler))) + .Returns([new RecordingNotificationHandler("only", [])]); + container + .Setup(currentContainer => currentContainer.GetAll(typeof(INotificationPublisher))) + .Returns(() => + { + notificationPublisherLookupCount++; + return notificationPublisherLookupCount switch + { + 1 => [firstPublisher], + 2 => [secondPublisher], + _ => throw new AssertionException("Notification publisher should be resolved at most once.") + }; + }); + }); + + await runtime.PublishAsync(new FakeCqrsContext(), new PublisherNotification()).ConfigureAwait(false); + await runtime.PublishAsync(new FakeCqrsContext(), new PublisherNotification()).ConfigureAwait(false); + + Assert.That(notificationPublisherLookupCount, Is.EqualTo(1)); + Assert.That(firstPublisher.PublishCallCount, Is.EqualTo(2)); + Assert.That(secondPublisher.PublishCallCount, Is.Zero); + } + /// /// 验证内置 `TaskWhenAll` 发布器会继续调度所有处理器,而不是沿用默认顺序发布器的失败即停语义。 /// @@ -262,6 +326,11 @@ internal sealed class CqrsNotificationPublisherTests /// public bool WasCalled { get; private set; } + /// + /// 获取当前发布器累计执行发布的次数。 + /// + public int PublishCallCount { get; private set; } + /// /// 记录当前发布器已被调用,并继续按当前顺序执行所有处理器。 /// @@ -275,6 +344,7 @@ internal sealed class CqrsNotificationPublisherTests where TNotification : INotification { WasCalled = true; + PublishCallCount++; foreach (var handler in context.Handlers) {