按 Prime Directive #10 记录。这是对我自己在 #3770 / #3867 里留下的测试形态的复盘,有实证。
实证:把闸门从生产接线上摘掉,没有一个测试会响
两个对照实验,各自只删掉「把探针递给服务」的那几行,不动实现、不动单测:
| 实验 |
改动 |
结果 |
| A |
从 service-analytics/src/plugin.ts 删掉 isRegisteredObject: (name) => … 那 5 行 |
service-analytics 299 全绿;dogfood(真实启动 + 真实 HTTP)395 全绿 |
| B |
从 metadata-protocol/src/protocol.ts 的 findData 删掉 this.assertObjectRegistered(request.object) |
立刻红 4 条 |
实验 A 的含义:#3867 的 cube 存在性闸门可以被一次重构整个删掉,仓库里没有任何测试会发现。 它只会打一行 logger.warn(stand-down 警告),而没人断言那行 warn。/analytics/query 会安静地退回到 #3867 修复前的状态 —— 也就是当时用 sqlite_master 实测过的「连接能看到的任何表都能读」。
为什么是这个不对称
不是运气,是测试形态的差别:
这正是 AGENTS.md Prime Directive #10 结尾那句话的镜像 —— 「case 标签不是强制;去看调用点」。#3106 当年漏的是 switch 里每个 case 都对但 updateMany 调用点没调它;这次方向反过来:实现和单测都对,接线没被测。
不是孤例,而且在扩散
packages/services/service-analytics/src/__tests__/measure-source-field-gate.test.ts:54 是后来新增的另一个闸门测试,同样以 isRegisteredObject: (n) => n === 'showcase_invoice' 注入。同一个模式正在被复制,所以缺口在扩大而不是收敛。
仓库里已有先例说明这类 boot 级 ratchet 是有价值的 —— analytics-rls.dogfood.test.ts 的文件头逐字写着:
Every pre-existing analytics RLS test injects getReadScope as a fake into a hand-built AnalyticsService. NONE booted the real plugin, so the getReadScope → security.getReadFilter auto-bridge had zero coverage — which is how the gap shipped.
#3597 就是这么漏出去的。#3867 现在处在完全相同的位置。
建议
加一条 boot 级 dogfood 用例(照 analytics-rls.dogfood.test.ts 的形态,bootStack + stack.apiAs),同时钉住两条接线:
关键是必须走真实插件启动,不能是 new AnalyticsService(...) —— 否则又在测服务而不是接线。
关联:#3770、#3867、PR #3866、PR #3875、#3597、#3106、ADR-0049。
按 Prime Directive #10 记录。这是对我自己在 #3770 / #3867 里留下的测试形态的复盘,有实证。
实证:把闸门从生产接线上摘掉,没有一个测试会响
两个对照实验,各自只删掉「把探针递给服务」的那几行,不动实现、不动单测:
service-analytics/src/plugin.ts删掉isRegisteredObject: (name) => …那 5 行metadata-protocol/src/protocol.ts的findData删掉this.assertObjectRegistered(request.object)实验 A 的含义:#3867 的 cube 存在性闸门可以被一次重构整个删掉,仓库里没有任何测试会发现。 它只会打一行
logger.warn(stand-down 警告),而没人断言那行 warn。/analytics/query会安静地退回到 #3867 修复前的状态 —— 也就是当时用sqlite_master实测过的「连接能看到的任何表都能读」。为什么是这个不对称
不是运气,是测试形态的差别:
protocol-unregistered-object.test.ts)驱动真实的protocol.findData,调用点在被测对象内部 → 删掉就红。cube-inference-gate.test.ts)把探针当配置注入:new AnalyticsService({ isRegisteredObject: … })。它测的是服务收到探针后的行为,不是有没有人把探针递给它。这正是 AGENTS.md Prime Directive #10 结尾那句话的镜像 —— 「
case标签不是强制;去看调用点」。#3106 当年漏的是switch里每个 case 都对但updateMany调用点没调它;这次方向反过来:实现和单测都对,接线没被测。不是孤例,而且在扩散
packages/services/service-analytics/src/__tests__/measure-source-field-gate.test.ts:54是后来新增的另一个闸门测试,同样以isRegisteredObject: (n) => n === 'showcase_invoice'注入。同一个模式正在被复制,所以缺口在扩大而不是收敛。仓库里已有先例说明这类 boot 级 ratchet 是有价值的 ——
analytics-rls.dogfood.test.ts的文件头逐字写着:#3597 就是这么漏出去的。#3867 现在处在完全相同的位置。
建议
加一条 boot 级 dogfood 用例(照
analytics-rls.dogfood.test.ts的形态,bootStack+stack.apiAs),同时钉住两条接线:GET /api/v1/data/<未注册对象>→ 404OBJECT_NOT_FOUND(曝露 gate 对未知对象放行所依赖的「data path 会 404」并不成立 —— findData 无存在性校验(#3545 同类前提) #3770 接线)POST /api/v1/analytics/query {"cube":"<未注册>"}→ 404(analytics /query 未做 cube 存在性校验,未注册名直达驱动当表名;且错误路径原样回显驱动 SQL(#3770 同类,另一子系统) #3867 接线)关键是必须走真实插件启动,不能是
new AnalyticsService(...)—— 否则又在测服务而不是接线。关联:#3770、#3867、PR #3866、PR #3875、#3597、#3106、ADR-0049。