轻松聘AI开发
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

7.3 KiB

账号注销测试夹具隔离修复报告

日期:2026-09-17 分支:refactor/r01-structure-performance 基线 HEAD:d5c37128e84b4fc83bf5334254dd8efeb88ccf64 性质:纯测试修复 —— 生产代码、Flyway、配置与接口零改动


1. 结论

AccountDeletionControllerTest 与关联测试组合的既有交叉污染已消除;修复后两种执行顺序各 5 次全部 F=0 / E=0 / S=0。


2. 稳定复现(修复前)

致冲突组合(连续运行 5 次,5/5 稳定复现):

mvn -Dtest="*Deletion*Test,*Verification*Test,*Risk*Test,*Report*Test,*Block*Test,
            *Notification*Test,*Recommendation*Test,*RequestContext*Test,*RequestId*Test" test

5 次运行全部失败于同一位置:

AccountDeletionControllerTest.lazyTransition_firstGet_returnsCompleted_andConsistent:228
  -> createUser:75
DataIntegrityViolation: Unique index or primary key violation:
  USERS(PHONE) = '930000000'

诊断结论(逐项回答)

问题 结论
谁先插入 phone='930000000' com.mydreamjob.common.logging.RequestContextMigrationIntegrationTest(HEAD 既有类)。其 @BeforeEach 用 AtomicLong(930_000_000L) 创建 user
冲突发生在哪个方法 AccountDeletionControllerTest#createUser:75(由 lazyTransition_firstGet_returnsCompleted_andConsistent:228 调用)
共享 H2 数据库名 integrationdb(jdbc:h2:mem:integrationdb;MODE=MySQL;DB_CLOSE_DELAY=-1)—— 同一 JVM 内所有 @SpringBootTest 共享
static 计数器何时重置 不重置 —— PHONE 为 per-class static,类加载时初始化为 930_000_000;冲突不是计数器重置所致,而是号段被残留记录占用
未清理的记录 users(phone=930000000);user_roles 随 users 级联删除

根因链

  1. 号段撞车:common/logging/RequestContextMigrationIntegrationTest 与 deletion/AccountDeletionControllerTest 都使用基数 930_000_000
  2. 污染源不清理:前者 @AfterEach 只清 RequestContextProbe 与 MDC,不删 users → 每个测试方法残留 1 条 930000000+N
  3. 受害者删除失败:后者的 @AfterEach 用 userRepository.deleteAll()(全表删除),会因其他测试类残留 user(被 notifications/offers/resume_versions 等无 CASCADE 的外键引用)而失败 → 自身 user 也未删净
  4. 执行顺序:诊断运行中 common.logging.RequestContextMigrationIntegrationTest 排在第 6 位、AccountDeletionControllerTest 排在第 13 位 → 前者残留必然影响后者

与 A2/A3 的关系(如实记录)

A2/A3 新建的测试类均不创建 user,故无污染:

  • common/r01a2/RequestContextMigrationIntegrationTest:无 AtomicLong、无 userRepository 注入
  • A3 的三个测试类:均使用 @MockBean UserRepository(不实际写库)

该缺陷完全存在于基线(污染源文件为 HEAD 既有),与 A2/A3 无关;此前用 HEAD 隔离 worktree 的归因结论正确。

同号段还有 candidatescore/CandidateScoringClockBoundaryIntegrationTest,但其标注 @Transactional(每方法自动回滚)→ 不残留,无风险。


3. 修复内容

3.1 common/logging/RequestContextMigrationIntegrationTest.java(污染源)

改动 内容
号段独占 930_000_000L → 986_000_000L(已确认全仓未占用)
清理 @AfterEach 增加:按自身 studentId 精确删除本测试创建的 user(findById(...).ifPresent(delete)),并补 javadoc 说明隔离原因

3.2 deletion/AccountDeletionControllerTest.java(受害者)

改动 内容
号段独占 930_000_000L → 987_000_000L(已确认全仓未占用)
记录归属 新增 List<Long> createdUserIds,createUser() 登记每个自建 userId
精确清理 @AfterEach 由 deleteAll()(整表)改为按本测试记录删除:account_deletions(按 userId)→ users(级联 user_roles),各步 flush(),遵循外键顺序;@BeforeEach 清空记录
防掩盖断言 createUser() 内先断言该号在共享库中不存在,存在即抛出可读错误(暴露上轮清理失效),而不是被唯一键异常吞掉

未使用:未改唯一约束、未捕获忽略 DataIntegrityViolationException、未用 @DirtiesContext、未清空整表、未 DELETE FROM users 无条件删除、未 sleep、未放宽任何业务断言、未依赖测试执行顺序。

3.3 新增反证测试 deletion/AccountDeletionFixtureIsolationTest.java(5 例)

反证 断言
1 同一 Spring 上下文内连续创建 5 个夹具不冲突且 ID 不重复
2 注销记录写入后,按本测试路径清理 → account_deletions / users / user_roles 残留均为 0
3 变异探测:预置同号记录(等价于"上轮未清理")→ 防掩盖断言必须失败并检出该号,证明残留不会被静默吞掉
4 独占号段 987_000_000 在测试开始前为空(不与任何他类重叠)
5 清理精确到本测试记录:预置的"他类"记录必须保留,证明不是整表删除

4. 验证结果

4.1 两种执行顺序各 5 次

组合 结果(5 次)
顺序 A *Deletion*Test,*RequestContext*Test,*RequestId*Test 95/95,F=0 E=0 S=0 ×5 ✔
顺序 B *RequestContext*Test,*Deletion*Test,*RequestId*Test 95/95,F=0 E=0 S=0 ×5 ✔

(修复前该组合稳定失败 5/5;修复后 10/10 全绿)

4.2 单类与反证

命令 结果
mvn -Dtest="AccountDeletionControllerTest" test 14 / 14 ✔
mvn -Dtest="AccountDeletionFixtureIsolationTest,AccountDeletionControllerTest" test 19 / 19(5 反证 + 14 原)✔

4.3 全量

命令 结果
mvn clean verify 2225 / 2225,Failures=0、Errors=0、Skipped=0,BUILD SUCCESS(≥ 2220 ✔)
python docs\verify_openapi.py 176 paths / 214 operations,All checks passed ✔

较修复前(2220)新增 5 个测试,全部为反证测试。

4.4 边界核验

项 结果
生产源码 git diff --stat -- src/main/ 空(零改动)✔
V1–V45 迁移 零修改 ✔
Flyway / 配置 / 接口 零改动 ✔
GlobalExceptionHandler.java 仍为工作区 stat 标记(内容零差异),未处理 ✔
Docker 未操作 ✔
是否进入 A4 否 ✔

5. 精确修改文件

修改(2)
  mydreamjob-server/src/test/java/com/mydreamjob/common/logging/RequestContextMigrationIntegrationTest.java
  mydreamjob-server/src/test/java/com/mydreamjob/deletion/AccountDeletionControllerTest.java

新增(1)
  mydreamjob-server/src/test/java/com/mydreamjob/deletion/AccountDeletionFixtureIsolationTest.java

文档(1)
  docs/account-deletion-test-isolation-fix-20260917.md

6. 提交

提交信息:test: isolate account deletion controller fixtures

(精确暂存上述测试文件与报告,敏感扫描后独立提交;推送当前 R01 功能分支)


账号注销测试夹具隔离修复完成;测试交叉污染已消除,生产代码零改动。