GFramework/GFramework.Core.Tests/Architectures/ArchitectureAdditionalCqrsHandlersTests.cs
GeWuYou 27266d037d feat(arch): 添加架构基础类和依赖注入容器实现
- 创建 Architecture 基类提供系统、模型、工具等组件的注册与管理功能
- 实现架构生命周期管理、初始化流程控制和阶段转换功能
- 添加 ArchitectureModules 模块管理器负责 CQRS 行为注册和模块安装
- 实现 MicrosoftDiContainer 依赖注入容器适配器
- 支持单例、瞬态、作用域服务注册和工厂方法注册
- 添加 CQRS 请求管道行为和处理器注册功能
- 实现线程安全的读写锁保护容器操作
- 提供服务获取、排序和优先级管理功能
2026-04-15 12:38:45 +08:00

198 lines
7.3 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Reflection;
using GFramework.Core.Abstractions.Cqrs;
using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Architectures;
using GFramework.Core.Logging;
namespace GFramework.Core.Tests.Architectures;
/// <summary>
/// 验证架构初始化阶段可以显式接入默认程序集之外的 CQRS handlers。
/// </summary>
[TestFixture]
public sealed class ArchitectureAdditionalCqrsHandlersTests
{
/// <summary>
/// 初始化日志工厂和共享测试状态。
/// </summary>
[SetUp]
public void SetUp()
{
_previousLoggerFactoryProvider = LoggerFactoryResolver.Provider;
LoggerFactoryResolver.Provider = new ConsoleLoggerFactoryProvider();
GameContext.Clear();
AdditionalAssemblyNotificationHandlerState.Reset();
}
/// <summary>
/// 清理测试过程中写入的共享状态。
/// </summary>
[TearDown]
public void TearDown()
{
AdditionalAssemblyNotificationHandlerState.Reset();
GameContext.Clear();
LoggerFactoryResolver.Provider = _previousLoggerFactoryProvider
?? throw new InvalidOperationException(
"LoggerFactoryResolver.Provider should be captured during setup.");
}
private ILoggerFactoryProvider? _previousLoggerFactoryProvider;
/// <summary>
/// 验证显式声明的额外程序集会在初始化阶段接入当前架构容器。
/// </summary>
[Test]
public async Task RegisterCqrsHandlersFromAssembly_Should_Register_Handlers_From_Explicit_Assembly()
{
var generatedAssembly = CreateGeneratedHandlerAssembly();
var architecture = new AdditionalHandlersTestArchitecture(target =>
target.RegisterCqrsHandlersFromAssembly(generatedAssembly.Object));
await architecture.InitializeAsync();
try
{
await architecture.Context.PublishAsync(new AdditionalAssemblyNotification());
Assert.That(AdditionalAssemblyNotificationHandlerState.InvocationCount, Is.EqualTo(1));
}
finally
{
await architecture.DestroyAsync();
}
}
/// <summary>
/// 验证同一额外程序集被重复声明时,不会向容器重复写入相同 handler 映射。
/// </summary>
[Test]
public async Task RegisterCqrsHandlersFromAssembly_Should_Deduplicate_Repeated_Assembly_Registration()
{
var generatedAssembly = CreateGeneratedHandlerAssembly();
var architecture = new AdditionalHandlersTestArchitecture(target =>
{
target.RegisterCqrsHandlersFromAssembly(generatedAssembly.Object);
target.RegisterCqrsHandlersFromAssemblies([generatedAssembly.Object]);
});
await architecture.InitializeAsync();
try
{
await architecture.Context.PublishAsync(new AdditionalAssemblyNotification());
Assert.That(AdditionalAssemblyNotificationHandlerState.InvocationCount, Is.EqualTo(1));
}
finally
{
await architecture.DestroyAsync();
}
}
/// <summary>
/// 创建一个仅暴露程序集级 CQRS registry 元数据的 mocked Assembly。
/// 该测试替身模拟“扩展程序集已经挂接 source-generator运行时只需显式接入该程序集”的真实路径。
/// </summary>
/// <returns>包含程序集级 handler registry 元数据的 mocked Assembly。</returns>
private static Mock<Assembly> CreateGeneratedHandlerAssembly()
{
var generatedAssembly = new Mock<Assembly>();
generatedAssembly
.SetupGet(static assembly => assembly.FullName)
.Returns("GFramework.Core.Tests.Architectures.ExplicitAdditionalHandlers, Version=1.0.0.0");
generatedAssembly
.Setup(static assembly => assembly.GetCustomAttributes(typeof(CqrsHandlerRegistryAttribute), false))
.Returns([new CqrsHandlerRegistryAttribute(typeof(AdditionalAssemblyNotificationHandlerRegistry))]);
return generatedAssembly;
}
/// <summary>
/// 用于测试额外程序集注册入口的最小架构实现。
/// </summary>
private sealed class AdditionalHandlersTestArchitecture(Action<AdditionalHandlersTestArchitecture> configure) :
Architecture
{
/// <summary>
/// 在初始化阶段执行测试注入的额外 CQRS 程序集接入逻辑。
/// </summary>
protected override void OnInitialize()
{
configure(this);
}
}
}
/// <summary>
/// 用于验证额外程序集接入是否成功的测试通知。
/// </summary>
public sealed record AdditionalAssemblyNotification : INotification;
/// <summary>
/// 记录模拟扩展程序集通知处理器的执行次数。
/// </summary>
public static class AdditionalAssemblyNotificationHandlerState
{
private static int _invocationCount;
/// <summary>
/// 获取当前测试进程中该处理器的执行次数。
/// </summary>
/// <remarks>
/// 该计数器通过原子读写维护,以支持 NUnit 并行执行环境中的并发访问。
/// </remarks>
public static int InvocationCount => Volatile.Read(ref _invocationCount);
/// <summary>
/// 记录一次通知处理,供测试断言显式程序集接入后的运行时行为。
/// </summary>
public static void RecordInvocation()
{
Interlocked.Increment(ref _invocationCount);
}
/// <summary>
/// 清理共享计数器,避免测试间相互污染。
/// </summary>
public static void Reset()
{
Interlocked.Exchange(ref _invocationCount, 0);
}
}
/// <summary>
/// 模拟由 source-generator 为扩展程序集生成的 CQRS handler registry。
/// </summary>
internal sealed class AdditionalAssemblyNotificationHandlerRegistry : ICqrsHandlerRegistry
{
/// <summary>
/// 将扩展程序集中的通知处理器映射写入服务集合。
/// </summary>
/// <param name="services">目标服务集合。</param>
/// <param name="logger">日志记录器。</param>
public void Register(IServiceCollection services, ILogger logger)
{
ArgumentNullException.ThrowIfNull(services);
ArgumentNullException.ThrowIfNull(logger);
services.AddTransient<INotificationHandler<AdditionalAssemblyNotification>>(_ => CreateHandler());
logger.Debug(
$"Registered CQRS handler proxy for {typeof(INotificationHandler<AdditionalAssemblyNotification>).FullName}.");
}
/// <summary>
/// 创建一个仅供显式程序集注册路径使用的动态通知处理器。
/// </summary>
/// <returns>用于记录通知触发次数的测试替身处理器。</returns>
private static INotificationHandler<AdditionalAssemblyNotification> CreateHandler()
{
var handler = new Mock<INotificationHandler<AdditionalAssemblyNotification>>();
handler
.Setup(target => target.Handle(It.IsAny<AdditionalAssemblyNotification>(), It.IsAny<CancellationToken>()))
.Returns(() =>
{
AdditionalAssemblyNotificationHandlerState.RecordInvocation();
return ValueTask.CompletedTask;
});
return handler.Object;
}
}