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.
9.1 KiB
9.1 KiB
W3-T9 独立评审报告
评审结论
- Verdict:PASS
- T9 是否允许提交:允许
- 是否允许继续 T10:允许继续实现与文档阶段,但不得在本次全量回归未全绿前判定 T10/B3 验证通过
- 评审范围:
mydreamjob-server/src/test/java/com/mydreamjob/skill/SkillCloudEndToEndIntegrationTest.javatarget/skill-cloud-t9-red.logtarget/skill-cloud-t9-green.logtarget/skill-cloud-t2-t9-regression.logtarget/skill-cloud-t9-full-regression.logtarget/skill-cloud-t9-unrelated-failures-isolated.log- 修订后的 contract、design、tasks、三份 spec,以及 T8 Controller
- 评审方式:静态核对测试与生产链路,使用 JDK 17 / Maven 3.9.7 独立执行指定 T2–T9 测试组、四个失败测试类及完整
mvn test。未修改实现、测试或其他源码文件。
T9 独立结论
真实链路
T9 使用 @SpringBootTest、@AutoConfigureMockMvc、@ActiveProfiles("integration-test") 和独立 H2 数据库,实际经过 Controller、Security、ProfileService、Skill 查询/匹配/学习路径 Service、Repository、Flyway 与 V36 种子数据。仅 RedisTemplate 被 Mock,核心业务链路没有被替换。
可证伪证据:
SkillCloudEndToEndIntegrationTest.java:48-55:真实 Spring 上下文、MockMvc、H2 和生产数据库 URL/Profile。:71-90:注入真实 MockMvc、JdbcTemplate、JwtTokenProvider、Repository;唯一@MockBean为 RedisTemplate。:100-134:真实 STUDENT JWT 和真实 StudentProfile 调用三个端点,断言后端职位返回 16 项真实 V36 技能、rank 连续、heat 降序。:269-304:匹配 union、命中/缺失顺序、学习路径 priority/顺序、建议非空均有断言。- 无 Flyway 或种子数据时,依赖的固定节点、16 项技能和 V36 来源字段无法成立;测试已独立通过,证明不是纯 Mock 测试。
词云 TOP10 与匹配一致性
T9 从真实词云结果直接取 allSkills.subList(0, min(10, allSkills.size())) 作为 TOP10,再验证:
totalCount等于实际 TOP10 大小。matchedCount等于 matchedSkills 大小。- matchedSkills 与 missingSkills 的并集严格等于词云 TOP10。
- matchedSkills 保持词云顺序,missingSkills 保持词云顺序。
matchScore等于round(matchedCount * 100 / expectedTop10.size())。- 3 项技能场景验证
1/3 = 33,并验证空热度末位。
该设计可证伪“匹配服务另起排序”“TOP10 与实际列表不一致”“missingSkills 丢项或重排”等缺陷。
学习路径
T9 将学习路径逐项与 match.missingSkills 对比:
- 总数为缺失技能数。
- 每项
priority从 1 连续递增。 skillId、skillName与缺失技能逐项一致。suggestion非空。- 已命中的技能不会出现在学习路径中。
确定性、空场景、身份与隐私
:123-133:三个端点重复调用后data完全相等,并断言 8 张相关表的行数不变。:179-225:事务内插入空分类,验证词云、匹配、学习路径分别返回空列表和 0 值。:227-240:学生 JWT 额外传入 COMPANY 的userId、studentId,三个端点结果均不被覆盖。:242-267:成功响应不包含skills_json、手机号、邮箱、简历 URL、简历原始文件名和 JWT;错误响应不包含异常类、Spring 内部路径、SQL 或堆栈。
T9 findings
- Critical:无。
- Important:无。
- Minor T9-M1:无写入断言以 8 张表行数前后不变为依据,可发现 INSERT/DELETE,但不能发现仅 UPDATE 同一行而不改变行数的情况。当前三个 Service 均为只读事务且测试通过,因此不阻断;后续可增加事务/数据库快照或只读约束断言。
- Minor T9-M2:隐私测试未单独断言
realName、bio的哨兵值,也未显式检查skillsJson驼峰名;已检查字段覆盖敏感号码、邮箱、简历标识、原始skills_json和 Token,风险为受控 DTO 下的低风险,不阻断。 - Minor T9-M3:T9 的匹配一致性验证以结果中的 matchedIds 反推期望,因此主要证明“匹配结果与 TOP10 一致”,不独立证明具体学生技能命中哪一项标准技能;别名和匹配判定本身已由 T6 测试覆盖,不阻断。
测试隔离与假绿风险
- T9 类级
@Transactional,创建的 Student、StudentProfile 和测试用空分类在方法结束后回滚。 - T9 使用独立 H2 URL,不与其他测试类共享数据库名。
- 空分类使用固定测试 ID 并依赖事务回滚,没有观察到跨方法或跨类污染。
@BeforeEach仅重置 Redis Mock,不依赖测试执行顺序。- 未发现共享 H2、事务传播或顺序依赖导致的假绿。
独立测试结果
T2–T9 指定测试组
执行命令:
$env:JAVA_HOME='D:\java\jdk'
& 'D:\maven\apache-maven-3.9.7\bin\mvn.cmd' '-Dtest=SkillTaxonomyMigrationTest,SkillCloudSeedDataIntegrationTest,SkillTaxonomyRepositoryTest,SkillCloudQueryServiceTest,SkillMatchServiceTest,SkillLearningPathServiceTest,SkillCloudControllerTest,SkillCloudAuthorizationIntegrationTest,SkillCloudEndToEndIntegrationTest' test
独立结果:
Tests run: 71, Failures: 0, Errors: 0, Skipped: 0
BUILD SUCCESS
Total time: 01:08 min
分类统计:
- SkillTaxonomyMigrationTest:2
- SkillCloudSeedDataIntegrationTest:8
- SkillTaxonomyRepositoryTest:6
- SkillCloudQueryServiceTest:8
- SkillMatchServiceTest:16
- SkillLearningPathServiceTest:10
- SkillCloudControllerTest:14
- SkillCloudAuthorizationIntegrationTest:2
- SkillCloudEndToEndIntegrationTest:5
合计:71。
全量测试
执行命令:
$env:JAVA_HOME='D:\java\jdk'
& 'D:\maven\apache-maven-3.9.7\bin\mvn.cmd' test
独立结果:
Tests run: 1393, Failures: 2, Errors: 9, Skipped: 0
BUILD FAILURE
Total time: 01:57 min
失败集合与实现者日志一致。
全量失败归因
T9 阻断
无。全量失败集合中没有任何 Skill Cloud 测试或依赖 T9 测试数据的用例。
项目既有失败
1. CareerProgressServiceTest.fullAggregation_returnsCorrectSummaries
- 位置:
CareerProgressServiceTest.java:213。 - 现象:期望 pendingOfferResponseCount=1,实际=0。
- 根因:测试构造的 Offer 固定过期时间
2026-09-05T23:59:59;当前日期为2026-09-15,生产CareerProgressService.buildTodo()使用expireAt.isAfter(now)过滤,因此该 Offer 已过期。属于时间依赖的既有测试问题。 - 与 N03 关系:该测试和生产类自
bf75fc0后未在d00a630..HEAD中修改,且在不运行任何 Skill Cloud 测试时独立复现。
2. JobReviewDimensionControllerTest.listReviews_shouldReturnDimensionScores
- 位置:
JobReviewDimensionControllerTest.java:160-162。 - 现象:HTTP 200 期望,实际 500;
JobReviewController.list收到 null page。 - 根因:Controller 已调用 7 参数
listReviews(jobId, ratingType, createdFrom, createdTo, unreplied, page, size),测试仍 stub 旧的 3 参数listReviews(JOB_ID, 0, 20),Mockito 未命中导致 null。 - 与 N03 关系:相关 Controller、Service 和测试均未在
d00a630..HEAD中修改,且独立复现。
3. JobReviewDimensionServiceTest.listReviews_shouldReturnDimensionScoresWithoutN1
- 位置:
JobReviewDimensionServiceTest.java:206-210。 - 现象:
reviewRepository.findPublicReviews(...)返回 null,触发JobReviewService.java:169NPE。 - 根因:测试仍 stub 旧方法
findByJobIdAndStatusAndModerationStatusInOrderByCreatedAtDesc(...),与当前实现调用的findPublicReviews(...)不一致。 - 与 N03 关系:相关 Repository、Service 和测试均未受 N03 变更修改,且独立复现。
环境阻塞
4. LocalResumeStorageServiceTest
- 现象:8 项测试在 JUnit extension context 清理阶段报
AccessDeniedException,路径为 Windows 临时目录下的junit-*。 - 根因:
WindowsPath.toRealPath()在测试@TempDir清理时访问被拒,测试业务逻辑本身未报断言失败。 - 与 N03 关系:属于 Windows 临时目录权限/沙箱环境问题;相关类和测试自初始提交后未受 N03 修改,且独立复现。
门禁决定
- T9:PASS。T9 测试具备真实持久化链路、TOP10 一致性、学习路径顺序、确定性、空场景、身份和隐私断言,并已通过 T2–T9 指定测试组。
- 提交 T9:允许仅提交
SkillCloudEndToEndIntegrationTest.java及本评审报告;不得把当前工作区其他无关改动混入该提交。 - 继续 T10:允许进入 T10 的实现与文档工作,但必须先明确处理全量
mvn test的 11 个非 T9 失败。可以将其登记为项目既有/环境阻断并单独修复,或在项目层面正式接受并记录豁免;在此之前不得宣称 TO-11、B3 或发布级验证通过,也不得执行最终收尾。 - 剩余阻断:仅存在项目级全量回归阻断,不存在 T9 阻断。
证据限制
- 本次验证使用 H2
MODE=MySQL和 Flyway,未执行真实 MySQL 黑盒迁移;MySQL 验证仍应在 T10/发布级验证门禁中完成。 - 本次评审未修改任何实现或测试源码。