// Copyright (c) 2025-2026 GeWuYou // SPDX-License-Identifier: Apache-2.0 using System.Collections.Generic; using System.Runtime.CompilerServices; using GFramework.Core.Abstractions.Ioc; using GFramework.Core.Abstractions.Logging; using GFramework.Cqrs.Abstractions.Cqrs; using GFramework.Cqrs.Cqrs; using GFramework.Cqrs.Tests.Logging; namespace GFramework.Cqrs.Tests.Cqrs; /// /// 验证默认 dispatcher 在上下文注入前置条件不满足时的失败语义。 /// [TestFixture] internal sealed class CqrsDispatcherContextValidationTests { /// /// 验证当 request handler 需要上下文注入、但当前 CQRS 上下文不实现 时, /// dispatcher 会在调用前显式失败。 /// [Test] public void SendAsync_Should_Throw_When_Context_Does_Not_Implement_IArchitectureContext() { var runtime = CreateRuntime( container => { container .Setup(currentContainer => currentContainer.Get(typeof(IRequestHandler))) .Returns(new ContextAwareRequestHandler()); container .Setup(currentContainer => currentContainer.GetAll(typeof(IPipelineBehavior))) .Returns(Array.Empty()); }); Assert.That( async () => await runtime.SendAsync(new FakeCqrsContext(), new ContextAwareRequest()).ConfigureAwait(false), Throws.InvalidOperationException.With.Message.Contains("does not implement IArchitectureContext")); } /// /// 验证当 notification handler 需要上下文注入、但当前 CQRS 上下文不实现 时, /// dispatcher 会在发布前显式失败。 /// [Test] public void PublishAsync_Should_Throw_When_Context_Does_Not_Implement_IArchitectureContext() { var runtime = CreateRuntime( container => { container .Setup(currentContainer => currentContainer.GetAll(typeof(INotificationHandler))) .Returns([new ContextAwareNotificationHandler()]); }); Assert.That( async () => await runtime.PublishAsync(new FakeCqrsContext(), new ContextAwareNotification()).ConfigureAwait(false), Throws.InvalidOperationException.With.Message.Contains("does not implement IArchitectureContext")); } /// /// 验证当 stream handler 需要上下文注入、但当前 CQRS 上下文不实现 时, /// dispatcher 会在建流前显式失败。 /// [Test] public void CreateStream_Should_Throw_When_Context_Does_Not_Implement_IArchitectureContext() { var runtime = CreateRuntime( container => { container .Setup(currentContainer => currentContainer.Get(typeof(IStreamRequestHandler))) .Returns(new ContextAwareStreamHandler()); }); Assert.That( () => runtime.CreateStream(new FakeCqrsContext(), new ContextAwareStreamRequest()), Throws.InvalidOperationException.With.Message.Contains("does not implement IArchitectureContext")); } /// /// 创建一个只满足当前测试最小依赖面的 dispatcher runtime。 /// /// 对容器 mock 的额外配置。 /// 默认 CQRS runtime。 private static GFramework.Cqrs.Abstractions.Cqrs.ICqrsRuntime CreateRuntime( Action> configureContainer) { var container = new Mock(MockBehavior.Strict); var logger = new TestLogger("CqrsDispatcherContextValidationTests", LogLevel.Debug); configureContainer(container); return CqrsRuntimeFactory.CreateRuntime(container.Object, logger); } /// /// 为失败语义测试提供最小 CQRS 上下文标记,但故意不实现架构上下文能力。 /// private sealed class FakeCqrsContext : ICqrsContext { } /// /// 为 request 上下文校验提供最小测试请求。 /// private sealed record ContextAwareRequest : IRequest; /// /// 为 notification 上下文校验提供最小测试通知。 /// private sealed record ContextAwareNotification : INotification; /// /// 为 stream 上下文校验提供最小测试请求。 /// private sealed record ContextAwareStreamRequest : IStreamRequest; /// /// 为 request 上下文校验提供需要注入架构上下文的最小 handler。 /// private sealed class ContextAwareRequestHandler : CqrsContextAwareHandlerBase, IRequestHandler { /// /// 返回固定结果;当前测试只关心调用前的上下文校验。 /// /// 当前请求。 /// 取消令牌。 /// 固定整型结果。 public ValueTask Handle(ContextAwareRequest request, CancellationToken cancellationToken) { return ValueTask.FromResult(1); } } /// /// 为 notification 上下文校验提供需要注入架构上下文的最小 handler。 /// private sealed class ContextAwareNotificationHandler : CqrsContextAwareHandlerBase, INotificationHandler { /// /// 返回已完成任务;当前测试只关心调用前的上下文校验。 /// /// 当前通知。 /// 取消令牌。 /// 已完成任务。 public ValueTask Handle(ContextAwareNotification notification, CancellationToken cancellationToken) { return ValueTask.CompletedTask; } } /// /// 为 stream 上下文校验提供需要注入架构上下文的最小 handler。 /// private sealed class ContextAwareStreamHandler : CqrsContextAwareHandlerBase, IStreamRequestHandler { /// /// 返回一个最小流;当前测试只关心建流前的上下文校验。 /// /// 当前流请求。 /// 取消枚举时使用的取消令牌。 /// 包含单个固定元素的异步流。 public async IAsyncEnumerable Handle( ContextAwareStreamRequest request, [EnumeratorCancellation] CancellationToken cancellationToken) { yield return 1; await ValueTask.CompletedTask.ConfigureAwait(false); } } }