Parcourir la source

docs: 修正计划中 mvn 命令(绝对路径+skipTests=false)与测试断言写法

iwt il y a 1 semaine
Parent
commit
1b8dab25cd
1 fichiers modifiés avec 21 ajouts et 9 suppressions
  1. 21 9
      docs/superpowers/plans/2026-10-17-health-plan-deletion.md

+ 21 - 9
docs/superpowers/plans/2026-10-17-health-plan-deletion.md

@@ -57,11 +57,13 @@ import com.etotem.cfc.mapper.TaskMapper;
 import com.etotem.cfc.service.impl.HealthPlanServiceImpl;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
 import org.mockito.InOrder;
 
 import java.lang.reflect.Field;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.List;
 import java.util.Map;
 
 import static org.junit.jupiter.api.Assertions.*;
@@ -113,10 +115,18 @@ class HealthPlanDeleteCascadeTest {
         Map<String, Object> r = service.deletePlanCascade(5L, 10L);
 
         assertEquals(5, ((Number) r.get("taskCount")).intValue(), "taskCount = 实例3 + 直接任务2");
-        // 顺序必须:先删实例 → 再删直接任务 → 最后删方案
+        // 两次 delete 的语义断言:第一次按 template_id 删每日实例,第二次按 source_type+source_id 删直接任务
+        ArgumentCaptor<Wrapper> captor = ArgumentCaptor.forClass(Wrapper.class);
+        verify(taskMapper, times(2)).delete(captor.capture());
+        List<Wrapper> wrappers = captor.getAllValues();
+        assertTrue(wrappers.get(0).getTargetSql().contains("template_id"),
+                "第一次删除必须按 template_id(每日实例),实际: " + wrappers.get(0).getTargetSql());
+        String secondSql = wrappers.get(1).getTargetSql();
+        assertTrue(secondSql.contains("source_type") && secondSql.contains("source_id"),
+                "第二次删除必须按 source_type+source_id(直接任务),实际: " + secondSql);
+        // 任务删除必须先于方案删除
         InOrder inOrder = inOrder(taskMapper, mapper);
-        inOrder.verify(taskMapper).delete(any(Wrapper.class));
-        inOrder.verify(taskMapper).delete(any(Wrapper.class));
+        inOrder.verify(taskMapper, atLeastOnce()).delete(any(Wrapper.class));
         inOrder.verify(mapper).deleteById(5L);
     }
 
@@ -169,9 +179,11 @@ class HealthPlanDeleteCascadeTest {
 
 - [ ] **步骤 2:运行测试确认失败**
 
-运行:`cd cfc-backend && mvn test -Dtest=HealthPlanDeleteCascadeTest -q`
+运行:`cd cfc-backend && /tmp/apache-maven-3.8.6/bin/mvn test -DskipTests=false -Dtest=HealthPlanDeleteCascadeTest -q`
 预期:**编译失败**(`deletePlanCascade` 方法不存在),错误 `cannot find symbol: method deletePlanCascade`。失败原因正确(接口未声明)。
 
+> ⚠️ 环境注意:项目 pom 默认 `skipTests=true`,`mvn test` 只编译不执行;必须显式 `-DskipTests=false` 才真正运行测试。本机 mvn 不在 PATH,用 `/tmp/apache-maven-3.8.6/bin/mvn`。
+
 - [ ] **步骤 3:在 Service 接口声明方法**
 
 在 `cfc-backend/src/main/java/com/etotem/cfc/service/HealthPlanService.java` 末尾(`generateFromSelfCheck` 声明之后、类闭合括号之前)追加:
@@ -253,8 +265,8 @@ import org.springframework.transaction.annotation.Transactional;
 
 - [ ] **步骤 5:运行测试确认通过**
 
-运行:`cd cfc-backend && mvn test -Dtest=HealthPlanDeleteCascadeTest -q`
-预期:**BUILD SUCCESS**,5 个测试全部 PASS(级联删除含顺序验证、无任务方案、IDOR 拒绝、方案不存在、管理端跳过校验)。
+运行:`cd cfc-backend && /tmp/apache-maven-3.8.6/bin/mvn test -DskipTests=false -Dtest=HealthPlanDeleteCascadeTest -q`
+预期:**BUILD SUCCESS**,5 个测试全部 PASS(级联删除含 wrapper 语义顺序验证、无任务方案、IDOR 拒绝、方案不存在、管理端跳过校验)。
 
 - [ ] **步骤 6:Commit**
 
@@ -323,7 +335,7 @@ git commit -m "feat(health-plan): 方案级联删除 Service 层(含每日实
 
 - [ ] **步骤 3:编译验证**
 
-运行:`cd cfc-backend && mvn clean compile -q`
+运行:`cd cfc-backend && /tmp/apache-maven-3.8.6/bin/mvn clean compile -q`
 预期:**BUILD SUCCESS**,无编译错误。
 
 - [ ] **步骤 4:Commit**
@@ -597,7 +609,7 @@ git commit -m "feat(admin): 健康方案管理支持删除(确认弹窗 + 级
 - [ ] **步骤 2:全量回归验证(后端)**
 
 ```bash
-cd cfc-backend && mvn clean compile -q && mvn test -Dtest=HealthPlanDeleteCascadeTest -q
+cd cfc-backend && /tmp/apache-maven-3.8.6/bin/mvn clean compile -q && /tmp/apache-maven-3.8.6/bin/mvn test -DskipTests=false -Dtest=HealthPlanDeleteCascadeTest -q
 ```
 
 预期:`BUILD SUCCESS`,5 个测试 PASS。
@@ -625,7 +637,7 @@ git commit -m "docs(overview): 健康方案级联删除完成(v2.18)"
 
 | # | 验收项 | 验证方式 | 状态 |
 |---|---|---|---|
-| 1 | `deletePlanCascade` 一次性事务内完成「实例→直接任务→方案」三级删除 | `HealthPlanDeleteCascadeTest` 5 用例(InOrder 验证顺序 + taskCount 汇总) | [ ] |
+| 1 | `deletePlanCascade` 一次性事务内完成「实例→直接任务→方案」三级删除 | `HealthPlanDeleteCascadeTest` 5 用例(ArgumentCaptor 断言第 1 次 delete 按 template_id、第 2 次按 source_type+source_id,且任务删除先于方案删除 + taskCount 汇总) | [ ] |
 | 2 | `familyId` 不匹配拒绝删除且零数据变动(IDOR) | 测试用例「方案不属于该家庭返回null且不删任何数据」 | [ ] |
 | 3 | 用户端 `POST /api/health/plan/delete` 返回 `{planId, taskCount}` | 编译 + 联调(HBuilderX 重打包后小程序点删除) | [ ] |
 | 4 | 管理端 `POST /api/admin/health-plan/delete` 五角色权限校验 | 编译 + 管理端联调 | [ ] |