From 0ac53a4ceec23a80ffd8b8a6b9a1841f6e9a41bf Mon Sep 17 00:00:00 2001
From: gewuyou <95328647+GeWuYou@users.noreply.github.com>
Date: Mon, 4 May 2026 18:49:26 +0800
Subject: [PATCH] =?UTF-8?q?test(cqrs):=20=E8=A1=A5=E9=BD=90=20request=20in?=
=?UTF-8?q?voker=20=E5=90=88=E5=90=8C=E5=9B=9E=E5=BD=92?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 新增 request invoker descriptor 缺失时的 generator 回归覆盖
- 新增 request invoker descriptor entry 缺失时的 generator 回归覆盖
- 更新 CQRS 恢复文档与本轮验证记录
---
...HandlerRegistryGenerator.SourceEmission.cs | 8 +--
.../Cqrs/CqrsHandlerRegistryGeneratorTests.cs | 52 +++++++++++++++++++
.../todos/cqrs-rewrite-migration-tracking.md | 15 ++++--
.../traces/cqrs-rewrite-migration-trace.md | 32 ++++++++++++
4 files changed, 99 insertions(+), 8 deletions(-)
diff --git a/GFramework.Cqrs.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.SourceEmission.cs b/GFramework.Cqrs.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.SourceEmission.cs
index 80926f1b..0bc762fb 100644
--- a/GFramework.Cqrs.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.SourceEmission.cs
+++ b/GFramework.Cqrs.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.SourceEmission.cs
@@ -79,8 +79,8 @@ public sealed partial class CqrsHandlerRegistryGenerator
/// 从可直接表达 handler 接口的注册描述中提取 request invoker 发射计划。
///
///
- /// 指示当前 runtime 是否同时暴露 ICqrsRequestInvokerProvider 与
- /// IEnumeratesCqrsRequestInvokerDescriptors 契约;若不支持,则本方法必须返回空结果并让后续发射路径整体跳过。
+ /// 指示当前 runtime 是否完整暴露 request invoker provider、descriptor 与 descriptor entry 契约;
+ /// 若不支持,则本方法必须返回空结果并让后续发射路径整体跳过。
///
/// 已按稳定顺序整理完成的 handler 注册描述。
///
@@ -136,8 +136,8 @@ public sealed partial class CqrsHandlerRegistryGenerator
/// 从可直接表达 handler 接口的注册描述中提取 stream invoker 发射计划。
///
///
- /// 指示当前 runtime 是否同时暴露 ICqrsStreamInvokerProvider 与
- /// IEnumeratesCqrsStreamInvokerDescriptors 契约;若不支持,则本方法必须返回空结果并让后续发射路径整体跳过。
+ /// 指示当前 runtime 是否完整暴露 stream invoker provider、descriptor 与 descriptor entry 契约;
+ /// 若不支持,则本方法必须返回空结果并让后续发射路径整体跳过。
///
/// 已按稳定顺序整理完成的 handler 注册描述。
///
diff --git a/GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs b/GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs
index 77185834..ce391657 100644
--- a/GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs
+++ b/GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs
@@ -2998,6 +2998,58 @@ public class CqrsHandlerRegistryGeneratorTests
});
}
+ ///
+ /// 验证当 runtime 缺少 CqrsRequestInvokerDescriptor 时,
+ /// 生成器不会继续发射依赖描述符类型的 request provider 元数据。
+ ///
+ [Test]
+ public void Does_Not_Emit_Request_Invoker_Provider_Metadata_When_Runtime_Lacks_Request_Descriptor_Type()
+ {
+ var source = RenameTypeIdentifier(
+ RequestInvokerProviderSource,
+ "CqrsRequestInvokerDescriptor",
+ "MissingCqrsRequestInvokerDescriptor");
+ var generatedSource = RunGenerator(source);
+
+ Assert.Multiple(() =>
+ {
+ Assert.That(
+ generatedSource,
+ Does.Contain(
+ "internal sealed class __GFrameworkGeneratedCqrsHandlerRegistry : global::GFramework.Cqrs.ICqrsHandlerRegistry"));
+ Assert.That(generatedSource, Does.Not.Contain("ICqrsRequestInvokerProvider"));
+ Assert.That(generatedSource, Does.Not.Contain("IEnumeratesCqrsRequestInvokerDescriptors"));
+ Assert.That(generatedSource, Does.Not.Contain("CqrsRequestInvokerDescriptorEntry("));
+ Assert.That(generatedSource, Does.Not.Contain("InvokeRequestHandler0"));
+ });
+ }
+
+ ///
+ /// 验证当 runtime 缺少 CqrsRequestInvokerDescriptorEntry 时,
+ /// 生成器不会继续保留 request provider 的枚举接口或静态 invoker 元数据。
+ ///
+ [Test]
+ public void Does_Not_Emit_Request_Invoker_Provider_Metadata_When_Runtime_Lacks_Request_Descriptor_Entry_Type()
+ {
+ var source = RenameTypeIdentifier(
+ RequestInvokerProviderSource,
+ "CqrsRequestInvokerDescriptorEntry",
+ "MissingCqrsRequestInvokerDescriptorEntry");
+ var generatedSource = RunGenerator(source);
+
+ Assert.Multiple(() =>
+ {
+ Assert.That(
+ generatedSource,
+ Does.Contain(
+ "internal sealed class __GFrameworkGeneratedCqrsHandlerRegistry : global::GFramework.Cqrs.ICqrsHandlerRegistry"));
+ Assert.That(generatedSource, Does.Not.Contain("ICqrsRequestInvokerProvider"));
+ Assert.That(generatedSource, Does.Not.Contain("IEnumeratesCqrsRequestInvokerDescriptors"));
+ Assert.That(generatedSource, Does.Not.Contain("CqrsRequestInvokerDescriptorEntry("));
+ Assert.That(generatedSource, Does.Not.Contain("InvokeRequestHandler0"));
+ });
+ }
+
///
/// 验证当 request handler 仍需走 precise reflected 注册时,
/// 生成器即使检测到 request invoker provider runtime 合同,也不会错误发射无法稳定表达隐藏请求/响应类型的 provider 元数据。
diff --git a/ai-plan/public/cqrs-rewrite/todos/cqrs-rewrite-migration-tracking.md b/ai-plan/public/cqrs-rewrite/todos/cqrs-rewrite-migration-tracking.md
index fff5461f..1ceb403a 100644
--- a/ai-plan/public/cqrs-rewrite/todos/cqrs-rewrite-migration-tracking.md
+++ b/ai-plan/public/cqrs-rewrite/todos/cqrs-rewrite-migration-tracking.md
@@ -7,13 +7,13 @@ CQRS 迁移与收敛。
## 当前恢复点
-- 恢复点编号:`CQRS-REWRITE-RP-076`
+- 恢复点编号:`CQRS-REWRITE-RP-077`
- 当前阶段:`Phase 8`
- 当前 PR 锚点:`PR #307`
- 当前结论:
- `GFramework.Cqrs` 已完成对外部 `Mediator` 的生产级替代,当前主线已从“是否可替代”转向“仓库内部收口与能力深化顺序”
- - `dispatch/invoker` 生成前移已扩展到 request / stream 路径,当前 `RP-076` 已补齐 stream invoker provider gate 的四项 runtime 合同分支
- - `ai-plan` active 入口现以 `PR #307` 和 `RP-076` 为唯一权威恢复锚点;更早 PR 与阶段细节均以下方归档为准
+ - `dispatch/invoker` 生成前移已扩展到 request / stream 路径,当前 `RP-077` 已补齐 request invoker provider gate 与 stream gate 对称的 descriptor / descriptor entry runtime 合同回归
+ - `ai-plan` active 入口现以 `PR #307` 和 `RP-077` 为唯一权威恢复锚点;更早 PR 与阶段细节均以下方归档为准
## 当前活跃事实
@@ -37,11 +37,18 @@ CQRS 迁移与收敛。
- 结果:通过,`0 warning / 0 error`
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Does_Not_Emit_Stream_Invoker_Provider_Metadata_When_Runtime_Lacks_Stream_Provider_Interface|FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Does_Not_Emit_Stream_Invoker_Provider_Metadata_When_Runtime_Lacks_Stream_Descriptor_Enumerator|FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Does_Not_Emit_Stream_Invoker_Provider_Metadata_When_Runtime_Lacks_Stream_Descriptor_Type|FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Does_Not_Emit_Stream_Invoker_Provider_Metadata_When_Runtime_Lacks_Stream_Descriptor_Entry_Type|FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Emits_Stream_Invoker_Provider_Metadata_When_Runtime_Contract_Is_Available"`
- 结果:通过,`5/5` passed
+- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Does_Not_Emit_Request_Invoker_Provider_Metadata_When_Runtime_Lacks_Request_Descriptor_Type|FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Does_Not_Emit_Request_Invoker_Provider_Metadata_When_Runtime_Lacks_Request_Descriptor_Entry_Type|FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Does_Not_Emit_Request_Invoker_Provider_Metadata_When_Runtime_Lacks_Request_Provider_Interface|FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Does_Not_Emit_Request_Invoker_Provider_Metadata_When_Runtime_Lacks_Request_Descriptor_Enumerator"`
+ - 结果:通过,`4/4` passed
+- `dotnet build GFramework.Cqrs.SourceGenerators/GFramework.Cqrs.SourceGenerators.csproj -c Release`
+ - 结果:通过,`0 warning / 0 error`
+- `python3 scripts/license-header.py --check`
+ - 结果:通过
+ - 备注:当前 WSL worktree 需要显式绑定 `GIT_DIR` / `GIT_WORK_TREE` 后运行,避免脚本内部 plain `git ls-files` 误判仓库上下文
## 下一推荐步骤
1. 继续处理 `PR #307` 的剩余 review 收尾,优先保持 `ai-plan` active 入口与 trace 的单一锚点一致
-2. 若继续推进代码切片,优先复核 request 侧是否存在与 stream gate 对称的生成合同遗漏,再决定是否补同批 generator 回归
+2. 若继续推进代码切片,优先复核 request / stream invoker provider runtime 合同以外是否还有同类对称测试缺口
3. 在进入下一批 runtime / generator 收敛前,保持最小 Release build 或 targeted test 作为权威验证
## 活跃文档
diff --git a/ai-plan/public/cqrs-rewrite/traces/cqrs-rewrite-migration-trace.md b/ai-plan/public/cqrs-rewrite/traces/cqrs-rewrite-migration-trace.md
index b46a73ea..7b5950db 100644
--- a/ai-plan/public/cqrs-rewrite/traces/cqrs-rewrite-migration-trace.md
+++ b/ai-plan/public/cqrs-rewrite/traces/cqrs-rewrite-migration-trace.md
@@ -30,3 +30,35 @@
1. 继续按 `PR #307` 的 latest-head review 收尾,优先保持 active tracking 与 active trace 的单一锚点一致
2. 若继续推进代码切片,先复核 request 侧是否仍存在与 stream invoker gate 对称的生成合同遗漏
3. 进入下一批前继续使用最小 Release build 或 targeted test 作为权威验证,避免把环境噪音误判为代码问题
+
+## 2026-05-04
+
+### 阶段:request invoker provider gate 对称回归(CQRS-REWRITE-RP-077)
+
+- 使用 `$gframework-batch-boot 25` 继续 `feat/cqrs-optimization` 的 CQRS 收口批次
+- 批次目标:在 branch diff 相对 `origin/main` 接近 `25` 个文件前,补齐低风险的 generator 合同回归切片
+- 本轮先确认当前 worktree 已无 `local-plan` 遗留恢复入口,随后转入 `cqrs-rewrite` 的 request / stream invoker provider gate 对称性复核
+- 结论:
+ - 生产代码已经同时检查 request provider、enumerator、descriptor 与 descriptor entry 四项 runtime 合同
+ - request 侧测试只覆盖缺少 provider / enumerator,缺少 descriptor / descriptor entry 的回归覆盖落后于 stream 侧
+- 已补齐:
+ - `Does_Not_Emit_Request_Invoker_Provider_Metadata_When_Runtime_Lacks_Request_Descriptor_Type`
+ - `Does_Not_Emit_Request_Invoker_Provider_Metadata_When_Runtime_Lacks_Request_Descriptor_Entry_Type`
+ - source emission XML 文档同步说明 provider gate 依赖完整 descriptor / descriptor entry 合同
+
+### 验证(RP-077)
+
+- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Does_Not_Emit_Request_Invoker_Provider_Metadata_When_Runtime_Lacks_Request_Descriptor_Type|FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Does_Not_Emit_Request_Invoker_Provider_Metadata_When_Runtime_Lacks_Request_Descriptor_Entry_Type|FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Does_Not_Emit_Request_Invoker_Provider_Metadata_When_Runtime_Lacks_Request_Provider_Interface|FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Does_Not_Emit_Request_Invoker_Provider_Metadata_When_Runtime_Lacks_Request_Descriptor_Enumerator"`
+ - 结果:通过,`4/4` passed
+- `dotnet build GFramework.Cqrs.SourceGenerators/GFramework.Cqrs.SourceGenerators.csproj -c Release`
+ - 结果:通过,`0 warning / 0 error`
+- `python3 scripts/license-header.py --check`
+ - 结果:通过
+ - 备注:当前 WSL worktree 需要显式绑定 `GIT_DIR` / `GIT_WORK_TREE` 后运行,避免脚本内部 plain `git ls-files` 误判仓库上下文
+- `git diff --check`
+ - 结果:通过
+
+### 当前下一步(RP-077)
+
+1. 继续使用 `origin/main` 作为 `$gframework-batch-boot 25` 的基线,复算 branch diff 后决定是否还能接下一批
+2. 若继续推进代码切片,优先查找 request / stream invoker provider runtime 合同之外的同类对称测试缺口