diff --git a/GFramework.Cqrs.Tests/Cqrs/CqrsRegistrationServiceTests.cs b/GFramework.Cqrs.Tests/Cqrs/CqrsRegistrationServiceTests.cs
new file mode 100644
index 00000000..b8b03b1c
--- /dev/null
+++ b/GFramework.Cqrs.Tests/Cqrs/CqrsRegistrationServiceTests.cs
@@ -0,0 +1,96 @@
+using GFramework.Core.Abstractions.Logging;
+using GFramework.Cqrs.Abstractions.Cqrs;
+using GFramework.Cqrs.Tests.Logging;
+
+namespace GFramework.Cqrs.Tests.Cqrs;
+
+///
+/// 验证 CQRS 程序集注册协调器在程序集键去重层面的可观察行为。
+///
+[TestFixture]
+internal sealed class CqrsRegistrationServiceTests
+{
+ ///
+ /// 验证同一次调用内出现重复程序集键时,底层注册器只会接收到一次注册请求。
+ ///
+ [Test]
+ public void RegisterHandlers_Should_Register_Duplicate_Assembly_Key_Only_Once_Per_Call()
+ {
+ var logger = new TestLogger("DefaultCqrsRegistrationService", LogLevel.Debug);
+ var registrar = new Mock(MockBehavior.Strict);
+ var duplicateAssemblyA = CreateAssembly("GFramework.Cqrs.Tests.DuplicateAssembly, Version=1.0.0.0");
+ var duplicateAssemblyB = CreateAssembly("GFramework.Cqrs.Tests.DuplicateAssembly, Version=1.0.0.0");
+ var expectedAssembly = duplicateAssemblyA.Object;
+ IEnumerable? registeredAssemblies = null;
+
+ registrar
+ .Setup(static currentRegistrar => currentRegistrar.RegisterHandlers(It.IsAny>()))
+ .Callback>(assemblies => registeredAssemblies = assemblies.ToArray());
+
+ var service = CqrsRuntimeFactory.CreateRegistrationService(registrar.Object, logger);
+
+ service.RegisterHandlers([duplicateAssemblyA.Object, duplicateAssemblyB.Object]);
+
+ registrar.Verify(
+ static currentRegistrar => currentRegistrar.RegisterHandlers(It.IsAny>()),
+ Times.Once);
+ Assert.Multiple(() =>
+ {
+ Assert.That(registeredAssemblies, Is.Not.Null);
+ Assert.That(registeredAssemblies, Is.EqualTo([expectedAssembly]));
+ Assert.That(logger.Logs, Has.Count.EqualTo(0));
+ });
+ }
+
+ ///
+ /// 验证跨两次调用重复程序集键时,协调器会跳过重复注册并写入 debug 日志。
+ ///
+ [Test]
+ public void RegisterHandlers_Should_Skip_Already_Registered_Assembly_Key_Across_Calls_And_Log_Debug_Message()
+ {
+ var logger = new TestLogger("DefaultCqrsRegistrationService", LogLevel.Debug);
+ var registrar = new Mock(MockBehavior.Strict);
+ var firstAssembly = CreateAssembly("GFramework.Cqrs.Tests.RegisteredAssembly, Version=1.0.0.0");
+ var secondAssembly = CreateAssembly("GFramework.Cqrs.Tests.RegisteredAssembly, Version=1.0.0.0");
+ IEnumerable? registeredAssemblies = null;
+
+ registrar
+ .Setup(static currentRegistrar => currentRegistrar.RegisterHandlers(It.IsAny>()))
+ .Callback>(assemblies => registeredAssemblies = assemblies.ToArray());
+
+ var service = CqrsRuntimeFactory.CreateRegistrationService(registrar.Object, logger);
+
+ service.RegisterHandlers([firstAssembly.Object]);
+ service.RegisterHandlers([secondAssembly.Object]);
+
+ registrar.Verify(
+ static currentRegistrar => currentRegistrar.RegisterHandlers(It.IsAny>()),
+ Times.Once);
+ Assert.Multiple(() =>
+ {
+ Assert.That(registeredAssemblies, Is.EqualTo([firstAssembly.Object]));
+ Assert.That(
+ logger.Logs.Where(static log => log.Level == LogLevel.Debug).Select(static log => log.Message),
+ Is.EqualTo(
+ [
+ "Skipping CQRS handler registration for assembly " +
+ "GFramework.Cqrs.Tests.RegisteredAssembly, Version=1.0.0.0 because it was already registered."
+ ]));
+ });
+ }
+
+ ///
+ /// 创建一个带稳定程序集键的程序集 mock,用于模拟不同 实例表示同一程序集的场景。
+ ///
+ /// 要返回的程序集完整名称。
+ /// 配置好完整名称的程序集 mock。
+ private static Mock CreateAssembly(string assemblyFullName)
+ {
+ var assembly = new Mock();
+ assembly
+ .SetupGet(static currentAssembly => currentAssembly.FullName)
+ .Returns(assemblyFullName);
+
+ return assembly;
+ }
+}