test(rest): getDiscovery 测试替身发 routes,不再发 #4828 已退役的 endpoints (#5674) - #5788
Merged
Conversation
`packages/rest/src` 下 15 个测试文件的 `getDiscovery` 替身返回
`endpoints: { data, metadata, ui, auth }`,但真实生产者
(`packages/metadata-protocol/src/protocol.ts` 的 `getDiscovery()`)发的是
`routes`(`ApiRoutesSchema`,`DiscoverySchema` 里是必填键),从来没有发过
`endpoints`。`endpoints` 只在 dispatcher 那条路径上作为 `routes` 的逐字副本
存在过,#4828 已按 ADR-0049 删除,并在
`discovery-schema-conformance.test.ts` 钉死「no `features`, no `endpoints`」。
按键名(`data`/`metadata`/`ui`/`auth`)可知这些替身本意就是 `routes`,只是
拼错了对象;纯键名翻正,值保持作者原样(handler 每次请求都会用服务端配置
覆写 `data`/`metadata`/`ui`,这些槽位按构造就是占位符)。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DWUR56YsttL5sTF72Q75TQ
同一个已退役键的退化写法:另有 10 个文件写 `endpoints: {}`。issue 与派发单
的清单来自 `endpoints: { data:` 这个 grep,看不见它们,但它们拼的是同一个
#4828 已删除的 discovery 键,拷贝危害完全相同 —— 只修 15 个会让 issue 自己
写下的第一条理由(「唯一还在拼写已退役键的地方」)原样留存。
翻正为 `routes: { data: '', metadata: '' }`:`ApiRoutesSchema` 里
`data`/`metadata` 必填,所以 `routes: {}` 不是合法生产者形状;这个字面量与
`rest.test.ts:2601` 早已存在的正确替身(唯一真正调用 /discovery handler 的
那处)完全一致,取仓库内已有先例而非另发明一种。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DWUR56YsttL5sTF72Q75TQ
前两个 commit 把 25 个替身翻正了,但没有任何东西阻止它回来:生产面的
`discovery-schema-conformance.test.ts` 只看真实 producer 发出的 body,fixture
层它看不见 —— 而 issue 记这一笔的第一条理由就是「下一个写 rest 测试的人照抄
这个替身,退役键就在 fixture 层复活」。
新增 pin 扫描 `packages/rest/src/*.test.ts` 里 `mockResolvedValue({ … })` 形态的
getDiscovery 替身(当前 28 处 / 27 个文件,含两处本来就没拼错的),断言没有一处
带 `endpoints` 键,并带一条 anti-vacuity 下限(#4642 的教训:扫不到东西的扫描器
会以「全部干净」的样子通过)。
只断言否定面,不要求必须有 `routes`:`rest-server` 的 handler 是
`if (discovery.routes)`,将来有测试要驱动这条假分支是正当的。
反向验证(方向为事前预测的 RED):把 rest-batch-size-cap.test.ts 改回
`endpoints` → pin 立即失败并点名该文件
(expected [ 'rest-batch-size-cap.test.ts' ] to deeply equal []),改回后转绿。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DWUR56YsttL5sTF72Q75TQ
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckNo hand-written docs reference the 0 changed package(s). ✅ |
自查发现的自身缺陷:pin 扫描目录下所有 `*.test.ts`,**包括它自己** —— 而它的
docblock 为了说明问题,原样引用了带 `endpoints:` 的坏替身。它之所以没红,只是
因为 docblock 行把键前缀成了 `* `,而 `[{,]\s*` 这个 lead-in 不接受 `*`。也就是说
这条钉子的绿是由注释排版决定的:重排一次 docblock,它就会在自己的散文上失败。
改为显式排除自身文件名,并把理由写在常量旁边。同时把 anti-vacuity 注释里的
计数改成排除自身后的实测值:27 处 / 26 个文件(#5674 翻正的 26 处,加上
`rest-route-ledger.conformance.test.ts` —— 它 resolve 成裸 `{}`,从来没拼过退役键,
正是「只断言否定面」让它不受影响)。
反向验证(RED,事前预测)在两种拼法上都复验过:
- `rest-batch-size-cap.test.ts` 改回 `endpoints: { data: … }` → 点名该文件;
- `rest-dropped-fields.test.ts` 改回 `endpoints: {}` → 点名该文件。
两次改回后均转绿。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DWUR56YsttL5sTF72Q75TQ
baozhoutao
marked this pull request as ready for review
August 6, 2026 05:50
This was referenced Aug 6, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #5674
前提复核(基线 origin/main
fc5f536)issue 写 14 个文件,派发单实测 15 个 —— 复核结论:15 个文件 / 16 处写
endpoints: { data: … },与派发单一致,issue 的 14 偏低。真实 producer(packages/metadata-protocol/src/protocol.ts:2767起)发的是routes(ApiRoutesSchema,在DiscoverySchema:353是必填键),endpoints从来没被任何 producer 发过,#4828 已删除并由discovery-schema-conformance.test.ts:197-201钉死。前提成立。openapi-endpoints.*/served-endpoints.test.ts:48/request-schema-gate.conformance.test.ts:173的endpoints是「声明式端点清单」语义,不是 discovery 键 —— 甄别后不动,已复核。逐文件复核:issue 担心的行为变更,实测中不存在
issue 说改成
routes会让if (discovery.routes)从假翻真、handler 开始跑路由增补块(含probeMcpServeable),所以要一次一个文件复核断言。逐个文件查完,没有一个文件会执行那个块 —— 原因不是块被跳过了,而是这 25 个替身没有一个被喂给 discovery handler:每个文件驱动的都是/api/v1/data/:object、/api/v1/meta/:type/:name、/batch等具体路由,没有一处取GET /api/v1/discovery的 handler 来调用。这一点有个很强的旁证:全仓唯一真正调用 discovery handler 的两个 describe 就在
rest.test.ts(discovery — routes.mcp、discovery — capabilities.transactionalBatch),而它们各自覆盖掉了createMockProtocol()的 getDiscovery,换成{ routes: { data: '', metadata: '' } }(rest.test.ts:2601)—— 那次覆盖本身就是在说「共享替身的键是错的,这里用不了」。所以:没有任何文件的断言需要靠「块不执行」才成立,派发单里预留的那种例外(显式去掉 routes 键 + 注释说明)一处都没有用到。
改动(4 个 commit,26 个文件)
1d2c4f5— 15 个文件 / 16 处endpoints: { data: … }→routes: { data: … }。纯键名翻正,值保持作者原样:handler 每次请求都用服务端配置覆写data/metadata/ui,这些槽位按构造就是占位符,键才是缺陷。另在最容易被抄的那个共享替身(rest.test.ts的createMockProtocol())上加了 4 行说明。7d5ec37— 另外 10 个文件写endpoints: {},同一个退役键的退化写法。issue 与派发单的清单来自endpoints: { data:这个 grep,看不见它们。只修 15 个会让 issue 自己写下的第一条理由(「唯一还在拼写已退役键的地方」)原样留存,所以一并翻正为routes: { data: '', metadata: '' }——ApiRoutesSchema里data/metadata必填,routes: {}不是合法生产者形状;这个字面量取自仓库里早已存在的正确先例rest.test.ts:2601,而不是另发明一种。这 10 个文件同样一处也不碰 discovery 路由。8b92c51— 新增 pinpackages/rest/src/discovery-double-retired-key.test.ts。没有它,退役键可以随下一次复制粘贴无声回来:生产面的 conformance 测试只看真实 producer 发出的 body,fixture 层它看不见。d51adee— 修 pin 自身的一个缺陷(见下)。pin 的边界写在文件头,不含糊:只扫
mockResolvedValue({ … })这一种形态(排除自身后实测 27 处 / 26 个文件,即本 PR 翻正的 26 处 +rest-route-ledger.conformance.test.ts的mockResolvedValue({})—— 后者 resolve 成裸{},从来没拼过退役键);只断言否定面(不得有endpoints),不要求必须有routes—— handler 是if (discovery.routes),将来有测试要驱动这条假分支是正当的,rest-route-ledger那处就是现成的例子;并带一条 anti-vacuity 下限(#4642 的教训:扫不到东西的扫描器会以「全部干净」的样子通过)。commit 4:pin 自身的缺陷,自查发现
扫描目录下所有
*.test.ts包括 pin 自己,而它的 docblock 为了说明问题原样引用了带endpoints:的坏替身。它当时没红,只是因为 docblock 行把键前缀成了*,而[{,]\s*这个 lead-in 不接受*—— 也就是说这条钉子的绿由注释排版决定,重排一次 docblock 它就会在自己的散文上失败。改为显式排除自身文件名并把理由写在常量旁,不继承这份侥幸。反向验证(方向为事前预测的 RED)
预测:把任一文件改回
endpoints,新 pin 应当变红并点名该文件(它是新增的钉子,不是既有断言,所以是标准 Red 方向,不是 #5046 那种「诊断变多」或 #5018 那种反转)。两种拼法各验一次:两次改回后均转绿。方向与预测一致。
测试
(改前基线 55 files / 782 tests 全绿,改后 56/784 —— 增量正是新 pin 的 2 条。)
其余本地闸门:
pnpm lint(仓库级 ESLint,CI 同一条)干净;pnpm check:type-check-coverageOK(63/78 包 type-checked);node scripts/check-nul-bytes.mjsOK,并按控制字符自查规则对改动文件跑了grep -naP扫描,无命中。@objectstack/rest不声明typecheck脚本(它在 DEBT + TEST_DEBT 台账里),所以本包的pnpm typecheck无法运行 ——ERR_PNPM_RECURSIVE_RUN_NO_SCRIPT。这是既有台账状态、不是本 PR 造成的;不伪造一条绿的 typecheck 证据。界外发现
endpoints(#4828 退役键)、且capabilities拼成数组 #5787(已 filed,未认领,finding标签):packages/client/src/client.test.ts:36-44的 discovery 替身同样拼endpoints,且把capabilities写成字符串数组(spec 里是 record)。生产类型与生产代码都是对的(DiscoveryResult = GetDiscoveryResponse,无一处读discovery.endpoints),同属 observation-class。按「只改本 issue 的范围」不在本 PR 处理;本 PR 的 pin 按包扫描,不覆盖packages/client。Changeset
tests-only,不发布任何东西,故无 changeset,已带
skip-changeset标签(读回确认:tests,skip-changeset)。首个opened事件那次 Check Changeset 早于打标一步开跑、判红,属已知竞态;打标后由labeled事件触发的两次 Check Changeset 均为skipped(豁免生效)。