9.6 KiB
R01-C 评分/Fairness 组合测试污染隔离修复(独立纯测试提交)
日期:2026-09-18
分支:refactor/r01-structure-performance
R01-C 提交:7d4704ee28e4d8e2d1fbcd174a990fc6bfe68169
本轮性质:纯测试修复 —— 生产代码 diff = 0
0. 结论
*CandidateScoring*Test,*Fairness*Test 组合在任意顺序下均全绿;
污染源已按逐类行级快照实测精确归因(未按类名猜测定性),
5 个候选类经证明零残留、未做任何修改。
1. 最初的错误归因及纠正
| 阶段 | 归因 | 判定 |
|---|---|---|
| R01-C 复验阶段(初判) | CompanyScoringDashboardPersistenceIntegrationTest(用 LocalDate.now() 生成当前期间且无清理) |
错误 —— 该类根本不在失败命令内;单独与 Fairness 组合 3 次全绿 |
| 二分定位 | *CandidateScoring*Test 是污染所在半边;进一步收敛到 CandidateScoringPersistenceIntegrationTest + FairnessMonitoringHttpIntegrationTest(7 用例 / 2 失败) |
正确但不完整 —— 修复后污染量仅由 +15 降到 +12,说明仍有其它污染源 |
| 本轮(逐类实测) | 见 §2,5 个类合计残留 15 个 student | 完整 —— 与受害测试观测的 +15(修复前)/ +12(仅修 1 类后)精确吻合 |
2. 每个候选类的实测残留表
方法:临时诊断探针 ZR01CLeakProbeTest(置于 candidatescore.zz 以确保 alphabetical 下最后运行),
对 candidate_dimension_scores 的当前期间抓取行级快照
(student_user_id / dimension / period / status / version)与相关表计数。
每次 mvn 均为全新 JVM + 全新 H2 库,故"运行后快照"即为该类在干净上下文中的净残留。
探针诊断完成后已删除(非交付物)。
| 候选类 | 单独运行 | 当前期间残留行数 | 残留 student 数 | 已有 @AfterEach | 是否需修 |
|---|---|---|---|---|---|
CandidateScoringPersistenceIntegrationTest |
3/3 通过 | 24(3×8) | 3 | 无 | 是(已修) |
CandidateScoringRefreshClaimFenceTest |
7/7 通过 | 16 | 2 | 无 | 是 |
CandidateScoringRefreshClaimTokenTest |
14/14 通过 | 8 | 1 | 有(不全) | 是 |
CandidateScoringRefreshFailureTransactionTest |
4/4 通过 | 8 | 1 | 无 | 是 |
CandidateScoringRefreshInfrastructureTest |
15/15 通过 | 57 | 8 | 无 | 是 |
CandidateScoringClockBoundaryIntegrationTest |
3/3 通过 | 0 | 0 | 无 | 否(无需修改) |
CandidateScoringRefreshBatchIsolationTest |
5/5 通过 | 0 | 0 | 无 | 否(无需修改) |
R01D4GetWritesSnapshotEvidenceTest |
6/6 通过 | 0 | 0 | 无 | 否(无需修改) |
CandidateScoringGovernancePersistenceIntegrationTest |
5/5 通过 | 0 | 0 | 无 | 否(无需修改) |
CompanyScoringDashboardPersistenceIntegrationTest |
11/11 通过 | 0 | 0 | 无 | 否(无需修改) |
5 个候选类经实测零残留(其中 4 个无
@AfterEach但依然不留残留, 说明其写入被事务回滚或未落入current_period)。未因"名字可疑"而修改它们。
3. +15、+12 的精确来源
| 项 | 组成 | 合计 |
|---|---|---|
| 修复前 +15 | Persistence 3 + ClaimFence 2 + ClaimToken 1 + FailureTransaction 1 + Infrastructure 8 | 15 ✔ 与受害测试观测 100 → 115 完全一致 |
| 仅修 Persistence 后 +12 | ClaimFence 2 + ClaimToken 1 + FailureTransaction 1 + Infrastructure 8 | 12 ✔ 与观测 100 → 112 完全一致 |
关键佐证:CandidateScoringRefreshInfrastructureTest:271 以循环
"131" + String.format("%08d", i + 7000)(i=0..7)创建 8 个用户,
与探针实测的 8 个残留 student 精确对应。
4. 修复内容(仅改测试,生产代码 0 改动)
| 文件 | 修复前 | 修复后 |
|---|---|---|
CandidateScoringPersistenceIntegrationTest |
3 个用例各建 1 个学生并物化当前期间快照,零清理 | 登记自有 userId;@BeforeEach 断言独占手机号不存在;@AfterEach 按外键顺序精确 id 删除快照 → 角色 → 用户 |
CandidateScoringRefreshClaimFenceTest |
残留 16 行 | 新增 @AfterEach:按本类独占手机号前缀 193/192 解析出精确 id 后按外键顺序删除 |
CandidateScoringRefreshClaimTokenTest |
残留 8 行(原有 @AfterEach 未覆盖快照) |
同上,前缀 190/189 |
CandidateScoringRefreshFailureTransactionTest |
残留 8 行 | 同上,前缀 130 |
CandidateScoringRefreshInfrastructureTest |
残留 57 行 | 同上,前缀 132/13107 |
统一遵守的约束:独占手机号(不与 V1_1 种子账号 138…、其它测试 t<id>、1390000xxx 冲突);
按外键顺序(candidate_dimension_scores / candidate_scoring_refresh_tasks → user_roles → users);
不使用 deleteAll/TRUNCATE/@DirtiesContext/sleep;不捕获忽略清理异常;
不修改生产统计或查询;不通过改期望 populationSize 掩盖污染;不依赖测试执行顺序。
未修改
FairnessMonitoringHttpIntegrationTest的任何断言 —— 其populationSize期望值 仍为既有的 100 / 200,污染消除后自然精确命中。
5. 修复前失败与修复后结果
| 组合 | 修复前 | 修复后 |
|---|---|---|
CandidateScoringPersistenceIntegrationTest,FairnessMonitoringHttpIntegrationTest |
7 run / 2 failures | 7 / 0 ✔ |
*CandidateScoring*Test,*Fairness*Test(原失败命令) |
296 / 2 failures(另一次 260 / 2) | 260 / 0 ✔ |
失败断言(修复前):$.data.populationSize expected:<100> but was:<115>、
expected:<200> but was:<215>。
修复后 populationSize 精确等于既有期望 100 / 200 ✔
6. 双向顺序各 3 次结果
实际执行顺序从日志核对(非依据命令参数):
| 顺序 | 实测类顺序 | 结果 |
|---|---|---|
-Dsurefire.runOrder=alphabetical ×3 |
candidatescore.CandidateScoringCalculatorTest … → … scoringmonitoring.FairnessStatisticsCalculatorTest;Fairness HTTP 在所有 Candidate 类之后 |
260 / F=0 / E=0 / S=0 ×3 ✔ |
-Dsurefire.runOrder=reversealphabetical ×3 |
scoringmonitoring.FairnessStatisticsCalculatorTest … → … candidatescore.CandidateScoringCalculatorTest;Fairness HTTP 在 Candidate 类之前 |
260 / F=0 / E=0 / S=0 ×3 ✔ |
两个方向 6 次运行全部 260/260,Failures=0、Errors=0、Skipped=0。
7. 行级清理与反证
新增独立隔离门禁 R01CScoringFixtureIsolationGateTest(与污染类共用 integrationdb):
| # | 断言 | 覆盖要求 |
|---|---|---|
| 1 | 5 个已修污染类的独占手机号段在库中零残留用户 | 自有 users 残留为 0 |
| 2 | 若发现残留,进一步报告其当前期间快照数(失败信息含类名/前缀/用户数/快照数) | 自有快照残留为 0 |
| 3 | 当前期间不得存在指向已删除用户的孤立快照行 | 清理不留孤儿 |
| 4 | 其它 period 的行数可查询且非负(清理不波及) | 其它 period 不受影响 |
| 5 | 断言以独占前缀为界 → 清理只影响本类登记的数据 | 不删除其他测试数据 |
| 6 | 门禁与污染类同库同 JVM 运行,只要任一污染类遗留自有行即失败 | Fairness 统计不再依赖先前顺序 |
CandidateScoringPersistenceIntegrationTest 的 @BeforeEach 前置断言
("独占夹具手机号不得预先存在")即"预置冲突时前置断言失败"的反证:
若上轮清理失效,测试在开始前即失败,而不是让残留静默参与断言。
8. clean verify 与 OpenAPI
docker info → ServerVersion=29.6.2(Testcontainers 真实执行,未跳过)
mvn clean verify → Tests run: 2746, Failures: 0, Errors: 0, Skipped: 0 BUILD SUCCESS
python ..\docs\verify_openapi.py → paths: 176 / operations: 214 / All checks passed!
| 项 | 数值 |
|---|---|
| 测试总数 | 2746 = R01-C 基线 2743 + 本轮新增隔离门禁 3 ✔(不低于 2743 + 新增数) |
| Failures / Errors / Skipped | 0 / 0 / 0 ✔ |
| OpenAPI | 176 / 214 ✔ |
| 生产代码 diff | 0 ✔ |
9. 精确文件清单
被修复的测试文件(5)
mydreamjob-server/src/test/java/com/mydreamjob/candidatescore/CandidateScoringPersistenceIntegrationTest.java
mydreamjob-server/src/test/java/com/mydreamjob/candidatescore/refresh/CandidateScoringRefreshClaimFenceTest.java
mydreamjob-server/src/test/java/com/mydreamjob/candidatescore/refresh/CandidateScoringRefreshClaimTokenTest.java
mydreamjob-server/src/test/java/com/mydreamjob/candidatescore/refresh/CandidateScoringRefreshFailureTransactionTest.java
mydreamjob-server/src/test/java/com/mydreamjob/candidatescore/refresh/CandidateScoringRefreshInfrastructureTest.java
新增隔离测试(1)
mydreamjob-server/src/test/java/com/mydreamjob/common/r01c/R01CScoringFixtureIsolationGateTest.java
报告(1):本文件
路线图(1):docs/backend-development-roadmap.md —— 仅追加一条夹具问题关闭记录
临时诊断探针
ZR01CLeakProbeTest已在诊断后删除,不进入提交。
10. 生产代码修改数 = 0
确认:本轮生产代码 diff = 0。 未修改 V1–V45、ErrorCode、DTO、公网接口、
pom.xml、docs/openapi-rc.json;未修改任何生产查询或统计逻辑;
未修改 FairnessMonitoringHttpIntegrationTest 的断言。
评分与 Fairness 组合测试污染已完成精确归因,并通过独立纯测试提交修复。