// Copyright (c) 2025-2026 GeWuYou // SPDX-License-Identifier: Apache-2.0 using GFramework.Core.Abstractions.Rule; using GFramework.Core.Cqrs; using GFramework.Cqrs.Abstractions.Cqrs; namespace GFramework.Core.Tests.Command; /// /// 记录 bridge 执行线程与收到请求的最小 CQRS runtime 测试替身。 /// internal sealed class RecordingCqrsRuntime(Func? responseFactory = null) : ICqrsRuntime { private static readonly Func DefaultResponseFactory = _ => null; private readonly Func _responseFactory = responseFactory ?? DefaultResponseFactory; /// /// 获取最近一次 观察到的同步上下文类型。 /// public Type? ObservedSynchronizationContextType { get; private set; } /// /// 获取最近一次收到的请求实例。 /// public object? LastRequest { get; private set; } /// public async ValueTask SendAsync( ICqrsContext context, IRequest request, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(context); ArgumentNullException.ThrowIfNull(request); ObservedSynchronizationContextType = SynchronizationContext.Current?.GetType(); LastRequest = request; object? response = request switch { LegacyCommandDispatchRequest legacyCommandDispatchRequest => ExecuteLegacyCommand(context, legacyCommandDispatchRequest), LegacyCommandResultDispatchRequest legacyCommandResultDispatchRequest => ExecuteContextAwareRequest( context, legacyCommandResultDispatchRequest.Target, legacyCommandResultDispatchRequest.Execute), LegacyQueryDispatchRequest legacyQueryDispatchRequest => ExecuteContextAwareRequest( context, legacyQueryDispatchRequest.Target, legacyQueryDispatchRequest.Execute), LegacyAsyncCommandDispatchRequest legacyAsyncCommandDispatchRequest => await ExecuteLegacyAsyncCommandAsync( context, legacyAsyncCommandDispatchRequest, cancellationToken).ConfigureAwait(false), LegacyAsyncCommandResultDispatchRequest legacyAsyncCommandResultDispatchRequest => await ExecuteContextAwareRequestAsync( context, legacyAsyncCommandResultDispatchRequest.Target, legacyAsyncCommandResultDispatchRequest.ExecuteAsync, cancellationToken).ConfigureAwait(false), LegacyAsyncQueryDispatchRequest legacyAsyncQueryDispatchRequest => await ExecuteContextAwareRequestAsync( context, legacyAsyncQueryDispatchRequest.Target, legacyAsyncQueryDispatchRequest.ExecuteAsync, cancellationToken).ConfigureAwait(false), IRequest => Unit.Value, _ => _responseFactory(request) }; return ConvertResponse(request, response); } /// public ValueTask PublishAsync( ICqrsContext context, TNotification notification, CancellationToken cancellationToken = default) where TNotification : INotification { throw new NotSupportedException(); } /// public IAsyncEnumerable CreateStream( ICqrsContext context, IStreamRequest request, CancellationToken cancellationToken = default) { throw new NotSupportedException(); } /// /// 将测试替身工厂返回的装箱结果显式还原为目标类型,并在类型不匹配时给出可诊断异常。 /// /// 当前请求声明的响应类型。 /// 触发响应工厂的请求实例。 /// 响应工厂返回的装箱结果。 /// 还原后的目标类型响应。 /// /// 响应工厂返回 或错误类型,导致无法还原为 。 /// private static TResponse ConvertResponse(IRequest request, object? response) { if (response is TResponse typedResponse) { return typedResponse; } if (response is null && !typeof(TResponse).IsValueType) { return (TResponse)response!; } string actualType = response?.GetType().FullName ?? "null"; throw new InvalidOperationException( $"RecordingCqrsRuntime 无法将响应类型从 '{actualType}' 转换为 '{typeof(TResponse).FullName}'。" + $" 请求类型:'{request.GetType().FullName}'。"); } /// /// 按 bridge handler 语义为 legacy 无返回值命令注入上下文并执行。 /// /// 当前运行时接收到的架构上下文。 /// 待执行的 legacy 命令桥接请求。 /// 桥接后的 响应。 private static Unit ExecuteLegacyCommand( ICqrsContext context, LegacyCommandDispatchRequest request) { PrepareTarget(context, request.Command); request.Command.Execute(); return Unit.Value; } /// /// 按 bridge handler 语义为 legacy 异步无返回值命令注入上下文并执行。 /// /// 当前运行时接收到的架构上下文。 /// 待执行的 legacy 异步命令桥接请求。 /// 调用方传入的取消令牌。 /// 表示 bridge 执行完成的异步结果。 private static async Task ExecuteLegacyAsyncCommandAsync( ICqrsContext context, LegacyAsyncCommandDispatchRequest request, CancellationToken cancellationToken) { PrepareTarget(context, request.Command); await request.Command.ExecuteAsync().WaitAsync(cancellationToken).ConfigureAwait(false); return Unit.Value; } /// /// 按 bridge handler 语义为带返回值 legacy 请求注入上下文并执行同步委托。 /// /// 当前运行时接收到的架构上下文。 /// 需要接收上下文注入的 legacy 目标对象。 /// 实际执行 legacy 目标逻辑的同步委托。 /// 同步执行结果。 private static object? ExecuteContextAwareRequest( ICqrsContext context, object target, Func execute) { PrepareTarget(context, target); return execute(); } /// /// 按 bridge handler 语义为带返回值 legacy 请求注入上下文并执行异步委托。 /// /// 当前运行时接收到的架构上下文。 /// 需要接收上下文注入的 legacy 目标对象。 /// 实际执行 legacy 目标逻辑的异步委托。 /// 调用方传入的取消令牌。 /// 异步执行结果。 private static async Task ExecuteContextAwareRequestAsync( ICqrsContext context, object target, Func> executeAsync, CancellationToken cancellationToken) { PrepareTarget(context, target); return await executeAsync().WaitAsync(cancellationToken).ConfigureAwait(false); } /// /// 模拟 legacy bridge handler 的上下文注入语义,使测试替身与生产桥接行为保持一致。 /// /// 当前运行时接收到的架构上下文。 /// 即将执行的 legacy 目标对象。 private static void PrepareTarget(ICqrsContext context, object target) { ArgumentNullException.ThrowIfNull(context); ArgumentNullException.ThrowIfNull(target); if (context is not GFramework.Core.Abstractions.Architectures.IArchitectureContext architectureContext) { throw new InvalidOperationException( $"RecordingCqrsRuntime 期望收到 IArchitectureContext,但实际为 '{context.GetType().FullName}'。"); } if (target is IContextAware contextAware) { contextAware.SetContext(architectureContext); } } }