Browse Source

docs(tdd): 研发→测试请求通道 TR-1 — 请求补写 getPlanDetail 契约级 RED(S-cfc-6)

Agnes Agent 1 week ago
parent
commit
18a61f7b79
1 changed files with 81 additions and 0 deletions
  1. 81 0
      docs/tdd/dev/test-requests.md

+ 81 - 0
docs/tdd/dev/test-requests.md

@@ -0,0 +1,81 @@
+# 研发 → 测试 请求通道(正式通道)
+
+> 纪律:本台账条目由**开发/研发角色**写入,**push 到远端才算送达测试**;
+> 测试处理完后在本文件追加「回复」段并置状态,再 push。
+> 与状态机解耦:本文件不改变 `state.json` 的 `stage`,阶段推进仍由 `--mark-*` + git 真理证据驱动。
+> 编号规则:`TR-*` 单调递增,不复用、不跳号。
+
+| 编号 | 关联键/对象 | 类型 | 描述 | 经办 | 登记时间 | 状态 |
+|---|---|---|---|---|---|---|
+| TR-1 | `guide.trainingPlan.getPlanDetail` | 用例缺失 | 能力键已于 2026-11-04 02:47 手动入池为 **RED**(`manual_entry: true`),但全仓 `src/test/` 下**无任何 `getPlanDetail` 引用**(`grep -rln "getPlanDetail" cfc-backend/src/test/` 无匹配),即 RED 契约测试尚未落地,阶段无法从 RED 推进。请测试侧补契约级单测。**详见下方「TR-1 详情」**。 | 开发(研发负责人1) | 2026-11-04 04:30 | 待测试处理 |
+
+---
+
+## TR-1 详情:请求补写 `getPlanDetail` 契约级 RED 测试
+
+### 1. 背景与阻塞性质
+
+- 状态机键:`guide.trainingPlan.getPlanDetail`,当前阶段 **RED**。
+- 实现**已存在且已推送远端**:`cfc-backend/src/main/java/com/etotem/cfc/controller/guide/TrainingPlanController.java`
+  (实现引入于 `0af81d688`,随 `origin/cfclub` 可读)。
+- 阻塞点:按 TDD 角色隔离,**RED 测试用例属测试角色职责**,研发侧不代写;因此该键虽已有可用实现,仍卡在 RED 等待测试侧补单测。
+
+### 2. 前置阻塞已解除(研发侧本轮已闭环)
+
+测试负责人 2026-11-04 02:16 汇报中上报的后端**编译回归**(`dfc78e9d3` 为 `CfBalanceController` 5 个方法新增 `@RequestAttribute currentMemberId` 形参,
+但 `CfBalanceControllerTest` 仍以旧签名直接调用,导致 `mvn test-compile` BUILD FAILURE,阻断全部后端单测),
+已由研发侧修复并推送:
+
+- 修复提交:`c9f9ec948` — `fix(test): 同步 CfBalanceControllerTest 到 dfc78e9d3 新签名(currentMemberId 形参),解除后端全量单测编译回归`
+- 目标分支:`cfclub`(data_ref = `origin/cfclub`),已推送 ✅
+- 验证:`mvn -o -B test-compile` → BUILD SUCCESS;定向跑 4 个契约测试类 → `Tests run: 29, Failures: 0, Errors: 0, Skipped: 0` + BUILD SUCCESS
+
+> 即:**当前后端单测已可正常编译执行**,测试侧补 RED 的环境前提已具备。
+
+### 3. 待测端点契约(供测试侧直接建用例)
+
+| 项 | 内容 |
+|---|---|
+| 能力键 | `guide.trainingPlan.getPlanDetail` |
+| 端点 | `POST /api/guide/training-plans/detail` |
+| 请求体 | `{"planId": <Long>}`(`Map<String,Object>`,经 `ParamUtils.getLong` 解析) |
+| 成功响应 | `Result.success({plan, items})`,`code=200`,`data` 为 `LinkedHashMap`,含 `plan`(TrainingPlan)与 `items`(List\<TrainingPlanItem\>)两个键 |
+| 参数缺失 | `planId` 为 null → `Result.error("planId不能为空")` |
+| 方案不存在 | `planId` 有效但查无方案 → `Result.error("方案不存在")` |
+| 调用链 | `trainingPlanService.getPlanById(planId)` → `trainingPlanService.getPlanItems(planId)` |
+
+### 4. 建议覆盖的用例点(测试侧自决,非研发指令)
+
+1. 正常路径:`planId` 有效 → `code=200`,`data.plan` 非空、`data.items` 为数组。
+2. 参数缺失:`planId` 缺省 / null / 非数字 → 返回「planId不能为空」(`ParamUtils` 语义:null/空串/非数字均视为未传)。
+3. 方案不存在:mock `getPlanById` 返回 null → 返回「方案不存在」。
+4. 空项目列表:`getPlanItems` 返回空集合 → `data.items` 为空数组而非 null。
+5. HTTP 方法契约:GET 应 405(本仓接口统一 `@PostMapping`)。
+6. **鉴权缺口观察(请测试侧一并记录缺陷,不必断言为绿灯)**:该 handler 在控制器层**未读取 `@RequestAttribute("role")`**,
+   也未列入 `JwtInterceptor` 的 `LOGIN_REQUIRED_PATHS`(对照同文件 `getPlanList` 既有测试的同类观察记录),
+   因此「规划师角色约束」名义成立但实际任何已认证角色(乃至匿名 `userId=-1`)均可到达 handler,属潜在越权缺口。
+   请按「缺陷观察」记录并回写研发侧,本轮研发侧不改实现(越权修复涉及鉴权面收敛,另案处理)。
+
+### 5. 测试写法参考(沿用同包既有风格,不复制实现代码)
+
+参考同目录既有 RED 契约测试 `cfc-backend/src/test/java/com/etotem/cfc/controller/guide/TrainingPlanControllerGetPlanListTest.java`
+(能力键 `guide.trainingPlan.getPlanList`,已 DONE):
+
+- MockMvc **standalone** 模式 + Mockito mock `TrainingPlanService`,无 Spring 上下文 / 无 DB;
+- 通过 `ReflectionTestUtils.setField(controller, "trainingPlanService", ...)` 注入;
+- 断言用 `jsonPath` + `status()`。
+
+### 6. 交付与阶段推进约定
+
+- 测试侧写完 RED 并 push 后,研发侧将执行 `POST /api/session`(能力键 `guide.trainingPlan.getPlanDetail`)→ `--mark-yellow ... --commit <sha>` 置 YELLOW;
+- 随后由**测试角色**执行 `--mark-green` 完成闭环(研发侧无权代置 GREEN)。
+
+---
+
+## 回复区(由测试角色追加)
+
+> 待测试填写。回复后请将对应 `TR-*` 状态置为「已回复」或「已关闭」并 push。
+
+| 编号 | 回复人 | 回复时间 | 回复内容 | 状态变更 |
+|---|---|---|---|---|
+| — | — | — | — | — |