Files
patbond-doc/docs/development/iterations/iteration-3/19-contract-sync-report.md
T
lixi a611acb358
CI / docs-build (push) Successful in 32s
docs: M3 第二波收口——报告 15~20 入档挂导航
- 15~17 社区后端纵切三单(帖子/Feed 作者链路/评论互动关注,226→310)
- 18 契约冻结 v1.3.0(31 路径/43 操作,26 项修正照单全收)
- 19 快照同步与全仓契约矩阵(173 格零漂移,修 allOf 校验盲区,→325)
- 20 收口总表:定型语义汇总(第三波接入依据)与质量事件记录

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-09 11:38:42 +08:00

83 lines
7.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# M3 契约同步报告(api 侧:v1.3.0 字节级快照同步 + community/media 契约矩阵入场)
> 作者:Senior Developer(后端)
> 日期:2026-09-09
> 工单:契约冻结 v1.3.0 的 api 侧收尾(18 号冻结报告 §5-2 注明的硬依赖工单)
> 输入:doc 仓 `docs/api/openapi.yaml` v1.3.0main@f84847631 路径 / 43 操作 / 72 schemas);patbond-api dev@7f1dd33310 测试基线)
> 结论先行:**正典 v1.3.0 已字节级复制为四个模块的 `openapi-v1.3.0.yaml` 快照(md5 与正典逐一比对一致),pet/auth 守卫期望同步升版;community 域 17 操作 64 单元格、media 域 2 操作 8 单元格的契约一致性测试全响应矩阵入场,均零豁免;实现与冻结契约零漂移(64+8 格无一漂移报告);发现并修复框架级校验盲区一处(ContractValidator 不支持 v1.3.0 引入的 `nullable + allOf: [$ref]` 模式,会静默跳过 coverImage/replyToUser 内部校验);mutation 自证两轮通过(普通路径 + allOf 定向路径注毒均红、还原即绿);全套 325 测试全绿(310 + 15),`check-secrets.sh --all` 通过。**
---
## 1. 快照同步(字节级)
| 位置 | 旧 | 新 | 处置 |
| --- | --- | --- | --- |
| `patbond-pet/src/test/resources/contract/` | openapi-v1.2.0.yaml | openapi-v1.3.0.yaml | 替换(删旧) |
| `patbond-auth/src/test/resources/contract/` | openapi-v1.2.0.yaml | openapi-v1.3.0.yaml | 替换(删旧) |
| `patbond-community/src/test/resources/contract/` | —(新建) | openapi-v1.3.0.yaml | 新增 |
| `patbond-user/src/test/resources/contract/` | —(新建) | openapi-v1.3.0.yaml | 新增 |
- 四份快照 md5 与 doc 仓正典(main@f848476)逐一比对一致(`5b550fabf8e94b715ac1161798cb2738`),满足「字节级复制」纪律。
- **旧 v1.2.0 快照删除而非保留**:每个模块的 `OpenApiContract.RESOURCE` 常量只认一份快照文件,守卫测试锁 `info.version`,保留旧文件只是死重——历史版本由 git 历史与 doc 仓承载。
- pet/auth 守卫期望同步升版:`1.2.0/18 路径/24 操作/45 schemas``1.3.0/31/43/72`;各域 `operationsTagged` 断言不变仍绿(pets 域 18 操作、auth 域 6 操作在 v1.3.0 中零变化,即 v1.2.0 冻结面未被 1.3.0 触碰的实证)。
- 契约框架(OpenApiContract + ContractValidator)按既有的模块内复制纪律扩为四份同构副本(pet/auth/community/user),同步纪律注释已改为「四模块各复制一份、各自更新期望」。
## 2. 覆盖矩阵规模(本单新增 19 操作 / 72 单元格,零豁免)
| 域 | 模块 | 测试类 | 操作 | (操作, 状态码) 单元格 | 豁免 |
| --- | --- | --- | --- | --- | --- |
| communityposts/feed/comments/interactions/follows | patbond-community | CommunityContractConformanceTest | 17 | 64 | **0** |
| media(两步上传,属 user 模块) | patbond-user | MediaContractConformanceTest | 2 | 8 | **0** |
| pets/dictionaries/health-records(既有) | patbond-pet | ContractConformanceTest | 18 | 82 | 1(沿用) |
| auth/user/analytics(既有) | patbond-auth | AuthContractConformanceTest | 6 | 19 | 0 |
| **合计(v1.3.0 全部 43 操作)** | 4 模块 | 4 类 | **43** | **173** | **1** |
- 机制与 pet 侧 T2-09 完全同构:真实起服务发请求(community 走 MockMvc + postgres:18 Testcontainer 全迁移链;media 走真实 MinIO Testcontainer,直传为真实 HTTP PUT)→ 严格校验器逐字段比对(未声明字段即报漂移)→ 末位全矩阵门禁断言每个声明单元格都被真实响应触发过。
- community 域覆盖要点:错误码全谱 40000/40101/40301/40401/40403/40404/40405/40406/40902/40905/42203/42204 各至少一格实证;双业务码单元格(POST /posts 404 的 40401/40405、POST comments 404 的 40403/40406)两种业务码分别触发;分页信封 hasMore/nextCursor 两态、coverImage 与 replyToUser 的 null/非空两分支、防枚举合并语义(幽灵 id 与他人 draft 同响应)均在矩阵内。
- media 域覆盖要点:201 凭据形态、直传后 complete 200(含幂等重复确认)、400mime 白名单外 + 畸形 assetId)、401、404 防枚举合并(他人 asset 与幽灵 asset 同答 40405)、422/42205(直传前确认)。
### 豁免格清单
**本单新增矩阵零豁免**——community 域的 409 均为幂等键/乐观锁冲突、422 均为业务规则拒绝,media 域 422 为状态机拒绝,单线程 MockMvc 均可确定性触发。全仓唯一豁免格仍为 pet 侧沿用的 `PATCH /api/v1/care-reminders/{reminderId} 409`(并发条件更新守卫落空,单线程无法确定性构造,行为语义由并发一致性设计文档背书)。
## 3. 发现并修复的漂移清单
### 3.1 实现 ↔ 冻结契约:零漂移
新增 72 单元格全部一次通过严格校验,无字段名/类型/必填/nullable/枚举/格式漂移,无需修实现;未发现语义级冲突。这与第二波「先定型表、后冻结照单全收」的流程预期一致——契约本就是按已定型实现冻结的,本单是对「冻结稿与实现零偏差」声明的全矩阵实证。
### 3.2 框架级校验盲区一处(发现并修复)
- **问题**v1.3.0 为表达「可空的 $ref」引入 `nullable: true + allOf: [$ref]` 模式(`FeedCard.coverImage``Comment.replyToUser`),而既有 ContractValidator 明文只支持无 allOf 子集——遇到该模式会解析出 `type=null` 而**静默跳过内部校验**coverImage/replyToUser 里新增泄漏字段或类型漂移将无法被察觉,属校验盲区而非误报。
- **修复**:四份 ContractValidator 副本同步加入单分支 allOf 展平合并(分支键先入、同级键——如外层 nullable——胜出;冻结契约只用单分支 allOf,浅合并即精确),并以定向 mutation 证明该路径生效(见 §4)。
## 4. mutation 自证(注毒应红、还原应绿)
| 轮次 | 注毒点 | 预期 | 实测 |
| --- | --- | --- | --- |
| 1a | community 快照 `PostMediaItem.required` 注入假必填字段 | 红 | 5 测试失败,`$.data.media[0].fakeContractField: 契约必填字段缺失` |
| 1b | user 快照 `MediaAsset.required` 注入假必填字段 | 红 | 2 测试失败,`$.data.fakeContractField: 契约必填字段缺失` |
| 2 | community 快照 `coverImage` 的 allOf 同级注入 `required: [fakeAllOfField]`(定向打 allOf 合并路径) | 红 | `GET /api/v1/feed 200` 漂移:`$.data.items[*].coverImage.fakeAllOfField: 契约必填字段缺失` |
| 还原 | 四快照 cp 回正典并 md5 复核 | 绿 | 全套 325 测试全绿 |
第 2 轮专为 §3.2 的修复自证:假必填字段被报告在 **coverImage 内部**,证明 allOf 合并后校验器确实下钻到了此前静默跳过的分支。
## 5. 测试数变化
| 模块 | 基线 | 现在 | 增量 |
| --- | --- | --- | --- |
| patbond-common | 3 | 3 | — |
| patbond-user | 96 | 100 | +4MediaContractConformanceTest |
| patbond-auth | 39 | 39 | —(守卫期望升版,数量不变) |
| patbond-pet | 89 | 89 | —(守卫期望升版,数量不变) |
| patbond-community | 83 | 94 | +11CommunityContractConformanceTest |
| **合计** | **310** | **325** | **+15** |
`JAVA_HOME=/usr/lib/jvm/java-17-openjdk ./mvnw clean test` 全绿;`scripts/check-secrets.sh --all` 通过(快照与测试无敏感信息,MinIO 凭据沿用 dummy 占位值先例)。
## 6. 遗留与交接
- 契约同步纪律自此为**四处复制**:doc 仓正典升版 → 四模块同字节复制新快照 + 各守卫期望更新,任一处忘记同步 CI 即红(守卫锁 `info.version` 与三项计数)。
- 契约测试框架仍为模块内四份同构副本(与 BearerAuthFilter 同纪律);若第三迭代后副本继续增多,可评估抽入 patbond-common 的 test-jar,此次不动。
- 本报告(19 号)随波末统一挂导航入档;mkdocs.yml 本次不动;doc 仓 openapi.yaml 本单未触碰。