From 9098490fbb7e2c934fc3ca24bd1eaae310387fd8 Mon Sep 17 00:00:00 2001 From: gewuyou <95328647+GeWuYou@users.noreply.github.com> Date: Tue, 28 Apr 2026 07:47:28 +0800 Subject: [PATCH 01/10] =?UTF-8?q?fix(cqrs-tests):=20=E6=8B=86=E5=88=86=20d?= =?UTF-8?q?ispatcher=20cache=20=E6=B5=8B=E8=AF=95=E8=BE=85=E5=8A=A9?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 拆分 CqrsDispatcherCacheTests 末尾的 request、handler 与 behavior 辅助类型到同目录独立文件 - 修复 dispatcher cache 测试中的 ConfigureAwait warning,保持现有断言与分发语义不变 --- .../Cqrs/CqrsDispatcherCacheTests.cs | 118 +----------------- .../Cqrs/DispatcherCacheNotification.cs | 8 ++ .../DispatcherCacheNotificationHandler.cs | 21 ++++ .../Cqrs/DispatcherCacheRequest.cs | 8 ++ .../Cqrs/DispatcherCacheRequestHandler.cs | 21 ++++ .../Cqrs/DispatcherCacheStreamHandler.cs | 27 ++++ .../Cqrs/DispatcherCacheStreamRequest.cs | 8 ++ .../Cqrs/DispatcherPipelineCacheBehavior.cs | 26 ++++ .../Cqrs/DispatcherPipelineCacheRequest.cs | 8 ++ .../DispatcherPipelineCacheRequestHandler.cs | 21 ++++ .../Cqrs/DispatcherStringCacheRequest.cs | 8 ++ .../DispatcherStringCacheRequestHandler.cs | 21 ++++ 12 files changed, 178 insertions(+), 117 deletions(-) create mode 100644 GFramework.Cqrs.Tests/Cqrs/DispatcherCacheNotification.cs create mode 100644 GFramework.Cqrs.Tests/Cqrs/DispatcherCacheNotificationHandler.cs create mode 100644 GFramework.Cqrs.Tests/Cqrs/DispatcherCacheRequest.cs create mode 100644 GFramework.Cqrs.Tests/Cqrs/DispatcherCacheRequestHandler.cs create mode 100644 GFramework.Cqrs.Tests/Cqrs/DispatcherCacheStreamHandler.cs create mode 100644 GFramework.Cqrs.Tests/Cqrs/DispatcherCacheStreamRequest.cs create mode 100644 GFramework.Cqrs.Tests/Cqrs/DispatcherPipelineCacheBehavior.cs create mode 100644 GFramework.Cqrs.Tests/Cqrs/DispatcherPipelineCacheRequest.cs create mode 100644 GFramework.Cqrs.Tests/Cqrs/DispatcherPipelineCacheRequestHandler.cs create mode 100644 GFramework.Cqrs.Tests/Cqrs/DispatcherStringCacheRequest.cs create mode 100644 GFramework.Cqrs.Tests/Cqrs/DispatcherStringCacheRequestHandler.cs diff --git a/GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherCacheTests.cs b/GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherCacheTests.cs index 470667ec..ee8c549a 100644 --- a/GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherCacheTests.cs +++ b/GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherCacheTests.cs @@ -224,124 +224,8 @@ internal sealed class CqrsDispatcherCacheTests /// private static async Task DrainAsync(IAsyncEnumerable stream) { - await foreach (var _ in stream) + await foreach (var _ in stream.ConfigureAwait(false)) { } } } - -/// -/// 用于验证 request 服务类型缓存的测试请求。 -/// -internal sealed record DispatcherCacheRequest : IRequest; - -/// -/// 用于验证 notification 服务类型缓存的测试通知。 -/// -internal sealed record DispatcherCacheNotification : INotification; - -/// -/// 用于验证 stream 服务类型缓存的测试请求。 -/// -internal sealed record DispatcherCacheStreamRequest : IStreamRequest; - -/// -/// 用于验证 pipeline invoker 缓存的测试请求。 -/// -internal sealed record DispatcherPipelineCacheRequest : IRequest; - -/// -/// 用于验证按响应类型分层 request invoker 缓存的测试请求。 -/// -internal sealed record DispatcherStringCacheRequest : IRequest; - -/// -/// 处理 。 -/// -internal sealed class DispatcherCacheRequestHandler : IRequestHandler -{ - /// - /// 返回固定结果,供缓存测试验证 dispatcher 请求路径。 - /// - public ValueTask Handle(DispatcherCacheRequest request, CancellationToken cancellationToken) - { - return ValueTask.FromResult(1); - } -} - -/// -/// 处理 。 -/// -internal sealed class DispatcherCacheNotificationHandler : INotificationHandler -{ - /// - /// 消费通知,不执行额外副作用。 - /// - public ValueTask Handle(DispatcherCacheNotification notification, CancellationToken cancellationToken) - { - return ValueTask.CompletedTask; - } -} - -/// -/// 处理 。 -/// -internal sealed class DispatcherCacheStreamHandler : IStreamRequestHandler -{ - /// - /// 返回一个最小流,供缓存测试命中 stream 分发路径。 - /// - public async IAsyncEnumerable Handle( - DispatcherCacheStreamRequest request, - [EnumeratorCancellation] CancellationToken cancellationToken) - { - yield return 1; - await Task.CompletedTask; - } -} - -/// -/// 处理 。 -/// -internal sealed class DispatcherPipelineCacheRequestHandler : IRequestHandler -{ - /// - /// 返回固定结果,供 pipeline 缓存测试使用。 - /// - public ValueTask Handle(DispatcherPipelineCacheRequest request, CancellationToken cancellationToken) - { - return ValueTask.FromResult(2); - } -} - -/// -/// 处理 。 -/// -internal sealed class DispatcherStringCacheRequestHandler : IRequestHandler -{ - /// - /// 返回固定字符串,供按响应类型缓存测试验证 string 路径。 - /// - public ValueTask Handle(DispatcherStringCacheRequest request, CancellationToken cancellationToken) - { - return ValueTask.FromResult("dispatcher-cache"); - } -} - -/// -/// 为 提供最小 pipeline 行为, -/// 用于命中 dispatcher 的 pipeline invoker 缓存分支。 -/// -internal sealed class DispatcherPipelineCacheBehavior : IPipelineBehavior -{ - /// - /// 直接转发到下一个处理器。 - /// - public ValueTask Handle( - DispatcherPipelineCacheRequest request, - MessageHandlerDelegate next, - CancellationToken cancellationToken) - { - return next(request, cancellationToken); - } -} diff --git a/GFramework.Cqrs.Tests/Cqrs/DispatcherCacheNotification.cs b/GFramework.Cqrs.Tests/Cqrs/DispatcherCacheNotification.cs new file mode 100644 index 00000000..00a2535c --- /dev/null +++ b/GFramework.Cqrs.Tests/Cqrs/DispatcherCacheNotification.cs @@ -0,0 +1,8 @@ +using GFramework.Cqrs.Abstractions.Cqrs; + +namespace GFramework.Cqrs.Tests.Cqrs; + +/// +/// 用于验证 notification 服务类型缓存的测试通知。 +/// +internal sealed record DispatcherCacheNotification : INotification; diff --git a/GFramework.Cqrs.Tests/Cqrs/DispatcherCacheNotificationHandler.cs b/GFramework.Cqrs.Tests/Cqrs/DispatcherCacheNotificationHandler.cs new file mode 100644 index 00000000..b023398a --- /dev/null +++ b/GFramework.Cqrs.Tests/Cqrs/DispatcherCacheNotificationHandler.cs @@ -0,0 +1,21 @@ +using System.Threading; +using GFramework.Cqrs.Abstractions.Cqrs; + +namespace GFramework.Cqrs.Tests.Cqrs; + +/// +/// 处理 。 +/// +internal sealed class DispatcherCacheNotificationHandler : INotificationHandler +{ + /// + /// 消费通知,不执行额外副作用。 + /// + /// 当前通知。 + /// 取消令牌。 + /// 已完成任务。 + public ValueTask Handle(DispatcherCacheNotification notification, CancellationToken cancellationToken) + { + return ValueTask.CompletedTask; + } +} diff --git a/GFramework.Cqrs.Tests/Cqrs/DispatcherCacheRequest.cs b/GFramework.Cqrs.Tests/Cqrs/DispatcherCacheRequest.cs new file mode 100644 index 00000000..005b06d0 --- /dev/null +++ b/GFramework.Cqrs.Tests/Cqrs/DispatcherCacheRequest.cs @@ -0,0 +1,8 @@ +using GFramework.Cqrs.Abstractions.Cqrs; + +namespace GFramework.Cqrs.Tests.Cqrs; + +/// +/// 用于验证 request 服务类型缓存的测试请求。 +/// +internal sealed record DispatcherCacheRequest : IRequest; diff --git a/GFramework.Cqrs.Tests/Cqrs/DispatcherCacheRequestHandler.cs b/GFramework.Cqrs.Tests/Cqrs/DispatcherCacheRequestHandler.cs new file mode 100644 index 00000000..acd1f043 --- /dev/null +++ b/GFramework.Cqrs.Tests/Cqrs/DispatcherCacheRequestHandler.cs @@ -0,0 +1,21 @@ +using System.Threading; +using GFramework.Cqrs.Abstractions.Cqrs; + +namespace GFramework.Cqrs.Tests.Cqrs; + +/// +/// 处理 。 +/// +internal sealed class DispatcherCacheRequestHandler : IRequestHandler +{ + /// + /// 返回固定结果,供缓存测试验证 dispatcher 请求路径。 + /// + /// 当前请求。 + /// 取消令牌。 + /// 固定整数结果。 + public ValueTask Handle(DispatcherCacheRequest request, CancellationToken cancellationToken) + { + return ValueTask.FromResult(1); + } +} diff --git a/GFramework.Cqrs.Tests/Cqrs/DispatcherCacheStreamHandler.cs b/GFramework.Cqrs.Tests/Cqrs/DispatcherCacheStreamHandler.cs new file mode 100644 index 00000000..476cbe43 --- /dev/null +++ b/GFramework.Cqrs.Tests/Cqrs/DispatcherCacheStreamHandler.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; +using GFramework.Cqrs.Abstractions.Cqrs; + +namespace GFramework.Cqrs.Tests.Cqrs; + +/// +/// 处理 。 +/// +internal sealed class DispatcherCacheStreamHandler : IStreamRequestHandler +{ + /// + /// 返回一个最小流,供缓存测试命中 stream 分发路径。 + /// + /// 当前流请求。 + /// 取消令牌。 + /// 包含一个固定元素的异步流。 + public async IAsyncEnumerable Handle( + DispatcherCacheStreamRequest request, + [EnumeratorCancellation] CancellationToken cancellationToken) + { + yield return 1; + await Task.CompletedTask.ConfigureAwait(false); + } +} diff --git a/GFramework.Cqrs.Tests/Cqrs/DispatcherCacheStreamRequest.cs b/GFramework.Cqrs.Tests/Cqrs/DispatcherCacheStreamRequest.cs new file mode 100644 index 00000000..e09b23cb --- /dev/null +++ b/GFramework.Cqrs.Tests/Cqrs/DispatcherCacheStreamRequest.cs @@ -0,0 +1,8 @@ +using GFramework.Cqrs.Abstractions.Cqrs; + +namespace GFramework.Cqrs.Tests.Cqrs; + +/// +/// 用于验证 stream 服务类型缓存的测试请求。 +/// +internal sealed record DispatcherCacheStreamRequest : IStreamRequest; diff --git a/GFramework.Cqrs.Tests/Cqrs/DispatcherPipelineCacheBehavior.cs b/GFramework.Cqrs.Tests/Cqrs/DispatcherPipelineCacheBehavior.cs new file mode 100644 index 00000000..7335616c --- /dev/null +++ b/GFramework.Cqrs.Tests/Cqrs/DispatcherPipelineCacheBehavior.cs @@ -0,0 +1,26 @@ +using System.Threading; +using GFramework.Cqrs.Abstractions.Cqrs; + +namespace GFramework.Cqrs.Tests.Cqrs; + +/// +/// 为 提供最小 pipeline 行为, +/// 用于命中 dispatcher 的 pipeline invoker 缓存分支。 +/// +internal sealed class DispatcherPipelineCacheBehavior : IPipelineBehavior +{ + /// + /// 直接转发到下一个处理器。 + /// + /// 当前请求。 + /// 下一个处理器委托。 + /// 取消令牌。 + /// 下游处理器结果。 + public ValueTask Handle( + DispatcherPipelineCacheRequest request, + MessageHandlerDelegate next, + CancellationToken cancellationToken) + { + return next(request, cancellationToken); + } +} diff --git a/GFramework.Cqrs.Tests/Cqrs/DispatcherPipelineCacheRequest.cs b/GFramework.Cqrs.Tests/Cqrs/DispatcherPipelineCacheRequest.cs new file mode 100644 index 00000000..53cd733d --- /dev/null +++ b/GFramework.Cqrs.Tests/Cqrs/DispatcherPipelineCacheRequest.cs @@ -0,0 +1,8 @@ +using GFramework.Cqrs.Abstractions.Cqrs; + +namespace GFramework.Cqrs.Tests.Cqrs; + +/// +/// 用于验证 pipeline invoker 缓存的测试请求。 +/// +internal sealed record DispatcherPipelineCacheRequest : IRequest; diff --git a/GFramework.Cqrs.Tests/Cqrs/DispatcherPipelineCacheRequestHandler.cs b/GFramework.Cqrs.Tests/Cqrs/DispatcherPipelineCacheRequestHandler.cs new file mode 100644 index 00000000..d52636a1 --- /dev/null +++ b/GFramework.Cqrs.Tests/Cqrs/DispatcherPipelineCacheRequestHandler.cs @@ -0,0 +1,21 @@ +using System.Threading; +using GFramework.Cqrs.Abstractions.Cqrs; + +namespace GFramework.Cqrs.Tests.Cqrs; + +/// +/// 处理 。 +/// +internal sealed class DispatcherPipelineCacheRequestHandler : IRequestHandler +{ + /// + /// 返回固定结果,供 pipeline 缓存测试使用。 + /// + /// 当前请求。 + /// 取消令牌。 + /// 固定整数结果。 + public ValueTask Handle(DispatcherPipelineCacheRequest request, CancellationToken cancellationToken) + { + return ValueTask.FromResult(2); + } +} diff --git a/GFramework.Cqrs.Tests/Cqrs/DispatcherStringCacheRequest.cs b/GFramework.Cqrs.Tests/Cqrs/DispatcherStringCacheRequest.cs new file mode 100644 index 00000000..65821391 --- /dev/null +++ b/GFramework.Cqrs.Tests/Cqrs/DispatcherStringCacheRequest.cs @@ -0,0 +1,8 @@ +using GFramework.Cqrs.Abstractions.Cqrs; + +namespace GFramework.Cqrs.Tests.Cqrs; + +/// +/// 用于验证按响应类型分层 request invoker 缓存的测试请求。 +/// +internal sealed record DispatcherStringCacheRequest : IRequest; diff --git a/GFramework.Cqrs.Tests/Cqrs/DispatcherStringCacheRequestHandler.cs b/GFramework.Cqrs.Tests/Cqrs/DispatcherStringCacheRequestHandler.cs new file mode 100644 index 00000000..83082053 --- /dev/null +++ b/GFramework.Cqrs.Tests/Cqrs/DispatcherStringCacheRequestHandler.cs @@ -0,0 +1,21 @@ +using System.Threading; +using GFramework.Cqrs.Abstractions.Cqrs; + +namespace GFramework.Cqrs.Tests.Cqrs; + +/// +/// 处理 。 +/// +internal sealed class DispatcherStringCacheRequestHandler : IRequestHandler +{ + /// + /// 返回固定字符串,供按响应类型缓存测试验证 string 路径。 + /// + /// 当前请求。 + /// 取消令牌。 + /// 固定字符串结果。 + public ValueTask Handle(DispatcherStringCacheRequest request, CancellationToken cancellationToken) + { + return ValueTask.FromResult("dispatcher-cache"); + } +} From a7be41367a1a9de80b5d4506f4d2dd63c65ca80d Mon Sep 17 00:00:00 2001 From: gewuyou <95328647+GeWuYou@users.noreply.github.com> Date: Tue, 28 Apr 2026 07:48:55 +0800 Subject: [PATCH 02/10] =?UTF-8?q?fix(core-tests):=20=E6=B6=88=E9=99=A4=20G?= =?UTF-8?q?ameContextTests=20=E8=AD=A6=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 拆分 TestArchitecture 与 TestArchitectureContext 到独立测试文件以消除 MA0048 - 修复 TestArchitectureContext 的可空签名以匹配 IArchitectureContext 契约 - 补充测试桩公开成员的 XML 文档并保持 GameContextTests 行为不变 --- .../Architectures/GameContextTests.cs | 396 ---------------- .../Architectures/TestArchitecture.cs | 16 + .../Architectures/TestArchitectureContext.cs | 427 ++++++++++++++++++ 3 files changed, 443 insertions(+), 396 deletions(-) create mode 100644 GFramework.Core.Tests/Architectures/TestArchitecture.cs create mode 100644 GFramework.Core.Tests/Architectures/TestArchitectureContext.cs diff --git a/GFramework.Core.Tests/Architectures/GameContextTests.cs b/GFramework.Core.Tests/Architectures/GameContextTests.cs index fd6e2bbb..0087102a 100644 --- a/GFramework.Core.Tests/Architectures/GameContextTests.cs +++ b/GFramework.Core.Tests/Architectures/GameContextTests.cs @@ -1,20 +1,4 @@ -using GFramework.Core.Abstractions.Architectures; -using GFramework.Core.Abstractions.Command; -using GFramework.Core.Abstractions.Environment; -using GFramework.Core.Abstractions.Events; -using GFramework.Core.Abstractions.Ioc; -using GFramework.Core.Abstractions.Model; -using GFramework.Core.Abstractions.Query; -using GFramework.Core.Abstractions.Systems; -using GFramework.Core.Abstractions.Utility; using GFramework.Core.Architectures; -using GFramework.Core.Command; -using GFramework.Core.Environment; -using GFramework.Core.Events; -using GFramework.Core.Ioc; -using GFramework.Core.Query; -using GFramework.Cqrs.Abstractions.Cqrs; -using ICommand = GFramework.Core.Abstractions.Command.ICommand; namespace GFramework.Core.Tests.Architectures; @@ -211,383 +195,3 @@ public class GameContextTests Assert.That(GameContext.ArchitectureReadOnlyDictionary.Count, Is.EqualTo(0)); } } - -/// -/// 测试用的架构类,继承自Architecture -/// -public class TestArchitecture : Architecture -{ - /// - /// 初始化方法,当前为空实现 - /// - protected override void OnInitialize() - { - } -} - -/// -/// 测试用的架构上下文类,实现了IArchitectureContext接口 -/// -public class TestArchitectureContext : IArchitectureContext -{ - private readonly MicrosoftDiContainer _container = new(); - - /// - /// 获取依赖注入容器 - /// - public IIocContainer Container => _container; - - /// - /// 获取事件总线 - /// - public IEventBus EventBus => new EventBus(); - - /// - /// 获取命令总线 - /// - public ICommandExecutor CommandExecutor => new CommandExecutor(); - - /// - /// 获取查询总线 - /// - public IQueryExecutor QueryExecutor => new QueryExecutor(); - - /// - /// 获取环境对象 - /// - public IEnvironment Environment => new DefaultEnvironment(); - - /// - /// 获取指定类型的服务 - /// - /// 服务类型 - /// 服务实例或null - public TService? GetService() where TService : class - { - return _container.Get(); - } - - /// - /// 获取指定类型的所有服务 - /// - /// 服务类型 - /// 所有服务实例列表 - public IReadOnlyList GetServices() where TService : class - { - return _container.GetAll(); - } - - /// - /// 获取指定类型的模型 - /// - /// 模型类型 - /// 模型实例或null - public TModel? GetModel() where TModel : class, IModel - { - return _container.Get(); - } - - /// - /// 获取指定类型的所有模型 - /// - /// 模型类型 - /// 所有模型实例列表 - public IReadOnlyList GetModels() where TModel : class, IModel - { - return _container.GetAll(); - } - - /// - /// 获取指定类型的系统 - /// - /// 系统类型 - /// 系统实例或null - public TSystem? GetSystem() where TSystem : class, ISystem - { - return _container.Get(); - } - - /// - /// 获取指定类型的所有系统 - /// - /// 系统类型 - /// 所有系统实例列表 - public IReadOnlyList GetSystems() where TSystem : class, ISystem - { - return _container.GetAll(); - } - - /// - /// 获取指定类型的工具 - /// - /// 工具类型 - /// 工具实例或null - public virtual TUtility? GetUtility() where TUtility : class, IUtility - { - return _container.Get(); - } - - /// - /// 获取指定类型的所有工具 - /// - /// 工具类型 - /// 所有工具实例列表 - public IReadOnlyList GetUtilities() where TUtility : class, IUtility - { - return _container.GetAll(); - } - - public IReadOnlyList GetServicesByPriority() where TService : class - { - return _container.GetAllByPriority(); - } - - public IReadOnlyList GetSystemsByPriority() where TSystem : class, ISystem - { - return _container.GetAllByPriority(); - } - - public IReadOnlyList GetModelsByPriority() where TModel : class, IModel - { - return _container.GetAllByPriority(); - } - - public IReadOnlyList GetUtilitiesByPriority() where TUtility : class, IUtility - { - return _container.GetAllByPriority(); - } - - /// - /// 发送事件 - /// - /// 事件类型 - public void SendEvent() where TEvent : new() - { - } - - /// - /// 发送事件 - /// - /// 事件类型 - /// 事件实例 - public void SendEvent(TEvent e) where TEvent : class - { - } - - /// - /// 注册事件处理器 - /// - /// 事件类型 - /// 事件处理委托 - /// 取消注册接口 - public IUnRegister RegisterEvent(Action handler) - { - return new DefaultUnRegister(() => { }); - } - - /// - /// 取消注册事件处理器 - /// - /// 事件类型 - /// 事件处理委托 - public void UnRegisterEvent(Action onEvent) - { - } - - /// - /// 测试桩:异步发送统一 CQRS 请求。 - /// - /// 响应类型。 - /// 要发送的请求。 - /// 取消令牌。 - /// 请求响应任务。 - /// 该测试桩不支持此成员。 - public ValueTask SendRequestAsync(IRequest request, - CancellationToken cancellationToken = default) - { - throw new NotSupportedException(); - } - - /// - /// 测试桩:同步发送统一 CQRS 请求。 - /// - /// 响应类型。 - /// 要发送的请求。 - /// 请求响应。 - /// 该测试桩不支持此成员。 - public TResponse SendRequest(IRequest request) - { - throw new NotSupportedException(); - } - - /// - /// 测试桩:异步发送 CQRS 命令并返回响应。 - /// - /// 命令响应类型。 - /// 要发送的命令。 - /// 取消令牌。 - /// 命令响应任务。 - /// 该测试桩不支持此成员。 - public ValueTask SendCommandAsync( - GFramework.Cqrs.Abstractions.Cqrs.Command.ICommand command, - CancellationToken cancellationToken = default) - { - throw new NotSupportedException(); - } - - /// - /// 测试桩:同步发送 CQRS 命令并返回响应。 - /// - /// 命令响应类型。 - /// 要发送的命令。 - /// 命令响应。 - /// 该测试桩不支持此成员。 - public TResponse SendCommand(GFramework.Cqrs.Abstractions.Cqrs.Command.ICommand command) - { - throw new NotSupportedException(); - } - - /// - /// 测试桩:异步发送 CQRS 查询并返回结果。 - /// - /// 查询结果类型。 - /// 要发送的查询。 - /// 取消令牌。 - /// 查询结果任务。 - /// 该测试桩不支持此成员。 - public ValueTask SendQueryAsync( - GFramework.Cqrs.Abstractions.Cqrs.Query.IQuery query, - CancellationToken cancellationToken = default) - { - throw new NotSupportedException(); - } - - /// - /// 测试桩:同步发送 CQRS 查询并返回结果。 - /// - /// 查询结果类型。 - /// 要发送的查询。 - /// 查询结果。 - /// 该测试桩不支持此成员。 - public TResponse SendQuery(GFramework.Cqrs.Abstractions.Cqrs.Query.IQuery query) - { - throw new NotSupportedException(); - } - - /// - /// 测试桩:异步发布 CQRS 通知。 - /// - /// 通知类型。 - /// 要发布的通知。 - /// 取消令牌。 - /// 通知发布任务。 - /// 该测试桩不支持此成员。 - public ValueTask PublishAsync(TNotification notification, - CancellationToken cancellationToken = default) where TNotification : INotification - { - throw new NotSupportedException(); - } - - /// - /// 测试桩:创建 CQRS 流式请求响应序列。 - /// - /// 流式响应元素类型。 - /// 流式请求。 - /// 取消令牌。 - /// 异步响应流。 - /// 该测试桩不支持此成员。 - public IAsyncEnumerable CreateStream( - IStreamRequest request, - CancellationToken cancellationToken = default) - { - throw new NotSupportedException(); - } - - /// - /// 测试桩:异步发送无返回值 CQRS 命令。 - /// - /// 命令类型。 - /// 要发送的命令。 - /// 取消令牌。 - /// 命令发送任务。 - /// 该测试桩不支持此成员。 - public ValueTask SendAsync(TCommand command, CancellationToken cancellationToken = default) - where TCommand : IRequest - { - throw new NotSupportedException(); - } - - /// - /// 测试桩:异步发送带返回值的 CQRS 请求。 - /// - /// 响应类型。 - /// 要发送的请求。 - /// 取消令牌。 - /// 请求响应任务。 - /// 该测试桩不支持此成员。 - public ValueTask SendAsync(IRequest command, - CancellationToken cancellationToken = default) - { - throw new NotSupportedException(); - } - - /// - /// 发送命令 - /// - /// 命令对象 - public void SendCommand(ICommand command) - { - } - - /// - /// 发送带返回值的命令 - /// - /// 返回值类型 - /// 命令对象 - /// 命令执行结果 - public TResult SendCommand(ICommand command) - { - return default!; - } - - public Task SendCommandAsync(IAsyncCommand command) - { - return Task.CompletedTask; - } - - public Task SendCommandAsync(IAsyncCommand command) - { - return (Task)Task.CompletedTask; - } - - /// - /// 发送查询请求 - /// - /// 查询结果类型 - /// 查询对象 - /// 查询结果 - public TResult SendQuery(IQuery query) - { - return default!; - } - - /// - /// 异步发送查询请求 - /// - /// 查询结果类型 - /// 异步查询对象 - /// 查询结果 - public Task SendQueryAsync(IAsyncQuery query) - { - return (Task)Task.CompletedTask; - } - - /// - /// 获取环境对象 - /// - /// 环境对象 - public IEnvironment GetEnvironment() - { - return Environment; - } -} diff --git a/GFramework.Core.Tests/Architectures/TestArchitecture.cs b/GFramework.Core.Tests/Architectures/TestArchitecture.cs new file mode 100644 index 00000000..686f44ad --- /dev/null +++ b/GFramework.Core.Tests/Architectures/TestArchitecture.cs @@ -0,0 +1,16 @@ +using GFramework.Core.Architectures; + +namespace GFramework.Core.Tests.Architectures; + +/// +/// 提供给 的最小架构测试桩。 +/// +public class TestArchitecture : Architecture +{ + /// + /// 保持空初始化流程,便于测试仅验证 的上下文绑定行为。 + /// + protected override void OnInitialize() + { + } +} diff --git a/GFramework.Core.Tests/Architectures/TestArchitectureContext.cs b/GFramework.Core.Tests/Architectures/TestArchitectureContext.cs new file mode 100644 index 00000000..cda590fd --- /dev/null +++ b/GFramework.Core.Tests/Architectures/TestArchitectureContext.cs @@ -0,0 +1,427 @@ +using GFramework.Core.Abstractions.Architectures; +using GFramework.Core.Abstractions.Command; +using GFramework.Core.Abstractions.Environment; +using GFramework.Core.Abstractions.Events; +using GFramework.Core.Abstractions.Ioc; +using GFramework.Core.Abstractions.Model; +using GFramework.Core.Abstractions.Query; +using GFramework.Core.Abstractions.Systems; +using GFramework.Core.Abstractions.Utility; +using GFramework.Core.Command; +using GFramework.Core.Environment; +using GFramework.Core.Events; +using GFramework.Core.Ioc; +using GFramework.Core.Query; +using GFramework.Cqrs.Abstractions.Cqrs; +using ICommand = GFramework.Core.Abstractions.Command.ICommand; + +namespace GFramework.Core.Tests.Architectures; + +/// +/// 为 提供最小可用的架构上下文测试桩。 +/// +/// +/// 该类型只实现当前测试切片会触达的基础行为,其余 CQRS 入口显式抛出 , +/// 避免测试误把未覆盖能力当成可用实现。 +/// +public class TestArchitectureContext : IArchitectureContext +{ + private readonly MicrosoftDiContainer _container = new(); + + /// + /// 获取用于解析测试服务的依赖注入容器。 + /// + public IIocContainer Container => _container; + + /// + /// 获取测试事件总线实例。 + /// + public IEventBus EventBus => new EventBus(); + + /// + /// 获取测试命令执行器实例。 + /// + public ICommandExecutor CommandExecutor => new CommandExecutor(); + + /// + /// 获取测试查询执行器实例。 + /// + public IQueryExecutor QueryExecutor => new QueryExecutor(); + + /// + /// 获取默认测试环境对象。 + /// + public IEnvironment Environment => new DefaultEnvironment(); + + /// + /// 获取指定类型的服务实例。 + /// + /// 服务类型。 + /// 已注册的服务实例。 + /// 未注册服务时抛出。 + public TService GetService() where TService : class + { + return _container.GetRequired(); + } + + /// + /// 获取指定类型的所有服务实例。 + /// + /// 服务类型。 + /// 服务实例列表。 + public IReadOnlyList GetServices() where TService : class + { + return _container.GetAll(); + } + + /// + /// 获取指定类型的模型实例。 + /// + /// 模型类型。 + /// 已注册的模型实例。 + /// 未注册模型时抛出。 + public TModel GetModel() where TModel : class, IModel + { + return _container.GetRequired(); + } + + /// + /// 获取指定类型的所有模型实例。 + /// + /// 模型类型。 + /// 模型实例列表。 + public IReadOnlyList GetModels() where TModel : class, IModel + { + return _container.GetAll(); + } + + /// + /// 获取指定类型的系统实例。 + /// + /// 系统类型。 + /// 已注册的系统实例。 + /// 未注册系统时抛出。 + public TSystem GetSystem() where TSystem : class, ISystem + { + return _container.GetRequired(); + } + + /// + /// 获取指定类型的所有系统实例。 + /// + /// 系统类型。 + /// 系统实例列表。 + public IReadOnlyList GetSystems() where TSystem : class, ISystem + { + return _container.GetAll(); + } + + /// + /// 获取指定类型的工具实例。 + /// + /// 工具类型。 + /// 已注册的工具实例。 + /// 未注册工具时抛出。 + public virtual TUtility GetUtility() where TUtility : class, IUtility + { + return _container.GetRequired(); + } + + /// + /// 获取指定类型的所有工具实例。 + /// + /// 工具类型。 + /// 工具实例列表。 + public IReadOnlyList GetUtilities() where TUtility : class, IUtility + { + return _container.GetAll(); + } + + /// + /// 获取指定类型的所有服务实例,并按优先级排序。 + /// + /// 服务类型。 + /// 按优先级排序后的服务实例列表。 + public IReadOnlyList GetServicesByPriority() where TService : class + { + return _container.GetAllByPriority(); + } + + /// + /// 获取指定类型的所有系统实例,并按优先级排序。 + /// + /// 系统类型。 + /// 按优先级排序后的系统实例列表。 + public IReadOnlyList GetSystemsByPriority() where TSystem : class, ISystem + { + return _container.GetAllByPriority(); + } + + /// + /// 获取指定类型的所有模型实例,并按优先级排序。 + /// + /// 模型类型。 + /// 按优先级排序后的模型实例列表。 + public IReadOnlyList GetModelsByPriority() where TModel : class, IModel + { + return _container.GetAllByPriority(); + } + + /// + /// 获取指定类型的所有工具实例,并按优先级排序。 + /// + /// 工具类型。 + /// 按优先级排序后的工具实例列表。 + public IReadOnlyList GetUtilitiesByPriority() where TUtility : class, IUtility + { + return _container.GetAllByPriority(); + } + + /// + /// 发送无参数事件。 + /// + /// 事件类型。 + public void SendEvent() where TEvent : new() + { + } + + /// + /// 发送带参数事件。 + /// + /// 事件类型。 + /// 事件实例。 + public void SendEvent(TEvent e) where TEvent : class + { + } + + /// + /// 注册事件处理器。 + /// + /// 事件类型。 + /// 事件处理委托。 + /// 用于测试的空注销句柄。 + public IUnRegister RegisterEvent(Action handler) + { + return new DefaultUnRegister(() => { }); + } + + /// + /// 取消注册事件处理器。 + /// + /// 事件类型。 + /// 事件处理委托。 + public void UnRegisterEvent(Action onEvent) + { + } + + /// + /// 测试桩:异步发送统一 CQRS 请求。 + /// + /// 响应类型。 + /// 要发送的请求。 + /// 取消令牌。 + /// 请求响应任务。 + /// 该测试桩不支持此成员。 + public ValueTask SendRequestAsync( + IRequest request, + CancellationToken cancellationToken = default) + { + throw new NotSupportedException(); + } + + /// + /// 测试桩:同步发送统一 CQRS 请求。 + /// + /// 响应类型。 + /// 要发送的请求。 + /// 请求响应。 + /// 该测试桩不支持此成员。 + public TResponse SendRequest(IRequest request) + { + throw new NotSupportedException(); + } + + /// + /// 测试桩:异步发送 CQRS 命令并返回响应。 + /// + /// 命令响应类型。 + /// 要发送的命令。 + /// 取消令牌。 + /// 命令响应任务。 + /// 该测试桩不支持此成员。 + public ValueTask SendCommandAsync( + GFramework.Cqrs.Abstractions.Cqrs.Command.ICommand command, + CancellationToken cancellationToken = default) + { + throw new NotSupportedException(); + } + + /// + /// 测试桩:同步发送 CQRS 命令并返回响应。 + /// + /// 命令响应类型。 + /// 要发送的命令。 + /// 命令响应。 + /// 该测试桩不支持此成员。 + public TResponse SendCommand(GFramework.Cqrs.Abstractions.Cqrs.Command.ICommand command) + { + throw new NotSupportedException(); + } + + /// + /// 测试桩:异步发送 CQRS 查询并返回结果。 + /// + /// 查询结果类型。 + /// 要发送的查询。 + /// 取消令牌。 + /// 查询结果任务。 + /// 该测试桩不支持此成员。 + public ValueTask SendQueryAsync( + GFramework.Cqrs.Abstractions.Cqrs.Query.IQuery query, + CancellationToken cancellationToken = default) + { + throw new NotSupportedException(); + } + + /// + /// 测试桩:同步发送 CQRS 查询并返回结果。 + /// + /// 查询结果类型。 + /// 要发送的查询。 + /// 查询结果。 + /// 该测试桩不支持此成员。 + public TResponse SendQuery(GFramework.Cqrs.Abstractions.Cqrs.Query.IQuery query) + { + throw new NotSupportedException(); + } + + /// + /// 测试桩:异步发布 CQRS 通知。 + /// + /// 通知类型。 + /// 要发布的通知。 + /// 取消令牌。 + /// 通知发布任务。 + /// 该测试桩不支持此成员。 + public ValueTask PublishAsync( + TNotification notification, + CancellationToken cancellationToken = default) where TNotification : INotification + { + throw new NotSupportedException(); + } + + /// + /// 测试桩:创建 CQRS 流式请求响应序列。 + /// + /// 流式响应元素类型。 + /// 流式请求。 + /// 取消令牌。 + /// 异步响应流。 + /// 该测试桩不支持此成员。 + public IAsyncEnumerable CreateStream( + IStreamRequest request, + CancellationToken cancellationToken = default) + { + throw new NotSupportedException(); + } + + /// + /// 测试桩:异步发送无返回值 CQRS 命令。 + /// + /// 命令类型。 + /// 要发送的命令。 + /// 取消令牌。 + /// 命令发送任务。 + /// 该测试桩不支持此成员。 + public ValueTask SendAsync(TCommand command, CancellationToken cancellationToken = default) + where TCommand : IRequest + { + throw new NotSupportedException(); + } + + /// + /// 测试桩:异步发送带返回值的 CQRS 请求。 + /// + /// 响应类型。 + /// 要发送的请求。 + /// 取消令牌。 + /// 请求响应任务。 + /// 该测试桩不支持此成员。 + public ValueTask SendAsync( + IRequest command, + CancellationToken cancellationToken = default) + { + throw new NotSupportedException(); + } + + /// + /// 发送旧版命令。 + /// + /// 命令对象。 + public void SendCommand(ICommand command) + { + } + + /// + /// 发送旧版带返回值命令。 + /// + /// 返回值类型。 + /// 命令对象。 + /// 测试桩默认返回值。 + public TResult SendCommand(ICommand command) + { + return default!; + } + + /// + /// 异步发送旧版命令。 + /// + /// 命令对象。 + /// 已完成任务。 + public Task SendCommandAsync(IAsyncCommand command) + { + return Task.CompletedTask; + } + + /// + /// 异步发送旧版带返回值命令。 + /// + /// 返回值类型。 + /// 命令对象。 + /// 包含测试桩默认返回值的任务。 + public Task SendCommandAsync(IAsyncCommand command) + { + return Task.FromResult(default(TResult)!); + } + + /// + /// 发送旧版查询请求。 + /// + /// 查询结果类型。 + /// 查询对象。 + /// 测试桩默认返回值。 + public TResult SendQuery(IQuery query) + { + return default!; + } + + /// + /// 异步发送旧版查询请求。 + /// + /// 查询结果类型。 + /// 异步查询对象。 + /// 包含测试桩默认返回值的任务。 + public Task SendQueryAsync(IAsyncQuery query) + { + return Task.FromResult(default(TResult)!); + } + + /// + /// 获取当前环境对象。 + /// + /// 默认测试环境对象。 + public IEnvironment GetEnvironment() + { + return Environment; + } +} From 54530d31d9fa880e30d57003228083f27c6cd6f1 Mon Sep 17 00:00:00 2001 From: gewuyou <95328647+GeWuYou@users.noreply.github.com> Date: Tue, 28 Apr 2026 07:50:36 +0800 Subject: [PATCH 03/10] =?UTF-8?q?test(architectures):=20=E6=8B=86=E5=88=86?= =?UTF-8?q?=20RegistryInitializationHookBaseTests=20=E8=BE=85=E5=8A=A9?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 拆分 RegistryInitializationHookBaseTests 末尾的测试辅助类型到同目录独立文件以消除 MA0048 - 更新 TestRegistry 的公开集合暴露方式为只读接口以规避 MA0016 - 补充新测试辅助类型的 XML 文档并保持原有测试行为不变 --- .../RegistryInitializationHookBaseTests.cs | 296 +----------------- .../TestArchitectureContextWithRegistry.cs | 35 +++ .../TestArchitectureWithRegistry.cs | 204 ++++++++++++ .../TestArchitectureWithoutRegistry.cs | 187 +++++++++++ .../Architectures/TestRegistry.cs | 26 ++ .../TestRegistryInitializationHook.cs | 33 ++ 6 files changed, 486 insertions(+), 295 deletions(-) create mode 100644 GFramework.Core.Tests/Architectures/TestArchitectureContextWithRegistry.cs create mode 100644 GFramework.Core.Tests/Architectures/TestArchitectureWithRegistry.cs create mode 100644 GFramework.Core.Tests/Architectures/TestArchitectureWithoutRegistry.cs create mode 100644 GFramework.Core.Tests/Architectures/TestRegistry.cs create mode 100644 GFramework.Core.Tests/Architectures/TestRegistryInitializationHook.cs diff --git a/GFramework.Core.Tests/Architectures/RegistryInitializationHookBaseTests.cs b/GFramework.Core.Tests/Architectures/RegistryInitializationHookBaseTests.cs index b3a3975f..58f73afb 100644 --- a/GFramework.Core.Tests/Architectures/RegistryInitializationHookBaseTests.cs +++ b/GFramework.Core.Tests/Architectures/RegistryInitializationHookBaseTests.cs @@ -1,11 +1,5 @@ -using System.Reflection; -using GFramework.Core.Abstractions.Architectures; +using System; using GFramework.Core.Abstractions.Enums; -using GFramework.Core.Abstractions.Lifecycle; -using GFramework.Core.Abstractions.Model; -using GFramework.Core.Abstractions.Systems; -using GFramework.Core.Abstractions.Utility; -using GFramework.Core.Architectures; namespace GFramework.Core.Tests.Architectures; @@ -116,291 +110,3 @@ public class RegistryInitializationHookBaseTests Assert.That(registry.RegisteredConfigs.Count, Is.EqualTo(2)); } } - -/// -/// 测试用的注册表初始化钩子实现 -/// -public class TestRegistryInitializationHook : RegistryInitializationHookBase -{ - public TestRegistryInitializationHook( - IEnumerable configs, - ArchitecturePhase targetPhase = ArchitecturePhase.AfterSystemInit) - : base(configs, targetPhase) - { - } - - protected override void RegisterConfig(TestRegistry registry, string config) - { - registry.Register(config); - } -} - -/// -/// 测试用的注册表类 -/// -public class TestRegistry : IUtility -{ - public List RegisteredConfigs { get; } = new(); - - public void Register(string config) - { - RegisteredConfigs.Add(config); - } -} - -/// -/// 测试用的架构类(包含注册表) -/// -public class TestArchitectureWithRegistry : IArchitecture -{ - private readonly TestRegistry _registry; - - public TestArchitectureWithRegistry(TestRegistry registry) - { - _registry = registry; - Context = new TestArchitectureContextWithRegistry(registry); - } - - public Action? Configurator { get; } - - public IArchitectureContext Context { get; } - Action? IArchitecture.Configurator => Configurator; - - T IArchitecture.RegisterSystem(T system) - { - throw new NotSupportedException(); - } - - T IArchitecture.RegisterModel(T model) - { - throw new NotSupportedException(); - } - - T IArchitecture.RegisterUtility(T utility) - { - throw new NotSupportedException(); - } - - public void RegisterCqrsPipelineBehavior() where TBehavior : class - { - throw new NotSupportedException(); - } - - /// - /// 测试替身未实现显式程序集 CQRS 处理器接入入口。 - /// - /// 包含 CQRS 处理器或生成注册器的程序集。 - /// 该测试替身不参与 CQRS 程序集接入路径验证。 - public void RegisterCqrsHandlersFromAssembly(Assembly assembly) - { - throw new NotSupportedException(); - } - - /// - /// 测试替身未实现显式程序集 CQRS 处理器接入入口。 - /// - /// 要接入的程序集集合。 - /// 该测试替身不参与 CQRS 程序集接入路径验证。 - public void RegisterCqrsHandlersFromAssemblies(IEnumerable assemblies) - { - throw new NotSupportedException(); - } - - public IArchitectureModule InstallModule(IArchitectureModule module) - { - throw new NotSupportedException(); - } - - IArchitectureLifecycleHook IArchitecture.RegisterLifecycleHook(IArchitectureLifecycleHook hook) - { - throw new NotSupportedException(); - } - - Task IArchitecture.WaitUntilReadyAsync() - { - return WaitUntilReadyAsync(); - } - - public void RegisterUtility(Action? onCreated = default(Action?)) where T : class, IUtility - { - throw new NotSupportedException(); - } - - public void RegisterModel(Action? onCreated = default(Action?)) where T : class, IModel - { - throw new NotSupportedException(); - } - - public void RegisterSystem(Action? onCreated = default(Action?)) where T : class, ISystem - { - throw new NotSupportedException(); - } - - public void Initialize() - { - } - - public void Destroy() - { - throw new NotSupportedException(); - } - - Task IAsyncInitializable.InitializeAsync() - { - return InitializeAsync(); - } - - ValueTask IAsyncDestroyable.DestroyAsync() - { - return DestroyAsync(); - } - - public Task WaitUntilReadyAsync() - { - throw new NotSupportedException(); - } - - public void RegisterLifecycleHook(IArchitectureLifecycleHook hook) - { - } - - public Task InitializeAsync() - { - throw new NotSupportedException(); - } - - public ValueTask DestroyAsync() - { - throw new NotSupportedException(); - } -} - -/// -/// 测试用的架构上下文类(包含注册表) -/// -public class TestArchitectureContextWithRegistry : TestArchitectureContext -{ - private readonly TestRegistry _registry; - - public TestArchitectureContextWithRegistry(TestRegistry registry) - { - _registry = registry; - } - - public override TUtility? GetUtility() where TUtility : class - { - if (typeof(TUtility) == typeof(TestRegistry)) - { - return _registry as TUtility; - } - - return base.GetUtility(); - } -} - -/// -/// 测试用的架构类(不包含注册表) -/// -public class TestArchitectureWithoutRegistry : IArchitecture -{ - public TestArchitectureWithoutRegistry() - { - Context = new TestArchitectureContext(); - } - - public IArchitectureContext Context { get; } - public Action? Configurator { get; } - - T IArchitecture.RegisterSystem(T system) - { - throw new NotSupportedException(); - } - - T IArchitecture.RegisterModel(T model) - { - throw new NotSupportedException(); - } - - T IArchitecture.RegisterUtility(T utility) - { - throw new NotSupportedException(); - } - - public void RegisterCqrsPipelineBehavior() where TBehavior : class - { - throw new NotSupportedException(); - } - - /// - /// 测试替身未实现显式程序集 CQRS 处理器接入入口。 - /// - /// 包含 CQRS 处理器或生成注册器的程序集。 - /// 该测试替身不参与 CQRS 程序集接入路径验证。 - public void RegisterCqrsHandlersFromAssembly(Assembly assembly) - { - throw new NotSupportedException(); - } - - /// - /// 测试替身未实现显式程序集 CQRS 处理器接入入口。 - /// - /// 要接入的程序集集合。 - /// 该测试替身不参与 CQRS 程序集接入路径验证。 - public void RegisterCqrsHandlersFromAssemblies(IEnumerable assemblies) - { - throw new NotSupportedException(); - } - - public IArchitectureModule InstallModule(IArchitectureModule module) - { - throw new NotSupportedException(); - } - - IArchitectureLifecycleHook IArchitecture.RegisterLifecycleHook(IArchitectureLifecycleHook hook) - { - throw new NotSupportedException(); - } - - public Task WaitUntilReadyAsync() - { - throw new NotSupportedException(); - } - - public void RegisterUtility(Action? onCreated = default(Action?)) where T : class, IUtility - { - throw new NotSupportedException(); - } - - public void RegisterModel(Action? onCreated = default(Action?)) where T : class, IModel - { - throw new NotSupportedException(); - } - - public void RegisterSystem(Action? onCreated = default(Action?)) where T : class, ISystem - { - throw new NotSupportedException(); - } - - public void Initialize() - { - } - - public Task InitializeAsync() - { - throw new NotSupportedException(); - } - - public ValueTask DestroyAsync() - { - throw new NotSupportedException(); - } - - public void Destroy() - { - throw new NotSupportedException(); - } - - public void RegisterLifecycleHook(IArchitectureLifecycleHook hook) - { - } -} diff --git a/GFramework.Core.Tests/Architectures/TestArchitectureContextWithRegistry.cs b/GFramework.Core.Tests/Architectures/TestArchitectureContextWithRegistry.cs new file mode 100644 index 00000000..373bfe3e --- /dev/null +++ b/GFramework.Core.Tests/Architectures/TestArchitectureContextWithRegistry.cs @@ -0,0 +1,35 @@ +using GFramework.Core.Architectures; + +namespace GFramework.Core.Tests.Architectures; + +/// +/// 为 在架构上下文中暴露 的测试替身。 +/// +public class TestArchitectureContextWithRegistry : TestArchitectureContext +{ + private readonly TestRegistry _registry; + + /// + /// 使用给定测试注册表创建上下文测试替身。 + /// + /// 需要通过 返回的测试注册表。 + public TestArchitectureContextWithRegistry(TestRegistry registry) + { + _registry = registry; + } + + /// + /// 在请求 时返回测试注册表,其余类型回退到基类实现。 + /// + /// 请求的工具类型。 + /// 匹配时返回测试注册表,否则返回基类结果。 + public override TUtility GetUtility() + { + if (typeof(TUtility) == typeof(TestRegistry)) + { + return (TUtility)(object)_registry; + } + + return base.GetUtility(); + } +} diff --git a/GFramework.Core.Tests/Architectures/TestArchitectureWithRegistry.cs b/GFramework.Core.Tests/Architectures/TestArchitectureWithRegistry.cs new file mode 100644 index 00000000..cdb52e81 --- /dev/null +++ b/GFramework.Core.Tests/Architectures/TestArchitectureWithRegistry.cs @@ -0,0 +1,204 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Threading.Tasks; +using GFramework.Core.Abstractions.Architectures; +using GFramework.Core.Abstractions.Lifecycle; +using GFramework.Core.Abstractions.Model; +using GFramework.Core.Abstractions.Systems; +using GFramework.Core.Abstractions.Utility; + +namespace GFramework.Core.Tests.Architectures; + +/// +/// 为 提供已挂接 的架构测试替身。 +/// +public class TestArchitectureWithRegistry : IArchitecture +{ + /// + /// 使用给定测试注册表创建架构测试替身。 + /// + /// 要通过架构上下文暴露给钩子的测试注册表。 + public TestArchitectureWithRegistry(TestRegistry registry) + { + Context = new TestArchitectureContextWithRegistry(registry); + } + + /// + /// 获取测试替身公开的服务配置入口。 + /// 当前切片不验证服务配置流程,因此始终保持为空。 + /// + public Action? Configurator { get; } + + /// + /// 获取当前测试替身使用的架构上下文。 + /// + public IArchitectureContext Context { get; } + + Action? IArchitecture.Configurator => Configurator; + + T IArchitecture.RegisterSystem(T system) + { + throw new NotSupportedException(); + } + + T IArchitecture.RegisterModel(T model) + { + throw new NotSupportedException(); + } + + T IArchitecture.RegisterUtility(T utility) + { + throw new NotSupportedException(); + } + + /// + /// 测试替身未实现 CQRS 管道行为注册。 + /// + /// 行为类型。 + /// 该测试替身不参与 CQRS 管道配置验证。 + public void RegisterCqrsPipelineBehavior() where TBehavior : class + { + throw new NotSupportedException(); + } + + /// + /// 测试替身未实现显式程序集 CQRS 处理器接入入口。 + /// + /// 包含 CQRS 处理器或生成注册器的程序集。 + /// 该测试替身不参与 CQRS 程序集接入路径验证。 + public void RegisterCqrsHandlersFromAssembly(Assembly assembly) + { + throw new NotSupportedException(); + } + + /// + /// 测试替身未实现显式程序集 CQRS 处理器接入入口。 + /// + /// 要接入的程序集集合。 + /// 该测试替身不参与 CQRS 程序集接入路径验证。 + public void RegisterCqrsHandlersFromAssemblies(IEnumerable assemblies) + { + throw new NotSupportedException(); + } + + /// + /// 测试替身未实现模块安装流程。 + /// + /// 要安装的模块。 + /// 此方法始终抛出异常,不返回模块实例。 + /// 该测试替身不参与模块安装路径验证。 + public IArchitectureModule InstallModule(IArchitectureModule module) + { + throw new NotSupportedException(); + } + + IArchitectureLifecycleHook IArchitecture.RegisterLifecycleHook(IArchitectureLifecycleHook hook) + { + throw new NotSupportedException(); + } + + Task IArchitecture.WaitUntilReadyAsync() + { + return WaitUntilReadyAsync(); + } + + /// + /// 测试替身未实现工具延迟注册入口。 + /// + /// 工具类型。 + /// 工具创建后的回调。 + /// 该测试替身不参与工具注册路径验证。 + public void RegisterUtility(Action? onCreated = default) where T : class, IUtility + { + throw new NotSupportedException(); + } + + /// + /// 测试替身未实现 Model 延迟注册入口。 + /// + /// Model 类型。 + /// Model 创建后的回调。 + /// 该测试替身不参与 Model 注册路径验证。 + public void RegisterModel(Action? onCreated = default) where T : class, IModel + { + throw new NotSupportedException(); + } + + /// + /// 测试替身未实现 System 延迟注册入口。 + /// + /// System 类型。 + /// System 创建后的回调。 + /// 该测试替身不参与 System 注册路径验证。 + public void RegisterSystem(Action? onCreated = default) where T : class, ISystem + { + throw new NotSupportedException(); + } + + /// + /// 初始化测试替身。 + /// 该切片只需要上下文可用,因此初始化过程保持为空实现。 + /// + public void Initialize() + { + } + + /// + /// 测试替身未实现销毁路径。 + /// + /// 该测试替身不参与销毁路径验证。 + public void Destroy() + { + throw new NotSupportedException(); + } + + Task IAsyncInitializable.InitializeAsync() + { + return InitializeAsync(); + } + + ValueTask IAsyncDestroyable.DestroyAsync() + { + return DestroyAsync(); + } + + /// + /// 测试替身未实现就绪等待流程。 + /// + /// 此方法始终抛出异常,不返回等待任务。 + /// 该测试替身不参与就绪等待路径验证。 + public Task WaitUntilReadyAsync() + { + throw new NotSupportedException(); + } + + /// + /// 注册架构生命周期钩子。 + /// 当前切片不依赖生命周期钩子执行,因此保持空实现。 + /// + /// 要忽略的生命周期钩子。 + public void RegisterLifecycleHook(IArchitectureLifecycleHook hook) + { + } + + /// + /// 测试替身未实现异步初始化路径。 + /// + /// 此方法始终抛出异常,不返回初始化任务。 + /// 该测试替身不参与异步初始化验证。 + public Task InitializeAsync() + { + throw new NotSupportedException(); + } + + /// + /// 测试替身未实现异步销毁路径。 + /// + /// 此方法始终抛出异常,不返回销毁任务。 + /// 该测试替身不参与异步销毁验证。 + public ValueTask DestroyAsync() + { + throw new NotSupportedException(); + } +} diff --git a/GFramework.Core.Tests/Architectures/TestArchitectureWithoutRegistry.cs b/GFramework.Core.Tests/Architectures/TestArchitectureWithoutRegistry.cs new file mode 100644 index 00000000..f4bd61ec --- /dev/null +++ b/GFramework.Core.Tests/Architectures/TestArchitectureWithoutRegistry.cs @@ -0,0 +1,187 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Threading.Tasks; +using GFramework.Core.Abstractions.Architectures; +using GFramework.Core.Abstractions.Lifecycle; +using GFramework.Core.Abstractions.Model; +using GFramework.Core.Abstractions.Systems; +using GFramework.Core.Abstractions.Utility; +using GFramework.Core.Architectures; + +namespace GFramework.Core.Tests.Architectures; + +/// +/// 为 提供不包含 的架构测试替身。 +/// +public class TestArchitectureWithoutRegistry : IArchitecture +{ + /// + /// 创建不包含测试注册表的架构替身。 + /// + public TestArchitectureWithoutRegistry() + { + Context = new TestArchitectureContext(); + } + + /// + /// 获取测试替身公开的服务配置入口。 + /// 当前切片不验证服务配置流程,因此始终保持为空。 + /// + public Action? Configurator { get; } + + /// + /// 获取当前测试替身使用的架构上下文。 + /// + public IArchitectureContext Context { get; } + + T IArchitecture.RegisterSystem(T system) + { + throw new NotSupportedException(); + } + + T IArchitecture.RegisterModel(T model) + { + throw new NotSupportedException(); + } + + T IArchitecture.RegisterUtility(T utility) + { + throw new NotSupportedException(); + } + + /// + /// 测试替身未实现 CQRS 管道行为注册。 + /// + /// 行为类型。 + /// 该测试替身不参与 CQRS 管道配置验证。 + public void RegisterCqrsPipelineBehavior() where TBehavior : class + { + throw new NotSupportedException(); + } + + /// + /// 测试替身未实现显式程序集 CQRS 处理器接入入口。 + /// + /// 包含 CQRS 处理器或生成注册器的程序集。 + /// 该测试替身不参与 CQRS 程序集接入路径验证。 + public void RegisterCqrsHandlersFromAssembly(Assembly assembly) + { + throw new NotSupportedException(); + } + + /// + /// 测试替身未实现显式程序集 CQRS 处理器接入入口。 + /// + /// 要接入的程序集集合。 + /// 该测试替身不参与 CQRS 程序集接入路径验证。 + public void RegisterCqrsHandlersFromAssemblies(IEnumerable assemblies) + { + throw new NotSupportedException(); + } + + /// + /// 测试替身未实现模块安装流程。 + /// + /// 要安装的模块。 + /// 此方法始终抛出异常,不返回模块实例。 + /// 该测试替身不参与模块安装路径验证。 + public IArchitectureModule InstallModule(IArchitectureModule module) + { + throw new NotSupportedException(); + } + + IArchitectureLifecycleHook IArchitecture.RegisterLifecycleHook(IArchitectureLifecycleHook hook) + { + throw new NotSupportedException(); + } + + /// + /// 测试替身未实现就绪等待流程。 + /// + /// 此方法始终抛出异常,不返回等待任务。 + /// 该测试替身不参与就绪等待路径验证。 + public Task WaitUntilReadyAsync() + { + throw new NotSupportedException(); + } + + /// + /// 测试替身未实现工具延迟注册入口。 + /// + /// 工具类型。 + /// 工具创建后的回调。 + /// 该测试替身不参与工具注册路径验证。 + public void RegisterUtility(Action? onCreated = default) where T : class, IUtility + { + throw new NotSupportedException(); + } + + /// + /// 测试替身未实现 Model 延迟注册入口。 + /// + /// Model 类型。 + /// Model 创建后的回调。 + /// 该测试替身不参与 Model 注册路径验证。 + public void RegisterModel(Action? onCreated = default) where T : class, IModel + { + throw new NotSupportedException(); + } + + /// + /// 测试替身未实现 System 延迟注册入口。 + /// + /// System 类型。 + /// System 创建后的回调。 + /// 该测试替身不参与 System 注册路径验证。 + public void RegisterSystem(Action? onCreated = default) where T : class, ISystem + { + throw new NotSupportedException(); + } + + /// + /// 初始化测试替身。 + /// 该切片只需要一个不含注册表的上下文,因此初始化过程保持为空实现。 + /// + public void Initialize() + { + } + + /// + /// 测试替身未实现异步初始化路径。 + /// + /// 此方法始终抛出异常,不返回初始化任务。 + /// 该测试替身不参与异步初始化验证。 + public Task InitializeAsync() + { + throw new NotSupportedException(); + } + + /// + /// 测试替身未实现异步销毁路径。 + /// + /// 此方法始终抛出异常,不返回销毁任务。 + /// 该测试替身不参与异步销毁验证。 + public ValueTask DestroyAsync() + { + throw new NotSupportedException(); + } + + /// + /// 测试替身未实现销毁路径。 + /// + /// 该测试替身不参与销毁路径验证。 + public void Destroy() + { + throw new NotSupportedException(); + } + + /// + /// 注册架构生命周期钩子。 + /// 当前切片不依赖生命周期钩子执行,因此保持空实现。 + /// + /// 要忽略的生命周期钩子。 + public void RegisterLifecycleHook(IArchitectureLifecycleHook hook) + { + } +} diff --git a/GFramework.Core.Tests/Architectures/TestRegistry.cs b/GFramework.Core.Tests/Architectures/TestRegistry.cs new file mode 100644 index 00000000..4daf601b --- /dev/null +++ b/GFramework.Core.Tests/Architectures/TestRegistry.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using GFramework.Core.Abstractions.Utility; + +namespace GFramework.Core.Tests.Architectures; + +/// +/// 为 记录注册结果的测试注册表。 +/// +public class TestRegistry : IUtility +{ + private readonly List _registeredConfigs = []; + + /// + /// 获取已注册配置值的只读视图,避免将测试内部使用的列表实现暴露给调用方。 + /// + public IReadOnlyList RegisteredConfigs => _registeredConfigs; + + /// + /// 记录一次配置注册。 + /// + /// 要追加到测试结果中的配置值。 + public void Register(string config) + { + _registeredConfigs.Add(config); + } +} diff --git a/GFramework.Core.Tests/Architectures/TestRegistryInitializationHook.cs b/GFramework.Core.Tests/Architectures/TestRegistryInitializationHook.cs new file mode 100644 index 00000000..3241882d --- /dev/null +++ b/GFramework.Core.Tests/Architectures/TestRegistryInitializationHook.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using GFramework.Core.Abstractions.Enums; +using GFramework.Core.Architectures; + +namespace GFramework.Core.Tests.Architectures; + +/// +/// 为 提供的注册表初始化钩子测试替身。 +/// +public class TestRegistryInitializationHook : RegistryInitializationHookBase +{ + /// + /// 使用给定配置集合和目标阶段创建测试钩子。 + /// + /// 测试期间要注册到目标注册表的配置值。 + /// 触发注册行为的架构阶段。 + public TestRegistryInitializationHook( + IEnumerable configs, + ArchitecturePhase targetPhase = ArchitecturePhase.AfterSystemInit) + : base(configs, targetPhase) + { + } + + /// + /// 将当前配置值写入测试注册表。 + /// + /// 要接收配置值的测试注册表。 + /// 当前遍历到的配置值。 + protected override void RegisterConfig(TestRegistry registry, string config) + { + registry.Register(config); + } +} From 98afcbffb3742af8bd1909461bcc417e73be16d6 Mon Sep 17 00:00:00 2001 From: gewuyou <95328647+GeWuYou@users.noreply.github.com> Date: Tue, 28 Apr 2026 07:56:38 +0800 Subject: [PATCH 04/10] =?UTF-8?q?fix(cqrs-tests):=20=E6=8B=86=E5=88=86?= =?UTF-8?q?=E5=A4=84=E7=90=86=E5=99=A8=E6=B3=A8=E5=86=8C=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E8=BE=85=E5=8A=A9=E7=B1=BB=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 拆分 CqrsHandlerRegistrarTests 尾部的测试辅助类型到同目录同名文件 - 保持 CQRS handler registrar 测试行为与 XML 文档不变并消除该切片的 MA0048 warning --- .../AlphaDeterministicNotificationHandler.cs | 23 ++ .../Cqrs/CapturingLoggerFactoryProvider.cs | 49 ++++ .../Cqrs/CqrsHandlerRegistrarTests.cs | 227 ------------------ .../DeterministicNotificationHandlerState.cs | 22 ++ .../Cqrs/DeterministicOrderNotification.cs | 8 + .../GeneratedNotificationHandlerRegistry.cs | 30 +++ .../Cqrs/GeneratedRegistryNotification.cs | 8 + .../GeneratedRegistryNotificationHandler.cs | 22 ++ ...ialGeneratedNotificationHandlerRegistry.cs | 30 +++ ...eConstructorNotificationHandlerRegistry.cs | 37 +++ ...ReflectionFallbackNotificationContainer.cs | 32 +++ .../ZetaDeterministicNotificationHandler.cs | 23 ++ 12 files changed, 284 insertions(+), 227 deletions(-) create mode 100644 GFramework.Cqrs.Tests/Cqrs/AlphaDeterministicNotificationHandler.cs create mode 100644 GFramework.Cqrs.Tests/Cqrs/CapturingLoggerFactoryProvider.cs create mode 100644 GFramework.Cqrs.Tests/Cqrs/DeterministicNotificationHandlerState.cs create mode 100644 GFramework.Cqrs.Tests/Cqrs/DeterministicOrderNotification.cs create mode 100644 GFramework.Cqrs.Tests/Cqrs/GeneratedNotificationHandlerRegistry.cs create mode 100644 GFramework.Cqrs.Tests/Cqrs/GeneratedRegistryNotification.cs create mode 100644 GFramework.Cqrs.Tests/Cqrs/GeneratedRegistryNotificationHandler.cs create mode 100644 GFramework.Cqrs.Tests/Cqrs/PartialGeneratedNotificationHandlerRegistry.cs create mode 100644 GFramework.Cqrs.Tests/Cqrs/PrivateConstructorNotificationHandlerRegistry.cs create mode 100644 GFramework.Cqrs.Tests/Cqrs/ReflectionFallbackNotificationContainer.cs create mode 100644 GFramework.Cqrs.Tests/Cqrs/ZetaDeterministicNotificationHandler.cs diff --git a/GFramework.Cqrs.Tests/Cqrs/AlphaDeterministicNotificationHandler.cs b/GFramework.Cqrs.Tests/Cqrs/AlphaDeterministicNotificationHandler.cs new file mode 100644 index 00000000..5142d5b7 --- /dev/null +++ b/GFramework.Cqrs.Tests/Cqrs/AlphaDeterministicNotificationHandler.cs @@ -0,0 +1,23 @@ +using System.Threading; +using System.Threading.Tasks; +using GFramework.Cqrs.Abstractions.Cqrs; + +namespace GFramework.Cqrs.Tests.Cqrs; + +/// +/// 名称排序上应先于 Zeta 处理器执行的通知处理器。 +/// +internal sealed class AlphaDeterministicNotificationHandler : INotificationHandler +{ + /// + /// 记录当前处理器已执行。 + /// + /// 通知实例。 + /// 取消令牌。 + /// 已完成任务。 + public ValueTask Handle(DeterministicOrderNotification notification, CancellationToken cancellationToken) + { + DeterministicNotificationHandlerState.InvocationOrder.Add(nameof(AlphaDeterministicNotificationHandler)); + return ValueTask.CompletedTask; + } +} diff --git a/GFramework.Cqrs.Tests/Cqrs/CapturingLoggerFactoryProvider.cs b/GFramework.Cqrs.Tests/Cqrs/CapturingLoggerFactoryProvider.cs new file mode 100644 index 00000000..00f3df65 --- /dev/null +++ b/GFramework.Cqrs.Tests/Cqrs/CapturingLoggerFactoryProvider.cs @@ -0,0 +1,49 @@ +using System.Collections.Generic; +using GFramework.Core.Abstractions.Logging; +using GFramework.Core.Logging; +using GFramework.Cqrs.Tests.Logging; + +namespace GFramework.Cqrs.Tests.Cqrs; + +/// +/// 为 CQRS 注册测试捕获真实启动路径中创建的日志记录器。 +/// +/// +/// 处理器注册入口会分别为测试运行时、容器和注册器创建日志器。 +/// 该提供程序统一保留这些测试日志器,以便断言警告是否经由公开入口真正发出。 +/// +internal sealed class CapturingLoggerFactoryProvider : ILoggerFactoryProvider +{ + private readonly List _loggers = []; + + /// + /// 使用指定的最小日志级别初始化一个新的捕获型日志工厂提供程序。 + /// + /// 要应用到新建测试日志器的最小日志级别。 + public CapturingLoggerFactoryProvider(LogLevel minLevel = LogLevel.Info) + { + MinLevel = minLevel; + } + + /// + /// 获取通过当前提供程序创建的全部测试日志器。 + /// + public IReadOnlyList Loggers => _loggers; + + /// + /// 获取或设置新建测试日志器的最小日志级别。 + /// + public LogLevel MinLevel { get; set; } + + /// + /// 创建一个测试日志器并将其纳入捕获集合。 + /// + /// 日志记录器名称。 + /// 用于后续断言的测试日志器。 + public ILogger CreateLogger(string name) + { + var logger = new TestLogger(name, MinLevel); + _loggers.Add(logger); + return logger; + } +} diff --git a/GFramework.Cqrs.Tests/Cqrs/CqrsHandlerRegistrarTests.cs b/GFramework.Cqrs.Tests/Cqrs/CqrsHandlerRegistrarTests.cs index dcbfbd2a..76a2b922 100644 --- a/GFramework.Cqrs.Tests/Cqrs/CqrsHandlerRegistrarTests.cs +++ b/GFramework.Cqrs.Tests/Cqrs/CqrsHandlerRegistrarTests.cs @@ -614,230 +614,3 @@ internal sealed class CqrsHandlerRegistrarTests .GetType("GFramework.Cqrs.Internal.CqrsHandlerRegistrar", throwOnError: true)!; } } - -/// -/// 记录确定性通知处理器的实际执行顺序。 -/// -internal static class DeterministicNotificationHandlerState -{ - /// - /// 获取当前测试中的通知处理器执行顺序。 - /// - public static List InvocationOrder { get; } = []; - - /// - /// 重置共享的执行顺序状态。 - /// - public static void Reset() - { - InvocationOrder.Clear(); - } -} - -/// -/// 用于验证同一通知的多个处理器是否按稳定顺序执行。 -/// -internal sealed record DeterministicOrderNotification : INotification; - -/// -/// 故意放在 Alpha 之前声明,用于验证注册器不会依赖源码声明顺序。 -/// -internal sealed class ZetaDeterministicNotificationHandler : INotificationHandler -{ - /// - /// 记录当前处理器已执行。 - /// - /// 通知实例。 - /// 取消令牌。 - /// 已完成任务。 - public ValueTask Handle(DeterministicOrderNotification notification, CancellationToken cancellationToken) - { - DeterministicNotificationHandlerState.InvocationOrder.Add(nameof(ZetaDeterministicNotificationHandler)); - return ValueTask.CompletedTask; - } -} - -/// -/// 名称排序上应先于 Zeta 处理器执行的通知处理器。 -/// -internal sealed class AlphaDeterministicNotificationHandler : INotificationHandler -{ - /// - /// 记录当前处理器已执行。 - /// - /// 通知实例。 - /// 取消令牌。 - /// 已完成任务。 - public ValueTask Handle(DeterministicOrderNotification notification, CancellationToken cancellationToken) - { - DeterministicNotificationHandlerState.InvocationOrder.Add(nameof(AlphaDeterministicNotificationHandler)); - return ValueTask.CompletedTask; - } -} - -/// -/// 为 CQRS 注册测试捕获真实启动路径中创建的日志记录器。 -/// -/// -/// 处理器注册入口会分别为测试运行时、容器和注册器创建日志器。 -/// 该提供程序统一保留这些测试日志器,以便断言警告是否经由公开入口真正发出。 -/// -internal sealed class CapturingLoggerFactoryProvider : ILoggerFactoryProvider -{ - private readonly List _loggers = []; - - /// - /// 使用指定的最小日志级别初始化一个新的捕获型日志工厂提供程序。 - /// - /// 要应用到新建测试日志器的最小日志级别。 - public CapturingLoggerFactoryProvider(LogLevel minLevel = LogLevel.Info) - { - MinLevel = minLevel; - } - - /// - /// 获取通过当前提供程序创建的全部测试日志器。 - /// - public IReadOnlyList Loggers => _loggers; - - /// - /// 获取或设置新建测试日志器的最小日志级别。 - /// - public LogLevel MinLevel { get; set; } - - /// - /// 创建一个测试日志器并将其纳入捕获集合。 - /// - /// 日志记录器名称。 - /// 用于后续断言的测试日志器。 - public ILogger CreateLogger(string name) - { - var logger = new TestLogger(name, MinLevel); - _loggers.Add(logger); - return logger; - } -} - -/// -/// 用于验证生成注册器路径的通知消息。 -/// -internal sealed record GeneratedRegistryNotification : INotification; - -/// -/// 由模拟的源码生成注册器显式注册的通知处理器。 -/// -internal sealed class GeneratedRegistryNotificationHandler : INotificationHandler -{ - /// - /// 处理生成注册器测试中的通知。 - /// - /// 通知实例。 - /// 取消令牌。 - /// 已完成任务。 - public ValueTask Handle(GeneratedRegistryNotification notification, CancellationToken cancellationToken) - { - return ValueTask.CompletedTask; - } -} - -/// -/// 模拟源码生成器为某个程序集生成的 CQRS 处理器注册器。 -/// -internal sealed class GeneratedNotificationHandlerRegistry : ICqrsHandlerRegistry -{ - /// - /// 将测试通知处理器注册到目标服务集合。 - /// - /// 承载处理器映射的服务集合。 - /// 用于记录注册诊断的日志器。 - public void Register(IServiceCollection services, ILogger logger) - { - ArgumentNullException.ThrowIfNull(services); - ArgumentNullException.ThrowIfNull(logger); - - services.AddTransient( - typeof(INotificationHandler), - typeof(GeneratedRegistryNotificationHandler)); - logger.Debug( - $"Registered CQRS handler {typeof(GeneratedRegistryNotificationHandler).FullName} as {typeof(INotificationHandler).FullName}."); - } -} - -/// -/// 用于验证“生成注册器 + reflection fallback”组合路径的私有嵌套处理器容器。 -/// -internal sealed class ReflectionFallbackNotificationContainer -{ - /// - /// 获取仅能通过反射补扫接入的私有嵌套处理器类型。 - /// - public static Type ReflectionOnlyHandlerType => typeof(ReflectionOnlyGeneratedRegistryNotificationHandler); - - private sealed class ReflectionOnlyGeneratedRegistryNotificationHandler - : INotificationHandler - { - /// - /// 处理测试通知。 - /// - /// 通知实例。 - /// 取消令牌。 - /// 已完成任务。 - public ValueTask Handle(GeneratedRegistryNotification notification, CancellationToken cancellationToken) - { - return ValueTask.CompletedTask; - } - } -} - -/// -/// 模拟局部生成注册器场景中,仅注册“可由生成代码直接引用”的那部分 handlers。 -/// -internal sealed class PartialGeneratedNotificationHandlerRegistry : ICqrsHandlerRegistry -{ - /// - /// 将生成路径可见的通知处理器注册到目标服务集合。 - /// - /// 承载处理器映射的服务集合。 - /// 用于记录注册诊断的日志器。 - public void Register(IServiceCollection services, ILogger logger) - { - ArgumentNullException.ThrowIfNull(services); - ArgumentNullException.ThrowIfNull(logger); - - services.AddTransient( - typeof(INotificationHandler), - typeof(GeneratedRegistryNotificationHandler)); - logger.Debug( - $"Registered CQRS handler {typeof(GeneratedRegistryNotificationHandler).FullName} as {typeof(INotificationHandler).FullName}."); - } -} - -/// -/// 模拟生成注册器使用私有无参构造器的场景,验证运行时仍可通过缓存工厂激活它。 -/// -internal sealed class PrivateConstructorNotificationHandlerRegistry : ICqrsHandlerRegistry -{ - /// - /// 初始化一个新的私有生成注册器实例。 - /// - private PrivateConstructorNotificationHandlerRegistry() - { - } - - /// - /// 将测试通知处理器注册到目标服务集合。 - /// - /// 承载处理器映射的服务集合。 - /// 用于记录注册诊断的日志器。 - public void Register(IServiceCollection services, ILogger logger) - { - ArgumentNullException.ThrowIfNull(services); - ArgumentNullException.ThrowIfNull(logger); - - services.AddTransient( - typeof(INotificationHandler), - typeof(GeneratedRegistryNotificationHandler)); - logger.Debug( - $"Registered CQRS handler {typeof(GeneratedRegistryNotificationHandler).FullName} as {typeof(INotificationHandler).FullName}."); - } -} diff --git a/GFramework.Cqrs.Tests/Cqrs/DeterministicNotificationHandlerState.cs b/GFramework.Cqrs.Tests/Cqrs/DeterministicNotificationHandlerState.cs new file mode 100644 index 00000000..957eda1d --- /dev/null +++ b/GFramework.Cqrs.Tests/Cqrs/DeterministicNotificationHandlerState.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; + +namespace GFramework.Cqrs.Tests.Cqrs; + +/// +/// 记录确定性通知处理器的实际执行顺序。 +/// +internal static class DeterministicNotificationHandlerState +{ + /// + /// 获取当前测试中的通知处理器执行顺序。 + /// + public static List InvocationOrder { get; } = []; + + /// + /// 重置共享的执行顺序状态。 + /// + public static void Reset() + { + InvocationOrder.Clear(); + } +} diff --git a/GFramework.Cqrs.Tests/Cqrs/DeterministicOrderNotification.cs b/GFramework.Cqrs.Tests/Cqrs/DeterministicOrderNotification.cs new file mode 100644 index 00000000..9bc0e059 --- /dev/null +++ b/GFramework.Cqrs.Tests/Cqrs/DeterministicOrderNotification.cs @@ -0,0 +1,8 @@ +using GFramework.Cqrs.Abstractions.Cqrs; + +namespace GFramework.Cqrs.Tests.Cqrs; + +/// +/// 用于验证同一通知的多个处理器是否按稳定顺序执行。 +/// +internal sealed record DeterministicOrderNotification : INotification; diff --git a/GFramework.Cqrs.Tests/Cqrs/GeneratedNotificationHandlerRegistry.cs b/GFramework.Cqrs.Tests/Cqrs/GeneratedNotificationHandlerRegistry.cs new file mode 100644 index 00000000..ab2c344f --- /dev/null +++ b/GFramework.Cqrs.Tests/Cqrs/GeneratedNotificationHandlerRegistry.cs @@ -0,0 +1,30 @@ +using System; +using GFramework.Core.Abstractions.Logging; +using GFramework.Core.Ioc; +using GFramework.Core.Logging; +using GFramework.Cqrs.Abstractions.Cqrs; + +namespace GFramework.Cqrs.Tests.Cqrs; + +/// +/// 模拟源码生成器为某个程序集生成的 CQRS 处理器注册器。 +/// +internal sealed class GeneratedNotificationHandlerRegistry : ICqrsHandlerRegistry +{ + /// + /// 将测试通知处理器注册到目标服务集合。 + /// + /// 承载处理器映射的服务集合。 + /// 用于记录注册诊断的日志器。 + public void Register(IServiceCollection services, ILogger logger) + { + ArgumentNullException.ThrowIfNull(services); + ArgumentNullException.ThrowIfNull(logger); + + services.AddTransient( + typeof(INotificationHandler), + typeof(GeneratedRegistryNotificationHandler)); + logger.Debug( + $"Registered CQRS handler {typeof(GeneratedRegistryNotificationHandler).FullName} as {typeof(INotificationHandler).FullName}."); + } +} diff --git a/GFramework.Cqrs.Tests/Cqrs/GeneratedRegistryNotification.cs b/GFramework.Cqrs.Tests/Cqrs/GeneratedRegistryNotification.cs new file mode 100644 index 00000000..98f79991 --- /dev/null +++ b/GFramework.Cqrs.Tests/Cqrs/GeneratedRegistryNotification.cs @@ -0,0 +1,8 @@ +using GFramework.Cqrs.Abstractions.Cqrs; + +namespace GFramework.Cqrs.Tests.Cqrs; + +/// +/// 用于验证生成注册器路径的通知消息。 +/// +internal sealed record GeneratedRegistryNotification : INotification; diff --git a/GFramework.Cqrs.Tests/Cqrs/GeneratedRegistryNotificationHandler.cs b/GFramework.Cqrs.Tests/Cqrs/GeneratedRegistryNotificationHandler.cs new file mode 100644 index 00000000..082b16f0 --- /dev/null +++ b/GFramework.Cqrs.Tests/Cqrs/GeneratedRegistryNotificationHandler.cs @@ -0,0 +1,22 @@ +using System.Threading; +using System.Threading.Tasks; +using GFramework.Cqrs.Abstractions.Cqrs; + +namespace GFramework.Cqrs.Tests.Cqrs; + +/// +/// 由模拟的源码生成注册器显式注册的通知处理器。 +/// +internal sealed class GeneratedRegistryNotificationHandler : INotificationHandler +{ + /// + /// 处理生成注册器测试中的通知。 + /// + /// 通知实例。 + /// 取消令牌。 + /// 已完成任务。 + public ValueTask Handle(GeneratedRegistryNotification notification, CancellationToken cancellationToken) + { + return ValueTask.CompletedTask; + } +} diff --git a/GFramework.Cqrs.Tests/Cqrs/PartialGeneratedNotificationHandlerRegistry.cs b/GFramework.Cqrs.Tests/Cqrs/PartialGeneratedNotificationHandlerRegistry.cs new file mode 100644 index 00000000..6bbcfb36 --- /dev/null +++ b/GFramework.Cqrs.Tests/Cqrs/PartialGeneratedNotificationHandlerRegistry.cs @@ -0,0 +1,30 @@ +using System; +using GFramework.Core.Abstractions.Logging; +using GFramework.Core.Ioc; +using GFramework.Core.Logging; +using GFramework.Cqrs.Abstractions.Cqrs; + +namespace GFramework.Cqrs.Tests.Cqrs; + +/// +/// 模拟局部生成注册器场景中,仅注册“可由生成代码直接引用”的那部分 handlers。 +/// +internal sealed class PartialGeneratedNotificationHandlerRegistry : ICqrsHandlerRegistry +{ + /// + /// 将生成路径可见的通知处理器注册到目标服务集合。 + /// + /// 承载处理器映射的服务集合。 + /// 用于记录注册诊断的日志器。 + public void Register(IServiceCollection services, ILogger logger) + { + ArgumentNullException.ThrowIfNull(services); + ArgumentNullException.ThrowIfNull(logger); + + services.AddTransient( + typeof(INotificationHandler), + typeof(GeneratedRegistryNotificationHandler)); + logger.Debug( + $"Registered CQRS handler {typeof(GeneratedRegistryNotificationHandler).FullName} as {typeof(INotificationHandler).FullName}."); + } +} diff --git a/GFramework.Cqrs.Tests/Cqrs/PrivateConstructorNotificationHandlerRegistry.cs b/GFramework.Cqrs.Tests/Cqrs/PrivateConstructorNotificationHandlerRegistry.cs new file mode 100644 index 00000000..b9bb1b06 --- /dev/null +++ b/GFramework.Cqrs.Tests/Cqrs/PrivateConstructorNotificationHandlerRegistry.cs @@ -0,0 +1,37 @@ +using System; +using GFramework.Core.Abstractions.Logging; +using GFramework.Core.Ioc; +using GFramework.Core.Logging; +using GFramework.Cqrs.Abstractions.Cqrs; + +namespace GFramework.Cqrs.Tests.Cqrs; + +/// +/// 模拟生成注册器使用私有无参构造器的场景,验证运行时仍可通过缓存工厂激活它。 +/// +internal sealed class PrivateConstructorNotificationHandlerRegistry : ICqrsHandlerRegistry +{ + /// + /// 初始化一个新的私有生成注册器实例。 + /// + private PrivateConstructorNotificationHandlerRegistry() + { + } + + /// + /// 将测试通知处理器注册到目标服务集合。 + /// + /// 承载处理器映射的服务集合。 + /// 用于记录注册诊断的日志器。 + public void Register(IServiceCollection services, ILogger logger) + { + ArgumentNullException.ThrowIfNull(services); + ArgumentNullException.ThrowIfNull(logger); + + services.AddTransient( + typeof(INotificationHandler), + typeof(GeneratedRegistryNotificationHandler)); + logger.Debug( + $"Registered CQRS handler {typeof(GeneratedRegistryNotificationHandler).FullName} as {typeof(INotificationHandler).FullName}."); + } +} diff --git a/GFramework.Cqrs.Tests/Cqrs/ReflectionFallbackNotificationContainer.cs b/GFramework.Cqrs.Tests/Cqrs/ReflectionFallbackNotificationContainer.cs new file mode 100644 index 00000000..b0475e01 --- /dev/null +++ b/GFramework.Cqrs.Tests/Cqrs/ReflectionFallbackNotificationContainer.cs @@ -0,0 +1,32 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using GFramework.Cqrs.Abstractions.Cqrs; + +namespace GFramework.Cqrs.Tests.Cqrs; + +/// +/// 用于验证“生成注册器 + reflection fallback”组合路径的私有嵌套处理器容器。 +/// +internal sealed class ReflectionFallbackNotificationContainer +{ + /// + /// 获取仅能通过反射补扫接入的私有嵌套处理器类型。 + /// + public static Type ReflectionOnlyHandlerType => typeof(ReflectionOnlyGeneratedRegistryNotificationHandler); + + private sealed class ReflectionOnlyGeneratedRegistryNotificationHandler + : INotificationHandler + { + /// + /// 处理测试通知。 + /// + /// 通知实例。 + /// 取消令牌。 + /// 已完成任务。 + public ValueTask Handle(GeneratedRegistryNotification notification, CancellationToken cancellationToken) + { + return ValueTask.CompletedTask; + } + } +} diff --git a/GFramework.Cqrs.Tests/Cqrs/ZetaDeterministicNotificationHandler.cs b/GFramework.Cqrs.Tests/Cqrs/ZetaDeterministicNotificationHandler.cs new file mode 100644 index 00000000..10ff18e0 --- /dev/null +++ b/GFramework.Cqrs.Tests/Cqrs/ZetaDeterministicNotificationHandler.cs @@ -0,0 +1,23 @@ +using System.Threading; +using System.Threading.Tasks; +using GFramework.Cqrs.Abstractions.Cqrs; + +namespace GFramework.Cqrs.Tests.Cqrs; + +/// +/// 故意放在 Alpha 之前声明,用于验证注册器不会依赖源码声明顺序。 +/// +internal sealed class ZetaDeterministicNotificationHandler : INotificationHandler +{ + /// + /// 记录当前处理器已执行。 + /// + /// 通知实例。 + /// 取消令牌。 + /// 已完成任务。 + public ValueTask Handle(DeterministicOrderNotification notification, CancellationToken cancellationToken) + { + DeterministicNotificationHandlerState.InvocationOrder.Add(nameof(ZetaDeterministicNotificationHandler)); + return ValueTask.CompletedTask; + } +} From ba4ace8d40ca4d7326fb1f0937a440adeb372fe7 Mon Sep 17 00:00:00 2001 From: gewuyou <95328647+GeWuYou@users.noreply.github.com> Date: Tue, 28 Apr 2026 08:32:00 +0800 Subject: [PATCH 05/10] =?UTF-8?q?test(analyzer):=20=E6=94=B6=E6=95=9B=20Co?= =?UTF-8?q?re.Tests=20=E4=B8=8E=20Cqrs.Tests=20=E8=AD=A6=E5=91=8A=E6=89=B9?= =?UTF-8?q?=E6=AC=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 拆分 GameContextTests、ArchitectureServicesTests、RegistryInitializationHookBaseTests 与 Cqrs 测试辅助类型,消除批次内 MA0048 热点 - 修复 Core.Tests 零散可空性、集合抽象和测试辅助 warning,使受影响 Release 构建清零 - 更新 analyzer-warning-reduction 跟踪与 trace,记录 236 条仓库根 warning 基线和 45/50 停止点 --- .../AnotherTestArchitectureContext.cs | 8 + .../ArchitectureServicesTests.cs | 212 --------- .../Architectures/ContextProviderTests.cs | 7 - .../Architectures/TestArchitectureBase.cs | 5 +- .../TestArchitectureContextV3.cs | 412 ++++++++++++++++++ .../CommandCoroutineExtensionsTests.cs | 4 +- .../Coroutine/WaitForMultipleEventsTests.cs | 4 +- GFramework.Core.Tests/Events/EmptyEvent.cs | 6 + .../Events/EventListenerScopeTests.cs | 4 +- GFramework.Core.Tests/Events/TestEvent.cs | 8 +- GFramework.Core.Tests/Logging/LoggerTests.cs | 41 -- .../Logging/LoggingConfigurationTests.cs | 5 +- GFramework.Core.Tests/Logging/TestLogger.cs | 45 ++ .../Resource/ResourceManagerTests.cs | 50 --- .../Resource/TestResource.cs | 17 + .../Resource/TestResourceLoader.cs | 52 +++ .../analyzer-warning-reduction-tracking.md | 62 ++- .../analyzer-warning-reduction-trace.md | 50 +++ 18 files changed, 634 insertions(+), 358 deletions(-) create mode 100644 GFramework.Core.Tests/Architectures/AnotherTestArchitectureContext.cs create mode 100644 GFramework.Core.Tests/Architectures/TestArchitectureContextV3.cs create mode 100644 GFramework.Core.Tests/Events/EmptyEvent.cs create mode 100644 GFramework.Core.Tests/Logging/TestLogger.cs create mode 100644 GFramework.Core.Tests/Resource/TestResource.cs create mode 100644 GFramework.Core.Tests/Resource/TestResourceLoader.cs diff --git a/GFramework.Core.Tests/Architectures/AnotherTestArchitectureContext.cs b/GFramework.Core.Tests/Architectures/AnotherTestArchitectureContext.cs new file mode 100644 index 00000000..850a3723 --- /dev/null +++ b/GFramework.Core.Tests/Architectures/AnotherTestArchitectureContext.cs @@ -0,0 +1,8 @@ +namespace GFramework.Core.Tests.Architectures; + +/// +/// 表示用于验证上下文类型不匹配分支的测试架构上下文。 +/// +public class AnotherTestArchitectureContext : TestArchitectureContext +{ +} diff --git a/GFramework.Core.Tests/Architectures/ArchitectureServicesTests.cs b/GFramework.Core.Tests/Architectures/ArchitectureServicesTests.cs index f1474403..2c060b5b 100644 --- a/GFramework.Core.Tests/Architectures/ArchitectureServicesTests.cs +++ b/GFramework.Core.Tests/Architectures/ArchitectureServicesTests.cs @@ -262,215 +262,3 @@ public class ArchitectureServicesTests Assert.That(_services!.ModuleManager, Is.Not.Null); } } - -#region Test Classes - -public class TestArchitectureContextV3 : IArchitectureContext -{ - private readonly MicrosoftDiContainer _container = new(); - private readonly DefaultEnvironment _environment = new(); - public int Id { get; init; } - - public TService? GetService() where TService : class - { - return _container.Get(); - } - - public IReadOnlyList GetServices() where TService : class - { - return _container.GetAll(); - } - - public TModel? GetModel() where TModel : class, IModel - { - return _container.Get(); - } - - public IReadOnlyList GetModels() where TModel : class, IModel - { - return _container.GetAll(); - } - - public TSystem? GetSystem() where TSystem : class, ISystem - { - return _container.Get(); - } - - public IReadOnlyList GetSystems() where TSystem : class, ISystem - { - return _container.GetAll(); - } - - public TUtility? GetUtility() where TUtility : class, IUtility - { - return _container.Get(); - } - - public IReadOnlyList GetUtilities() where TUtility : class, IUtility - { - return _container.GetAll(); - } - - public IReadOnlyList GetServicesByPriority() where TService : class - { - return _container.GetAllByPriority(); - } - - public IReadOnlyList GetSystemsByPriority() where TSystem : class, ISystem - { - return _container.GetAllByPriority(); - } - - public IReadOnlyList GetModelsByPriority() where TModel : class, IModel - { - return _container.GetAllByPriority(); - } - - public IReadOnlyList GetUtilitiesByPriority() where TUtility : class, IUtility - { - return _container.GetAllByPriority(); - } - - public void SendEvent() where TEvent : new() - { - } - - public void SendEvent(TEvent e) where TEvent : class - { - } - - public IUnRegister RegisterEvent(Action handler) - { - return new DefaultUnRegister(() => { }); - } - - public void UnRegisterEvent(Action onEvent) - { - } - - public ValueTask SendRequestAsync(IRequest request, - CancellationToken cancellationToken = default) - { - throw new NotSupportedException(); - } - - public TResponse SendRequest(IRequest request) - { - throw new NotSupportedException(); - } - - /// - /// 测试桩:异步发送 CQRS 命令并返回响应。 - /// - /// 命令响应类型。 - /// 要发送的命令。 - /// 取消令牌。 - /// 命令响应任务。 - /// 该测试桩不支持此成员。 - public ValueTask SendCommandAsync( - GFramework.Cqrs.Abstractions.Cqrs.Command.ICommand command, - CancellationToken cancellationToken = default) - { - throw new NotSupportedException(); - } - - /// - /// 测试桩:同步发送 CQRS 命令并返回响应。 - /// - /// 命令响应类型。 - /// 要发送的命令。 - /// 命令响应。 - /// 该测试桩不支持此成员。 - public TResponse SendCommand(GFramework.Cqrs.Abstractions.Cqrs.Command.ICommand command) - { - throw new NotSupportedException(); - } - - /// - /// 测试桩:异步发送 CQRS 查询并返回结果。 - /// - /// 查询结果类型。 - /// 要发送的查询。 - /// 取消令牌。 - /// 查询结果任务。 - /// 该测试桩不支持此成员。 - public ValueTask SendQueryAsync( - GFramework.Cqrs.Abstractions.Cqrs.Query.IQuery query, - CancellationToken cancellationToken = default) - { - throw new NotSupportedException(); - } - - /// - /// 测试桩:同步发送 CQRS 查询并返回结果。 - /// - /// 查询结果类型。 - /// 要发送的查询。 - /// 查询结果。 - /// 该测试桩不支持此成员。 - public TResponse SendQuery(GFramework.Cqrs.Abstractions.Cqrs.Query.IQuery query) - { - throw new NotSupportedException(); - } - - public ValueTask PublishAsync(TNotification notification, - CancellationToken cancellationToken = default) where TNotification : INotification - { - throw new NotSupportedException(); - } - - public IAsyncEnumerable CreateStream( - IStreamRequest request, - CancellationToken cancellationToken = default) - { - throw new NotSupportedException(); - } - - public ValueTask SendAsync(TCommand command, CancellationToken cancellationToken = default) - where TCommand : IRequest - { - throw new NotSupportedException(); - } - - public ValueTask SendAsync(IRequest command, - CancellationToken cancellationToken = default) - { - throw new NotSupportedException(); - } - - public void SendCommand(ICommand command) - { - } - - public TResult SendCommand(Abstractions.Command.ICommand command) - { - return default!; - } - - public Task SendCommandAsync(IAsyncCommand command) - { - return Task.CompletedTask; - } - - public Task SendCommandAsync(IAsyncCommand command) - { - return (Task)Task.CompletedTask; - } - - public TResult SendQuery(Abstractions.Query.IQuery query) - { - return default!; - } - - public Task SendQueryAsync(IAsyncQuery query) - { - return (Task)Task.CompletedTask; - } - - public IEnvironment GetEnvironment() - { - return _environment; - } -} - -#endregion diff --git a/GFramework.Core.Tests/Architectures/ContextProviderTests.cs b/GFramework.Core.Tests/Architectures/ContextProviderTests.cs index 5fc205ce..ee196cdf 100644 --- a/GFramework.Core.Tests/Architectures/ContextProviderTests.cs +++ b/GFramework.Core.Tests/Architectures/ContextProviderTests.cs @@ -147,10 +147,3 @@ public class ContextProviderTests Assert.That(foundContext, Is.SameAs(context)); } } - -/// -/// 另一个测试用的架构上下文类,用于测试类型不匹配的情况 -/// -public class AnotherTestArchitectureContext : TestArchitectureContext -{ -} \ No newline at end of file diff --git a/GFramework.Core.Tests/Architectures/TestArchitectureBase.cs b/GFramework.Core.Tests/Architectures/TestArchitectureBase.cs index 57cf9a71..fdd144f7 100644 --- a/GFramework.Core.Tests/Architectures/TestArchitectureBase.cs +++ b/GFramework.Core.Tests/Architectures/TestArchitectureBase.cs @@ -9,6 +9,7 @@ namespace GFramework.Core.Tests.Architectures; public abstract class TestArchitectureBase : Architecture { private Action? _postRegistrationHook; + private readonly List _phaseHistory = []; /// /// 获取就绪事件是否已触发的状态 @@ -23,7 +24,7 @@ public abstract class TestArchitectureBase : Architecture /// /// 获取架构阶段历史记录列表 /// - public List PhaseHistory { get; } = []; + public IReadOnlyList PhaseHistory => _phaseHistory; /// /// 添加注册后钩子函数 @@ -43,6 +44,6 @@ public abstract class TestArchitectureBase : Architecture _postRegistrationHook?.Invoke(this); // 订阅阶段变更事件以记录历史 - PhaseChanged += (_, eventArgs) => PhaseHistory.Add(eventArgs.Phase); + PhaseChanged += (_, eventArgs) => _phaseHistory.Add(eventArgs.Phase); } } diff --git a/GFramework.Core.Tests/Architectures/TestArchitectureContextV3.cs b/GFramework.Core.Tests/Architectures/TestArchitectureContextV3.cs new file mode 100644 index 00000000..6fda6f2d --- /dev/null +++ b/GFramework.Core.Tests/Architectures/TestArchitectureContextV3.cs @@ -0,0 +1,412 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using GFramework.Core.Abstractions.Architectures; +using GFramework.Core.Abstractions.Command; +using GFramework.Core.Abstractions.Environment; +using GFramework.Core.Abstractions.Events; +using GFramework.Core.Abstractions.Model; +using GFramework.Core.Abstractions.Query; +using GFramework.Core.Abstractions.Systems; +using GFramework.Core.Abstractions.Utility; +using GFramework.Core.Environment; +using GFramework.Core.Events; +using GFramework.Core.Ioc; +using GFramework.Cqrs; +using GFramework.Cqrs.Abstractions.Cqrs; +using ICommand = GFramework.Core.Abstractions.Command.ICommand; + +namespace GFramework.Core.Tests.Architectures; + +/// +/// 为 提供最小实现的架构上下文测试桩。 +/// +/// +/// 该类型仅用于验证 的上下文传递行为,因此仅保留当前测试切片需要的容器、 +/// 环境与接口实现。所有不在本测试范围内的 CQRS 调用均明确抛出 , +/// 以避免误把测试桩当作真实运行时上下文使用。 +/// +public class TestArchitectureContextV3 : IArchitectureContext +{ + private readonly MicrosoftDiContainer _container = new(); + private readonly DefaultEnvironment _environment = new(); + + /// + /// 获取或初始化用于区分测试上下文实例的标识。 + /// + public int Id { get; init; } + + /// + /// 获取指定类型的服务实例。 + /// + /// 服务类型。 + /// 已注册的服务实例。 + /// 未注册或存在多个同类型实例时抛出。 + public TService GetService() where TService : class + { + return _container.GetRequired(); + } + + /// + /// 获取指定类型的所有服务实例。 + /// + /// 服务类型。 + /// 所有已注册的服务实例。 + public IReadOnlyList GetServices() where TService : class + { + return _container.GetAll(); + } + + /// + /// 获取指定类型的模型实例。 + /// + /// 模型类型。 + /// 已注册的模型实例。 + /// 未注册或存在多个同类型实例时抛出。 + public TModel GetModel() where TModel : class, IModel + { + return _container.GetRequired(); + } + + /// + /// 获取指定类型的所有模型实例。 + /// + /// 模型类型。 + /// 所有已注册的模型实例。 + public IReadOnlyList GetModels() where TModel : class, IModel + { + return _container.GetAll(); + } + + /// + /// 获取指定类型的系统实例。 + /// + /// 系统类型。 + /// 已注册的系统实例。 + /// 未注册或存在多个同类型实例时抛出。 + public TSystem GetSystem() where TSystem : class, ISystem + { + return _container.GetRequired(); + } + + /// + /// 获取指定类型的所有系统实例。 + /// + /// 系统类型。 + /// 所有已注册的系统实例。 + public IReadOnlyList GetSystems() where TSystem : class, ISystem + { + return _container.GetAll(); + } + + /// + /// 获取指定类型的工具实例。 + /// + /// 工具类型。 + /// 已注册的工具实例。 + /// 未注册或存在多个同类型实例时抛出。 + public TUtility GetUtility() where TUtility : class, IUtility + { + return _container.GetRequired(); + } + + /// + /// 获取指定类型的所有工具实例。 + /// + /// 工具类型。 + /// 所有已注册的工具实例。 + public IReadOnlyList GetUtilities() where TUtility : class, IUtility + { + return _container.GetAll(); + } + + /// + /// 获取指定类型的所有服务实例,并按优先级排序。 + /// + /// 服务类型。 + /// 按优先级排序后的服务实例。 + public IReadOnlyList GetServicesByPriority() where TService : class + { + return _container.GetAllByPriority(); + } + + /// + /// 获取指定类型的所有系统实例,并按优先级排序。 + /// + /// 系统类型。 + /// 按优先级排序后的系统实例。 + public IReadOnlyList GetSystemsByPriority() where TSystem : class, ISystem + { + return _container.GetAllByPriority(); + } + + /// + /// 获取指定类型的所有模型实例,并按优先级排序。 + /// + /// 模型类型。 + /// 按优先级排序后的模型实例。 + public IReadOnlyList GetModelsByPriority() where TModel : class, IModel + { + return _container.GetAllByPriority(); + } + + /// + /// 获取指定类型的所有工具实例,并按优先级排序。 + /// + /// 工具类型。 + /// 按优先级排序后的工具实例。 + public IReadOnlyList GetUtilitiesByPriority() where TUtility : class, IUtility + { + return _container.GetAllByPriority(); + } + + /// + /// 发送无参数事件。 + /// + /// 事件类型。 + public void SendEvent() where TEvent : new() + { + } + + /// + /// 发送带参数事件。 + /// + /// 事件类型。 + /// 事件实例。 + public void SendEvent(TEvent e) where TEvent : class + { + } + + /// + /// 注册事件处理器。 + /// + /// 事件类型。 + /// 事件处理回调。 + /// 用于注销回调的句柄。 + public IUnRegister RegisterEvent(Action handler) + { + return new DefaultUnRegister(() => { }); + } + + /// + /// 取消事件处理器注册。 + /// + /// 事件类型。 + /// 要取消的事件回调。 + public void UnRegisterEvent(Action onEvent) + { + } + + /// + /// 异步发送新版 CQRS 请求。 + /// + /// 响应类型。 + /// 要发送的请求。 + /// 取消令牌。 + /// 请求响应任务。 + /// 该测试桩不支持此成员。 + public ValueTask SendRequestAsync( + IRequest request, + CancellationToken cancellationToken = default) + { + throw new NotSupportedException(); + } + + /// + /// 同步发送新版 CQRS 请求。 + /// + /// 响应类型。 + /// 要发送的请求。 + /// 请求响应。 + /// 该测试桩不支持此成员。 + public TResponse SendRequest(IRequest request) + { + throw new NotSupportedException(); + } + + /// + /// 异步发送新版 CQRS 命令并返回响应。 + /// + /// 命令响应类型。 + /// 要发送的命令。 + /// 取消令牌。 + /// 命令响应任务。 + /// 该测试桩不支持此成员。 + public ValueTask SendCommandAsync( + GFramework.Cqrs.Abstractions.Cqrs.Command.ICommand command, + CancellationToken cancellationToken = default) + { + throw new NotSupportedException(); + } + + /// + /// 同步发送新版 CQRS 命令并返回响应。 + /// + /// 命令响应类型。 + /// 要发送的命令。 + /// 命令响应。 + /// 该测试桩不支持此成员。 + public TResponse SendCommand(GFramework.Cqrs.Abstractions.Cqrs.Command.ICommand command) + { + throw new NotSupportedException(); + } + + /// + /// 异步发送新版 CQRS 查询并返回结果。 + /// + /// 查询结果类型。 + /// 要发送的查询。 + /// 取消令牌。 + /// 查询结果任务。 + /// 该测试桩不支持此成员。 + public ValueTask SendQueryAsync( + GFramework.Cqrs.Abstractions.Cqrs.Query.IQuery query, + CancellationToken cancellationToken = default) + { + throw new NotSupportedException(); + } + + /// + /// 同步发送新版 CQRS 查询并返回结果。 + /// + /// 查询结果类型。 + /// 要发送的查询。 + /// 查询结果。 + /// 该测试桩不支持此成员。 + public TResponse SendQuery(GFramework.Cqrs.Abstractions.Cqrs.Query.IQuery query) + { + throw new NotSupportedException(); + } + + /// + /// 发布通知消息。 + /// + /// 通知类型。 + /// 通知实例。 + /// 取消令牌。 + /// 通知发布任务。 + /// 该测试桩不支持此成员。 + public ValueTask PublishAsync( + TNotification notification, + CancellationToken cancellationToken = default) + where TNotification : INotification + { + throw new NotSupportedException(); + } + + /// + /// 创建流式请求结果。 + /// + /// 流元素类型。 + /// 流式请求。 + /// 取消令牌。 + /// 流式响应序列。 + /// 该测试桩不支持此成员。 + public IAsyncEnumerable CreateStream( + IStreamRequest request, + CancellationToken cancellationToken = default) + { + throw new NotSupportedException(); + } + + /// + /// 异步发送无返回值请求命令。 + /// + /// 命令类型。 + /// 要发送的命令。 + /// 取消令牌。 + /// 命令发送任务。 + /// 该测试桩不支持此成员。 + public ValueTask SendAsync(TCommand command, CancellationToken cancellationToken = default) + where TCommand : IRequest + { + throw new NotSupportedException(); + } + + /// + /// 异步发送带返回值请求。 + /// + /// 响应类型。 + /// 要发送的请求。 + /// 取消令牌。 + /// 请求响应任务。 + /// 该测试桩不支持此成员。 + public ValueTask SendAsync( + IRequest command, + CancellationToken cancellationToken = default) + { + throw new NotSupportedException(); + } + + /// + /// 发送旧版无返回值命令。 + /// + /// 要发送的命令。 + public void SendCommand(ICommand command) + { + } + + /// + /// 发送旧版带返回值命令。 + /// + /// 命令响应类型。 + /// 要发送的命令。 + /// 默认响应值。 + public TResult SendCommand(GFramework.Core.Abstractions.Command.ICommand command) + { + return default!; + } + + /// + /// 异步发送旧版无返回值命令。 + /// + /// 要发送的命令。 + /// 已完成任务。 + public Task SendCommandAsync(IAsyncCommand command) + { + return Task.CompletedTask; + } + + /// + /// 异步发送旧版带返回值命令。 + /// + /// 命令响应类型。 + /// 要发送的命令。 + /// 占位任务。 + public Task SendCommandAsync(IAsyncCommand command) + { + return (Task)Task.CompletedTask; + } + + /// + /// 发送旧版查询请求。 + /// + /// 查询结果类型。 + /// 要发送的查询。 + /// 默认查询结果。 + public TResult SendQuery(GFramework.Core.Abstractions.Query.IQuery query) + { + return default!; + } + + /// + /// 异步发送旧版查询请求。 + /// + /// 查询结果类型。 + /// 要发送的查询。 + /// 占位任务。 + public Task SendQueryAsync(IAsyncQuery query) + { + return (Task)Task.CompletedTask; + } + + /// + /// 获取当前测试上下文绑定的环境实例。 + /// + /// 默认测试环境实例。 + public IEnvironment GetEnvironment() + { + return _environment; + } +} diff --git a/GFramework.Core.Tests/Coroutine/CommandCoroutineExtensionsTests.cs b/GFramework.Core.Tests/Coroutine/CommandCoroutineExtensionsTests.cs index 494688bd..84378a6b 100644 --- a/GFramework.Core.Tests/Coroutine/CommandCoroutineExtensionsTests.cs +++ b/GFramework.Core.Tests/Coroutine/CommandCoroutineExtensionsTests.cs @@ -405,7 +405,7 @@ public class CommandCoroutineExtensionsTests CommandCoroutineExtensions.SendCommandAndWaitEventCoroutine( contextAware, command, - null, + _ => { }, -1.0f)); } @@ -421,7 +421,7 @@ public class CommandCoroutineExtensionsTests // 设置上下文服务以返回null事件总线 contextAware._mockContext .Setup(ctx => ctx.GetService()) - .Returns((IEventBus?)null); + .Returns((IEventBus)null!); // 创建协程 var coroutine = CommandCoroutineExtensions.SendCommandAndWaitEventCoroutine( diff --git a/GFramework.Core.Tests/Coroutine/WaitForMultipleEventsTests.cs b/GFramework.Core.Tests/Coroutine/WaitForMultipleEventsTests.cs index 1d0588d0..88baf123 100644 --- a/GFramework.Core.Tests/Coroutine/WaitForMultipleEventsTests.cs +++ b/GFramework.Core.Tests/Coroutine/WaitForMultipleEventsTests.cs @@ -20,7 +20,7 @@ namespace GFramework.Core.Tests.Coroutine (eventBus as IDisposable)?.Dispose(); } - private IEventBus eventBus; + private IEventBus eventBus = null!; [Test] public void Constructor_RegistersBothEventTypes() @@ -138,4 +138,4 @@ namespace GFramework.Core.Tests.Coroutine public string Data { get; set; } = string.Empty; } } -} \ No newline at end of file +} diff --git a/GFramework.Core.Tests/Events/EmptyEvent.cs b/GFramework.Core.Tests/Events/EmptyEvent.cs new file mode 100644 index 00000000..60d479e2 --- /dev/null +++ b/GFramework.Core.Tests/Events/EmptyEvent.cs @@ -0,0 +1,6 @@ +namespace GFramework.Core.Tests.Events; + +/// +/// 表示不携带任何负载的空测试事件。 +/// +public sealed class EmptyEvent; diff --git a/GFramework.Core.Tests/Events/EventListenerScopeTests.cs b/GFramework.Core.Tests/Events/EventListenerScopeTests.cs index a88edba6..2c6b9569 100644 --- a/GFramework.Core.Tests/Events/EventListenerScopeTests.cs +++ b/GFramework.Core.Tests/Events/EventListenerScopeTests.cs @@ -1,3 +1,4 @@ +using System.Runtime.InteropServices; using GFramework.Core.Abstractions.Events; using GFramework.Core.Events; using Moq; @@ -303,6 +304,7 @@ public class EventListenerScopeTests /// /// 测试用的结构体事件 /// + [StructLayout(LayoutKind.Auto)] private struct StructEvent { public int Id { get; set; } @@ -318,4 +320,4 @@ public class EventListenerScopeTests using var scope = new EventListenerScope(eventBusMock.Object); // 作用域内部不验证 } -} \ No newline at end of file +} diff --git a/GFramework.Core.Tests/Events/TestEvent.cs b/GFramework.Core.Tests/Events/TestEvent.cs index c7e47ed6..d3806918 100644 --- a/GFramework.Core.Tests/Events/TestEvent.cs +++ b/GFramework.Core.Tests/Events/TestEvent.cs @@ -1,8 +1,12 @@ namespace GFramework.Core.Tests.Events; +/// +/// 表示包含整型载荷的测试事件。 +/// public sealed class TestEvent { + /// + /// 获取初始化阶段写入的接收值。 + /// public int ReceivedValue { get; init; } } - -public sealed class EmptyEvent; \ No newline at end of file diff --git a/GFramework.Core.Tests/Logging/LoggerTests.cs b/GFramework.Core.Tests/Logging/LoggerTests.cs index 3bba60a4..f26e68b6 100644 --- a/GFramework.Core.Tests/Logging/LoggerTests.cs +++ b/GFramework.Core.Tests/Logging/LoggerTests.cs @@ -419,44 +419,3 @@ public class LoggerTests Assert.That(fatalLogger.Logs[0].Level, Is.EqualTo(LogLevel.Fatal)); } } - -/// -/// 测试用的日志记录器实现类,继承自AbstractLogger -/// -public sealed class TestLogger : AbstractLogger -{ - private readonly List _logs = new(); - - /// - /// 初始化TestLogger的新实例 - /// - /// 日志记录器的名称,默认为null - /// 最小日志级别,默认为LogLevel.Info - public TestLogger(string? name = null, LogLevel minLevel = LogLevel.Info) : base(name, minLevel) - { - } - - /// - /// 获取按写入顺序保存的日志条目只读视图 - /// - public IReadOnlyList Logs => _logs; - - /// - /// 将日志信息写入内部存储 - /// - /// 日志级别 - /// 日志消息 - /// 相关异常(可选) - protected override void Write(LogLevel level, string message, Exception? exception) - { - _logs.Add(new LogEntry(level, message, exception)); - } - - /// - /// 表示单个日志条目的记录类型 - /// - /// 日志级别 - /// 日志消息 - /// 相关异常(可选) - public sealed record LogEntry(LogLevel Level, string Message, Exception? Exception); -} diff --git a/GFramework.Core.Tests/Logging/LoggingConfigurationTests.cs b/GFramework.Core.Tests/Logging/LoggingConfigurationTests.cs index 9586533f..533d368c 100644 --- a/GFramework.Core.Tests/Logging/LoggingConfigurationTests.cs +++ b/GFramework.Core.Tests/Logging/LoggingConfigurationTests.cs @@ -307,8 +307,9 @@ public class LoggingConfigurationTests }}"; var config = LoggingConfigurationLoader.LoadFromJsonString(json); - Assert.That(config.Appenders[0].Filter, Is.Not.Null); - Assert.That(config.Appenders[0].Filter.Type, Is.EqualTo("Namespace")); + var filter = config.Appenders[0].Filter; + Assert.That(filter, Is.Not.Null); + Assert.That(filter!.Type, Is.EqualTo("Namespace")); } finally { diff --git a/GFramework.Core.Tests/Logging/TestLogger.cs b/GFramework.Core.Tests/Logging/TestLogger.cs new file mode 100644 index 00000000..594b6de7 --- /dev/null +++ b/GFramework.Core.Tests/Logging/TestLogger.cs @@ -0,0 +1,45 @@ +using GFramework.Core.Abstractions.Logging; +using GFramework.Core.Logging; + +namespace GFramework.Core.Tests.Logging; + +/// +/// 表示供日志相关测试复用的内存日志记录器。 +/// +public sealed class TestLogger : AbstractLogger +{ + private readonly List _logs = new(); + + /// + /// 初始化 的新实例。 + /// + /// 日志记录器的名称;未指定时沿用基类默认行为。 + /// 允许写入的最小日志级别。 + public TestLogger(string? name = null, LogLevel minLevel = LogLevel.Info) : base(name, minLevel) + { + } + + /// + /// 获取按写入顺序保存的日志条目只读视图。 + /// + public IReadOnlyList Logs => _logs; + + /// + /// 将日志信息追加到内存列表,供断言读取。 + /// + /// 日志级别。 + /// 日志消息。 + /// 相关异常;没有异常时为 。 + protected override void Write(LogLevel level, string message, Exception? exception) + { + _logs.Add(new LogEntry(level, message, exception)); + } + + /// + /// 表示单个日志条目的不可变快照。 + /// + /// 日志级别。 + /// 日志消息。 + /// 相关异常;没有异常时为 。 + public sealed record LogEntry(LogLevel Level, string Message, Exception? Exception); +} diff --git a/GFramework.Core.Tests/Resource/ResourceManagerTests.cs b/GFramework.Core.Tests/Resource/ResourceManagerTests.cs index dba15f62..d91f447b 100644 --- a/GFramework.Core.Tests/Resource/ResourceManagerTests.cs +++ b/GFramework.Core.Tests/Resource/ResourceManagerTests.cs @@ -1,58 +1,8 @@ -using System.IO; -using GFramework.Core.Abstractions.Resource; using GFramework.Core.Resource; using NUnit.Framework; namespace GFramework.Core.Tests.Resource; -/// -/// 测试用的简单资源类 -/// -public class TestResource -{ - public string Content { get; set; } = string.Empty; - public bool IsDisposed { get; set; } -} - -/// -/// 测试用的资源加载器 -/// -public class TestResourceLoader : IResourceLoader -{ - private readonly Dictionary _resourceData = new(); - - public TestResource Load(string path) - { - if (_resourceData.TryGetValue(path, out var content)) - { - return new TestResource { Content = content }; - } - - throw new FileNotFoundException($"Resource not found: {path}"); - } - - public async Task LoadAsync(string path) - { - await Task.Delay(10).ConfigureAwait(false); // 模拟异步加载 - return Load(path); - } - - public void Unload(TestResource resource) - { - resource.IsDisposed = true; - } - - public bool CanLoad(string path) - { - return _resourceData.ContainsKey(path); - } - - public void AddTestData(string path, string content) - { - _resourceData[path] = content; - } -} - /// /// ResourceManager 功能测试类 /// diff --git a/GFramework.Core.Tests/Resource/TestResource.cs b/GFramework.Core.Tests/Resource/TestResource.cs new file mode 100644 index 00000000..a9f78cae --- /dev/null +++ b/GFramework.Core.Tests/Resource/TestResource.cs @@ -0,0 +1,17 @@ +namespace GFramework.Core.Tests.Resource; + +/// +/// 表示 ResourceManager 测试使用的简单资源对象。 +/// +public class TestResource +{ + /// + /// 获取或设置资源内容。 + /// + public string Content { get; set; } = string.Empty; + + /// + /// 获取或设置一个值,指示资源是否已经被测试加载器标记为已卸载。 + /// + public bool IsDisposed { get; set; } +} diff --git a/GFramework.Core.Tests/Resource/TestResourceLoader.cs b/GFramework.Core.Tests/Resource/TestResourceLoader.cs new file mode 100644 index 00000000..5dcf6a14 --- /dev/null +++ b/GFramework.Core.Tests/Resource/TestResourceLoader.cs @@ -0,0 +1,52 @@ +using System.IO; +using GFramework.Core.Abstractions.Resource; + +namespace GFramework.Core.Tests.Resource; + +/// +/// 为 ResourceManager 测试提供可控数据源的资源加载器。 +/// +public class TestResourceLoader : IResourceLoader +{ + private readonly Dictionary _resourceData = new(StringComparer.Ordinal); + + /// + public TestResource Load(string path) + { + if (_resourceData.TryGetValue(path, out var content)) + { + return new TestResource { Content = content }; + } + + throw new FileNotFoundException($"Resource not found: {path}"); + } + + /// + public async Task LoadAsync(string path) + { + await Task.Delay(10).ConfigureAwait(false); // 模拟异步加载 + return Load(path); + } + + /// + public void Unload(TestResource resource) + { + resource.IsDisposed = true; + } + + /// + public bool CanLoad(string path) + { + return _resourceData.ContainsKey(path); + } + + /// + /// 向测试加载器注册一条可返回的资源数据。 + /// + /// 资源路径。 + /// 资源内容。 + public void AddTestData(string path, string content) + { + _resourceData[path] = content; + } +} diff --git a/ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md b/ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md index 13d194c0..4c823765 100644 --- a/ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md +++ b/ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md @@ -6,53 +6,41 @@ ## 当前恢复点 -- 恢复点编号:`ANALYZER-WARNING-REDUCTION-RP-086` -- 当前阶段:`Phase 86` +- 恢复点编号:`ANALYZER-WARNING-REDUCTION-RP-087` +- 当前阶段:`Phase 87` - 当前焦点: - - `2026-04-27` 已按 `$gframework-batch-boot 100` 连续执行多波 `MA0048` 小切片,当前以 `GFramework.Core.Tests` 的测试辅助类型拆分为主 - - `2026-04-27` 已按 `$gframework-pr-review` 收敛 `PR #298` 的有效 nitpick,修复测试辅助类型的只读暴露、线程安全、空安全与文档一致性问题 - - 本轮已完成 `ArchitectureContextTests`、`AsyncQueryExecutorTests`、`CommandExecutorTests`、`StateTests`、`StateMachineTests`、`StateMachineSystemTests`、`ArchitectureModulesBehaviorTests`、`ArchitectureAdditionalCqrsHandlersTests`、`QueryCoroutineExtensionsTests`、`ObjectPoolTests`、`AbstractContextUtilityTests` 等低风险单文件切片 - - 当前仓库根权威基线已从 `353 Warning(s)` / `279` 个唯一位点下降到 `288 Warning(s)` / `214` 个唯一位点 - - 当前分支下一波更适合转向 `GameContextTests.cs`、`ArchitectureServicesTests.cs`、`RegistryInitializationHookBaseTests.cs` 这类仍在 `GFramework.Core.Tests` 内、但已混入 `CS8766` / `MA0016` 的小型混合切片 + - `2026-04-28` 已按 `$gframework-batch-boot 50` 先执行仓库根 `dotnet clean` + `dotnet build`,建立本轮权威基线 `288 Warning(s)` / `214` 个唯一位点 + - 本轮已并行收敛 `GameContextTests.cs`、`ArchitectureServicesTests.cs`、`RegistryInitializationHookBaseTests.cs`、`CqrsDispatcherCacheTests.cs` 与 `CqrsHandlerRegistrarTests.cs` + - 主线程已补齐 `ResourceManagerTests.cs`、`TestEvent.cs`、`LoggerTests.cs`、`ContextProviderTests.cs`、`TestArchitectureBase.cs`、`CommandCoroutineExtensionsTests.cs` 等 `Core.Tests` 零散 warning + - 当前 `GFramework.Core.Tests` 与 `GFramework.Cqrs.Tests` 的受影响项目 Release 构建都已恢复到 `0 Warning(s)` / `0 Error(s)` + - 当前仓库根权威基线已从本轮开始时的 `288 Warning(s)` / `214` 个唯一位点下降到 `236 Warning(s)` / `162` 个唯一位点;剩余 warning 只集中在 `Mediator/*` 与 `YamlConfigSchemaValidator*` ## 当前活跃事实 -- 当前 `origin/main` 基线提交为 `7cfdd2c`(`2026-04-27T16:59:57+08:00`)。 +- 当前 `origin/main` 基线提交为 `6cc87a9`(`2026-04-27T20:28:50+08:00`)。 - 当前直接验证结果: - - `dotnet build GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release` - - 最新结果:成功;`28 Warning(s)`、`0 Error(s)`;当前 warning 来自 `GameContextTests.cs`、`ArchitectureServicesTests.cs`、`RegistryInitializationHookBaseTests.cs` 等既有热点 - - `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --no-build` - - 最新结果:成功;`1610` 通过、`0` 失败 - `dotnet clean` - - 最新结果:成功;已刷新仓库根 non-incremental 基线 + - 最新结果:成功;已刷新本轮 final non-incremental 仓库根基线 - `dotnet build` - - 最新结果:成功;`288 Warning(s)`、`0 Error(s)`,唯一位点 `214` - - `dotnet build GFramework.Game/GFramework.Game.csproj -c Release` + - 最新结果:成功;`236 Warning(s)`、`0 Error(s)`,唯一位点 `162` + - `dotnet build GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release` + - 最新结果:成功;`0 Warning(s)`、`0 Error(s)` + - `dotnet build GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release` - 最新结果:成功;`0 Warning(s)`、`0 Error(s)` - - `dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release --filter "FullyQualifiedName~YamlConfigLoaderTests.ReadYamlAsync_Should_Preserve_OperationCanceledException_When_Cancellation_Is_Requested"` - - 最新结果:成功;`1` 通过、`0` 失败 - - `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --filter "FullyQualifiedName~MicrosoftDiContainerTests.GetAllByPriority_Should_Sort_By_Priority_Ascending"` - - 最新结果:成功;`1` 通过、`0` 失败 - - `dotnet format GFramework.sln --verify-no-changes --include GFramework.Game/Config/YamlConfigLoader.cs GFramework.Game.Tests/Config/YamlConfigLoaderTests.cs GFramework.Core.Tests/Ioc/IMixedService.cs GFramework.Core.Tests/Ioc/IPrioritizedService.cs GFramework.Core.Tests/Ioc/PrioritizedService.cs GFramework.Core.Tests/Query/TestAsyncQueryWithExceptionV4.cs` - - 最新结果:成功;本次 PR follow-up 改动文件无需额外格式化 - 当前批次摘要: - - 本轮通过多批并行 worker 共完成 `20+` 个 `GFramework.Core.Tests` 文件的测试辅助类型拆分,集中消化纯 `MA0048` warning 热点 - - 本轮停止时共享工作树共有 `61` 个变更条目,仍低于 `$gframework-batch-boot 100` 的文件停止线 - - 本轮仓库根权威 warning 已从开始时的 `353` 下降到 `288`,且 `GFramework.Core.Tests` 受影响项目的 Release 构建已恢复到 `0 Warning(s)` / `0 Error(s)` + - 本轮接受并集成 `GameContextTests.cs`、`ArchitectureServicesTests.cs`、`RegistryInitializationHookBaseTests.cs`、`CqrsDispatcherCacheTests.cs`、`CqrsHandlerRegistrarTests.cs` 五个并行 worker 切片 + - 主线程补齐 `Core.Tests` 内剩余零散 warning,使 `GFramework.Core.Tests` 项目级 Release 构建回到 `0 Warning(s)` / `0 Error(s)` + - 当前 `origin/main...HEAD` 已提交 branch diff 仍为 `21` 个文件;计入当前待提交工作树后的并集 footprint 为 `45 / 50` 个文件,已接近本轮停止线 - 当前建议保留到下一波次的候选: - - `GFramework.Core.Tests/Architectures/GameContextTests.cs` 的 `4` 个 `CS8766` 与 `2` 个 `MA0048` - - `GFramework.Core.Tests/Architectures/ArchitectureServicesTests.cs` 的 `4` 个 `CS8766` 与 `1` 个 `MA0048` - - `GFramework.Core.Tests/Architectures/RegistryInitializationHookBaseTests.cs` 的 `1` 个 `MA0016` 与 `5` 个 `MA0048` + - `GFramework.Cqrs.Tests/Mediator/MediatorArchitectureIntegrationTests.cs`、`MediatorComprehensiveTests.cs`、`MediatorAdvancedFeaturesTests.cs` 的高密度 `MA0048` / `MA0004` - `GFramework.Game/Config/YamlConfigSchemaValidator.cs` 与 `YamlConfigSchemaValidator.ObjectKeywords.cs` 的高耦合 warning 热点 ## 当前风险 -- `GFramework.Cqrs.Tests/Mediator/*` 仍有 `47` / `44` / `34` 个唯一 warning 位点,属于高 changed-file 风险的 `MA0048` 大波次。 - - 缓解措施:优先继续处理 `6-7` 个 warning 的小文件切片,避免一次性推高文件数。 -- `GameContextTests.cs`、`ArchitectureServicesTests.cs` 这类混合 `CS8766` / `MA0048` 文件不再适合继续用“纯拆分”模式批量下发。 - - 缓解措施:下一波由主线程先局部修正可空签名,再决定是否继续并行拆分。 -- `YamlConfigSchemaValidator*` 仍然聚集多类高耦合 warning。 - - 缓解措施:继续把它们留在独立波次,不与测试项目的低风险拆分混提。 +- `GFramework.Cqrs.Tests/Mediator/*` 仍有 `94` / `88` / `68` 条输出 warning,属于高 changed-file 风险的 `MA0048` 大波次。 + - 缓解措施:当前 footprint 已到 `45 / 50`,下一轮应在新提交基础上单独规划 `Mediator*` 波次,而不是继续叠在本轮工作树上。 +- `YamlConfigSchemaValidator*` 仍然聚集 `222` 条输出 warning,且同时混有 `MA0048`、`MA0009`、`MA0051`、`MA0006`。 + - 缓解措施:保持为独立高耦合波次,不与测试项目拆分混提。 ## 活跃文档 @@ -72,11 +60,11 @@ ## 验证说明 - 权威验证结果统一维护在“当前活跃事实”。 -- `GFramework.Core.Tests` 项目级 Release 构建已在本轮清零,但仓库根 non-incremental 构建仍保留大量既有 warning。 +- `GFramework.Core.Tests` 与 `GFramework.Cqrs.Tests` 的当前受影响项目 Release 构建都已在本轮清零,但仓库根 non-incremental 构建仍保留 `Mediator/*` 与 `YamlConfigSchemaValidator*` 既有 warning。 - warning reduction 的仓库级真值只以同轮 `dotnet clean` 后的 `dotnet build` 为准。 ## 下一步建议 -1. 提交本轮多批 `MA0048` warning reduction 与 `ai-plan` 同步。 -2. 下一波由主线程先处理 `GameContextTests.cs` / `ArchitectureServicesTests.cs` 的 `CS8766`,再决定是否继续拆分剩余 `MA0048`。 -3. 继续将 `YamlConfigSchemaValidator*` 与 `GFramework.Cqrs.Tests/Mediator/*` 作为独立高风险波次处理。 +1. 提交本轮 `Core.Tests` / `Cqrs.Tests` warning reduction 与 `ai-plan` 同步。 +2. 下一轮在新提交基础上单独规划 `Mediator/*` 波次,避免在 `45 / 50` footprint 状态继续扩批。 +3. 将 `YamlConfigSchemaValidator*` 保持为独立高耦合波次,必要时先由主线程局部切分再决定是否并行。 diff --git a/ai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md b/ai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md index b57f4b67..edbc3a06 100644 --- a/ai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md +++ b/ai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md @@ -1,5 +1,55 @@ # Analyzer Warning Reduction 追踪 +## 2026-04-28 — RP-087 + +### 阶段:按 `$gframework-batch-boot 50` 并行收敛 `Core.Tests` / `Cqrs.Tests` 低风险切片 + +- 触发背景: + - 用户再次要求先拿仓库根构建 warning,再把可切分的 warning 批次分派给多个 subagent,以降低主线程上下文压力 + - 当前分支与 `origin/main@6cc87a9` 无提交差异,适合从单文件和小型混合 warning 切片重新起步 +- 主线程实施: + - 先执行仓库规则要求的 non-incremental 基线:`dotnet restore GFramework.sln -p:RestoreFallbackFolders=` 以修复当前 WSL 环境里的旧 fallback 资产,再执行仓库根 `dotnet clean` + `dotnet build` + - 基线结果:`288 Warning(s)`、唯一位点 `214` + - 并行下发四个 disjoint worker: + - `GameContextTests.cs` + - `ArchitectureServicesTests.cs` + - `RegistryInitializationHookBaseTests.cs` + - `CqrsDispatcherCacheTests.cs` + - 主线程补齐不与 worker 重叠的零散切片: + - `ResourceManagerTests.cs` + - `TestEvent.cs` + - `EventListenerScopeTests.cs` + - `TestArchitectureBase.cs` + - `ContextProviderTests.cs` + - `LoggerTests.cs` + - `LoggingConfigurationTests.cs` + - `WaitForMultipleEventsTests.cs` + - `CommandCoroutineExtensionsTests.cs` + - 集成 `GameContextTests`、`CqrsDispatcherCacheTests` 与 `CqrsHandlerRegistrarTests` worker 产出的 commit `a7be413` / `9098490` / `98afcbf`,并接受已直接落到共享工作树的 `ArchitectureServicesTests` / `RegistryInitializationHookBaseTests` 切片 + - 重新执行仓库根 `dotnet clean` + `dotnet build` + - final 结果:`236 Warning(s)`、唯一位点 `162` +- 验证里程碑: + - `dotnet build GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release` + - 结果:成功;`0 Warning(s)`、`0 Error(s)` + - `dotnet build GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release` + - 结果:成功;`0 Warning(s)`、`0 Error(s)` + - `dotnet clean` + - 结果:成功 + - `dotnet build` + - 结果:成功;`236 Warning(s)`、`0 Error(s)`,唯一位点 `162` + - `git diff --name-only refs/remotes/origin/main...HEAD | wc -l` + - 结果:`21` + - 已提交 diff 与当前工作树变更并集文件数 + - 结果:`45 / 50` +- 当前结论: + - 本轮已经把剩余 `Core.Tests` warning 全部清空,并把 `CqrsDispatcherCacheTests.cs` 与 `CqrsHandlerRegistrarTests.cs` 这两处 `Cqrs.Tests` 单文件 `MA0048` 热点从仓库根中移除 + - 仓库根权威 warning 从 `288` 下降到 `236`,唯一位点从 `214` 下降到 `162` + - 在 `45 / 50` footprint 下,继续扩批将明显压缩 review 余量;剩余切片也只剩 `Mediator*` 与 `YamlConfigSchemaValidator*` 这两组高风险热点,因此本轮应在这里收口 +- 下一步: + 1. 提交本轮已验证的 warning reduction 与 `ai-plan` 同步。 + 2. 下一轮在新提交基础上单独规划 `Mediator*` 波次。 + 3. 将 `YamlConfigSchemaValidator*` 保持为独立高耦合波次,再决定是否需要新的并行切法。 + ## 2026-04-27 — RP-086 ### 阶段:收敛 PR #298 的 CodeRabbit nitpick follow-up From 5693ab7e6f1839a29c7dd5ecb2f378e18cdd7987 Mon Sep 17 00:00:00 2001 From: gewuyou <95328647+GeWuYou@users.noreply.github.com> Date: Tue, 28 Apr 2026 09:26:20 +0800 Subject: [PATCH 06/10] =?UTF-8?q?fix(test-helpers):=20=E6=94=B6=E6=95=9BPR?= =?UTF-8?q?300=E8=AF=84=E5=AE=A1=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修复测试架构上下文、生命周期钩子与注册表初始化钩子的评审问题,避免静默成功或错误共享状态 - 补充 TestResourceLoader、TestLogger、CapturingLoggerFactoryProvider 与 CQRS 测试辅助类型的契约文档和并发语义 - 新增测试覆盖并更新 analyzer-warning-reduction 活跃跟踪,记录 PR #300 跟进验证与现存 Cqrs warning blocker --- .../Architectures/TestArchitectureContext.cs | 45 +++-- .../TestArchitectureContextBehaviorTests.cs | 156 ++++++++++++++++++ .../TestArchitectureContextV3.cs | 24 +-- .../TestArchitectureWithRegistry.cs | 3 +- .../TestArchitectureWithoutRegistry.cs | 3 +- .../Coroutine/WaitForMultipleEventsTests.cs | 40 +++-- GFramework.Core.Tests/Logging/TestLogger.cs | 25 ++- .../Resource/TestResourceLoader.cs | 56 +++++-- .../RegistryInitializationHookBase.cs | 26 ++- .../Cqrs/CapturingLoggerFactoryProvider.cs | 23 ++- .../DeterministicNotificationHandlerState.cs | 7 + ...ialGeneratedNotificationHandlerRegistry.cs | 3 + .../analyzer-warning-reduction-tracking.md | 48 +++--- .../analyzer-warning-reduction-trace.md | 32 ++++ 14 files changed, 403 insertions(+), 88 deletions(-) create mode 100644 GFramework.Core.Tests/Architectures/TestArchitectureContextBehaviorTests.cs diff --git a/GFramework.Core.Tests/Architectures/TestArchitectureContext.cs b/GFramework.Core.Tests/Architectures/TestArchitectureContext.cs index cda590fd..a1d85189 100644 --- a/GFramework.Core.Tests/Architectures/TestArchitectureContext.cs +++ b/GFramework.Core.Tests/Architectures/TestArchitectureContext.cs @@ -1,3 +1,7 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; using GFramework.Core.Abstractions.Architectures; using GFramework.Core.Abstractions.Command; using GFramework.Core.Abstractions.Environment; @@ -27,6 +31,7 @@ namespace GFramework.Core.Tests.Architectures; public class TestArchitectureContext : IArchitectureContext { private readonly MicrosoftDiContainer _container = new(); + private readonly EventBus _eventBus = new(); /// /// 获取用于解析测试服务的依赖注入容器。 @@ -36,7 +41,11 @@ public class TestArchitectureContext : IArchitectureContext /// /// 获取测试事件总线实例。 /// - public IEventBus EventBus => new EventBus(); + /// + /// 返回同一个缓存事件总线,以便 与 + /// 在同一份订阅状态上协作。 + /// + public IEventBus EventBus => _eventBus; /// /// 获取测试命令执行器实例。 @@ -183,6 +192,7 @@ public class TestArchitectureContext : IArchitectureContext /// 事件类型。 public void SendEvent() where TEvent : new() { + _eventBus.Send(); } /// @@ -192,6 +202,8 @@ public class TestArchitectureContext : IArchitectureContext /// 事件实例。 public void SendEvent(TEvent e) where TEvent : class { + ArgumentNullException.ThrowIfNull(e); + _eventBus.Send(e); } /// @@ -199,10 +211,11 @@ public class TestArchitectureContext : IArchitectureContext /// /// 事件类型。 /// 事件处理委托。 - /// 用于测试的空注销句柄。 + /// 用于测试的事件注销句柄。 public IUnRegister RegisterEvent(Action handler) { - return new DefaultUnRegister(() => { }); + ArgumentNullException.ThrowIfNull(handler); + return _eventBus.Register(handler); } /// @@ -212,6 +225,8 @@ public class TestArchitectureContext : IArchitectureContext /// 事件处理委托。 public void UnRegisterEvent(Action onEvent) { + ArgumentNullException.ThrowIfNull(onEvent); + _eventBus.UnRegister(onEvent); } /// @@ -358,8 +373,10 @@ public class TestArchitectureContext : IArchitectureContext /// 发送旧版命令。 /// /// 命令对象。 + /// 该测试桩不支持旧版命令执行入口。 public void SendCommand(ICommand command) { + throw new NotSupportedException(); } /// @@ -367,20 +384,21 @@ public class TestArchitectureContext : IArchitectureContext /// /// 返回值类型。 /// 命令对象。 - /// 测试桩默认返回值。 + /// 此方法始终抛出异常,不返回结果。 + /// 该测试桩不支持旧版命令执行入口。 public TResult SendCommand(ICommand command) { - return default!; + throw new NotSupportedException(); } /// /// 异步发送旧版命令。 /// /// 命令对象。 - /// 已完成任务。 + /// 已失败的任务。 public Task SendCommandAsync(IAsyncCommand command) { - return Task.CompletedTask; + return Task.FromException(new NotSupportedException()); } /// @@ -388,10 +406,10 @@ public class TestArchitectureContext : IArchitectureContext /// /// 返回值类型。 /// 命令对象。 - /// 包含测试桩默认返回值的任务。 + /// 已失败的任务。 public Task SendCommandAsync(IAsyncCommand command) { - return Task.FromResult(default(TResult)!); + return Task.FromException(new NotSupportedException()); } /// @@ -399,10 +417,11 @@ public class TestArchitectureContext : IArchitectureContext /// /// 查询结果类型。 /// 查询对象。 - /// 测试桩默认返回值。 + /// 此方法始终抛出异常,不返回结果。 + /// 该测试桩不支持旧版查询执行入口。 public TResult SendQuery(IQuery query) { - return default!; + throw new NotSupportedException(); } /// @@ -410,10 +429,10 @@ public class TestArchitectureContext : IArchitectureContext /// /// 查询结果类型。 /// 异步查询对象。 - /// 包含测试桩默认返回值的任务。 + /// 已失败的任务。 public Task SendQueryAsync(IAsyncQuery query) { - return Task.FromResult(default(TResult)!); + return Task.FromException(new NotSupportedException()); } /// diff --git a/GFramework.Core.Tests/Architectures/TestArchitectureContextBehaviorTests.cs b/GFramework.Core.Tests/Architectures/TestArchitectureContextBehaviorTests.cs new file mode 100644 index 00000000..a4532a0f --- /dev/null +++ b/GFramework.Core.Tests/Architectures/TestArchitectureContextBehaviorTests.cs @@ -0,0 +1,156 @@ +using System; +using System.Threading.Tasks; +using GFramework.Core.Abstractions.Architectures; +using GFramework.Core.Abstractions.Command; +using GFramework.Core.Abstractions.Enums; +using GFramework.Core.Abstractions.Query; + +namespace GFramework.Core.Tests.Architectures; + +/// +/// 覆盖测试架构上下文替身的共享事件与显式失败契约。 +/// +[TestFixture] +public class TestArchitectureContextBehaviorTests +{ + /// + /// 验证测试上下文会把事件注册与发送委托到同一个事件总线实例。 + /// + [Test] + public void RegisterEvent_And_SendEvent_Should_Use_Shared_EventBus() + { + var context = new TestArchitectureContext(); + var eventReceived = false; + + context.RegisterEvent(_ => eventReceived = true); + context.SendEvent(); + + Assert.That(eventReceived, Is.True); + } + + /// + /// 验证测试上下文的旧版命令与查询入口会显式抛出未支持异常。 + /// + [Test] + public async Task Legacy_Entries_Should_Throw_Or_Return_Faulted_Tasks() + { + var context = new TestArchitectureContext(); + + Assert.That(() => context.SendCommand(new TestCommandV2()), Throws.TypeOf()); + Assert.That( + () => context.SendCommand(new TestCommandWithResultV2 { Result = 1 }), + Throws.TypeOf()); + Assert.That(() => context.SendQuery(new TestQueryV2 { Result = 1 }), Throws.TypeOf()); + Assert.That( + async () => await context.SendCommandAsync(new TestAsyncCommand()).ConfigureAwait(false), + Throws.TypeOf()); + Assert.That( + async () => await context.SendCommandAsync(new TestAsyncCommandWithResult()).ConfigureAwait(false), + Throws.TypeOf()); + Assert.That( + async () => await context.SendQueryAsync(new TestAsyncQuery()).ConfigureAwait(false), + Throws.TypeOf()); + } + + /// + /// 验证用于 ArchitectureServices 的上下文替身也会把旧版入口显式标记为不支持。 + /// + [Test] + public async Task Legacy_Entries_On_TestArchitectureContextV3_Should_Throw_Or_Return_Faulted_Tasks() + { + var context = new TestArchitectureContextV3(); + + Assert.That(() => context.SendCommand(new TestCommandV2()), Throws.TypeOf()); + Assert.That( + () => context.SendCommand(new TestCommandWithResultV2 { Result = 1 }), + Throws.TypeOf()); + Assert.That(() => context.SendQuery(new TestQueryV2 { Result = 1 }), Throws.TypeOf()); + Assert.That( + async () => await context.SendCommandAsync(new TestAsyncCommand()).ConfigureAwait(false), + Throws.TypeOf()); + Assert.That( + async () => await context.SendCommandAsync(new TestAsyncCommandWithResult()).ConfigureAwait(false), + Throws.TypeOf()); + Assert.That( + async () => await context.SendQueryAsync(new TestAsyncQuery()).ConfigureAwait(false), + Throws.TypeOf()); + } + + /// + /// 验证两类架构测试替身在接口视角下都会以 no-op 方式接受生命周期钩子。 + /// + [Test] + public void RegisterLifecycleHook_Via_Interface_Should_Return_Original_Hook() + { + IArchitecture withRegistry = new TestArchitectureWithRegistry(new TestRegistry()); + IArchitecture withoutRegistry = new TestArchitectureWithoutRegistry(); + var hook = new NoOpLifecycleHook(); + + Assert.That(withRegistry.RegisterLifecycleHook(hook), Is.SameAs(hook)); + Assert.That(withoutRegistry.RegisterLifecycleHook(hook), Is.SameAs(hook)); + } + + /// + /// 为旧版异步命令入口提供最小实现的测试命令。 + /// + private sealed class TestAsyncCommand : IAsyncCommand + { + public Task ExecuteAsync() + { + return Task.CompletedTask; + } + + public IArchitectureContext GetContext() + { + throw new NotSupportedException(); + } + + public void SetContext(IArchitectureContext context) + { + ArgumentNullException.ThrowIfNull(context); + } + } + + /// + /// 为旧版异步命令入口提供最小实现的带结果测试命令。 + /// + private sealed class TestAsyncCommandWithResult : IAsyncCommand + { + public Task ExecuteAsync() + { + return Task.FromResult(1); + } + + public IArchitectureContext GetContext() + { + throw new NotSupportedException(); + } + + public void SetContext(IArchitectureContext context) + { + ArgumentNullException.ThrowIfNull(context); + } + } + + /// + /// 为旧版异步查询入口提供最小实现的测试查询。 + /// + private sealed class TestAsyncQuery : IAsyncQuery + { + public Task DoAsync() + { + return Task.FromResult(1); + } + } + + /// + /// 为生命周期钩子接口提供空实现的测试替身。 + /// + private sealed class NoOpLifecycleHook : IArchitectureLifecycleHook + { + public void OnPhase(ArchitecturePhase phase, IArchitecture architecture) + { + ArgumentNullException.ThrowIfNull(architecture); + } + } +} diff --git a/GFramework.Core.Tests/Architectures/TestArchitectureContextV3.cs b/GFramework.Core.Tests/Architectures/TestArchitectureContextV3.cs index 6fda6f2d..7cdd8a57 100644 --- a/GFramework.Core.Tests/Architectures/TestArchitectureContextV3.cs +++ b/GFramework.Core.Tests/Architectures/TestArchitectureContextV3.cs @@ -343,8 +343,10 @@ public class TestArchitectureContextV3 : IArchitectureContext /// 发送旧版无返回值命令。 /// /// 要发送的命令。 + /// 该测试桩不支持旧版命令执行入口。 public void SendCommand(ICommand command) { + throw new NotSupportedException(); } /// @@ -352,20 +354,21 @@ public class TestArchitectureContextV3 : IArchitectureContext /// /// 命令响应类型。 /// 要发送的命令。 - /// 默认响应值。 + /// 此方法始终抛出异常,不返回结果。 + /// 该测试桩不支持旧版命令执行入口。 public TResult SendCommand(GFramework.Core.Abstractions.Command.ICommand command) { - return default!; + throw new NotSupportedException(); } /// /// 异步发送旧版无返回值命令。 /// /// 要发送的命令。 - /// 已完成任务。 + /// 已失败的任务。 public Task SendCommandAsync(IAsyncCommand command) { - return Task.CompletedTask; + return Task.FromException(new NotSupportedException()); } /// @@ -373,10 +376,10 @@ public class TestArchitectureContextV3 : IArchitectureContext /// /// 命令响应类型。 /// 要发送的命令。 - /// 占位任务。 + /// 已失败的任务。 public Task SendCommandAsync(IAsyncCommand command) { - return (Task)Task.CompletedTask; + return Task.FromException(new NotSupportedException()); } /// @@ -384,10 +387,11 @@ public class TestArchitectureContextV3 : IArchitectureContext /// /// 查询结果类型。 /// 要发送的查询。 - /// 默认查询结果。 + /// 此方法始终抛出异常,不返回结果。 + /// 该测试桩不支持旧版查询执行入口。 public TResult SendQuery(GFramework.Core.Abstractions.Query.IQuery query) { - return default!; + throw new NotSupportedException(); } /// @@ -395,10 +399,10 @@ public class TestArchitectureContextV3 : IArchitectureContext /// /// 查询结果类型。 /// 要发送的查询。 - /// 占位任务。 + /// 已失败的任务。 public Task SendQueryAsync(IAsyncQuery query) { - return (Task)Task.CompletedTask; + return Task.FromException(new NotSupportedException()); } /// diff --git a/GFramework.Core.Tests/Architectures/TestArchitectureWithRegistry.cs b/GFramework.Core.Tests/Architectures/TestArchitectureWithRegistry.cs index cdb52e81..df0f93e5 100644 --- a/GFramework.Core.Tests/Architectures/TestArchitectureWithRegistry.cs +++ b/GFramework.Core.Tests/Architectures/TestArchitectureWithRegistry.cs @@ -95,7 +95,8 @@ public class TestArchitectureWithRegistry : IArchitecture IArchitectureLifecycleHook IArchitecture.RegisterLifecycleHook(IArchitectureLifecycleHook hook) { - throw new NotSupportedException(); + RegisterLifecycleHook(hook); + return hook; } Task IArchitecture.WaitUntilReadyAsync() diff --git a/GFramework.Core.Tests/Architectures/TestArchitectureWithoutRegistry.cs b/GFramework.Core.Tests/Architectures/TestArchitectureWithoutRegistry.cs index f4bd61ec..b9387e54 100644 --- a/GFramework.Core.Tests/Architectures/TestArchitectureWithoutRegistry.cs +++ b/GFramework.Core.Tests/Architectures/TestArchitectureWithoutRegistry.cs @@ -93,7 +93,8 @@ public class TestArchitectureWithoutRegistry : IArchitecture IArchitectureLifecycleHook IArchitecture.RegisterLifecycleHook(IArchitectureLifecycleHook hook) { - throw new NotSupportedException(); + RegisterLifecycleHook(hook); + return hook; } /// diff --git a/GFramework.Core.Tests/Coroutine/WaitForMultipleEventsTests.cs b/GFramework.Core.Tests/Coroutine/WaitForMultipleEventsTests.cs index 88baf123..e35b9a6c 100644 --- a/GFramework.Core.Tests/Coroutine/WaitForMultipleEventsTests.cs +++ b/GFramework.Core.Tests/Coroutine/WaitForMultipleEventsTests.cs @@ -1,3 +1,4 @@ +using System; using GFramework.Core.Abstractions.Events; using GFramework.Core.Coroutine.Instructions; using GFramework.Core.Events; @@ -8,25 +9,28 @@ namespace GFramework.Core.Tests.Coroutine [TestFixture] public class WaitForMultipleEventsTests { + private IEventBus? _eventBus; + + private IEventBus EventBus => _eventBus ?? throw new InvalidOperationException("EventBus has not been initialized."); + [SetUp] public void SetUp() { - eventBus = new EventBus(); + _eventBus = new EventBus(); } [TearDown] public void TearDown() { - (eventBus as IDisposable)?.Dispose(); + (EventBus as IDisposable)?.Dispose(); + _eventBus = null; } - private IEventBus eventBus = null!; - [Test] public void Constructor_RegistersBothEventTypes() { // Arrange & Act - var waitForMultipleEvents = new WaitForMultipleEvents(eventBus); + var waitForMultipleEvents = new WaitForMultipleEvents(EventBus); // Assert Assert.That(waitForMultipleEvents.IsDone, Is.False); @@ -37,11 +41,11 @@ namespace GFramework.Core.Tests.Coroutine public async Task FirstEventWins_WhenBothEventsFired() { // Arrange - var waitForMultipleEvents = new WaitForMultipleEvents(eventBus); + var waitForMultipleEvents = new WaitForMultipleEvents(EventBus); // Act - eventBus.Send(new TestEvent1 { Data = "first_event" }); - eventBus.Send(new TestEvent2 { Data = "second_event" }); + EventBus.Send(new TestEvent1 { Data = "first_event" }); + EventBus.Send(new TestEvent2 { Data = "second_event" }); // Assert Assert.That(waitForMultipleEvents.IsDone, Is.True); @@ -54,10 +58,10 @@ namespace GFramework.Core.Tests.Coroutine public async Task SecondEventWins_WhenOnlySecondEventFired() { // Arrange - var waitForMultipleEvents = new WaitForMultipleEvents(eventBus); + var waitForMultipleEvents = new WaitForMultipleEvents(EventBus); // Act - eventBus.Send(new TestEvent2 { Data = "second_event" }); + EventBus.Send(new TestEvent2 { Data = "second_event" }); // Assert Assert.That(waitForMultipleEvents.IsDone, Is.True); @@ -70,11 +74,11 @@ namespace GFramework.Core.Tests.Coroutine public async Task FirstEventWins_WhenBothEventsFiredInReverseOrder() { // Arrange - var waitForMultipleEvents = new WaitForMultipleEvents(eventBus); + var waitForMultipleEvents = new WaitForMultipleEvents(EventBus); // Act - eventBus.Send(new TestEvent2 { Data = "second_event" }); - eventBus.Send(new TestEvent1 { Data = "first_event" }); + EventBus.Send(new TestEvent2 { Data = "second_event" }); + EventBus.Send(new TestEvent1 { Data = "first_event" }); // Assert Assert.That(waitForMultipleEvents.IsDone, Is.True); @@ -88,10 +92,10 @@ namespace GFramework.Core.Tests.Coroutine public async Task MultipleEvents_AfterCompletion_DoNotOverrideState() { // Arrange - var waitForMultipleEvents = new WaitForMultipleEvents(eventBus); + var waitForMultipleEvents = new WaitForMultipleEvents(EventBus); // Act - Fire first event - eventBus.Send(new TestEvent1 { Data = "first_event" }); + EventBus.Send(new TestEvent1 { Data = "first_event" }); // Verify first event was processed Assert.That(waitForMultipleEvents.IsDone, Is.True); @@ -99,7 +103,7 @@ namespace GFramework.Core.Tests.Coroutine Assert.That(waitForMultipleEvents.FirstEventData?.Data, Is.EqualTo("first_event")); // Fire second event after completion - eventBus.Send(new TestEvent2 { Data = "second_event" }); + EventBus.Send(new TestEvent2 { Data = "second_event" }); // Assert - The state should not change Assert.That(waitForMultipleEvents.IsDone, Is.True); @@ -113,13 +117,13 @@ namespace GFramework.Core.Tests.Coroutine public async Task Disposal_PreventsFurtherEventHandling() { // Arrange - var waitForMultipleEvents = new WaitForMultipleEvents(eventBus); + var waitForMultipleEvents = new WaitForMultipleEvents(EventBus); // Act - Dispose the instance waitForMultipleEvents.Dispose(); // Fire an event after disposal - eventBus.Send(new TestEvent1 { Data = "after_disposal" }); + EventBus.Send(new TestEvent1 { Data = "after_disposal" }); // Assert - Event should not be processed due to disposal // Since we disposed, no event data should be captured diff --git a/GFramework.Core.Tests/Logging/TestLogger.cs b/GFramework.Core.Tests/Logging/TestLogger.cs index 594b6de7..317123e8 100644 --- a/GFramework.Core.Tests/Logging/TestLogger.cs +++ b/GFramework.Core.Tests/Logging/TestLogger.cs @@ -1,3 +1,6 @@ +using System; +using System.Collections.Generic; +using System.Threading; using GFramework.Core.Abstractions.Logging; using GFramework.Core.Logging; @@ -6,9 +9,13 @@ namespace GFramework.Core.Tests.Logging; /// /// 表示供日志相关测试复用的内存日志记录器。 /// +/// +/// 并发写入会通过内部锁串行化; 每次返回快照,避免断言观察到正在被修改的可变集合。 +/// public sealed class TestLogger : AbstractLogger { private readonly List _logs = new(); + private readonly Lock _sync = new(); /// /// 初始化 的新实例。 @@ -20,9 +27,18 @@ public sealed class TestLogger : AbstractLogger } /// - /// 获取按写入顺序保存的日志条目只读视图。 + /// 获取按写入顺序保存的日志条目快照。 /// - public IReadOnlyList Logs => _logs; + public IReadOnlyList Logs + { + get + { + lock (_sync) + { + return _logs.ToArray(); + } + } + } /// /// 将日志信息追加到内存列表,供断言读取。 @@ -32,7 +48,10 @@ public sealed class TestLogger : AbstractLogger /// 相关异常;没有异常时为 。 protected override void Write(LogLevel level, string message, Exception? exception) { - _logs.Add(new LogEntry(level, message, exception)); + lock (_sync) + { + _logs.Add(new LogEntry(level, message, exception)); + } } /// diff --git a/GFramework.Core.Tests/Resource/TestResourceLoader.cs b/GFramework.Core.Tests/Resource/TestResourceLoader.cs index 5dcf6a14..05ef4d13 100644 --- a/GFramework.Core.Tests/Resource/TestResourceLoader.cs +++ b/GFramework.Core.Tests/Resource/TestResourceLoader.cs @@ -1,18 +1,30 @@ +using System; +using System.Collections.Generic; using System.IO; +using System.Threading.Tasks; using GFramework.Core.Abstractions.Resource; namespace GFramework.Core.Tests.Resource; -/// -/// 为 ResourceManager 测试提供可控数据源的资源加载器。 -/// + /// + /// 为 ResourceManager 测试提供可控数据源的资源加载器。 + /// public class TestResourceLoader : IResourceLoader { private readonly Dictionary _resourceData = new(StringComparer.Ordinal); - /// + /// + /// 同步加载指定路径的测试资源。 + /// + /// 资源路径。 + /// 加载得到的测试资源。 + /// + /// 为空字符串。 + /// 指定路径的测试资源不存在。 public TestResource Load(string path) { + ArgumentException.ThrowIfNullOrEmpty(path); + if (_resourceData.TryGetValue(path, out var content)) { return new TestResource { Content = content }; @@ -21,22 +33,40 @@ public class TestResourceLoader : IResourceLoader throw new FileNotFoundException($"Resource not found: {path}"); } - /// - public async Task LoadAsync(string path) + /// + /// 异步加载指定路径的测试资源。 + /// + /// 资源路径。 + /// 加载得到的测试资源任务。 + /// + /// 为空字符串。 + /// 指定路径的测试资源不存在。 + public Task LoadAsync(string path) { - await Task.Delay(10).ConfigureAwait(false); // 模拟异步加载 - return Load(path); + return Task.FromResult(Load(path)); } - /// + /// + /// 卸载已加载的测试资源。 + /// + /// 要标记为已释放的资源。 + /// public void Unload(TestResource resource) { + ArgumentNullException.ThrowIfNull(resource); resource.IsDisposed = true; } - /// + /// + /// 判断当前加载器是否包含指定路径的测试资源。 + /// + /// 资源路径。 + /// 存在对应测试资源时返回 ;否则返回 + /// + /// 为空字符串。 public bool CanLoad(string path) { + ArgumentException.ThrowIfNullOrEmpty(path); return _resourceData.ContainsKey(path); } @@ -45,8 +75,14 @@ public class TestResourceLoader : IResourceLoader /// /// 资源路径。 /// 资源内容。 + /// + /// 。 + /// + /// 为空字符串。 public void AddTestData(string path, string content) { + ArgumentException.ThrowIfNullOrEmpty(path); + ArgumentNullException.ThrowIfNull(content); _resourceData[path] = content; } } diff --git a/GFramework.Core/Architectures/RegistryInitializationHookBase.cs b/GFramework.Core/Architectures/RegistryInitializationHookBase.cs index fe746dbc..49b99c18 100644 --- a/GFramework.Core/Architectures/RegistryInitializationHookBase.cs +++ b/GFramework.Core/Architectures/RegistryInitializationHookBase.cs @@ -1,3 +1,4 @@ +using System; using GFramework.Core.Abstractions.Architectures; using GFramework.Core.Abstractions.Enums; using GFramework.Core.Abstractions.Utility; @@ -33,12 +34,29 @@ public abstract class RegistryInitializationHookBase : IArch /// /// 当前的架构阶段 /// 相关的架构实例 + /// + /// 当目标注册表未被装入当前架构上下文时,该钩子会保持 no-op, + /// 以便同一组配置可以安全复用于不包含该注册表的测试或裁剪场景。 + /// public void OnPhase(ArchitecturePhase phase, IArchitecture architecture) { - if (phase != _targetPhase) return; + ArgumentNullException.ThrowIfNull(architecture); - var registry = architecture.Context.GetUtility(); - if (registry == null) return; + if (phase != _targetPhase) + { + return; + } + + TRegistry registry; + + try + { + registry = architecture.Context.GetUtility(); + } + catch (InvalidOperationException) + { + return; + } foreach (var config in _configs) { @@ -52,4 +70,4 @@ public abstract class RegistryInitializationHookBase : IArch /// 注册表实例 /// 配置项 protected abstract void RegisterConfig(TRegistry registry, TConfig config); -} \ No newline at end of file +} diff --git a/GFramework.Cqrs.Tests/Cqrs/CapturingLoggerFactoryProvider.cs b/GFramework.Cqrs.Tests/Cqrs/CapturingLoggerFactoryProvider.cs index 00f3df65..dfb8dff4 100644 --- a/GFramework.Cqrs.Tests/Cqrs/CapturingLoggerFactoryProvider.cs +++ b/GFramework.Cqrs.Tests/Cqrs/CapturingLoggerFactoryProvider.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Threading; using GFramework.Core.Abstractions.Logging; using GFramework.Core.Logging; using GFramework.Cqrs.Tests.Logging; @@ -11,10 +12,12 @@ namespace GFramework.Cqrs.Tests.Cqrs; /// /// 处理器注册入口会分别为测试运行时、容器和注册器创建日志器。 /// 该提供程序统一保留这些测试日志器,以便断言警告是否经由公开入口真正发出。 +/// 并发创建日志器时会通过内部锁串行化, 每次返回快照,避免调用方观察到可变集合。 /// internal sealed class CapturingLoggerFactoryProvider : ILoggerFactoryProvider { private readonly List _loggers = []; + private readonly Lock _sync = new(); /// /// 使用指定的最小日志级别初始化一个新的捕获型日志工厂提供程序。 @@ -26,9 +29,18 @@ internal sealed class CapturingLoggerFactoryProvider : ILoggerFactoryProvider } /// - /// 获取通过当前提供程序创建的全部测试日志器。 + /// 获取通过当前提供程序创建的全部测试日志器快照。 /// - public IReadOnlyList Loggers => _loggers; + public IReadOnlyList Loggers + { + get + { + lock (_sync) + { + return _loggers.ToArray(); + } + } + } /// /// 获取或设置新建测试日志器的最小日志级别。 @@ -43,7 +55,12 @@ internal sealed class CapturingLoggerFactoryProvider : ILoggerFactoryProvider public ILogger CreateLogger(string name) { var logger = new TestLogger(name, MinLevel); - _loggers.Add(logger); + + lock (_sync) + { + _loggers.Add(logger); + } + return logger; } } diff --git a/GFramework.Cqrs.Tests/Cqrs/DeterministicNotificationHandlerState.cs b/GFramework.Cqrs.Tests/Cqrs/DeterministicNotificationHandlerState.cs index 957eda1d..451b011c 100644 --- a/GFramework.Cqrs.Tests/Cqrs/DeterministicNotificationHandlerState.cs +++ b/GFramework.Cqrs.Tests/Cqrs/DeterministicNotificationHandlerState.cs @@ -10,11 +10,18 @@ internal static class DeterministicNotificationHandlerState /// /// 获取当前测试中的通知处理器执行顺序。 /// + /// + /// 该集合仅供顺序测试断言使用,不提供并发安全保证。 + /// 若多个处理器在并行测试中同时写入,调用方可能观察到竞争条件或未定义顺序。 + /// public static List InvocationOrder { get; } = []; /// /// 重置共享的执行顺序状态。 /// + /// + /// 该方法只支持在单线程测试准备阶段调用;并发调用会与 的直接写入互相竞争。 + /// public static void Reset() { InvocationOrder.Clear(); diff --git a/GFramework.Cqrs.Tests/Cqrs/PartialGeneratedNotificationHandlerRegistry.cs b/GFramework.Cqrs.Tests/Cqrs/PartialGeneratedNotificationHandlerRegistry.cs index 6bbcfb36..8e7d47ea 100644 --- a/GFramework.Cqrs.Tests/Cqrs/PartialGeneratedNotificationHandlerRegistry.cs +++ b/GFramework.Cqrs.Tests/Cqrs/PartialGeneratedNotificationHandlerRegistry.cs @@ -16,6 +16,9 @@ internal sealed class PartialGeneratedNotificationHandlerRegistry : ICqrsHandler /// /// 承载处理器映射的服务集合。 /// 用于记录注册诊断的日志器。 + /// + /// 。 + /// public void Register(IServiceCollection services, ILogger logger) { ArgumentNullException.ThrowIfNull(services); diff --git a/ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md b/ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md index 4c823765..441e2b3e 100644 --- a/ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md +++ b/ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md @@ -6,41 +6,38 @@ ## 当前恢复点 -- 恢复点编号:`ANALYZER-WARNING-REDUCTION-RP-087` -- 当前阶段:`Phase 87` +- 恢复点编号:`ANALYZER-WARNING-REDUCTION-RP-088` +- 当前阶段:`Phase 88` - 当前焦点: - - `2026-04-28` 已按 `$gframework-batch-boot 50` 先执行仓库根 `dotnet clean` + `dotnet build`,建立本轮权威基线 `288 Warning(s)` / `214` 个唯一位点 - - 本轮已并行收敛 `GameContextTests.cs`、`ArchitectureServicesTests.cs`、`RegistryInitializationHookBaseTests.cs`、`CqrsDispatcherCacheTests.cs` 与 `CqrsHandlerRegistrarTests.cs` - - 主线程已补齐 `ResourceManagerTests.cs`、`TestEvent.cs`、`LoggerTests.cs`、`ContextProviderTests.cs`、`TestArchitectureBase.cs`、`CommandCoroutineExtensionsTests.cs` 等 `Core.Tests` 零散 warning - - 当前 `GFramework.Core.Tests` 与 `GFramework.Cqrs.Tests` 的受影响项目 Release 构建都已恢复到 `0 Warning(s)` / `0 Error(s)` - - 当前仓库根权威基线已从本轮开始时的 `288 Warning(s)` / `214` 个唯一位点下降到 `236 Warning(s)` / `162` 个唯一位点;剩余 warning 只集中在 `Mediator/*` 与 `YamlConfigSchemaValidator*` + - `2026-04-28` 已执行 `$gframework-pr-review`,确认 `PR #300` 最新 head 上仍有 `8` 条 CodeRabbit open threads、`1` 个 failed test,以及 `dotnet-format restore failed` 的 CI 噪音 + - 本轮已核对并收敛仍然成立的 review comments:`TestArchitectureContext*` 旧入口显式失败、共享事件总线、`RegisterLifecycleHook` 语义统一、`TestResourceLoader` 契约、`TestLogger` / `CapturingLoggerFactoryProvider` 快照访问、`DeterministicNotificationHandlerState` 并发说明与 `PartialGeneratedNotificationHandlerRegistry` XML 异常文档 + - 已新增 `TestArchitectureContextBehaviorTests.cs`,直接覆盖共享事件总线、旧入口失败契约与接口视角生命周期钩子行为 + - `RegistryInitializationHookBase` 现已在注册表缺失时保持 no-op,修复了 PR 上报的失败测试 `OnPhase_Should_Not_Throw_When_Registry_Not_Found` ## 当前活跃事实 - 当前 `origin/main` 基线提交为 `6cc87a9`(`2026-04-27T20:28:50+08:00`)。 - 当前直接验证结果: - - `dotnet clean` - - 最新结果:成功;已刷新本轮 final non-incremental 仓库根基线 - - `dotnet build` - - 最新结果:成功;`236 Warning(s)`、`0 Error(s)`,唯一位点 `162` - `dotnet build GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release` - 最新结果:成功;`0 Warning(s)`、`0 Error(s)` - `dotnet build GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release` + - 最新结果:成功;`125 Warning(s)`、`0 Error(s)`;warning 仍集中在既有 `Mediator/*` 文件,不在本轮 PR review 修复写集内 + - `dotnet build GFramework.Core/GFramework.Core.csproj -c Release` - 最新结果:成功;`0 Warning(s)`、`0 Error(s)` + - `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --no-build --filter "FullyQualifiedName~RegistryInitializationHookBaseTests|FullyQualifiedName~WaitForMultipleEventsTests|FullyQualifiedName~ResourceManagerTests|FullyQualifiedName~LoggerTests|FullyQualifiedName~TestArchitectureContextBehaviorTests"` + - 最新结果:成功;`97` 通过、`0` 失败 + - `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --no-build --filter "FullyQualifiedName~CqrsHandlerRegistrarTests"` + - 最新结果:成功;`11` 通过、`0` 失败 - 当前批次摘要: - - 本轮接受并集成 `GameContextTests.cs`、`ArchitectureServicesTests.cs`、`RegistryInitializationHookBaseTests.cs`、`CqrsDispatcherCacheTests.cs`、`CqrsHandlerRegistrarTests.cs` 五个并行 worker 切片 - - 主线程补齐 `Core.Tests` 内剩余零散 warning,使 `GFramework.Core.Tests` 项目级 Release 构建回到 `0 Warning(s)` / `0 Error(s)` - - 当前 `origin/main...HEAD` 已提交 branch diff 仍为 `21` 个文件;计入当前待提交工作树后的并集 footprint 为 `45 / 50` 个文件,已接近本轮停止线 -- 当前建议保留到下一波次的候选: - - `GFramework.Cqrs.Tests/Mediator/MediatorArchitectureIntegrationTests.cs`、`MediatorComprehensiveTests.cs`、`MediatorAdvancedFeaturesTests.cs` 的高密度 `MA0048` / `MA0004` - - `GFramework.Game/Config/YamlConfigSchemaValidator.cs` 与 `YamlConfigSchemaValidator.ObjectKeywords.cs` 的高耦合 warning 热点 + - 当前工作树包含 `11` 个已修改文件和 `1` 个新增测试文件,全部来自 `Core` / `Core.Tests` / `Cqrs.Tests` 的 PR review follow-up + - 本轮没有触碰 `Mediator/*` 或 `YamlConfigSchemaValidator*` 的高耦合 warning 波次 ## 当前风险 -- `GFramework.Cqrs.Tests/Mediator/*` 仍有 `94` / `88` / `68` 条输出 warning,属于高 changed-file 风险的 `MA0048` 大波次。 - - 缓解措施:当前 footprint 已到 `45 / 50`,下一轮应在新提交基础上单独规划 `Mediator*` 波次,而不是继续叠在本轮工作树上。 -- `YamlConfigSchemaValidator*` 仍然聚集 `222` 条输出 warning,且同时混有 `MA0048`、`MA0009`、`MA0051`、`MA0006`。 - - 缓解措施:保持为独立高耦合波次,不与测试项目拆分混提。 +- `GFramework.Cqrs.Tests` 当前项目级 Release 构建仍有 `125` 条既有 warning,主要集中在 `MediatorArchitectureIntegrationTests.cs`、`MediatorAdvancedFeaturesTests.cs` 与 `MediatorComprehensiveTests.cs`。 + - 缓解措施:本轮仅记录为现存 blocker,不在 PR #300 的 review follow-up 里扩展到 `Mediator/*` warning reduction 波次。 +- `GFramework.Game/Config/YamlConfigSchemaValidator*` 仍然是仓库根 warning 热点,但与本轮 review 修复无交集。 + - 缓解措施:继续保持为独立高耦合波次。 ## 活跃文档 @@ -60,11 +57,12 @@ ## 验证说明 - 权威验证结果统一维护在“当前活跃事实”。 -- `GFramework.Core.Tests` 与 `GFramework.Cqrs.Tests` 的当前受影响项目 Release 构建都已在本轮清零,但仓库根 non-incremental 构建仍保留 `Mediator/*` 与 `YamlConfigSchemaValidator*` 既有 warning。 +- `GFramework.Core` 与 `GFramework.Core.Tests` 的当前受影响项目 Release 构建都已清零,并通过对应定向测试回归。 +- `GFramework.Cqrs.Tests` 的本轮 helper 改动已由 `CqrsHandlerRegistrarTests` 回归覆盖,但项目级 Release 构建仍暴露 `Mediator/*` 的既有 warning。 - warning reduction 的仓库级真值只以同轮 `dotnet clean` 后的 `dotnet build` 为准。 ## 下一步建议 -1. 提交本轮 `Core.Tests` / `Cqrs.Tests` warning reduction 与 `ai-plan` 同步。 -2. 下一轮在新提交基础上单独规划 `Mediator/*` 波次,避免在 `45 / 50` footprint 状态继续扩批。 -3. 将 `YamlConfigSchemaValidator*` 保持为独立高耦合波次,必要时先由主线程局部切分再决定是否并行。 +1. 提交本轮 `PR #300` review follow-up 与 `ai-plan` 同步。 +2. 若继续处理 `GFramework.Cqrs.Tests` warning,下一轮单独切到 `Mediator/*` 波次,并先接受当前 `125` 条 warning 作为显式基线。 +3. `YamlConfigSchemaValidator*` 继续保持为独立高耦合波次,不与 `Mediator/*` 混提。 diff --git a/ai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md b/ai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md index edbc3a06..334eb204 100644 --- a/ai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md +++ b/ai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md @@ -1,5 +1,37 @@ # Analyzer Warning Reduction 追踪 +## 2026-04-28 — RP-088 + +### 阶段:收敛 PR #300 的 open review threads 与 failed-test follow-up + +- 触发背景: + - 用户执行 `$gframework-pr-review`,要求以当前分支 PR 真值为准核对 AI review / failed-test / linter 信号 + - `fetch_current_pr_review.py --json-output /tmp/current-pr-review.json` 返回 `PR #300`,latest head 上仍有 `8` 条 CodeRabbit open threads、`1` 个失败测试 `RegistryInitializationHookBaseTests.OnPhase_Should_Not_Throw_When_Registry_Not_Found`,以及 `dotnet-format` restore failed 的 CI 噪音 +- 主线程实施: + - 核对 `TestArchitectureContext` / `TestArchitectureContextV3` 后,修复共享事件总线与旧版命令/查询入口的静默成功问题,统一改为显式 `NotSupportedException` 或 faulted task + - 校正 `TestArchitectureWithRegistry` / `TestArchitectureWithoutRegistry` 的显式接口 `RegisterLifecycleHook`,使接口视角与公开 no-op 语义一致 + - 修复 `RegistryInitializationHookBase` 在注册表缺失场景下的 no-op 行为,并保持有注册表路径继续使用单实例 `GetUtility()` + - 收敛 `TestResourceLoader`、`TestLogger`、`CapturingLoggerFactoryProvider`、`DeterministicNotificationHandlerState`、`PartialGeneratedNotificationHandlerRegistry` 的判空、XML 文档、快照访问与并发语义说明 + - 新增 `TestArchitectureContextBehaviorTests.cs`,覆盖共享事件总线、旧入口失败契约与 `RegisterLifecycleHook` 接口行为 +- 验证里程碑: + - `dotnet build GFramework.Core/GFramework.Core.csproj -c Release` + - 结果:成功;`0 Warning(s)`、`0 Error(s)` + - `dotnet build GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release` + - 结果:成功;`0 Warning(s)`、`0 Error(s)` + - `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --no-build --filter "FullyQualifiedName~RegistryInitializationHookBaseTests|FullyQualifiedName~WaitForMultipleEventsTests|FullyQualifiedName~ResourceManagerTests|FullyQualifiedName~LoggerTests|FullyQualifiedName~TestArchitectureContextBehaviorTests"` + - 结果:成功;`97` 通过、`0` 失败 + - `dotnet build GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release` + - 结果:成功;`125 Warning(s)`、`0 Error(s)`;warning 全部来自既有 `Mediator/*` 文件,当前 helper 改动未新增 warning + - `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --no-build --filter "FullyQualifiedName~CqrsHandlerRegistrarTests"` + - 结果:成功;`11` 通过、`0` 失败 +- 当前结论: + - `PR #300` 当前仍然成立的 open review threads 已在本地收敛,且 failed-test 信号已被现有测试回归覆盖 + - `Core` / `Core.Tests` 的本轮受影响项目均保持 `0 Warning(s)`,新增测试也覆盖了此前没有直接回归的测试替身行为 + - `GFramework.Cqrs.Tests` 仍保留 `125` 条既有 `Mediator/*` warning;这属于下一轮 warning reduction 波次,而不是本轮 PR review follow-up 的直接写集 +- 下一步: + 1. 提交本轮 PR review follow-up 与 `ai-plan` 同步。 + 2. 若继续处理 `Cqrs.Tests` warning,以下一轮单独规划 `Mediator/*` 波次为起点。 + ## 2026-04-28 — RP-087 ### 阶段:按 `$gframework-batch-boot 50` 并行收敛 `Core.Tests` / `Cqrs.Tests` 低风险切片 From 1635a03a99a9367d3f4b1767303ef0590b3608a2 Mon Sep 17 00:00:00 2001 From: gewuyou <95328647+GeWuYou@users.noreply.github.com> Date: Tue, 28 Apr 2026 10:11:05 +0800 Subject: [PATCH 07/10] =?UTF-8?q?fix(tests):=20=E6=94=B6=E6=95=9BPR300?= =?UTF-8?q?=E5=AE=A1=E6=9F=A5=E9=81=97=E7=95=99=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修复 TestArchitectureContextV3 的共享事件总线语义并补充对应回归测试 - 优化 CapturingLoggerFactoryProvider 的最小级别同步策略并补充验证 - 更新 RegistryInitializationHookBase 异常文档与 analyzer warning reduction 的恢复文档归档 --- .../TestArchitectureContextBehaviorTests.cs | 15 ++ .../TestArchitectureContextV3.cs | 12 +- .../RegistryInitializationHookBase.cs | 1 + .../Cqrs/CapturingLoggerFactoryProvider.cs | 28 ++- .../Cqrs/CqrsHandlerRegistrarTests.cs | 21 ++ ...r-warning-reduction-history-rp083-rp088.md | 55 +++++ .../analyzer-warning-reduction-tracking.md | 29 +-- .../analyzer-warning-reduction-trace.md | 222 ++---------------- 8 files changed, 159 insertions(+), 224 deletions(-) create mode 100644 ai-plan/public/analyzer-warning-reduction/archive/traces/analyzer-warning-reduction-history-rp083-rp088.md diff --git a/GFramework.Core.Tests/Architectures/TestArchitectureContextBehaviorTests.cs b/GFramework.Core.Tests/Architectures/TestArchitectureContextBehaviorTests.cs index a4532a0f..4c0889a1 100644 --- a/GFramework.Core.Tests/Architectures/TestArchitectureContextBehaviorTests.cs +++ b/GFramework.Core.Tests/Architectures/TestArchitectureContextBehaviorTests.cs @@ -28,6 +28,21 @@ public class TestArchitectureContextBehaviorTests Assert.That(eventReceived, Is.True); } + /// + /// 验证用于 ArchitectureServices 的上下文替身也会把事件注册与发送委托到同一个事件总线实例。 + /// + [Test] + public void RegisterEvent_And_SendEvent_On_TestArchitectureContextV3_Should_Use_Shared_EventBus() + { + var context = new TestArchitectureContextV3(); + var eventReceived = false; + + context.RegisterEvent(_ => eventReceived = true); + context.SendEvent(); + + Assert.That(eventReceived, Is.True); + } + /// /// 验证测试上下文的旧版命令与查询入口会显式抛出未支持异常。 /// diff --git a/GFramework.Core.Tests/Architectures/TestArchitectureContextV3.cs b/GFramework.Core.Tests/Architectures/TestArchitectureContextV3.cs index 7cdd8a57..313864cd 100644 --- a/GFramework.Core.Tests/Architectures/TestArchitectureContextV3.cs +++ b/GFramework.Core.Tests/Architectures/TestArchitectureContextV3.cs @@ -31,6 +31,7 @@ public class TestArchitectureContextV3 : IArchitectureContext { private readonly MicrosoftDiContainer _container = new(); private readonly DefaultEnvironment _environment = new(); + private readonly EventBus _eventBus = new(); /// /// 获取或初始化用于区分测试上下文实例的标识。 @@ -167,6 +168,7 @@ public class TestArchitectureContextV3 : IArchitectureContext /// 事件类型。 public void SendEvent() where TEvent : new() { + _eventBus.Send(); } /// @@ -174,8 +176,11 @@ public class TestArchitectureContextV3 : IArchitectureContext /// /// 事件类型。 /// 事件实例。 + /// public void SendEvent(TEvent e) where TEvent : class { + ArgumentNullException.ThrowIfNull(e); + _eventBus.Send(e); } /// @@ -184,9 +189,11 @@ public class TestArchitectureContextV3 : IArchitectureContext /// 事件类型。 /// 事件处理回调。 /// 用于注销回调的句柄。 + /// public IUnRegister RegisterEvent(Action handler) { - return new DefaultUnRegister(() => { }); + ArgumentNullException.ThrowIfNull(handler); + return _eventBus.Register(handler); } /// @@ -194,8 +201,11 @@ public class TestArchitectureContextV3 : IArchitectureContext /// /// 事件类型。 /// 要取消的事件回调。 + /// public void UnRegisterEvent(Action onEvent) { + ArgumentNullException.ThrowIfNull(onEvent); + _eventBus.UnRegister(onEvent); } /// diff --git a/GFramework.Core/Architectures/RegistryInitializationHookBase.cs b/GFramework.Core/Architectures/RegistryInitializationHookBase.cs index 49b99c18..277dc132 100644 --- a/GFramework.Core/Architectures/RegistryInitializationHookBase.cs +++ b/GFramework.Core/Architectures/RegistryInitializationHookBase.cs @@ -34,6 +34,7 @@ public abstract class RegistryInitializationHookBase : IArch /// /// 当前的架构阶段 /// 相关的架构实例 + /// /// /// 当目标注册表未被装入当前架构上下文时,该钩子会保持 no-op, /// 以便同一组配置可以安全复用于不包含该注册表的测试或裁剪场景。 diff --git a/GFramework.Cqrs.Tests/Cqrs/CapturingLoggerFactoryProvider.cs b/GFramework.Cqrs.Tests/Cqrs/CapturingLoggerFactoryProvider.cs index dfb8dff4..01b825a9 100644 --- a/GFramework.Cqrs.Tests/Cqrs/CapturingLoggerFactoryProvider.cs +++ b/GFramework.Cqrs.Tests/Cqrs/CapturingLoggerFactoryProvider.cs @@ -17,6 +17,7 @@ namespace GFramework.Cqrs.Tests.Cqrs; internal sealed class CapturingLoggerFactoryProvider : ILoggerFactoryProvider { private readonly List _loggers = []; + private LogLevel _minLevel; private readonly Lock _sync = new(); /// @@ -25,7 +26,7 @@ internal sealed class CapturingLoggerFactoryProvider : ILoggerFactoryProvider /// 要应用到新建测试日志器的最小日志级别。 public CapturingLoggerFactoryProvider(LogLevel minLevel = LogLevel.Info) { - MinLevel = minLevel; + _minLevel = minLevel; } /// @@ -45,7 +46,24 @@ internal sealed class CapturingLoggerFactoryProvider : ILoggerFactoryProvider /// /// 获取或设置新建测试日志器的最小日志级别。 /// - public LogLevel MinLevel { get; set; } + public LogLevel MinLevel + { + get + { + lock (_sync) + { + return _minLevel; + } + } + + set + { + lock (_sync) + { + _minLevel = value; + } + } + } /// /// 创建一个测试日志器并将其纳入捕获集合。 @@ -54,13 +72,11 @@ internal sealed class CapturingLoggerFactoryProvider : ILoggerFactoryProvider /// 用于后续断言的测试日志器。 public ILogger CreateLogger(string name) { - var logger = new TestLogger(name, MinLevel); - lock (_sync) { + var logger = new TestLogger(name, _minLevel); _loggers.Add(logger); + return logger; } - - return logger; } } diff --git a/GFramework.Cqrs.Tests/Cqrs/CqrsHandlerRegistrarTests.cs b/GFramework.Cqrs.Tests/Cqrs/CqrsHandlerRegistrarTests.cs index 76a2b922..7e5afc7d 100644 --- a/GFramework.Cqrs.Tests/Cqrs/CqrsHandlerRegistrarTests.cs +++ b/GFramework.Cqrs.Tests/Cqrs/CqrsHandlerRegistrarTests.cs @@ -216,6 +216,27 @@ internal sealed class CqrsHandlerRegistrarTests } } + /// + /// 验证捕获型日志工厂在更新最小日志级别后,会将新值应用到后续创建的日志器。 + /// + [Test] + public void CapturingLoggerFactoryProvider_Should_Apply_Updated_MinLevel_To_Subsequent_Loggers() + { + var provider = new CapturingLoggerFactoryProvider(LogLevel.Warning); + var warningLogger = (TestLogger)provider.CreateLogger("warning"); + + provider.MinLevel = LogLevel.Debug; + + var debugLogger = (TestLogger)provider.CreateLogger("debug"); + + Assert.Multiple(() => + { + Assert.That(warningLogger.IsDebugEnabled(), Is.False); + Assert.That(debugLogger.IsDebugEnabled(), Is.True); + Assert.That(provider.Loggers, Has.Count.EqualTo(2)); + }); + } + /// /// 验证当生成注册器提供精确 fallback 类型名时,运行时会定向补扫剩余 handlers, /// 而不是重新枚举整个程序集的类型列表。 diff --git a/ai-plan/public/analyzer-warning-reduction/archive/traces/analyzer-warning-reduction-history-rp083-rp088.md b/ai-plan/public/analyzer-warning-reduction/archive/traces/analyzer-warning-reduction-history-rp083-rp088.md new file mode 100644 index 00000000..8ea52800 --- /dev/null +++ b/ai-plan/public/analyzer-warning-reduction/archive/traces/analyzer-warning-reduction-history-rp083-rp088.md @@ -0,0 +1,55 @@ +# Analyzer Warning Reduction 历史归档(RP-083 ~ RP-088) + +## 范围说明 + +- 归档区间:`RP-083` 到 `RP-088` +- 归档原因:active trace 已累计多个已完成阶段,不再适合作为默认恢复入口 +- 当前活跃恢复点:返回 `ai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md` + +## RP-088 + +- 阶段:收敛 `PR #300` 的 open review threads 与 failed-test follow-up +- 主结论: + - 核对 `TestArchitectureContext*`、`RegistryInitializationHookBase`、`TestResourceLoader`、`CapturingLoggerFactoryProvider`、`PartialGeneratedNotificationHandlerRegistry` 等 review 位点 + - 新增 `TestArchitectureContextBehaviorTests.cs`,覆盖共享事件总线、旧入口失败契约与 `RegisterLifecycleHook` 接口行为 + - 受影响项目验证通过,`GFramework.Cqrs.Tests` 仍保留既有 `Mediator/*` warning 基线 + +## RP-087 + +- 阶段:按 `$gframework-batch-boot 50` 并行收敛 `Core.Tests` / `Cqrs.Tests` 低风险切片 +- 主结论: + - 建立仓库根 non-incremental warning 基线后,并行消化 `Core.Tests` 与 `Cqrs.Tests` 的低风险 warning + - 仓库根 warning 从 `288` 下降到 `236` + - 剩余热点开始集中到 `Mediator/*` 与 `YamlConfigSchemaValidator*` + +## RP-086 + +- 阶段:收敛 `PR #298` 的 CodeRabbit nitpick follow-up +- 主结论: + - 修复测试辅助类型的可维护性 nitpick + - `GFramework.Core.Tests` 定向验证通过 + - 剩余 warning 仍集中在既有热点文件 + +## RP-085 + +- 阶段:按 `$gframework-batch-boot 100` 并行消化 `GFramework.Core.Tests` 低风险 `MA0048` +- 主结论: + - 四波次并行拆分 `GFramework.Core.Tests` 测试辅助类型 + - 仓库根 warning 从 `353` 下降到 `288` + - active footprint 接近阈值后主动收口 + +## RP-084 + +- 阶段:收敛 `PR #297` 的 CodeRabbit follow-up +- 主结论: + - 校正 `YamlConfigLoader` 取消语义与若干 XML 文档问题 + - 新增定向回归测试覆盖取消异常路径 + - 相关构建与测试全部通过 + +## RP-083 + +- 阶段:修复 `YamlConfigLoader` 单文件 warning,并拆分 `MicrosoftDiContainerTests` 的辅助类型 +- 主结论: + - 从仓库根基线出发完成单文件 warning 修复与两组测试辅助类型拆分 + - 仓库根 warning 从 `397` 下降到 `353` + - 后续工作切入点转向 `ArchitectureContextTests.cs` / `AsyncQueryExecutorTests.cs` diff --git a/ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md b/ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md index 441e2b3e..f6f40fd8 100644 --- a/ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md +++ b/ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md @@ -6,42 +6,43 @@ ## 当前恢复点 -- 恢复点编号:`ANALYZER-WARNING-REDUCTION-RP-088` -- 当前阶段:`Phase 88` +- 恢复点编号:`ANALYZER-WARNING-REDUCTION-RP-089` +- 当前阶段:`Phase 89` - 当前焦点: - - `2026-04-28` 已执行 `$gframework-pr-review`,确认 `PR #300` 最新 head 上仍有 `8` 条 CodeRabbit open threads、`1` 个 failed test,以及 `dotnet-format restore failed` 的 CI 噪音 - - 本轮已核对并收敛仍然成立的 review comments:`TestArchitectureContext*` 旧入口显式失败、共享事件总线、`RegisterLifecycleHook` 语义统一、`TestResourceLoader` 契约、`TestLogger` / `CapturingLoggerFactoryProvider` 快照访问、`DeterministicNotificationHandlerState` 并发说明与 `PartialGeneratedNotificationHandlerRegistry` XML 异常文档 - - 已新增 `TestArchitectureContextBehaviorTests.cs`,直接覆盖共享事件总线、旧入口失败契约与接口视角生命周期钩子行为 - - `RegistryInitializationHookBase` 现已在注册表缺失时保持 no-op,修复了 PR 上报的失败测试 `OnPhase_Should_Not_Throw_When_Registry_Not_Found` + - `2026-04-28` 重新执行 `$gframework-pr-review`,确认 `PR #300` 最新 head 上仍显示 `6` 条 CodeRabbit open threads;其中 `Task.CompletedTask` 强转与 failed-test 信号已是 stale + - 本轮继续补齐仍然成立的差异:`TestArchitectureContextV3` 共享事件总线、`RegistryInitializationHookBase.OnPhase` XML 异常文档、`CapturingLoggerFactoryProvider.MinLevel` 同步,以及 active trace 归档瘦身 + - 已扩展 `TestArchitectureContextBehaviorTests.cs` 覆盖 `TestArchitectureContextV3` 的共享事件总线行为 + - 已新增 `CapturingLoggerFactoryProvider_Should_Apply_Updated_MinLevel_To_Subsequent_Loggers`,验证后续 logger 会读取更新后的最小级别 ## 当前活跃事实 - 当前 `origin/main` 基线提交为 `6cc87a9`(`2026-04-27T20:28:50+08:00`)。 - 当前直接验证结果: + - `dotnet build GFramework.Core/GFramework.Core.csproj -c Release` + - 最新结果:成功;`0 Warning(s)`、`0 Error(s)` - `dotnet build GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release` - 最新结果:成功;`0 Warning(s)`、`0 Error(s)` - `dotnet build GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release` - - 最新结果:成功;`125 Warning(s)`、`0 Error(s)`;warning 仍集中在既有 `Mediator/*` 文件,不在本轮 PR review 修复写集内 - - `dotnet build GFramework.Core/GFramework.Core.csproj -c Release` - - 最新结果:成功;`0 Warning(s)`、`0 Error(s)` - - `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --no-build --filter "FullyQualifiedName~RegistryInitializationHookBaseTests|FullyQualifiedName~WaitForMultipleEventsTests|FullyQualifiedName~ResourceManagerTests|FullyQualifiedName~LoggerTests|FullyQualifiedName~TestArchitectureContextBehaviorTests"` - - 最新结果:成功;`97` 通过、`0` 失败 - - `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --no-build --filter "FullyQualifiedName~CqrsHandlerRegistrarTests"` + - 最新结果:成功;`125 Warning(s)`、`0 Error(s)`;warning 仍集中在既有 `Mediator/*` 文件,不在本轮 PR review follow-up 写集内 + - `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --no-build --filter "FullyQualifiedName~TestArchitectureContextBehaviorTests|FullyQualifiedName~RegistryInitializationHookBaseTests"` - 最新结果:成功;`11` 通过、`0` 失败 + - `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --no-build --filter "FullyQualifiedName~CqrsHandlerRegistrarTests"` + - 最新结果:成功;`12` 通过、`0` 失败 - 当前批次摘要: - - 当前工作树包含 `11` 个已修改文件和 `1` 个新增测试文件,全部来自 `Core` / `Core.Tests` / `Cqrs.Tests` 的 PR review follow-up + - 当前工作树包含 `8` 个已修改文件和 `1` 个新增归档 trace 文件,全部来自 `Core` / `Core.Tests` / `Cqrs.Tests` / `ai-plan` 的 `PR #300` follow-up - 本轮没有触碰 `Mediator/*` 或 `YamlConfigSchemaValidator*` 的高耦合 warning 波次 ## 当前风险 - `GFramework.Cqrs.Tests` 当前项目级 Release 构建仍有 `125` 条既有 warning,主要集中在 `MediatorArchitectureIntegrationTests.cs`、`MediatorAdvancedFeaturesTests.cs` 与 `MediatorComprehensiveTests.cs`。 - - 缓解措施:本轮仅记录为现存 blocker,不在 PR #300 的 review follow-up 里扩展到 `Mediator/*` warning reduction 波次。 + - 缓解措施:本轮仅记录为现存 blocker,不在 `PR #300` 的 review follow-up 里扩展到 `Mediator/*` warning reduction 波次。 - `GFramework.Game/Config/YamlConfigSchemaValidator*` 仍然是仓库根 warning 热点,但与本轮 review 修复无交集。 - 缓解措施:继续保持为独立高耦合波次。 ## 活跃文档 - 当前轮次归档: + - [analyzer-warning-reduction-history-rp083-rp088.md](../archive/traces/analyzer-warning-reduction-history-rp083-rp088.md) - [analyzer-warning-reduction-history-rp074-rp078.md](../archive/todos/analyzer-warning-reduction-history-rp074-rp078.md) - [analyzer-warning-reduction-history-rp042-rp048.md](../archive/todos/analyzer-warning-reduction-history-rp042-rp048.md) - 历史跟踪归档: diff --git a/ai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md b/ai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md index 334eb204..ce044c93 100644 --- a/ai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md +++ b/ai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md @@ -1,229 +1,45 @@ # Analyzer Warning Reduction 追踪 -## 2026-04-28 — RP-088 +## 2026-04-28 — RP-089 -### 阶段:收敛 PR #300 的 open review threads 与 failed-test follow-up +### 阶段:复核 `PR #300` 最新 review 真值并收敛仍然成立的本地差异 - 触发背景: - - 用户执行 `$gframework-pr-review`,要求以当前分支 PR 真值为准核对 AI review / failed-test / linter 信号 - - `fetch_current_pr_review.py --json-output /tmp/current-pr-review.json` 返回 `PR #300`,latest head 上仍有 `8` 条 CodeRabbit open threads、`1` 个失败测试 `RegistryInitializationHookBaseTests.OnPhase_Should_Not_Throw_When_Registry_Not_Found`,以及 `dotnet-format` restore failed 的 CI 噪音 + - 用户再次执行 `$gframework-pr-review` + - `fetch_current_pr_review.py --json-output /tmp/current-pr-review.json` 返回 `PR #300`,latest head 仍显示 `6` 条 CodeRabbit open threads;其中 `Task.CompletedTask` 强转与 failed-test 信号已是 stale,本地仍需处理的是 `TestArchitectureContextV3` 事件语义、`RegistryInitializationHookBase.OnPhase` XML 异常文档、`CapturingLoggerFactoryProvider.MinLevel` 同步,以及 active trace 过长问题 - 主线程实施: - - 核对 `TestArchitectureContext` / `TestArchitectureContextV3` 后,修复共享事件总线与旧版命令/查询入口的静默成功问题,统一改为显式 `NotSupportedException` 或 faulted task - - 校正 `TestArchitectureWithRegistry` / `TestArchitectureWithoutRegistry` 的显式接口 `RegisterLifecycleHook`,使接口视角与公开 no-op 语义一致 - - 修复 `RegistryInitializationHookBase` 在注册表缺失场景下的 no-op 行为,并保持有注册表路径继续使用单实例 `GetUtility()` - - 收敛 `TestResourceLoader`、`TestLogger`、`CapturingLoggerFactoryProvider`、`DeterministicNotificationHandlerState`、`PartialGeneratedNotificationHandlerRegistry` 的判空、XML 文档、快照访问与并发语义说明 - - 新增 `TestArchitectureContextBehaviorTests.cs`,覆盖共享事件总线、旧入口失败契约与 `RegisterLifecycleHook` 接口行为 + - 将 `TestArchitectureContextV3` 的事件发送/注册/注销统一接入共享 `EventBus`,避免静默 no-op + - 为 `RegistryInitializationHookBase.OnPhase` 补充 `ArgumentNullException` 异常契约 + - 让 `CapturingLoggerFactoryProvider.MinLevel` 与 `CreateLogger` 共享同一把锁,并新增回归测试覆盖更新后的最小级别行为 + - 将 active trace 的 `RP-083` ~ `RP-088` 迁移到 [analyzer-warning-reduction-history-rp083-rp088.md](../archive/traces/analyzer-warning-reduction-history-rp083-rp088.md),恢复单一恢复入口 - 验证里程碑: - `dotnet build GFramework.Core/GFramework.Core.csproj -c Release` - 结果:成功;`0 Warning(s)`、`0 Error(s)` - `dotnet build GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release` - 结果:成功;`0 Warning(s)`、`0 Error(s)` - - `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --no-build --filter "FullyQualifiedName~RegistryInitializationHookBaseTests|FullyQualifiedName~WaitForMultipleEventsTests|FullyQualifiedName~ResourceManagerTests|FullyQualifiedName~LoggerTests|FullyQualifiedName~TestArchitectureContextBehaviorTests"` - - 结果:成功;`97` 通过、`0` 失败 - `dotnet build GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release` - - 结果:成功;`125 Warning(s)`、`0 Error(s)`;warning 全部来自既有 `Mediator/*` 文件,当前 helper 改动未新增 warning - - `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --no-build --filter "FullyQualifiedName~CqrsHandlerRegistrarTests"` + - 结果:成功;`125 Warning(s)`、`0 Error(s)`;warning 仍全部集中在既有 `Mediator/*` 文件 + - `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --no-build --filter "FullyQualifiedName~TestArchitectureContextBehaviorTests|FullyQualifiedName~RegistryInitializationHookBaseTests"` - 结果:成功;`11` 通过、`0` 失败 -- 当前结论: - - `PR #300` 当前仍然成立的 open review threads 已在本地收敛,且 failed-test 信号已被现有测试回归覆盖 - - `Core` / `Core.Tests` 的本轮受影响项目均保持 `0 Warning(s)`,新增测试也覆盖了此前没有直接回归的测试替身行为 - - `GFramework.Cqrs.Tests` 仍保留 `125` 条既有 `Mediator/*` warning;这属于下一轮 warning reduction 波次,而不是本轮 PR review follow-up 的直接写集 -- 下一步: - 1. 提交本轮 PR review follow-up 与 `ai-plan` 同步。 - 2. 若继续处理 `Cqrs.Tests` warning,以下一轮单独规划 `Mediator/*` 波次为起点。 - -## 2026-04-28 — RP-087 - -### 阶段:按 `$gframework-batch-boot 50` 并行收敛 `Core.Tests` / `Cqrs.Tests` 低风险切片 - -- 触发背景: - - 用户再次要求先拿仓库根构建 warning,再把可切分的 warning 批次分派给多个 subagent,以降低主线程上下文压力 - - 当前分支与 `origin/main@6cc87a9` 无提交差异,适合从单文件和小型混合 warning 切片重新起步 -- 主线程实施: - - 先执行仓库规则要求的 non-incremental 基线:`dotnet restore GFramework.sln -p:RestoreFallbackFolders=` 以修复当前 WSL 环境里的旧 fallback 资产,再执行仓库根 `dotnet clean` + `dotnet build` - - 基线结果:`288 Warning(s)`、唯一位点 `214` - - 并行下发四个 disjoint worker: - - `GameContextTests.cs` - - `ArchitectureServicesTests.cs` - - `RegistryInitializationHookBaseTests.cs` - - `CqrsDispatcherCacheTests.cs` - - 主线程补齐不与 worker 重叠的零散切片: - - `ResourceManagerTests.cs` - - `TestEvent.cs` - - `EventListenerScopeTests.cs` - - `TestArchitectureBase.cs` - - `ContextProviderTests.cs` - - `LoggerTests.cs` - - `LoggingConfigurationTests.cs` - - `WaitForMultipleEventsTests.cs` - - `CommandCoroutineExtensionsTests.cs` - - 集成 `GameContextTests`、`CqrsDispatcherCacheTests` 与 `CqrsHandlerRegistrarTests` worker 产出的 commit `a7be413` / `9098490` / `98afcbf`,并接受已直接落到共享工作树的 `ArchitectureServicesTests` / `RegistryInitializationHookBaseTests` 切片 - - 重新执行仓库根 `dotnet clean` + `dotnet build` - - final 结果:`236 Warning(s)`、唯一位点 `162` -- 验证里程碑: - - `dotnet build GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release` - - 结果:成功;`0 Warning(s)`、`0 Error(s)` - - `dotnet build GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release` - - 结果:成功;`0 Warning(s)`、`0 Error(s)` - - `dotnet clean` - - 结果:成功 - - `dotnet build` - - 结果:成功;`236 Warning(s)`、`0 Error(s)`,唯一位点 `162` - - `git diff --name-only refs/remotes/origin/main...HEAD | wc -l` - - 结果:`21` - - 已提交 diff 与当前工作树变更并集文件数 - - 结果:`45 / 50` -- 当前结论: - - 本轮已经把剩余 `Core.Tests` warning 全部清空,并把 `CqrsDispatcherCacheTests.cs` 与 `CqrsHandlerRegistrarTests.cs` 这两处 `Cqrs.Tests` 单文件 `MA0048` 热点从仓库根中移除 - - 仓库根权威 warning 从 `288` 下降到 `236`,唯一位点从 `214` 下降到 `162` - - 在 `45 / 50` footprint 下,继续扩批将明显压缩 review 余量;剩余切片也只剩 `Mediator*` 与 `YamlConfigSchemaValidator*` 这两组高风险热点,因此本轮应在这里收口 -- 下一步: - 1. 提交本轮已验证的 warning reduction 与 `ai-plan` 同步。 - 2. 下一轮在新提交基础上单独规划 `Mediator*` 波次。 - 3. 将 `YamlConfigSchemaValidator*` 保持为独立高耦合波次,再决定是否需要新的并行切法。 - -## 2026-04-27 — RP-086 - -### 阶段:收敛 PR #298 的 CodeRabbit nitpick follow-up - -- 触发背景: - - 用户再次执行 `$gframework-pr-review` 后,要求按 `PR #298` 的 nitpick comments 收敛仍然适用的问题 - - 复核 PR 真值后确认当前分支无 failed checks、无 open review threads,但仍有一批测试辅助类型的可维护性 nitpick 值得本地落地 -- 主线程实施: - - 校验 `TestStateMachineSystemV5`、`ComplexQuery`、`TrackingPipelineBehavior`、`TestEnvironment`、`TestContextUtilityV1/V2` 等改动后,分别修复可变内部状态暴露、`_context!` 空抑制、静态计数器非原子递增、`new Register(...)` 测试辅助入口和生命周期标记公开 setter 问题 - - 统一更新 `TestQueryV2`、`TestCommandWithResultV2`、`TestAsyncQueryInput`、`TestAsyncQueryResult*` 的 XML 文档,使 `init` 属性语义与文档一致 - - 将三倍结果属性从 `DoubleValue` 更名为 `TripleValue`,同步更新 `TestAsyncComplexQuery*` 与相关断言,避免名称与 `* 3` 的行为不一致 - - 精简 active tracking,移除重复的 `GFramework.Core.Tests` Release build 记录,并把该项目的当前真值修正为 `28 Warning(s)` -- 验证里程碑: - - `dotnet build GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release` - - 结果:成功;`28 Warning(s)`、`0 Error(s)` - - `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --no-build` - - 结果:成功;`1610` 通过、`0` 失败 -- 当前结论: - - `PR #298` 中仍然适用的低风险 nitpick 已完成收敛,且没有为当前 touched files 引入新的构建 warning 或测试回归 - - `GFramework.Core.Tests` 的剩余 warning 仍集中在 `GameContextTests.cs`、`ArchitectureServicesTests.cs`、`RegistryInitializationHookBaseTests.cs` 等既有热点,不属于本轮 nitpick follow-up 新引入问题 -- 下一步: - 1. 提交本轮 `PR #298` nitpick follow-up 与 `ai-plan` 同步。 - 2. 回到 `GameContextTests.cs` / `ArchitectureServicesTests.cs` 的 `CS8766` warning reduction 主线。 - -## 2026-04-27 — RP-085 - -### 阶段:按 `$gframework-batch-boot 100` 并行消化 `GFramework.Core.Tests` 低风险 `MA0048` - -- 触发背景: - - 用户要求以仓库根 non-incremental 构建 warning 为准,并在上下文可控前提下把小切片分派给多个 subagent 并行处理 - - 本轮开始时,当前分支与 `origin/main@7cfdd2c` 无提交差异,适合从纯 `MA0048` 单文件切片起步 -- 主线程实施: - - 执行权威基线:`dotnet clean` + 仓库根 `dotnet build` - - 初始结果:`353 Warning(s)`、唯一位点 `279` - - 分四波次并行下发 `GFramework.Core.Tests` 小切片,累计完成 `20+` 个文件的测试辅助类型拆分 - - 主线程持续复核共享工作树、处理并发编译阻断,并在每一轮后复跑 `GFramework.Core.Tests` Release 构建 - - 在工作树达到 `61` 个变更条目时主动停止扩批,保留对 `100` 文件停止线的充分余量 -- 代表性已落地切片: - - `ArchitectureContextTests.cs` - - `AsyncQueryExecutorTests.cs` - - `CommandExecutorTests.cs` - - `StateTests.cs` - - `StateMachineTests.cs` - - `StateMachineSystemTests.cs` - - `ArchitectureModulesBehaviorTests.cs` - - `ArchitectureAdditionalCqrsHandlersTests.cs` - - `QueryCoroutineExtensionsTests.cs` - - `ObjectPoolTests.cs` - - `AbstractContextUtilityTests.cs` - - `EnvironmentTests.cs` - - `EventBusTests.cs` - - `ContextAwareTests.cs` -- 验证里程碑: - - `dotnet build GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release` - - 结果:成功;`0 Warning(s)`、`0 Error(s)` - - `dotnet clean` - - 结果:成功 - - `dotnet build` - - 结果:成功;`288 Warning(s)`、`0 Error(s)`,唯一位点 `214` -- 当前结论: - - 本轮主要收益来自 `GFramework.Core.Tests` 内的纯 `MA0048` 大范围收敛 - - 仓库根权威 warning 已从 `353` 降到 `288`,唯一位点从 `279` 降到 `214` - - 下一波不再适合继续盲目平铺纯拆分,因为剩余 `GFramework.Core.Tests` 热点已开始混入 `CS8766` / `MA0016` -- 下一步: - 1. 提交本轮 warning reduction 与 `ai-plan` 同步。 - 2. 下一波优先由主线程处理 `GameContextTests.cs` / `ArchitectureServicesTests.cs` 的混合 warning。 - 3. 保持 `YamlConfigSchemaValidator*` 与 `GFramework.Cqrs.Tests/Mediator/*` 为独立高风险波次。 - -## 2026-04-27 — RP-084 - -### 阶段:收敛 PR #297 的 CodeRabbit follow-up - -- 触发背景: - - 用户执行 `$gframework-pr-review`,要求以当前分支对应 PR 为准,提取并核对 AI review / check 信号 - - `fetch_current_pr_review.py` 返回 PR `#297` 的最新 head review 中仍有 `3` 个 open threads,另有 `2` 个 folded nitpick 仍然适用 -- 主线程实施: - - 校验 `GFramework.Game/Config/YamlConfigLoader.cs` 后,保留 `ReadYamlAsync` 的原始取消语义,并把 `IntegerTryParseDelegate` 第一个参数改为 `string?` - - 校验 `GFramework.Core.Tests/Ioc/*` 与 `Query/TestAsyncQueryWithExceptionV4.cs` 后,补齐缺失 XML 文档,让 `IPrioritizedService` 继承 `IMixedService` 复用 `Name` 契约,并补上 `` 文档 - - 新增 `YamlConfigLoaderTests.ReadYamlAsync_Should_Preserve_OperationCanceledException_When_Cancellation_Is_Requested`,用反射直接命中私有读取路径,稳定回归本次取消语义修复 - - 用 `dotnet format --verify-no-changes --include ...` 清理并验证本次改动文件的格式状态 -- 验证里程碑: - - `dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release --filter "FullyQualifiedName~YamlConfigLoaderTests.ReadYamlAsync_Should_Preserve_OperationCanceledException_When_Cancellation_Is_Requested"` - - 结果:成功;`1` 通过、`0` 失败 - - `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --filter "FullyQualifiedName~MicrosoftDiContainerTests.GetAllByPriority_Should_Sort_By_Priority_Ascending"` - - 结果:成功;`1` 通过、`0` 失败 - - `dotnet build GFramework.Game/GFramework.Game.csproj -c Release` - - 结果:成功;`0 Warning(s)`、`0 Error(s)` - - `dotnet build GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release` - - 结果:成功;`0 Warning(s)`、`0 Error(s)` - - `dotnet format GFramework.sln --verify-no-changes --include GFramework.Game/Config/YamlConfigLoader.cs GFramework.Game.Tests/Config/YamlConfigLoaderTests.cs GFramework.Core.Tests/Ioc/IMixedService.cs GFramework.Core.Tests/Ioc/IPrioritizedService.cs GFramework.Core.Tests/Ioc/PrioritizedService.cs GFramework.Core.Tests/Query/TestAsyncQueryWithExceptionV4.cs` - - 结果:成功 -- 当前结论: - - PR `#297` 当前仍然有效的 CodeRabbit open threads 与 folded nitpick 已在本地全部核对并收敛 - - 当前恢复点完成后,分支可以回到 `ArchitectureContextTests.cs` / `AsyncQueryExecutorTests.cs` / `YamlConfigSchemaValidator*` 的 warning reduction 主线 -- 下一步: - 1. 提交本轮 PR review follow-up。 - 2. 继续执行下一波 `MA0048` 小切片,优先避免一次性进入 `Mediator*` 的高 changed-file 风险波次。 - -## 2026-04-27 — RP-083 - -### 阶段:修复 `YamlConfigLoader` 单文件 warning,并拆分 `MicrosoftDiContainerTests` 的辅助类型 - -- 触发背景: - - 用户执行 `$gframework-batch-boot 50`,要求先拿仓库根构建 warning,再按 bounded slice 分派给不同 subagent 并持续推进 - - 当前分支在本轮开始时与 `origin/main@b6a9fef` 零提交差异,适合从低风险 warning slice 起步 -- 主线程实施: - - 先执行 non-incremental 仓库根基线:`dotnet clean` + `dotnet build`,得到 `397 Warning(s)` / `316` 个唯一位点 - - 主线程修复 `GFramework.Game/Config/YamlConfigLoader.cs` 的 `MA0051`、`MA0002` 与 `MA0158` - - 接受一个 worker batch:将 `GFramework.Core.Tests/Ioc/MicrosoftDiContainerTests.cs` 末尾的 `10` 个测试辅助接口/类拆分到 `Ioc/` 同目录独立文件 - - 接受第二波 worker 的已落地结果:将 `GFramework.Core.Tests/Query/AbstractAsyncQueryTests.cs` 末尾的 `7` 个测试辅助类型拆分到 `Query/` 同目录独立文件 - - 启动 `ArchitectureContextTests.cs` 候选 worker,但在共享工作树落地前主动停止,以避免本轮上下文与 review 面积继续膨胀 -- 验证里程碑: - - `dotnet build GFramework.Game/GFramework.Game.csproj -c Release` - - 结果:成功;`111 Warning(s)`、`0 Error(s)` - - 观察:构建输出未再报告 `GFramework.Game/Config/YamlConfigLoader.cs` - - `dotnet build GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release` - - 结果:成功;`0 Warning(s)`、`0 Error(s)` - - `dotnet clean` - - 结果:成功;刷新最终 non-incremental 仓库根 warning 基线 - - `dotnet build` - - 结果:成功;`353 Warning(s)`、`0 Error(s)`,唯一位点 `279` - - 观察:构建输出未再报告 `GFramework.Game/Config/YamlConfigLoader.cs`、`GFramework.Core.Tests/Ioc/MicrosoftDiContainerTests.cs` 与 `GFramework.Core.Tests/Query/AbstractAsyncQueryTests.cs` -- 当前结论: - - 本轮已完成一个主线程单文件 slice 和两个 worker 拆分 slice;仓库根 non-incremental warning 从 `397` 降到 `353` - - 当前共享工作树 footprint 为 `22` 个 changed files,仍低于 `$gframework-batch-boot 50` 的停止线 - - 下一波更适合继续处理 `7` 个 `MA0048` 的小文件,而不是立即进入 `Mediator*` 或 `YamlConfigSchemaValidator*` 的高耦合热点 + - `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --no-build --filter "FullyQualifiedName~CqrsHandlerRegistrarTests"` + - 结果:成功;`12` 通过、`0` 失败 ## 活跃风险 -- `GFramework.Cqrs.Tests/Mediator/*` 的 `MA0048` 位点密度很高,一次性拆分会迅速推高 changed-file 数。 - - 缓解措施:下一波优先继续拿 `7` warning 级别的小切片。 -- `YamlConfigSchemaValidator*` 仍然聚集多类高耦合 warning。 - - 缓解措施:继续维持为独立波次,不与测试项目拆分混提。 +- `GFramework.Cqrs.Tests/Mediator/*` 仍保留 `125` 条既有 warning。 + - 缓解措施:保持为下一轮独立 warning reduction 波次,不把当前 PR review follow-up 扩展到 `Mediator/*` 写集。 +- GitHub PR 上的 open threads 在本地提交前仍可能显示为未关闭。 + - 缓解措施:以当前工作树和定向验证作为真值,推送后再让 PR 线程重新比对最新 head。 ## 下一步 -1. 完成本轮 `YamlConfigLoader.cs`、`MicrosoftDiContainerTests.cs` 与 `ai-plan` 的提交。 -2. 下一波优先从 `ArchitectureContextTests.cs` 或 `AsyncQueryExecutorTests.cs` 继续拆分纯 `MA0048`。 +1. 提交本轮 `PR #300` follow-up 与 `ai-plan` 同步。 +2. 若继续推进 warning reduction,下一轮单独规划 `Mediator/*` 切片。 ## 历史归档指针 - 最新 trace 归档: + - [analyzer-warning-reduction-history-rp083-rp088.md](../archive/traces/analyzer-warning-reduction-history-rp083-rp088.md) - [analyzer-warning-reduction-history-rp073-rp078.md](../archive/traces/analyzer-warning-reduction-history-rp073-rp078.md) - [analyzer-warning-reduction-history-rp062-rp071.md](../archive/traces/analyzer-warning-reduction-history-rp062-rp071.md) - 历史 todo 归档: From a1bfd8294560d532bc3bb2a1776c587b12dd0d0c Mon Sep 17 00:00:00 2001 From: gewuyou <95328647+GeWuYou@users.noreply.github.com> Date: Tue, 28 Apr 2026 13:00:16 +0800 Subject: [PATCH 08/10] =?UTF-8?q?test(core-tests):=20=E8=A1=A5=E9=BD=90?= =?UTF-8?q?=E4=BA=8B=E4=BB=B6=E4=B8=8A=E4=B8=8B=E6=96=87=E5=9B=9E=E5=BD=92?= =?UTF-8?q?=E8=A6=86=E7=9B=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 补齐 TestArchitectureContext 与 TestArchitectureContextV3 的事件注销与空参数契约回归测试 - 整理 TestResourceLoader 的命名空间缩进以收口局部格式噪音 - 更新 analyzer-warning-reduction 的恢复点与验证记录 --- .../TestArchitectureContextBehaviorTests.cs | 47 +++++++++++++++++++ .../Resource/TestResourceLoader.cs | 6 +-- .../analyzer-warning-reduction-tracking.md | 40 +++++++--------- .../analyzer-warning-reduction-trace.md | 33 ++++++------- 4 files changed, 83 insertions(+), 43 deletions(-) diff --git a/GFramework.Core.Tests/Architectures/TestArchitectureContextBehaviorTests.cs b/GFramework.Core.Tests/Architectures/TestArchitectureContextBehaviorTests.cs index 4c0889a1..d784ab1d 100644 --- a/GFramework.Core.Tests/Architectures/TestArchitectureContextBehaviorTests.cs +++ b/GFramework.Core.Tests/Architectures/TestArchitectureContextBehaviorTests.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Threading.Tasks; using GFramework.Core.Abstractions.Architectures; using GFramework.Core.Abstractions.Command; @@ -13,6 +14,16 @@ namespace GFramework.Core.Tests.Architectures; [TestFixture] public class TestArchitectureContextBehaviorTests { + private static IEnumerable EventContextFactories() + { + yield return new TestCaseData( + new Func(() => new TestArchitectureContext())) + .SetArgDisplayNames(nameof(TestArchitectureContext)); + yield return new TestCaseData( + new Func(() => new TestArchitectureContextV3())) + .SetArgDisplayNames(nameof(TestArchitectureContextV3)); + } + /// /// 验证测试上下文会把事件注册与发送委托到同一个事件总线实例。 /// @@ -43,6 +54,42 @@ public class TestArchitectureContextBehaviorTests Assert.That(eventReceived, Is.True); } + /// + /// 验证两类架构测试替身都会对事件 API 的空参数执行显式校验。 + /// + /// 创建待验证测试上下文的工厂。 + [TestCaseSource(nameof(EventContextFactories))] + public void Event_APIs_Should_Validate_Null_Arguments(Func createContext) + { + var context = createContext(); + + Assert.That(() => context.SendEvent(null!), Throws.TypeOf()); + Assert.That(() => context.RegisterEvent(null!), Throws.TypeOf()); + Assert.That(() => context.UnRegisterEvent(null!), Throws.TypeOf()); + } + + /// + /// 验证两类架构测试替身在注销事件处理器后都不会继续分发同一回调。 + /// + /// 创建待验证测试上下文的工厂。 + [TestCaseSource(nameof(EventContextFactories))] + public void UnRegisterEvent_Should_Stop_Dispatch(Func createContext) + { + var context = createContext(); + var eventReceived = false; + + void Handler(TestEventV2 _) + { + eventReceived = true; + } + + context.RegisterEvent(Handler); + context.UnRegisterEvent(Handler); + context.SendEvent(); + + Assert.That(eventReceived, Is.False); + } + /// /// 验证测试上下文的旧版命令与查询入口会显式抛出未支持异常。 /// diff --git a/GFramework.Core.Tests/Resource/TestResourceLoader.cs b/GFramework.Core.Tests/Resource/TestResourceLoader.cs index 05ef4d13..c31bf941 100644 --- a/GFramework.Core.Tests/Resource/TestResourceLoader.cs +++ b/GFramework.Core.Tests/Resource/TestResourceLoader.cs @@ -6,9 +6,9 @@ using GFramework.Core.Abstractions.Resource; namespace GFramework.Core.Tests.Resource; - /// - /// 为 ResourceManager 测试提供可控数据源的资源加载器。 - /// +/// +/// 为 ResourceManager 测试提供可控数据源的资源加载器。 +/// public class TestResourceLoader : IResourceLoader { private readonly Dictionary _resourceData = new(StringComparer.Ordinal); diff --git a/ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md b/ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md index f6f40fd8..bc0e5d04 100644 --- a/ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md +++ b/ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md @@ -6,36 +6,32 @@ ## 当前恢复点 -- 恢复点编号:`ANALYZER-WARNING-REDUCTION-RP-089` -- 当前阶段:`Phase 89` +- 恢复点编号:`ANALYZER-WARNING-REDUCTION-RP-090` +- 当前阶段:`Phase 90` - 当前焦点: - - `2026-04-28` 重新执行 `$gframework-pr-review`,确认 `PR #300` 最新 head 上仍显示 `6` 条 CodeRabbit open threads;其中 `Task.CompletedTask` 强转与 failed-test 信号已是 stale - - 本轮继续补齐仍然成立的差异:`TestArchitectureContextV3` 共享事件总线、`RegistryInitializationHookBase.OnPhase` XML 异常文档、`CapturingLoggerFactoryProvider.MinLevel` 同步,以及 active trace 归档瘦身 - - 已扩展 `TestArchitectureContextBehaviorTests.cs` 覆盖 `TestArchitectureContextV3` 的共享事件总线行为 - - 已新增 `CapturingLoggerFactoryProvider_Should_Apply_Updated_MinLevel_To_Subsequent_Loggers`,验证后续 logger 会读取更新后的最小级别 + - `2026-04-28` 再次执行 `$gframework-pr-review`,确认 `PR #300` 最新 head 仍显示 `6` 条 CodeRabbit open threads,但本地复核后只有事件 API 回归覆盖仍然成立 + - 已在 `TestArchitectureContextBehaviorTests.cs` 补齐 `UnRegisterEvent` 行为与 `SendEvent` / `RegisterEvent` / `UnRegisterEvent` 的空参数契约测试 + - 已整理 `TestResourceLoader.cs` 的命名空间缩进,收口本轮顺手处理的局部格式噪音 + - `dotnet format --verify-no-changes` 当前仍会暴露 `GFramework.Core.Tests` 项目里跨多个无关文件的既有 `FINALNEWLINE`、`CHARSET`、`WHITESPACE` 基线,本轮不扩展到整项目格式波次 ## 当前活跃事实 - 当前 `origin/main` 基线提交为 `6cc87a9`(`2026-04-27T20:28:50+08:00`)。 - 当前直接验证结果: - - `dotnet build GFramework.Core/GFramework.Core.csproj -c Release` - - 最新结果:成功;`0 Warning(s)`、`0 Error(s)` - `dotnet build GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release` - 最新结果:成功;`0 Warning(s)`、`0 Error(s)` - - `dotnet build GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release` - - 最新结果:成功;`125 Warning(s)`、`0 Error(s)`;warning 仍集中在既有 `Mediator/*` 文件,不在本轮 PR review follow-up 写集内 - - `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --no-build --filter "FullyQualifiedName~TestArchitectureContextBehaviorTests|FullyQualifiedName~RegistryInitializationHookBaseTests"` - - 最新结果:成功;`11` 通过、`0` 失败 - - `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --no-build --filter "FullyQualifiedName~CqrsHandlerRegistrarTests"` - - 最新结果:成功;`12` 通过、`0` 失败 + - `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --no-build --filter "FullyQualifiedName~TestArchitectureContextBehaviorTests"` + - 最新结果:成功;`9` 通过、`0` 失败 + - `dotnet format GFramework.Core.Tests/GFramework.Core.Tests.csproj --verify-no-changes --no-restore` + - 最新结果:失败;暴露 `GFramework.Core.Tests` 项目中跨多个未触碰文件的既有 `FINALNEWLINE`、`CHARSET`、`WHITESPACE` 诊断,本轮新增写集未引入 `git diff --check` 异常 - 当前批次摘要: - - 当前工作树包含 `8` 个已修改文件和 `1` 个新增归档 trace 文件,全部来自 `Core` / `Core.Tests` / `Cqrs.Tests` / `ai-plan` 的 `PR #300` follow-up - - 本轮没有触碰 `Mediator/*` 或 `YamlConfigSchemaValidator*` 的高耦合 warning 波次 + - 当前工作树包含 `4` 个已修改文件,分别位于 `GFramework.Core.Tests` 与 `ai-plan/public/analyzer-warning-reduction` + - 本轮没有触碰 `Mediator/*`、`YamlConfigSchemaValidator*` 或 `GFramework.Core.Tests` 的整项目格式基线波次 ## 当前风险 -- `GFramework.Cqrs.Tests` 当前项目级 Release 构建仍有 `125` 条既有 warning,主要集中在 `MediatorArchitectureIntegrationTests.cs`、`MediatorAdvancedFeaturesTests.cs` 与 `MediatorComprehensiveTests.cs`。 - - 缓解措施:本轮仅记录为现存 blocker,不在 `PR #300` 的 review follow-up 里扩展到 `Mediator/*` warning reduction 波次。 +- `dotnet format GFramework.Core.Tests/GFramework.Core.Tests.csproj --verify-no-changes` 当前会命中项目内大量历史格式诊断。 + - 缓解措施:本轮只记录为现存基线,不把 `PR #300` 的 review follow-up 扩展成整项目格式清理。 - `GFramework.Game/Config/YamlConfigSchemaValidator*` 仍然是仓库根 warning 热点,但与本轮 review 修复无交集。 - 缓解措施:继续保持为独立高耦合波次。 @@ -58,12 +54,12 @@ ## 验证说明 - 权威验证结果统一维护在“当前活跃事实”。 -- `GFramework.Core` 与 `GFramework.Core.Tests` 的当前受影响项目 Release 构建都已清零,并通过对应定向测试回归。 -- `GFramework.Cqrs.Tests` 的本轮 helper 改动已由 `CqrsHandlerRegistrarTests` 回归覆盖,但项目级 Release 构建仍暴露 `Mediator/*` 的既有 warning。 +- `GFramework.Core.Tests` 的当前受影响项目 Release 构建已清零,并通过对应定向测试回归。 +- `git diff --check` 结果为空,说明本轮新增改动没有引入新的尾随空格或冲突标记。 - warning reduction 的仓库级真值只以同轮 `dotnet clean` 后的 `dotnet build` 为准。 ## 下一步建议 1. 提交本轮 `PR #300` review follow-up 与 `ai-plan` 同步。 -2. 若继续处理 `GFramework.Cqrs.Tests` warning,下一轮单独切到 `Mediator/*` 波次,并先接受当前 `125` 条 warning 作为显式基线。 -3. `YamlConfigSchemaValidator*` 继续保持为独立高耦合波次,不与 `Mediator/*` 混提。 +2. 若需要继续收口 PR 线程,可单独评估是否接受 `TestArchitectureContext` / `TestArchitectureContextV3` 的共享 helper nitpick。 +3. 若要清理 `dotnet format` 基线,另开 `GFramework.Core.Tests` 格式治理切片,不与当前 PR review 修复混提。 diff --git a/ai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md b/ai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md index ce044c93..3f293957 100644 --- a/ai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md +++ b/ai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md @@ -1,40 +1,37 @@ # Analyzer Warning Reduction 追踪 -## 2026-04-28 — RP-089 +## 2026-04-28 — RP-090 -### 阶段:复核 `PR #300` 最新 review 真值并收敛仍然成立的本地差异 +### 阶段:复核 `PR #300` 最新 review 真值并补齐事件 API 回归覆盖 - 触发背景: - 用户再次执行 `$gframework-pr-review` - - `fetch_current_pr_review.py --json-output /tmp/current-pr-review.json` 返回 `PR #300`,latest head 仍显示 `6` 条 CodeRabbit open threads;其中 `Task.CompletedTask` 强转与 failed-test 信号已是 stale,本地仍需处理的是 `TestArchitectureContextV3` 事件语义、`RegistryInitializationHookBase.OnPhase` XML 异常文档、`CapturingLoggerFactoryProvider.MinLevel` 同步,以及 active trace 过长问题 + - `fetch_current_pr_review.py --json-output /tmp/gframework-current-pr-review.json` 返回 `PR #300`,latest head 仍显示 `6` 条 CodeRabbit open threads;本地复核后,`Task.CompletedTask` 强转、`RegisterLifecycleHook` 语义、`TestResourceLoader` 文档与 `PartialGeneratedNotificationHandlerRegistry` XML 契约都已在当前 head 上成立,唯一仍未锁住的是事件 API 回归覆盖 - 主线程实施: - - 将 `TestArchitectureContextV3` 的事件发送/注册/注销统一接入共享 `EventBus`,避免静默 no-op - - 为 `RegistryInitializationHookBase.OnPhase` 补充 `ArgumentNullException` 异常契约 - - 让 `CapturingLoggerFactoryProvider.MinLevel` 与 `CreateLogger` 共享同一把锁,并新增回归测试覆盖更新后的最小级别行为 - - 将 active trace 的 `RP-083` ~ `RP-088` 迁移到 [analyzer-warning-reduction-history-rp083-rp088.md](../archive/traces/analyzer-warning-reduction-history-rp083-rp088.md),恢复单一恢复入口 + - 为 `TestArchitectureContext` 与 `TestArchitectureContextV3` 新增共享测试数据源,补齐 `SendEvent` / `RegisterEvent` / `UnRegisterEvent` 的空参数异常契约 + - 新增 `UnRegisterEvent_Should_Stop_Dispatch` 回归测试,防止后续把注销路径退化成 `no-op` + - 整理 `TestResourceLoader.cs` 的命名空间缩进,避免当前修改继续叠加局部格式噪音 - 验证里程碑: - - `dotnet build GFramework.Core/GFramework.Core.csproj -c Release` - - 结果:成功;`0 Warning(s)`、`0 Error(s)` - `dotnet build GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release` - 结果:成功;`0 Warning(s)`、`0 Error(s)` - - `dotnet build GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release` - - 结果:成功;`125 Warning(s)`、`0 Error(s)`;warning 仍全部集中在既有 `Mediator/*` 文件 - - `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --no-build --filter "FullyQualifiedName~TestArchitectureContextBehaviorTests|FullyQualifiedName~RegistryInitializationHookBaseTests"` - - 结果:成功;`11` 通过、`0` 失败 - - `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --no-build --filter "FullyQualifiedName~CqrsHandlerRegistrarTests"` - - 结果:成功;`12` 通过、`0` 失败 + - `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --no-build --filter "FullyQualifiedName~TestArchitectureContextBehaviorTests"` + - 结果:成功;`9` 通过、`0` 失败 + - `dotnet format GFramework.Core.Tests/GFramework.Core.Tests.csproj --verify-no-changes --no-restore` + - 结果:失败;输出落在 `ObjectExtensionsTests.cs`、多处 `FINALNEWLINE` 与若干 `CHARSET` 基线文件,均不属于本轮写集 + - `git diff --check` + - 结果:成功;无新增 whitespace / conflict-marker 问题 ## 活跃风险 -- `GFramework.Cqrs.Tests/Mediator/*` 仍保留 `125` 条既有 warning。 - - 缓解措施:保持为下一轮独立 warning reduction 波次,不把当前 PR review follow-up 扩展到 `Mediator/*` 写集。 - GitHub PR 上的 open threads 在本地提交前仍可能显示为未关闭。 - 缓解措施:以当前工作树和定向验证作为真值,推送后再让 PR 线程重新比对最新 head。 +- `GFramework.Core.Tests` 项目当前存在独立于本轮改动的 `dotnet format` 基线。 + - 缓解措施:保持为后续单独格式治理切片,不在当前 PR review follow-up 中扩写。 ## 下一步 1. 提交本轮 `PR #300` follow-up 与 `ai-plan` 同步。 -2. 若继续推进 warning reduction,下一轮单独规划 `Mediator/*` 切片。 +2. 若继续收口 PR 线程,单独评估是否接受 `TestArchitectureContext` / `TestArchitectureContextV3` 的共享 helper nitpick。 ## 历史归档指针 From f33a1765700d902e57aea3614dbf83579b694bd0 Mon Sep 17 00:00:00 2001 From: gewuyou <95328647+GeWuYou@users.noreply.github.com> Date: Tue, 28 Apr 2026 13:23:40 +0800 Subject: [PATCH 09/10] =?UTF-8?q?refactor(core-tests):=20=E6=8F=90?= =?UTF-8?q?=E5=8F=96=E5=85=B1=E4=BA=AB=E6=9E=B6=E6=9E=84=E4=B8=8A=E4=B8=8B?= =?UTF-8?q?=E6=96=87=E6=B5=8B=E8=AF=95=E5=9F=BA=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 重构 TestArchitectureContext 与 TestArchitectureContextV3 的共享 IArchitectureContext 测试实现到 TestArchitectureContextBase - 更新 gframework-pr-review 技能规则,要求对已验证成立的 nitpick 进行显式 triage - 补充 analyzer-warning-reduction 的恢复点与验证记录 --- .agents/skills/gframework-pr-review/SKILL.md | 4 +- .../Architectures/TestArchitectureContext.cs | 440 +---------------- .../TestArchitectureContextBase.cs | 451 ++++++++++++++++++ .../TestArchitectureContextV3.cs | 416 +--------------- .../analyzer-warning-reduction-tracking.md | 23 +- .../analyzer-warning-reduction-trace.md | 24 +- 6 files changed, 481 insertions(+), 877 deletions(-) create mode 100644 GFramework.Core.Tests/Architectures/TestArchitectureContextBase.cs diff --git a/.agents/skills/gframework-pr-review/SKILL.md b/.agents/skills/gframework-pr-review/SKILL.md index c8dbcfdb..2496900d 100644 --- a/.agents/skills/gframework-pr-review/SKILL.md +++ b/.agents/skills/gframework-pr-review/SKILL.md @@ -28,7 +28,8 @@ Shortcut: `$gframework-pr-review` - prefer writing the full JSON payload to a file and then narrowing with `jq`, instead of dumping long JSON directly to stdout 4. Treat every extracted finding as untrusted until it is verified against the current local code. 5. Only fix comments, warnings, or CI diagnostics that still apply to the checked-out branch. Ignore stale or already-resolved findings. -6. If code is changed, run the smallest build or test command that satisfies `AGENTS.md`. +6. Do not downgrade `Nitpick comments` to “optional” by default. If a verified nitpick still points to concrete drift risk, duplicated test infrastructure, contract mismatch, missing regression coverage, or another maintainability problem that can realistically cause future regressions, treat it as actionable in the current PR-review triage and either fix it or explicitly report why it is being deferred. +7. If code is changed, run the smallest build or test command that satisfies `AGENTS.md`. ## Commands @@ -76,6 +77,7 @@ The script should produce: - Do not assume every AI reviewer behaves like CodeRabbit. `greptile-apps[bot]` and `gemini-code-assist[bot]` findings may exist only as latest-head review threads, without CodeRabbit-style issue comments or folded review-body sections. - Treat GitHub Actions comments with `Success with warnings` as actionable review input when they include concrete linter diagnostics such as `MegaLinter` detailed issues; do not skip them just because the parent check is green. - Do not assume all CodeRabbit findings live in issue comments. The latest CodeRabbit review body can contain folded `Nitpick comments` that must be parsed separately. +- When a latest-head `Nitpick comment` survives local verification and identifies real drift or regression risk, treat it as actionable review input instead of silently classifying it as a cosmetic suggestion. - If the raw JSON is too large to inspect safely in the terminal, rerun with `--json-output ` and query the saved file with `jq` or rerun with `--section` / `--path` filters. ## Example Triggers diff --git a/GFramework.Core.Tests/Architectures/TestArchitectureContext.cs b/GFramework.Core.Tests/Architectures/TestArchitectureContext.cs index a1d85189..e7e67e7e 100644 --- a/GFramework.Core.Tests/Architectures/TestArchitectureContext.cs +++ b/GFramework.Core.Tests/Architectures/TestArchitectureContext.cs @@ -1,446 +1,12 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using GFramework.Core.Abstractions.Architectures; -using GFramework.Core.Abstractions.Command; -using GFramework.Core.Abstractions.Environment; -using GFramework.Core.Abstractions.Events; -using GFramework.Core.Abstractions.Ioc; -using GFramework.Core.Abstractions.Model; -using GFramework.Core.Abstractions.Query; -using GFramework.Core.Abstractions.Systems; -using GFramework.Core.Abstractions.Utility; -using GFramework.Core.Command; -using GFramework.Core.Environment; -using GFramework.Core.Events; -using GFramework.Core.Ioc; -using GFramework.Core.Query; -using GFramework.Cqrs.Abstractions.Cqrs; -using ICommand = GFramework.Core.Abstractions.Command.ICommand; - namespace GFramework.Core.Tests.Architectures; /// /// 为 提供最小可用的架构上下文测试桩。 /// /// -/// 该类型只实现当前测试切片会触达的基础行为,其余 CQRS 入口显式抛出 , -/// 避免测试误把未覆盖能力当成可用实现。 +/// 共享的容器解析、事件总线协作与 legacy CQRS 失败契约由 提供, +/// 当前类型仅作为默认测试上下文命名入口,供现有测试与派生替身继续复用。 /// -public class TestArchitectureContext : IArchitectureContext +public class TestArchitectureContext : TestArchitectureContextBase { - private readonly MicrosoftDiContainer _container = new(); - private readonly EventBus _eventBus = new(); - - /// - /// 获取用于解析测试服务的依赖注入容器。 - /// - public IIocContainer Container => _container; - - /// - /// 获取测试事件总线实例。 - /// - /// - /// 返回同一个缓存事件总线,以便 与 - /// 在同一份订阅状态上协作。 - /// - public IEventBus EventBus => _eventBus; - - /// - /// 获取测试命令执行器实例。 - /// - public ICommandExecutor CommandExecutor => new CommandExecutor(); - - /// - /// 获取测试查询执行器实例。 - /// - public IQueryExecutor QueryExecutor => new QueryExecutor(); - - /// - /// 获取默认测试环境对象。 - /// - public IEnvironment Environment => new DefaultEnvironment(); - - /// - /// 获取指定类型的服务实例。 - /// - /// 服务类型。 - /// 已注册的服务实例。 - /// 未注册服务时抛出。 - public TService GetService() where TService : class - { - return _container.GetRequired(); - } - - /// - /// 获取指定类型的所有服务实例。 - /// - /// 服务类型。 - /// 服务实例列表。 - public IReadOnlyList GetServices() where TService : class - { - return _container.GetAll(); - } - - /// - /// 获取指定类型的模型实例。 - /// - /// 模型类型。 - /// 已注册的模型实例。 - /// 未注册模型时抛出。 - public TModel GetModel() where TModel : class, IModel - { - return _container.GetRequired(); - } - - /// - /// 获取指定类型的所有模型实例。 - /// - /// 模型类型。 - /// 模型实例列表。 - public IReadOnlyList GetModels() where TModel : class, IModel - { - return _container.GetAll(); - } - - /// - /// 获取指定类型的系统实例。 - /// - /// 系统类型。 - /// 已注册的系统实例。 - /// 未注册系统时抛出。 - public TSystem GetSystem() where TSystem : class, ISystem - { - return _container.GetRequired(); - } - - /// - /// 获取指定类型的所有系统实例。 - /// - /// 系统类型。 - /// 系统实例列表。 - public IReadOnlyList GetSystems() where TSystem : class, ISystem - { - return _container.GetAll(); - } - - /// - /// 获取指定类型的工具实例。 - /// - /// 工具类型。 - /// 已注册的工具实例。 - /// 未注册工具时抛出。 - public virtual TUtility GetUtility() where TUtility : class, IUtility - { - return _container.GetRequired(); - } - - /// - /// 获取指定类型的所有工具实例。 - /// - /// 工具类型。 - /// 工具实例列表。 - public IReadOnlyList GetUtilities() where TUtility : class, IUtility - { - return _container.GetAll(); - } - - /// - /// 获取指定类型的所有服务实例,并按优先级排序。 - /// - /// 服务类型。 - /// 按优先级排序后的服务实例列表。 - public IReadOnlyList GetServicesByPriority() where TService : class - { - return _container.GetAllByPriority(); - } - - /// - /// 获取指定类型的所有系统实例,并按优先级排序。 - /// - /// 系统类型。 - /// 按优先级排序后的系统实例列表。 - public IReadOnlyList GetSystemsByPriority() where TSystem : class, ISystem - { - return _container.GetAllByPriority(); - } - - /// - /// 获取指定类型的所有模型实例,并按优先级排序。 - /// - /// 模型类型。 - /// 按优先级排序后的模型实例列表。 - public IReadOnlyList GetModelsByPriority() where TModel : class, IModel - { - return _container.GetAllByPriority(); - } - - /// - /// 获取指定类型的所有工具实例,并按优先级排序。 - /// - /// 工具类型。 - /// 按优先级排序后的工具实例列表。 - public IReadOnlyList GetUtilitiesByPriority() where TUtility : class, IUtility - { - return _container.GetAllByPriority(); - } - - /// - /// 发送无参数事件。 - /// - /// 事件类型。 - public void SendEvent() where TEvent : new() - { - _eventBus.Send(); - } - - /// - /// 发送带参数事件。 - /// - /// 事件类型。 - /// 事件实例。 - public void SendEvent(TEvent e) where TEvent : class - { - ArgumentNullException.ThrowIfNull(e); - _eventBus.Send(e); - } - - /// - /// 注册事件处理器。 - /// - /// 事件类型。 - /// 事件处理委托。 - /// 用于测试的事件注销句柄。 - public IUnRegister RegisterEvent(Action handler) - { - ArgumentNullException.ThrowIfNull(handler); - return _eventBus.Register(handler); - } - - /// - /// 取消注册事件处理器。 - /// - /// 事件类型。 - /// 事件处理委托。 - public void UnRegisterEvent(Action onEvent) - { - ArgumentNullException.ThrowIfNull(onEvent); - _eventBus.UnRegister(onEvent); - } - - /// - /// 测试桩:异步发送统一 CQRS 请求。 - /// - /// 响应类型。 - /// 要发送的请求。 - /// 取消令牌。 - /// 请求响应任务。 - /// 该测试桩不支持此成员。 - public ValueTask SendRequestAsync( - IRequest request, - CancellationToken cancellationToken = default) - { - throw new NotSupportedException(); - } - - /// - /// 测试桩:同步发送统一 CQRS 请求。 - /// - /// 响应类型。 - /// 要发送的请求。 - /// 请求响应。 - /// 该测试桩不支持此成员。 - public TResponse SendRequest(IRequest request) - { - throw new NotSupportedException(); - } - - /// - /// 测试桩:异步发送 CQRS 命令并返回响应。 - /// - /// 命令响应类型。 - /// 要发送的命令。 - /// 取消令牌。 - /// 命令响应任务。 - /// 该测试桩不支持此成员。 - public ValueTask SendCommandAsync( - GFramework.Cqrs.Abstractions.Cqrs.Command.ICommand command, - CancellationToken cancellationToken = default) - { - throw new NotSupportedException(); - } - - /// - /// 测试桩:同步发送 CQRS 命令并返回响应。 - /// - /// 命令响应类型。 - /// 要发送的命令。 - /// 命令响应。 - /// 该测试桩不支持此成员。 - public TResponse SendCommand(GFramework.Cqrs.Abstractions.Cqrs.Command.ICommand command) - { - throw new NotSupportedException(); - } - - /// - /// 测试桩:异步发送 CQRS 查询并返回结果。 - /// - /// 查询结果类型。 - /// 要发送的查询。 - /// 取消令牌。 - /// 查询结果任务。 - /// 该测试桩不支持此成员。 - public ValueTask SendQueryAsync( - GFramework.Cqrs.Abstractions.Cqrs.Query.IQuery query, - CancellationToken cancellationToken = default) - { - throw new NotSupportedException(); - } - - /// - /// 测试桩:同步发送 CQRS 查询并返回结果。 - /// - /// 查询结果类型。 - /// 要发送的查询。 - /// 查询结果。 - /// 该测试桩不支持此成员。 - public TResponse SendQuery(GFramework.Cqrs.Abstractions.Cqrs.Query.IQuery query) - { - throw new NotSupportedException(); - } - - /// - /// 测试桩:异步发布 CQRS 通知。 - /// - /// 通知类型。 - /// 要发布的通知。 - /// 取消令牌。 - /// 通知发布任务。 - /// 该测试桩不支持此成员。 - public ValueTask PublishAsync( - TNotification notification, - CancellationToken cancellationToken = default) where TNotification : INotification - { - throw new NotSupportedException(); - } - - /// - /// 测试桩:创建 CQRS 流式请求响应序列。 - /// - /// 流式响应元素类型。 - /// 流式请求。 - /// 取消令牌。 - /// 异步响应流。 - /// 该测试桩不支持此成员。 - public IAsyncEnumerable CreateStream( - IStreamRequest request, - CancellationToken cancellationToken = default) - { - throw new NotSupportedException(); - } - - /// - /// 测试桩:异步发送无返回值 CQRS 命令。 - /// - /// 命令类型。 - /// 要发送的命令。 - /// 取消令牌。 - /// 命令发送任务。 - /// 该测试桩不支持此成员。 - public ValueTask SendAsync(TCommand command, CancellationToken cancellationToken = default) - where TCommand : IRequest - { - throw new NotSupportedException(); - } - - /// - /// 测试桩:异步发送带返回值的 CQRS 请求。 - /// - /// 响应类型。 - /// 要发送的请求。 - /// 取消令牌。 - /// 请求响应任务。 - /// 该测试桩不支持此成员。 - public ValueTask SendAsync( - IRequest command, - CancellationToken cancellationToken = default) - { - throw new NotSupportedException(); - } - - /// - /// 发送旧版命令。 - /// - /// 命令对象。 - /// 该测试桩不支持旧版命令执行入口。 - public void SendCommand(ICommand command) - { - throw new NotSupportedException(); - } - - /// - /// 发送旧版带返回值命令。 - /// - /// 返回值类型。 - /// 命令对象。 - /// 此方法始终抛出异常,不返回结果。 - /// 该测试桩不支持旧版命令执行入口。 - public TResult SendCommand(ICommand command) - { - throw new NotSupportedException(); - } - - /// - /// 异步发送旧版命令。 - /// - /// 命令对象。 - /// 已失败的任务。 - public Task SendCommandAsync(IAsyncCommand command) - { - return Task.FromException(new NotSupportedException()); - } - - /// - /// 异步发送旧版带返回值命令。 - /// - /// 返回值类型。 - /// 命令对象。 - /// 已失败的任务。 - public Task SendCommandAsync(IAsyncCommand command) - { - return Task.FromException(new NotSupportedException()); - } - - /// - /// 发送旧版查询请求。 - /// - /// 查询结果类型。 - /// 查询对象。 - /// 此方法始终抛出异常,不返回结果。 - /// 该测试桩不支持旧版查询执行入口。 - public TResult SendQuery(IQuery query) - { - throw new NotSupportedException(); - } - - /// - /// 异步发送旧版查询请求。 - /// - /// 查询结果类型。 - /// 异步查询对象。 - /// 已失败的任务。 - public Task SendQueryAsync(IAsyncQuery query) - { - return Task.FromException(new NotSupportedException()); - } - - /// - /// 获取当前环境对象。 - /// - /// 默认测试环境对象。 - public IEnvironment GetEnvironment() - { - return Environment; - } } diff --git a/GFramework.Core.Tests/Architectures/TestArchitectureContextBase.cs b/GFramework.Core.Tests/Architectures/TestArchitectureContextBase.cs new file mode 100644 index 00000000..a0fe74cf --- /dev/null +++ b/GFramework.Core.Tests/Architectures/TestArchitectureContextBase.cs @@ -0,0 +1,451 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using GFramework.Core.Abstractions.Architectures; +using GFramework.Core.Abstractions.Command; +using GFramework.Core.Abstractions.Environment; +using GFramework.Core.Abstractions.Events; +using GFramework.Core.Abstractions.Ioc; +using GFramework.Core.Abstractions.Model; +using GFramework.Core.Abstractions.Query; +using GFramework.Core.Abstractions.Systems; +using GFramework.Core.Abstractions.Utility; +using GFramework.Core.Command; +using GFramework.Core.Environment; +using GFramework.Core.Events; +using GFramework.Core.Ioc; +using GFramework.Core.Query; +using GFramework.Cqrs.Abstractions.Cqrs; +using ICommand = GFramework.Core.Abstractions.Command.ICommand; + +namespace GFramework.Core.Tests.Architectures; + +/// +/// 为架构相关测试替身提供共享的 基础实现。 +/// +/// +/// 该基类统一维护容器解析、共享 语义,以及 legacy / CQRS 入口的显式失败契约, +/// 以避免多个测试上下文因为并行复制实现而在后续演进中发生语义漂移。 +/// +public abstract class TestArchitectureContextBase : IArchitectureContext +{ + private readonly MicrosoftDiContainer _container = new(); + private readonly DefaultEnvironment _environment = new(); + private readonly EventBus _eventBus = new(); + + /// + /// 获取用于解析测试服务的依赖注入容器。 + /// + public IIocContainer Container => _container; + + /// + /// 获取测试事件总线实例。 + /// + /// + /// 返回同一个缓存事件总线,以便 与 + /// 在同一份订阅状态上协作。 + /// + public IEventBus EventBus => _eventBus; + + /// + /// 获取测试命令执行器实例。 + /// + public ICommandExecutor CommandExecutor => new CommandExecutor(); + + /// + /// 获取测试查询执行器实例。 + /// + public IQueryExecutor QueryExecutor => new QueryExecutor(); + + /// + /// 获取默认测试环境对象。 + /// + public IEnvironment Environment => _environment; + + /// + /// 获取指定类型的服务实例。 + /// + /// 服务类型。 + /// 已注册的服务实例。 + /// 未注册服务时抛出。 + public TService GetService() where TService : class + { + return _container.GetRequired(); + } + + /// + /// 获取指定类型的所有服务实例。 + /// + /// 服务类型。 + /// 服务实例列表。 + public IReadOnlyList GetServices() where TService : class + { + return _container.GetAll(); + } + + /// + /// 获取指定类型的模型实例。 + /// + /// 模型类型。 + /// 已注册的模型实例。 + /// 未注册模型时抛出。 + public TModel GetModel() where TModel : class, IModel + { + return _container.GetRequired(); + } + + /// + /// 获取指定类型的所有模型实例。 + /// + /// 模型类型。 + /// 模型实例列表。 + public IReadOnlyList GetModels() where TModel : class, IModel + { + return _container.GetAll(); + } + + /// + /// 获取指定类型的系统实例。 + /// + /// 系统类型。 + /// 已注册的系统实例。 + /// 未注册系统时抛出。 + public TSystem GetSystem() where TSystem : class, ISystem + { + return _container.GetRequired(); + } + + /// + /// 获取指定类型的所有系统实例。 + /// + /// 系统类型。 + /// 系统实例列表。 + public IReadOnlyList GetSystems() where TSystem : class, ISystem + { + return _container.GetAll(); + } + + /// + /// 获取指定类型的工具实例。 + /// + /// 工具类型。 + /// 已注册的工具实例。 + /// 未注册工具时抛出。 + public virtual TUtility GetUtility() where TUtility : class, IUtility + { + return _container.GetRequired(); + } + + /// + /// 获取指定类型的所有工具实例。 + /// + /// 工具类型。 + /// 工具实例列表。 + public IReadOnlyList GetUtilities() where TUtility : class, IUtility + { + return _container.GetAll(); + } + + /// + /// 获取指定类型的所有服务实例,并按优先级排序。 + /// + /// 服务类型。 + /// 按优先级排序后的服务实例列表。 + public IReadOnlyList GetServicesByPriority() where TService : class + { + return _container.GetAllByPriority(); + } + + /// + /// 获取指定类型的所有系统实例,并按优先级排序。 + /// + /// 系统类型。 + /// 按优先级排序后的系统实例列表。 + public IReadOnlyList GetSystemsByPriority() where TSystem : class, ISystem + { + return _container.GetAllByPriority(); + } + + /// + /// 获取指定类型的所有模型实例,并按优先级排序。 + /// + /// 模型类型。 + /// 按优先级排序后的模型实例列表。 + public IReadOnlyList GetModelsByPriority() where TModel : class, IModel + { + return _container.GetAllByPriority(); + } + + /// + /// 获取指定类型的所有工具实例,并按优先级排序。 + /// + /// 工具类型。 + /// 按优先级排序后的工具实例列表。 + public IReadOnlyList GetUtilitiesByPriority() where TUtility : class, IUtility + { + return _container.GetAllByPriority(); + } + + /// + /// 发送无参数事件。 + /// + /// 事件类型。 + public void SendEvent() where TEvent : new() + { + _eventBus.Send(); + } + + /// + /// 发送带参数事件。 + /// + /// 事件类型。 + /// 事件实例。 + /// + public void SendEvent(TEvent e) where TEvent : class + { + ArgumentNullException.ThrowIfNull(e); + _eventBus.Send(e); + } + + /// + /// 注册事件处理器。 + /// + /// 事件类型。 + /// 事件处理委托。 + /// 用于测试的事件注销句柄。 + /// + public IUnRegister RegisterEvent(Action handler) + { + ArgumentNullException.ThrowIfNull(handler); + return _eventBus.Register(handler); + } + + /// + /// 取消注册事件处理器。 + /// + /// 事件类型。 + /// 事件处理委托。 + /// + public void UnRegisterEvent(Action onEvent) + { + ArgumentNullException.ThrowIfNull(onEvent); + _eventBus.UnRegister(onEvent); + } + + /// + /// 测试桩:异步发送统一 CQRS 请求。 + /// + /// 响应类型。 + /// 要发送的请求。 + /// 取消令牌。 + /// 请求响应任务。 + /// 该测试桩不支持此成员。 + public ValueTask SendRequestAsync( + IRequest request, + CancellationToken cancellationToken = default) + { + throw new NotSupportedException(); + } + + /// + /// 测试桩:同步发送统一 CQRS 请求。 + /// + /// 响应类型。 + /// 要发送的请求。 + /// 请求响应。 + /// 该测试桩不支持此成员。 + public TResponse SendRequest(IRequest request) + { + throw new NotSupportedException(); + } + + /// + /// 测试桩:异步发送 CQRS 命令并返回响应。 + /// + /// 命令响应类型。 + /// 要发送的命令。 + /// 取消令牌。 + /// 命令响应任务。 + /// 该测试桩不支持此成员。 + public ValueTask SendCommandAsync( + GFramework.Cqrs.Abstractions.Cqrs.Command.ICommand command, + CancellationToken cancellationToken = default) + { + throw new NotSupportedException(); + } + + /// + /// 测试桩:同步发送 CQRS 命令并返回响应。 + /// + /// 命令响应类型。 + /// 要发送的命令。 + /// 命令响应。 + /// 该测试桩不支持此成员。 + public TResponse SendCommand(GFramework.Cqrs.Abstractions.Cqrs.Command.ICommand command) + { + throw new NotSupportedException(); + } + + /// + /// 测试桩:异步发送 CQRS 查询并返回结果。 + /// + /// 查询结果类型。 + /// 要发送的查询。 + /// 取消令牌。 + /// 查询结果任务。 + /// 该测试桩不支持此成员。 + public ValueTask SendQueryAsync( + GFramework.Cqrs.Abstractions.Cqrs.Query.IQuery query, + CancellationToken cancellationToken = default) + { + throw new NotSupportedException(); + } + + /// + /// 测试桩:同步发送 CQRS 查询并返回结果。 + /// + /// 查询结果类型。 + /// 要发送的查询。 + /// 查询结果。 + /// 该测试桩不支持此成员。 + public TResponse SendQuery(GFramework.Cqrs.Abstractions.Cqrs.Query.IQuery query) + { + throw new NotSupportedException(); + } + + /// + /// 测试桩:异步发布 CQRS 通知。 + /// + /// 通知类型。 + /// 要发布的通知。 + /// 取消令牌。 + /// 通知发布任务。 + /// 该测试桩不支持此成员。 + public ValueTask PublishAsync( + TNotification notification, + CancellationToken cancellationToken = default) + where TNotification : INotification + { + throw new NotSupportedException(); + } + + /// + /// 测试桩:创建 CQRS 流式请求响应序列。 + /// + /// 流式响应元素类型。 + /// 流式请求。 + /// 取消令牌。 + /// 异步响应流。 + /// 该测试桩不支持此成员。 + public IAsyncEnumerable CreateStream( + IStreamRequest request, + CancellationToken cancellationToken = default) + { + throw new NotSupportedException(); + } + + /// + /// 测试桩:异步发送无返回值 CQRS 命令。 + /// + /// 命令类型。 + /// 要发送的命令。 + /// 取消令牌。 + /// 命令发送任务。 + /// 该测试桩不支持此成员。 + public ValueTask SendAsync(TCommand command, CancellationToken cancellationToken = default) + where TCommand : IRequest + { + throw new NotSupportedException(); + } + + /// + /// 测试桩:异步发送带返回值的 CQRS 请求。 + /// + /// 响应类型。 + /// 要发送的请求。 + /// 取消令牌。 + /// 请求响应任务。 + /// 该测试桩不支持此成员。 + public ValueTask SendAsync( + IRequest command, + CancellationToken cancellationToken = default) + { + throw new NotSupportedException(); + } + + /// + /// 发送旧版命令。 + /// + /// 命令对象。 + /// 该测试桩不支持旧版命令执行入口。 + public void SendCommand(ICommand command) + { + throw new NotSupportedException(); + } + + /// + /// 发送旧版带返回值命令。 + /// + /// 返回值类型。 + /// 命令对象。 + /// 此方法始终抛出异常,不返回结果。 + /// 该测试桩不支持旧版命令执行入口。 + public TResult SendCommand(ICommand command) + { + throw new NotSupportedException(); + } + + /// + /// 异步发送旧版命令。 + /// + /// 命令对象。 + /// 已失败的任务。 + public Task SendCommandAsync(IAsyncCommand command) + { + return Task.FromException(new NotSupportedException()); + } + + /// + /// 异步发送旧版带返回值命令。 + /// + /// 返回值类型。 + /// 命令对象。 + /// 已失败的任务。 + public Task SendCommandAsync(IAsyncCommand command) + { + return Task.FromException(new NotSupportedException()); + } + + /// + /// 发送旧版查询请求。 + /// + /// 查询结果类型。 + /// 查询对象。 + /// 此方法始终抛出异常,不返回结果。 + /// 该测试桩不支持旧版查询执行入口。 + public TResult SendQuery(IQuery query) + { + throw new NotSupportedException(); + } + + /// + /// 异步发送旧版查询请求。 + /// + /// 查询结果类型。 + /// 异步查询对象。 + /// 已失败的任务。 + public Task SendQueryAsync(IAsyncQuery query) + { + return Task.FromException(new NotSupportedException()); + } + + /// + /// 获取当前环境对象。 + /// + /// 默认测试环境对象。 + public IEnvironment GetEnvironment() + { + return Environment; + } +} diff --git a/GFramework.Core.Tests/Architectures/TestArchitectureContextV3.cs b/GFramework.Core.Tests/Architectures/TestArchitectureContextV3.cs index 313864cd..b67a6d98 100644 --- a/GFramework.Core.Tests/Architectures/TestArchitectureContextV3.cs +++ b/GFramework.Core.Tests/Architectures/TestArchitectureContextV3.cs @@ -1,426 +1,16 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using GFramework.Core.Abstractions.Architectures; -using GFramework.Core.Abstractions.Command; -using GFramework.Core.Abstractions.Environment; -using GFramework.Core.Abstractions.Events; -using GFramework.Core.Abstractions.Model; -using GFramework.Core.Abstractions.Query; -using GFramework.Core.Abstractions.Systems; -using GFramework.Core.Abstractions.Utility; -using GFramework.Core.Environment; -using GFramework.Core.Events; -using GFramework.Core.Ioc; -using GFramework.Cqrs; -using GFramework.Cqrs.Abstractions.Cqrs; -using ICommand = GFramework.Core.Abstractions.Command.ICommand; - namespace GFramework.Core.Tests.Architectures; /// /// 为 提供最小实现的架构上下文测试桩。 /// /// -/// 该类型仅用于验证 的上下文传递行为,因此仅保留当前测试切片需要的容器、 -/// 环境与接口实现。所有不在本测试范围内的 CQRS 调用均明确抛出 , -/// 以避免误把测试桩当作真实运行时上下文使用。 +/// 共享的容器解析、事件总线协作与 legacy CQRS 失败契约由 提供, +/// 当前类型只补充 需要的上下文实例标识。 /// -public class TestArchitectureContextV3 : IArchitectureContext +public class TestArchitectureContextV3 : TestArchitectureContextBase { - private readonly MicrosoftDiContainer _container = new(); - private readonly DefaultEnvironment _environment = new(); - private readonly EventBus _eventBus = new(); - /// /// 获取或初始化用于区分测试上下文实例的标识。 /// public int Id { get; init; } - - /// - /// 获取指定类型的服务实例。 - /// - /// 服务类型。 - /// 已注册的服务实例。 - /// 未注册或存在多个同类型实例时抛出。 - public TService GetService() where TService : class - { - return _container.GetRequired(); - } - - /// - /// 获取指定类型的所有服务实例。 - /// - /// 服务类型。 - /// 所有已注册的服务实例。 - public IReadOnlyList GetServices() where TService : class - { - return _container.GetAll(); - } - - /// - /// 获取指定类型的模型实例。 - /// - /// 模型类型。 - /// 已注册的模型实例。 - /// 未注册或存在多个同类型实例时抛出。 - public TModel GetModel() where TModel : class, IModel - { - return _container.GetRequired(); - } - - /// - /// 获取指定类型的所有模型实例。 - /// - /// 模型类型。 - /// 所有已注册的模型实例。 - public IReadOnlyList GetModels() where TModel : class, IModel - { - return _container.GetAll(); - } - - /// - /// 获取指定类型的系统实例。 - /// - /// 系统类型。 - /// 已注册的系统实例。 - /// 未注册或存在多个同类型实例时抛出。 - public TSystem GetSystem() where TSystem : class, ISystem - { - return _container.GetRequired(); - } - - /// - /// 获取指定类型的所有系统实例。 - /// - /// 系统类型。 - /// 所有已注册的系统实例。 - public IReadOnlyList GetSystems() where TSystem : class, ISystem - { - return _container.GetAll(); - } - - /// - /// 获取指定类型的工具实例。 - /// - /// 工具类型。 - /// 已注册的工具实例。 - /// 未注册或存在多个同类型实例时抛出。 - public TUtility GetUtility() where TUtility : class, IUtility - { - return _container.GetRequired(); - } - - /// - /// 获取指定类型的所有工具实例。 - /// - /// 工具类型。 - /// 所有已注册的工具实例。 - public IReadOnlyList GetUtilities() where TUtility : class, IUtility - { - return _container.GetAll(); - } - - /// - /// 获取指定类型的所有服务实例,并按优先级排序。 - /// - /// 服务类型。 - /// 按优先级排序后的服务实例。 - public IReadOnlyList GetServicesByPriority() where TService : class - { - return _container.GetAllByPriority(); - } - - /// - /// 获取指定类型的所有系统实例,并按优先级排序。 - /// - /// 系统类型。 - /// 按优先级排序后的系统实例。 - public IReadOnlyList GetSystemsByPriority() where TSystem : class, ISystem - { - return _container.GetAllByPriority(); - } - - /// - /// 获取指定类型的所有模型实例,并按优先级排序。 - /// - /// 模型类型。 - /// 按优先级排序后的模型实例。 - public IReadOnlyList GetModelsByPriority() where TModel : class, IModel - { - return _container.GetAllByPriority(); - } - - /// - /// 获取指定类型的所有工具实例,并按优先级排序。 - /// - /// 工具类型。 - /// 按优先级排序后的工具实例。 - public IReadOnlyList GetUtilitiesByPriority() where TUtility : class, IUtility - { - return _container.GetAllByPriority(); - } - - /// - /// 发送无参数事件。 - /// - /// 事件类型。 - public void SendEvent() where TEvent : new() - { - _eventBus.Send(); - } - - /// - /// 发送带参数事件。 - /// - /// 事件类型。 - /// 事件实例。 - /// - public void SendEvent(TEvent e) where TEvent : class - { - ArgumentNullException.ThrowIfNull(e); - _eventBus.Send(e); - } - - /// - /// 注册事件处理器。 - /// - /// 事件类型。 - /// 事件处理回调。 - /// 用于注销回调的句柄。 - /// - public IUnRegister RegisterEvent(Action handler) - { - ArgumentNullException.ThrowIfNull(handler); - return _eventBus.Register(handler); - } - - /// - /// 取消事件处理器注册。 - /// - /// 事件类型。 - /// 要取消的事件回调。 - /// - public void UnRegisterEvent(Action onEvent) - { - ArgumentNullException.ThrowIfNull(onEvent); - _eventBus.UnRegister(onEvent); - } - - /// - /// 异步发送新版 CQRS 请求。 - /// - /// 响应类型。 - /// 要发送的请求。 - /// 取消令牌。 - /// 请求响应任务。 - /// 该测试桩不支持此成员。 - public ValueTask SendRequestAsync( - IRequest request, - CancellationToken cancellationToken = default) - { - throw new NotSupportedException(); - } - - /// - /// 同步发送新版 CQRS 请求。 - /// - /// 响应类型。 - /// 要发送的请求。 - /// 请求响应。 - /// 该测试桩不支持此成员。 - public TResponse SendRequest(IRequest request) - { - throw new NotSupportedException(); - } - - /// - /// 异步发送新版 CQRS 命令并返回响应。 - /// - /// 命令响应类型。 - /// 要发送的命令。 - /// 取消令牌。 - /// 命令响应任务。 - /// 该测试桩不支持此成员。 - public ValueTask SendCommandAsync( - GFramework.Cqrs.Abstractions.Cqrs.Command.ICommand command, - CancellationToken cancellationToken = default) - { - throw new NotSupportedException(); - } - - /// - /// 同步发送新版 CQRS 命令并返回响应。 - /// - /// 命令响应类型。 - /// 要发送的命令。 - /// 命令响应。 - /// 该测试桩不支持此成员。 - public TResponse SendCommand(GFramework.Cqrs.Abstractions.Cqrs.Command.ICommand command) - { - throw new NotSupportedException(); - } - - /// - /// 异步发送新版 CQRS 查询并返回结果。 - /// - /// 查询结果类型。 - /// 要发送的查询。 - /// 取消令牌。 - /// 查询结果任务。 - /// 该测试桩不支持此成员。 - public ValueTask SendQueryAsync( - GFramework.Cqrs.Abstractions.Cqrs.Query.IQuery query, - CancellationToken cancellationToken = default) - { - throw new NotSupportedException(); - } - - /// - /// 同步发送新版 CQRS 查询并返回结果。 - /// - /// 查询结果类型。 - /// 要发送的查询。 - /// 查询结果。 - /// 该测试桩不支持此成员。 - public TResponse SendQuery(GFramework.Cqrs.Abstractions.Cqrs.Query.IQuery query) - { - throw new NotSupportedException(); - } - - /// - /// 发布通知消息。 - /// - /// 通知类型。 - /// 通知实例。 - /// 取消令牌。 - /// 通知发布任务。 - /// 该测试桩不支持此成员。 - public ValueTask PublishAsync( - TNotification notification, - CancellationToken cancellationToken = default) - where TNotification : INotification - { - throw new NotSupportedException(); - } - - /// - /// 创建流式请求结果。 - /// - /// 流元素类型。 - /// 流式请求。 - /// 取消令牌。 - /// 流式响应序列。 - /// 该测试桩不支持此成员。 - public IAsyncEnumerable CreateStream( - IStreamRequest request, - CancellationToken cancellationToken = default) - { - throw new NotSupportedException(); - } - - /// - /// 异步发送无返回值请求命令。 - /// - /// 命令类型。 - /// 要发送的命令。 - /// 取消令牌。 - /// 命令发送任务。 - /// 该测试桩不支持此成员。 - public ValueTask SendAsync(TCommand command, CancellationToken cancellationToken = default) - where TCommand : IRequest - { - throw new NotSupportedException(); - } - - /// - /// 异步发送带返回值请求。 - /// - /// 响应类型。 - /// 要发送的请求。 - /// 取消令牌。 - /// 请求响应任务。 - /// 该测试桩不支持此成员。 - public ValueTask SendAsync( - IRequest command, - CancellationToken cancellationToken = default) - { - throw new NotSupportedException(); - } - - /// - /// 发送旧版无返回值命令。 - /// - /// 要发送的命令。 - /// 该测试桩不支持旧版命令执行入口。 - public void SendCommand(ICommand command) - { - throw new NotSupportedException(); - } - - /// - /// 发送旧版带返回值命令。 - /// - /// 命令响应类型。 - /// 要发送的命令。 - /// 此方法始终抛出异常,不返回结果。 - /// 该测试桩不支持旧版命令执行入口。 - public TResult SendCommand(GFramework.Core.Abstractions.Command.ICommand command) - { - throw new NotSupportedException(); - } - - /// - /// 异步发送旧版无返回值命令。 - /// - /// 要发送的命令。 - /// 已失败的任务。 - public Task SendCommandAsync(IAsyncCommand command) - { - return Task.FromException(new NotSupportedException()); - } - - /// - /// 异步发送旧版带返回值命令。 - /// - /// 命令响应类型。 - /// 要发送的命令。 - /// 已失败的任务。 - public Task SendCommandAsync(IAsyncCommand command) - { - return Task.FromException(new NotSupportedException()); - } - - /// - /// 发送旧版查询请求。 - /// - /// 查询结果类型。 - /// 要发送的查询。 - /// 此方法始终抛出异常,不返回结果。 - /// 该测试桩不支持旧版查询执行入口。 - public TResult SendQuery(GFramework.Core.Abstractions.Query.IQuery query) - { - throw new NotSupportedException(); - } - - /// - /// 异步发送旧版查询请求。 - /// - /// 查询结果类型。 - /// 要发送的查询。 - /// 已失败的任务。 - public Task SendQueryAsync(IAsyncQuery query) - { - return Task.FromException(new NotSupportedException()); - } - - /// - /// 获取当前测试上下文绑定的环境实例。 - /// - /// 默认测试环境实例。 - public IEnvironment GetEnvironment() - { - return _environment; - } } diff --git a/ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md b/ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md index bc0e5d04..be8ac1bb 100644 --- a/ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md +++ b/ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md @@ -6,13 +6,12 @@ ## 当前恢复点 -- 恢复点编号:`ANALYZER-WARNING-REDUCTION-RP-090` -- 当前阶段:`Phase 90` +- 恢复点编号:`ANALYZER-WARNING-REDUCTION-RP-091` +- 当前阶段:`Phase 91` - 当前焦点: - - `2026-04-28` 再次执行 `$gframework-pr-review`,确认 `PR #300` 最新 head 仍显示 `6` 条 CodeRabbit open threads,但本地复核后只有事件 API 回归覆盖仍然成立 - - 已在 `TestArchitectureContextBehaviorTests.cs` 补齐 `UnRegisterEvent` 行为与 `SendEvent` / `RegisterEvent` / `UnRegisterEvent` 的空参数契约测试 - - 已整理 `TestResourceLoader.cs` 的命名空间缩进,收口本轮顺手处理的局部格式噪音 - - `dotnet format --verify-no-changes` 当前仍会暴露 `GFramework.Core.Tests` 项目里跨多个无关文件的既有 `FINALNEWLINE`、`CHARSET`、`WHITESPACE` 基线,本轮不扩展到整项目格式波次 + - `2026-04-28` 继续收口 `PR #300` 的剩余 nitpick:已把 `TestArchitectureContext` / `TestArchitectureContextV3` 的重复 `IArchitectureContext` 测试实现抽到共享基类 `TestArchitectureContextBase` + - 已将 “本地验证后仍然成立的 nitpick 不得默认降级为可选项” 写入 `.agents/skills/gframework-pr-review/SKILL.md` + - `dotnet format --verify-no-changes` 的 `GFramework.Core.Tests` 既有 `FINALNEWLINE`、`CHARSET`、`WHITESPACE` 基线仍保持独立,不与本轮共享基类重构混提 ## 当前活跃事实 @@ -20,12 +19,10 @@ - 当前直接验证结果: - `dotnet build GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release` - 最新结果:成功;`0 Warning(s)`、`0 Error(s)` - - `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --no-build --filter "FullyQualifiedName~TestArchitectureContextBehaviorTests"` - - 最新结果:成功;`9` 通过、`0` 失败 - - `dotnet format GFramework.Core.Tests/GFramework.Core.Tests.csproj --verify-no-changes --no-restore` - - 最新结果:失败;暴露 `GFramework.Core.Tests` 项目中跨多个未触碰文件的既有 `FINALNEWLINE`、`CHARSET`、`WHITESPACE` 诊断,本轮新增写集未引入 `git diff --check` 异常 + - `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --no-build --filter "FullyQualifiedName~ArchitectureServicesTests|FullyQualifiedName~ContextAwareServiceExtensionsTests|FullyQualifiedName~TestArchitectureContextBehaviorTests|FullyQualifiedName~RegistryInitializationHookBaseTests|FullyQualifiedName~ArchitectureContextTests"` + - 最新结果:成功;`67` 通过、`0` 失败 - 当前批次摘要: - - 当前工作树包含 `4` 个已修改文件,分别位于 `GFramework.Core.Tests` 与 `ai-plan/public/analyzer-warning-reduction` + - 当前工作树包含 `6` 个已修改文件与 `1` 个新增文件,分别位于 `GFramework.Core.Tests`、`.agents/skills/gframework-pr-review/` 与 `ai-plan/public/analyzer-warning-reduction` - 本轮没有触碰 `Mediator/*`、`YamlConfigSchemaValidator*` 或 `GFramework.Core.Tests` 的整项目格式基线波次 ## 当前风险 @@ -60,6 +57,6 @@ ## 下一步建议 -1. 提交本轮 `PR #300` review follow-up 与 `ai-plan` 同步。 -2. 若需要继续收口 PR 线程,可单独评估是否接受 `TestArchitectureContext` / `TestArchitectureContextV3` 的共享 helper nitpick。 +1. 提交本轮 `PR #300` nitpick follow-up、技能规则更新与 `ai-plan` 同步。 +2. 推送后重新执行 `$gframework-pr-review`,确认该 nitpick thread 是否随最新 head 自动收口。 3. 若要清理 `dotnet format` 基线,另开 `GFramework.Core.Tests` 格式治理切片,不与当前 PR review 修复混提。 diff --git a/ai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md b/ai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md index 3f293957..5d2477e9 100644 --- a/ai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md +++ b/ai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md @@ -1,23 +1,21 @@ # Analyzer Warning Reduction 追踪 -## 2026-04-28 — RP-090 +## 2026-04-28 — RP-091 -### 阶段:复核 `PR #300` 最新 review 真值并补齐事件 API 回归覆盖 +### 阶段:收口 `PR #300` 的共享测试基础设施 nitpick,并升级 PR-review triage 规则 - 触发背景: - - 用户再次执行 `$gframework-pr-review` - - `fetch_current_pr_review.py --json-output /tmp/gframework-current-pr-review.json` 返回 `PR #300`,latest head 仍显示 `6` 条 CodeRabbit open threads;本地复核后,`Task.CompletedTask` 强转、`RegisterLifecycleHook` 语义、`TestResourceLoader` 文档与 `PartialGeneratedNotificationHandlerRegistry` XML 契约都已在当前 head 上成立,唯一仍未锁住的是事件 API 回归覆盖 + - 用户追问 `TestArchitectureContext` / `TestArchitectureContextV3` 的共享基础设施 nitpick 是否已经处理完成 + - 同时要求把“本地验证后仍然成立的 nitpick 不能默认降级为可选项”写入 `AGENTS.md` 或 `$gframework-pr-review` - 主线程实施: - - 为 `TestArchitectureContext` 与 `TestArchitectureContextV3` 新增共享测试数据源,补齐 `SendEvent` / `RegisterEvent` / `UnRegisterEvent` 的空参数异常契约 - - 新增 `UnRegisterEvent_Should_Stop_Dispatch` 回归测试,防止后续把注销路径退化成 `no-op` - - 整理 `TestResourceLoader.cs` 的命名空间缩进,避免当前修改继续叠加局部格式噪音 + - 新增 `TestArchitectureContextBase`,把容器解析、共享 `EventBus` 行为,以及 legacy / CQRS 失败契约统一收敛到一处 + - 将 `TestArchitectureContext` 与 `TestArchitectureContextV3` 收窄为薄包装类型,只保留各自的命名入口与 `Id` 差异 + - 更新 `.agents/skills/gframework-pr-review/SKILL.md`,明确要求:latest-head `Nitpick comment` 一旦本地验证仍成立且指向真实漂移/回归风险,就必须作为 actionable review input 处理,而不是默认视作可选 - 验证里程碑: - `dotnet build GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release` - 结果:成功;`0 Warning(s)`、`0 Error(s)` - - `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --no-build --filter "FullyQualifiedName~TestArchitectureContextBehaviorTests"` - - 结果:成功;`9` 通过、`0` 失败 - - `dotnet format GFramework.Core.Tests/GFramework.Core.Tests.csproj --verify-no-changes --no-restore` - - 结果:失败;输出落在 `ObjectExtensionsTests.cs`、多处 `FINALNEWLINE` 与若干 `CHARSET` 基线文件,均不属于本轮写集 + - `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --no-build --filter "FullyQualifiedName~ArchitectureServicesTests|FullyQualifiedName~ContextAwareServiceExtensionsTests|FullyQualifiedName~TestArchitectureContextBehaviorTests|FullyQualifiedName~RegistryInitializationHookBaseTests|FullyQualifiedName~ArchitectureContextTests"` + - 结果:成功;`67` 通过、`0` 失败 - `git diff --check` - 结果:成功;无新增 whitespace / conflict-marker 问题 @@ -30,8 +28,8 @@ ## 下一步 -1. 提交本轮 `PR #300` follow-up 与 `ai-plan` 同步。 -2. 若继续收口 PR 线程,单独评估是否接受 `TestArchitectureContext` / `TestArchitectureContextV3` 的共享 helper nitpick。 +1. 提交本轮共享基类重构、技能规则更新与 `ai-plan` 同步。 +2. 推送后重新执行 `$gframework-pr-review`,确认剩余 PR 线程是否已经下降。 ## 历史归档指针 From 5c4f2df15b3073f334b1dfb49b400fbc7355c645 Mon Sep 17 00:00:00 2001 From: gewuyou <95328647+GeWuYou@users.noreply.github.com> Date: Tue, 28 Apr 2026 15:49:26 +0800 Subject: [PATCH 10/10] =?UTF-8?q?docs(analyzer-warning-reduction):=20?= =?UTF-8?q?=E5=90=8C=E6=AD=A5PR=E5=AE=A1=E6=9F=A5=E6=81=A2=E5=A4=8D?= =?UTF-8?q?=E8=AE=B0=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 更新 tracking 文档中的变更文件计数与验证口径,使其与当前 PR head 和 trace 记录一致。 - 补充 RP-092 trace,记录当前 open threads 复核结论、定向测试结果与后续恢复步骤。 --- .../analyzer-warning-reduction-tracking.md | 18 ++++++++------- .../analyzer-warning-reduction-trace.md | 23 +++++++++++++++++-- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md b/ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md index be8ac1bb..9eef9b7e 100644 --- a/ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md +++ b/ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md @@ -6,12 +6,12 @@ ## 当前恢复点 -- 恢复点编号:`ANALYZER-WARNING-REDUCTION-RP-091` -- 当前阶段:`Phase 91` +- 恢复点编号:`ANALYZER-WARNING-REDUCTION-RP-092` +- 当前阶段:`Phase 92` - 当前焦点: - - `2026-04-28` 继续收口 `PR #300` 的剩余 nitpick:已把 `TestArchitectureContext` / `TestArchitectureContextV3` 的重复 `IArchitectureContext` 测试实现抽到共享基类 `TestArchitectureContextBase` - - 已将 “本地验证后仍然成立的 nitpick 不得默认降级为可选项” 写入 `.agents/skills/gframework-pr-review/SKILL.md` - - `dotnet format --verify-no-changes` 的 `GFramework.Core.Tests` 既有 `FINALNEWLINE`、`CHARSET`、`WHITESPACE` 基线仍保持独立,不与本轮共享基类重构混提 + - `2026-04-28` 复核 `PR #300` 最新 open threads:代码类线程已与当前工作树对齐,仅剩 `ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md` 的文件计数与验证口径漂移仍然成立 + - 已将 tracking 文档修正为与 `6cc87a9...HEAD` 的实际变更规模一致,并与 trace 中记录的 `dotnet build`、定向 `dotnet test`、`git diff --check` 验证口径保持一致 + - `dotnet format --verify-no-changes` 的 `GFramework.Core.Tests` 既有 `FINALNEWLINE`、`CHARSET`、`WHITESPACE` 基线仍保持独立,不与当前 `ai-plan` 同步修复混提 ## 当前活跃事实 @@ -22,11 +22,13 @@ - `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --no-build --filter "FullyQualifiedName~ArchitectureServicesTests|FullyQualifiedName~ContextAwareServiceExtensionsTests|FullyQualifiedName~TestArchitectureContextBehaviorTests|FullyQualifiedName~RegistryInitializationHookBaseTests|FullyQualifiedName~ArchitectureContextTests"` - 最新结果:成功;`67` 通过、`0` 失败 - 当前批次摘要: - - 当前工作树包含 `6` 个已修改文件与 `1` 个新增文件,分别位于 `GFramework.Core.Tests`、`.agents/skills/gframework-pr-review/` 与 `ai-plan/public/analyzer-warning-reduction` + - 当前分支相对 `6cc87a9...HEAD` 包含 `18` 个已修改文件与 `38` 个新增文件(合计 `56` 个变更文件),分别位于 `GFramework.Core.Tests`、`GFramework.Cqrs.Tests`、`GFramework.Core`、`.agents/skills/gframework-pr-review/` 与 `ai-plan/public/analyzer-warning-reduction` - 本轮没有触碰 `Mediator/*`、`YamlConfigSchemaValidator*` 或 `GFramework.Core.Tests` 的整项目格式基线波次 ## 当前风险 +- GitHub PR 上的 open threads 可能仍显示为未关闭,因为当前只同步了 `ai-plan` 文档,尚未推送新的 head 供审查机器人重新折叠线程。 + - 缓解措施:推送本次 `ai-plan` 同步提交后重新执行 `$gframework-pr-review`,以最新 head 再核对 thread 状态。 - `dotnet format GFramework.Core.Tests/GFramework.Core.Tests.csproj --verify-no-changes` 当前会命中项目内大量历史格式诊断。 - 缓解措施:本轮只记录为现存基线,不把 `PR #300` 的 review follow-up 扩展成整项目格式清理。 - `GFramework.Game/Config/YamlConfigSchemaValidator*` 仍然是仓库根 warning 热点,但与本轮 review 修复无交集。 @@ -53,10 +55,10 @@ - 权威验证结果统一维护在“当前活跃事实”。 - `GFramework.Core.Tests` 的当前受影响项目 Release 构建已清零,并通过对应定向测试回归。 - `git diff --check` 结果为空,说明本轮新增改动没有引入新的尾随空格或冲突标记。 -- warning reduction 的仓库级真值只以同轮 `dotnet clean` 后的 `dotnet build` 为准。 +- warning reduction 的仓库级真值以同轮 `dotnet build`、定向 `dotnet test` 与 `git diff --check` 为准,并与 trace 中的验证里程碑保持一致。 ## 下一步建议 1. 提交本轮 `PR #300` nitpick follow-up、技能规则更新与 `ai-plan` 同步。 -2. 推送后重新执行 `$gframework-pr-review`,确认该 nitpick thread 是否随最新 head 自动收口。 +2. 推送后重新执行 `$gframework-pr-review`,确认 `ai-plan` 相关 thread 是否随最新 head 自动收口。 3. 若要清理 `dotnet format` 基线,另开 `GFramework.Core.Tests` 格式治理切片,不与当前 PR review 修复混提。 diff --git a/ai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md b/ai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md index 5d2477e9..e41efd96 100644 --- a/ai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md +++ b/ai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md @@ -1,5 +1,24 @@ # Analyzer Warning Reduction 追踪 +## 2026-04-28 — RP-092 + +### 阶段:复核 `PR #300` 的 open threads,并只修正当前分支仍然成立的 `ai-plan` 漂移 + +- 触发背景: + - 用户要求恢复当前 `$gframework-pr-review` 任务,继续以 PR head 上的开放线程为准做 triage +- 主线程实施: + - 重新读取 `fetch_current_pr_review.py --json-output /tmp/current-pr-review.json` 的 latest head open threads + - 逐条对照本地文件后确认:`TestArchitectureContextBehaviorTests`、`TestArchitectureWithRegistry`、`TestResourceLoader`、`PartialGeneratedNotificationHandlerRegistry` 相关 CodeRabbit 线程在当前工作树上都已匹配修复,仅线程状态尚未随新 head 折叠 + - 继续核对 `RegistryInitializationHookBaseTests.OnPhase_Should_Not_Throw_When_Registry_Not_Found`,确认当前实现 `RegistryInitializationHookBase.OnPhase` 已在缺少注册表时保持 no-op,定向回归测试通过 + - 修正 `analyzer-warning-reduction-tracking.md` 中仍然成立的两处漂移: + - 将文件计数更新为相对 `6cc87a9...HEAD` 的实际规模:`18` 个已修改文件、`38` 个新增文件、合计 `56` 个变更文件 + - 将验证口径统一为 trace 已记录的 `dotnet build`、定向 `dotnet test`、`git diff --check` +- 验证里程碑: + - `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --filter "FullyQualifiedName~RegistryInitializationHookBaseTests.OnPhase_Should_Not_Throw_When_Registry_Not_Found|FullyQualifiedName~TestArchitectureContextBehaviorTests"` + - 结果:成功;`10` 通过、`0` 失败 + - `git diff --check` + - 结果:成功;无新增 whitespace / conflict-marker 问题 + ## 2026-04-28 — RP-091 ### 阶段:收口 `PR #300` 的共享测试基础设施 nitpick,并升级 PR-review triage 规则 @@ -28,8 +47,8 @@ ## 下一步 -1. 提交本轮共享基类重构、技能规则更新与 `ai-plan` 同步。 -2. 推送后重新执行 `$gframework-pr-review`,确认剩余 PR 线程是否已经下降。 +1. 提交本轮 `ai-plan` 同步修复,使 PR head 能重新折叠文档相关线程。 +2. 推送后重新执行 `$gframework-pr-review`,确认剩余 open threads 是否已经下降。 ## 历史归档指针