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

W3-T9 独立评审报告

评审结论

  • Verdict:PASS
  • T9 是否允许提交:允许
  • 是否允许继续 T10:允许继续实现与文档阶段,但不得在本次全量回归未全绿前判定 T10/B3 验证通过
  • 评审范围:
    • mydreamjob-server/src/test/java/com/mydreamjob/skill/SkillCloudEndToEndIntegrationTest.java
    • target/skill-cloud-t9-red.log
    • target/skill-cloud-t9-green.log
    • target/skill-cloud-t2-t9-regression.log
    • target/skill-cloud-t9-full-regression.log
    • target/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:169 NPE。
  • 根因:测试仍 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/发布级验证门禁中完成。
  • 本次评审未修改任何实现或测试源码。