Răsfoiți Sursa

fix(test): B-21 SC-P-11 契约修复——TaskControllerReviewTest 补 RewardRuleService mock 恢复 17/17(52 轮测试自修)

- TaskControllerReviewTest +6 行:@Mock RewardRuleService + setUp 注入(预估挂载非契约关注点,mock 空实现)
- 修复前后实测:15/17(2 NPE) → 17/17 全绿;完整 9 类 79 例复验 74 绿/3 红(P-2)/0 错
- G8 契约红 5→3;blockers B-21 置已解除(附修复证据);新增 0502 轮报告
E2E Test Bot 6 zile în urmă
părinte
comite
a939397bbd

+ 6 - 0
cfc-backend/src/test/java/com/etotem/cfc/controller/task/TaskControllerReviewTest.java

@@ -4,6 +4,7 @@ import com.etotem.cfc.common.Result;
 import com.etotem.cfc.dto.TaskReviewDTO;
 import com.etotem.cfc.entity.Task;
 import com.etotem.cfc.mapper.TaskMapper;
+import com.etotem.cfc.service.RewardRuleService;
 import com.etotem.cfc.service.TaskService;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -71,6 +72,9 @@ public class TaskControllerReviewTest {
     @Mock
     private TaskMapper taskMapper;
 
+    @Mock
+    private RewardRuleService rewardRuleService;
+
     private MockMvc mockMvc;
     private TaskController controller;
 
@@ -81,6 +85,8 @@ public class TaskControllerReviewTest {
         // review/pending-review 仅依赖 taskService;accept/reject 的 handler 内经 checkMemberOnly 触碰 taskMapper
         ReflectionTestUtils.setField(controller, "taskService", taskService);
         ReflectionTestUtils.setField(controller, "taskMapper", taskMapper);
+        // SC-X-22 合入后 /pending-review /today-parent 新增 rewardRuleService 挂载预估调用,mock 空实现(旁路估算非本契约关注点)
+        ReflectionTestUtils.setField(controller, "rewardRuleService", rewardRuleService);
         mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
     }
 

+ 58 - 0
docs/tdd/test/automation-run-2026-11-06-0502.md

@@ -0,0 +1,58 @@
+# TDD 闭环自动化运行报告 — 2026-11-06 05:02(测试负责人轮 · 第 52 次执行)
+
+- 角色:测试负责人(cfc-tester)。未派发 opencode;离线契约测试实跑 ×2 轮(修复前基线 + 修复后复验)+ **B-21 测试装置自修** + git 真理核验 + 台账更新。
+- 基线:本地 HEAD `d1750bc29`(03:46 轮汇报),与 `origin/cfclub` **完全一致**(fetch 校验 0 差异);工作区净(仅本轮改动)。
+
+## 本轮核心证据(实测)
+
+### 1. 契约套件修复前基线:79 例 → 74 绿 / 3 红 / 2 错(第 7 次同口径复现)
+
+命令:`mvn -o -B -f cfc-backend/pom.xml -DskipTests=false -Dtest='HealthReportControllerCreateReportTest,DanAssessmentControllerGetActiveMaterialTest,TrainingPlanControllerGetPlanDetailTest,TaskControllerCreateTaskTest,TaskControllerReviewTest,TaskServiceReviewTest,DimensionRegistryTreeTest,DimensionRegistryMapContractTest,CfBalanceControllerTest' test`
+
+| 测试类 | 修复前 | 说明 |
+|---|---|---|
+| createReport 5 + getActiveMaterial 3 + getPlanDetail 8 + createTask 7 + TaskServiceReview 15 | 38/38 绿 | 2 个 YELLOW 键(createReport/getActiveMaterial)**连续第 11 轮**独立复验通过 |
+| CfBalanceControllerTest 8 + DimensionRegistryTreeTest 9 | 17/17 绿 | B-10 连续第 11 轮无复发;维度合并树测试在 fresh 编译下正常 |
+| DimensionRegistryMapContractTest 7 | **3 红**(F) | P-2 口径未决(map_unknownLegacy:76 / map_emptyLegacy:85 / map_missingLegacy:92)——与既往第 1~6 轮完全一致,非新回归 |
+| **TaskControllerReviewTest 17** | **2 错**(E, NPE) | **B-21 第 7 次确认**:pendingReview_directCall:316 + pendingReview_parent:176,`new TaskController()` 未注入 `RewardRuleService` |
+
+### 2. B-21 测试装置自修(本轮实质推进)
+
+- **修改**:`TaskControllerReviewTest.java` +6 行——补 `import RewardRuleService`、`@Mock RewardRuleService rewardRuleService`、`setUp()` 中 `ReflectionTestUtils.setField(controller, "rewardRuleService", rewardRuleService)`(mock 空实现,预估挂载非本契约关注点)。
+- **定向复跑**:`-Dtest=TaskControllerReviewTest` → **17/17 全绿,BUILD SUCCESS**(修复前 15/17)。
+
+### 3. 契约套件修复后复验:79 例 → **74 绿 / 3 红 / 0 错**
+
+- 同命令复跑:全部 9 类,TaskControllerReviewTest **17/17 绿**;仅剩 **P-2 MapContract 3 红**(口径待拍板,非测试可解)。
+- **G8 契约红由 5 → 3**;SC-P-11 契约 32 例恢复全绿。
+
+## 状态机(纯同步缺口第 20 轮维持)
+
+- 本地 `.tdd/state.json` 仍 3 伪 RED(createReport/getActiveMaterial/getPlanDetail,mtime 11-04 10:14 **第 20 轮未动**)。
+- `--mark-green` 实测仍被拒(「当前阶段 RED,须先 YELLOW」)——责任在编排侧/主理人同步,测试侧不可自解。
+- **mark-green 证据链已完全就绪**:4 个证据 sha(YELLOW `1767d4414`/`c3e32460a` + RED `81d8883f7`/`faf3ca174`)本轮全部 `git merge-base --is-ancestor` 核验 **IN CHAIN ✓**;2 YELLOW 键契约连续 11 轮复验通过;B-21 已自修清零。只差同步动作。
+
+## 台账与 git
+
+- 工作区:本轮仅 1 个改动文件(B-21 测试装置)。`git status` 无 untracked 测试资产漂移(B-23 未复发)。
+- 本次提交:B-21 测试装置修复 + blockers B-21 状态更新(待处理 → 已解除)+ 本报告。
+- push 后将复核 HEAD == origin/cfclub。
+
+## 结论:不通过(承袭,有条件)—— 但有实质推进
+
+| 门禁 | 判定 | 依据 |
+|---|---|---|
+| G4 testCompile | ✅ 0 错(本轮实跑) | 9 类契约 fresh 编译通过 |
+| G5 契约 | ⚠️ 74 绿(**3 红**,P-2 口径) | **B-21 已清零**(2 错 → 0),仅剩 P-2 3 红 |
+| G3 前端 | ✅ 100%(采信 02:43 轮) | 双套件 386/386 全绿 |
+| G7 状态机 | ❌ 本地 3 伪 RED 第 20 轮 | 编排侧/主理人同步缺口 |
+| G8 非环境红 | ❌ 收窄中 | B-21 已移除;P-2 3 红 + B-18 A 类 + B-25 等继续收口 |
+| G1/G2/G6 | ⚠️/❌/❌ | 全量采信 02:43 轮(92.47%);A 类 65 例可修;无 CI 仍旧 |
+
+**使绿顺序(下轮前置)**:
+1. 编排侧/主理人同步 `.tdd/state.json`(第 20 轮卡点,证据链已就绪)→ 测试 mark-green 收口 2 YELLOW 键(B-21 已不阻碍)
+2. P-2 MapContract 口径拍板(G8 剩余 3 红)
+3. B-18 A 类 54 例 / B-25 反射字段 2 例自修(G1 → ≈96%)
+4. B-13 环境红 8 例 / 最小 CI
+
+**未实测项(如实记录)**:全量 surefire 与前端 jest 本轮采信 02:43 兄弟批次(220 类 1700 例 92.47%;386/386);Playwright/接口冒烟/langgraph pytest(联网未授权 + 环境限制);JaCoCo/PIT/性能/依赖漏洞(无工具);mark-green 落账受阻(状态机过时第 20 轮,实测受阻非未尝试)。

+ 1 - 1
docs/tdd/test/blockers.md

@@ -25,7 +25,7 @@
 | B-18 | A 类测试漂移 54 例集中爆发(Mockito 误用 + 断言基线过期) | 研发/测试 | 39 轮全量实测非环境红 112 例/34 类,其中 **A 类 54 例/17 类**可纯测试侧修复:`doNothing()` 用于非 void 方法(`ArticleControllerTest`/`PlannerBindInviteFlowTest`/`DailyCheckinStreakFlowTest`)、matcher 混用(`ProductControllerTest` 3E `InvalidUseOfMatchers`、`ProductOrderServiceFulfillmentMultiModeTest` 6E)、桩名过期(`ProductOrderControllerTest` 6E 打 `confirm`/实现 `confirmReceive`)、反射注入已删字段(`TaskProgressUpdateTest` 3E `NoSuchFieldException: platformPointsService`)、断言基线漂移(`WishControllerTest` 文案 `childId`→`memberId`;`StatsControllerTest`/`MindFortuneControllerTest` 500 vs 5001;`HealthDimensionControllerTest` **期望 500 实得 200**)。**批量修完 54 例后 G1 由 92.41% → ≈95.7%,G2 由 112 → 58**,是当前性价比最高的一步。**精确化(22:13 轮实测,维度合并后首验 28 例 26 绿/2 红)**:`HealthDimensionControllerTest.overview_missingMemberId:58` / `history_missingMemberId:217` 期望 `result.getCode()==500` 实得 `200`——根因=用例调用 `controller.overview(params, 1L, 1L)` / `history(params, 1L, 1L)` **误传 currentMemberId=1L**,实现 `ParamUtils.getLong(params.get("memberId"), currentMemberId)` 触发双角色「家长视角 currentMemberId 兜底」(设计行为)→ memberId=1L 非空走正常路径 200。**测试装置口径错误,非生产缺陷**;修复:第 3 参传 `null`(或断言兜底 200) | 测试(39 轮登记,22:13 轮精确化) | 2026-11-05 09:40 | 待处理 |
 | B-19 | `cfc-langgraph` pytest 长期「未实测」结论已过期,首次实测 19 红 | 测试/研发 | 41 轮(11-05 15:19)探活发现 `cfc-langgraph/.venv/Scripts/python.exe` = **Python 3.12.7**,`pytest 9.2.0` / `fastapi` / `httpx` / `langgraph` 全部可 import → 此前 #28–#40 多轮报告的「无 .venv / 缺 httpx → pytest 47 例未实测」**结论过期**(度量失真,同 N-1 类)。首次实跑 `pytest tests --ignore=tests/qna`:**43 例 24 绿 / 19 红(55.81%)**。19 红分层:**C 环境 14 例**(`401 Authentication Fails, Your api key: ****ocal is invalid` → analyze 2 + auto_tag 5 + chat 1 + intent 4;另 report_parse 2 例因用例写死容器路径 `/app/cfc/docs/参考资料/*.pdf` 在本机 400);**A 测试漂移 5 例**(`test_graph` 2:`GenerateRequest.respondent_name Field required` 而用例仍发 `member_name`;`test_memory` 3:`MemoryManager.__init__() missing 'embeddings'`)。另 `tests/qna/test_graph.py` 收集期 `ModuleNotFoundError: No module named 'src.qna'` → 整模块 0 例可执行(死资产)。建议:①注入测试 LLM 凭据或对 LLM client 全 mock;②`test_report_parse` 路径改相对仓库路径;③删/重写 `tests/qna/test_graph.py`;④pytest 纳入 CI | 测试(41 轮巡检登记) | 2026-11-05 16:20 | 待处理 |
 | B-20 | Playwright 规模扩张至 96 spec 而长期未实测 | 测试/环境 | #40 记录 69 spec / 402 `test()`,#41 复核为 **96 个 spec 文件**(+27),但唯一一次实测是 11-05 12:14 execute 档(用户显式授权联网)的 API 级 83 例(66 绿 / 6 红 / 3 跳过 / 8 串行阻塞,91.67%);UI 级因 `cfc.iwintrue.com` 无前端页面(`/` 404)不可跑。此后各轮均记「未授权联网 → 未实测」。风险:spec 持续增长而信号为零,用例腐化不可见(如 12:14 已发现 2 条路径/断言过期)。建议:①本地 9082 在听时改用 `playwright.config.api.js`(无 globalSetup,不 reset 共享库)纳入每轮;②默认配置的 `globalSetup` 会 `reset-db.sh` 重置共享测试库,禁止在共享环境直接跑;③UI 级待补独立前置环境 | 测试(41 轮登记) | 2026-11-05 16:20 | 待处理 |
-| B-21 | SC-P-11 契约回归(YELLOW 键首次转红) | 研发/测试 | 42 轮全量实测:`TaskControllerReviewTest` 由连续 7 轮 17/17 全绿降为 **15/17**(`pendingReview_parent_success_returnsList:176` 与 `pendingReview_directCall_uniformResultShape:316` 均 NPE)。根因=SC-X-22 合入 `f14e46f84` 在 `TaskController.java:197/205` 新增 `rewardRuleService.attachTaskListEstimate(...)`,而该测试用 `new TaskController()` + 手工注入(`:80-84`)未补 `RewardRuleService` → 字段 null。**性质:A 类(测试夹具未跟进生产新增依赖)**,生产侧 `resolveMemberIdByUser:157`/`attachTaskListEstimate:193` 均有 null 守卫,非生产缺陷。影响:SC-P-11 契约 32 → 30 绿、G8 由「3 红」扩为「5 红」;证明「契约绿无回归网」——无 CI 时绿是瞬时绿。建议:该类补 `@Mock RewardRuleService` 并注入,同批 `grep -l "new TaskController()" src/test` 全量排查 | 测试(42 轮实测登记) | 2026-11-05 18:26 | 待处理(**20:24 轮第 3 次 / 22:13 轮第 4 次复现确认**:同码复跑 15/17、2 例 NPE(pendingReview_directCall:316 + pendingReview_parent:176),维度合并 aa6ccb2dd 后首轮 79 例复验依旧,非瞬时抖动、非维度合并引入;修复方案见 20:24 轮报告 P-1) |
+| B-21 | SC-P-11 契约回归(YELLOW 键首次转红) | 研发/测试 | 42 轮全量实测:`TaskControllerReviewTest` 由连续 7 轮 17/17 全绿降为 **15/17**(`pendingReview_parent_success_returnsList:176` 与 `pendingReview_directCall_uniformResultShape:316` 均 NPE)。根因=SC-X-22 合入 `f14e46f84` 在 `TaskController.java:197/205` 新增 `rewardRuleService.attachTaskListEstimate(...)`,而该测试用 `new TaskController()` + 手工注入(`:80-84`)未补 `RewardRuleService` → 字段 null。**性质:A 类(测试夹具未跟进生产新增依赖)**,生产侧 `resolveMemberIdByUser:157`/`attachTaskListEstimate:193` 均有 null 守卫,非生产缺陷。影响:SC-P-11 契约 32 → 30 绿、G8 由「3 红」扩为「5 红」;证明「契约绿无回归网」——无 CI 时绿是瞬时绿。建议:该类补 `@Mock RewardRuleService` 并注入,同批 `grep -l "new TaskController()" src/test` 全量排查 | 测试(42 轮实测登记) | 2026-11-05 18:26 | **已解除(52 轮测试侧自修,2026-11-06 04:5x)**:测试侧在 `TaskControllerReviewTest.java` 补 `@Mock RewardRuleService` + `ReflectionTestUtils.setField(controller, "rewardRuleService", ...)`(+6 行)后,定向复跑 **17/17 全绿**(BUILD SUCCESS),并对完整 9 类 79 例契约套件复跑:**74 绿 / 3 红(P-2 MapContract 口径·与既往一致)/ 0 错**——B-21 的 2 个 NPE 错误清零,G8 契约红由 5 降至 3。同批 `new TaskController()` 手工注入排查仍建议执行(B-22 关联) |
 | B-22 | CF 预估挂载的 N+1 读与鉴权清单无守护 | 研发/测试 | ①**N+1**:`attachTaskListEstimate`(`RewardRuleService:193`)对列表逐条调 `estimate()`,已挂 6 处(`TaskController:109/197/205/358`、`GuideFamilyTaskController:271/295`),命中 `/api/tasks/today`、`/pending-review`、`/today-parent`、规划师任务历史与待审等高频接口;`attachTaskEstimate:186` 用 `catch (Exception) log.warn` 静默吞异常,降级不可见。既有 `RewardRuleServiceEstimateTest` 仅测单条计算,**无「N 条任务 → 查询次数」与「降级仍 200」断言**。②**鉴权清单**:`/api/reward/estimate` 已正确加入 `JwtInterceptor.LOGIN_REQUIRED_PATHS:124-127`,但 11 例契约测试为 standalone MockMvc,不经拦截器 → 该清单条目零断言覆盖(历史上 SEC-20261031-01 / SC-G-06 均因鉴权清单漂移返工)。建议:补批量查询次数断言 + 降级断言;补对 `LOGIN_REQUIRED_PATHS` 全条目的「无 token → 401」参数化契约用例 | 测试(42 轮巡检登记) | 2026-11-05 18:26 | 待处理 |
 | B-23 | SC-X-22 契约测试 15 例未入库 | 测试/流程 | `RewardEstimateControllerEstimateTest`(11) 与 `RewardRuleServiceEstimateTest`(4) 参与 43 轮全量执行且 **15/15 全绿**,但 `git ls-files` 对 `cfc-backend/src/test/java/com/etotem/cfc/controller/reward/`、`.../service/RewardRuleServiceEstimateTest.java` **零命中**(`git status` 为 `??`,mtime 17:34/17:42)→ 只存在于主工作区。任何 `git clean -fd` / 工作区重置都会静默丢失(同类事故已发生:B-15 度量脚本丢失)。且违反「完成 = 已提交 + 工作目录干净」铁律,与 O-3 同源(主工作区本轮仍有 126 项未提交/未跟踪)。建议:`git add` 两路径 → commit + push origin/cfclub → `--clean-check` 复核;测试/契约文件入库纳入 DoD | 测试(43 轮实测登记) | 2026-11-05 21:02 | 待处理 |
 | B-24 | 跑测时延劣化 +18%,结果零漂移(环境时延) | 测试/环境 | 同码复跑对账:XML 单类耗时总和 **1649.3s → 1952.2s(+18.4%)**,Maven **31:36 → 36:49**;用例/F/E/S 完全一致(218 类 1664 例 71F 51E 4S)。增量集中 `@SpringBootTest` 类:GuideOrder +200s、AIChat +120s、Wish +62s、AdminSpecialPrice +53s;期间 `Exception during pool initialization` 8 次(3 类硬编码 `127.0.0.1:3306`,B-13 未修),`Too many connections` 0 次 → 判为外部依赖时延,非代码劣化。风险:长窗口放大 B-14(窗口内并发提交),CI 时长不可估。建议:容器化固定测试库/Redis + 报告中固化「总耗时 / 单类 Top5」基线(±5% 带) | 测试(43 轮实测登记) | 2026-11-05 21:02 | 待处理 |