Skip to content

test(service-sms): sms settings provider 表 ↔ transports 双向契约测试 (#5773) - #5982

Merged
hotlong merged 1 commit into
mainfrom
claude/issue-5773-sms-manifest-provider-contract
Aug 6, 2026
Merged

test(service-sms): sms settings provider 表 ↔ transports 双向契约测试 (#5773)#5982
hotlong merged 1 commit into
mainfrom
claude/issue-5773-sms-manifest-provider-contract

Conversation

@hotlong

@hotlong hotlong commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Fixes #5773

mail 侧自 #5094 起有一道可执行的闸门,把 mail.manifest.ts 的 provider 取值与 EMAIL_TRANSPORT_PROVIDERS / makeTransport 双向锁死;sms 侧一直只是两份独立维护的字面量「恰好一致」,任一方向漂移都不会有任何东西报警。PR #5771(#5713)把 sms transports 的词表提成具名导出 SMS_TRANSPORT_PROVIDERS 之后,写这道测试的原料齐备,本 PR 补上。

前提复核(实施前,基于 origin/main 5582e18)

正文四条事实全部成立,无一失效:

  • SMS_TRANSPORT_PROVIDERS = ['log','aliyun','twilio'] as consttransports/index.ts:27,isSmsTransportProvider() 在 :38,均经 service-sms/src/index.ts:13-14 出包;SmsProviderTag 由数组派生。
  • sms.manifest.tsprovider select 仍是三项字面量,未变。
  • sms.manifest.test.ts 仍只断言 schema / 权限 / 两个密钥字段,无任何取值集合比对
  • grep smsSettingsManifest 全仓命中只在 service-settings 包内部,transport 侧无消费者。

落点判定(正文列的第一步待决项)

实测两个方向都不成环,所以这不是被依赖图逼出来的选择,而是架构选择:

方向 依赖闭包(含自身 devDeps) 成环?
service-settings core, metadata-core, platform-objects, spec, types 不含 service-sms
service-sms core, spec 不含 service-settings

选了「测试住 service-sms(transport owner),service-settings 作 devDependency」,与 mail 先例同向(plugin-emailmail-manifest-providers.contract.test.ts,service-settings 是它的 devDependency,test-only 无运行时边)。理由:service-settings 是与具体 provider 无关的 manifest 注册表,它为 mail / sms / storage… 各发一张表;若让它反向 import 每一个 provider 实现,每加一张表就多一条边,注册表逐步变成所有实现的汇聚点。由 provider 自证它满足已发布的契约,箭头才只有一个方向。

一条不构成区分度、但值得写明的事实:两侧今天都不会对这个测试文件做类型检查 —— service-smstypecheck 脚本但 tsconfig 排除了 **/*.test.ts(TEST_DEBT 在册),service-settings 则根本没有 typecheck 脚本(DEBT 在册)。所以此项不参与落点判定,由上面的分层理由定。

测试内容(两层比对,照 mail 先例)

第一层 —— 集合等价,两个方向各自独立成 it(正文要求「各自变红」),断言信息点名漂移的取值:

第二层 —— 可构建性:每个下拉项都要 makeSmsTransport正确的 transport 类(不是「没抛异常」);再加 isSmsTransportProvider 与下拉框同词表(#5713 的 CLI 启动期拒收判据必须与 settings 面同一份词表),以及默认值 log 既在表内又可构建。

反向验证(方向先预测,含一次预测落空)

三个探针,每次改完重新 build service-settings 再跑(测试读的是构建产物):

探针 预测 实测
A:manifest 加假 provider sendgrid_sms ⊆ 红并点名 ✅ 3 红:⊆ 点名 ["sendgrid_sms"]、无 build recipe、predicate 拒收
B:manifest 删掉 twilio 选项 ⊇ 红并点名,恰好 1 条 ⚠️ 首轮实测 2 条,见下
C:从数组侧删掉 twilio ⊆ 红 + fixture 覆盖红 ✅ 3 红,均点名 twilio

探针 B 首轮预测落空,而且落空是有价值的:多出来的第二条红来自我原先照抄 mail 先例写的三行 expect(byProvider.twilio).toBeInstanceOf(TwilioSmsTransport) —— 那三行把词表在本文件里又抄了第三遍,而这正是本测试存在的理由(「一份字面量可以靠改另一份字面量来『修好』」)。它的副作用是双向的:删一项时重复报红,加一项时反而静默不覆盖(新 provider 没有对应断言行也不会红)。

改法:把 build 参数与期望类合并成 PROVIDER_FIXTURES 表,并加一条 exercises every provider in the vocabulary 断言表的键集 ≡ SMS_TRANSPORT_PROVIDERS。改完后 B 恰好 1 红(⊇ 点名 ["twilio"]),A 仍 3 红,C 变成 3 红(新增的 fixture 覆盖断言接住了「加了 provider 没加 fixture」)。

该断言刻意写成运行时断言而非 Record< SmsProviderTag, … > 类型层穷尽:本包 tsconfig 排除自己的测试文件,类型层写在这里没有任何 tsc program 会求值 —— 就是 #5286 的 phantom check 形状,删掉它每道闸门照样绿。

其余改动

  • sms.manifest.ts provider 表处加注释(「a CONTRACT, not a menu of aspirations」形),指名执行它的测试文件路径与两个方向。
  • scripts/check-type-check-coverage.mjs 的 service-sms TEST_DEBT:tests: 3 → 4。实测(临时解除 exclusion 跑 tsc --noEmit)错误数仍是 1(transports.test.ts 那条既有 TS2493),新文件本身零类型错误 —— 数在动、错误没动,note 里写明了。

验证

逐条枚举 .github/workflows/lint.yml,凡 diff 可能触及的全跑(容器级验证锁 + heap 上限 + --filter 范围化):lint / check:nul-bytes / check:type-check-coverage / check:published-files / check:engine-double-contract / check:startup-registry-verdict / check:durability-log-level / check:service-providers / check:adr-anchors / check:doc-authoring / check:i18n / check:i18n-coverage 全绿;service-sms + cli typecheck 绿;service-settings 15 文件 243 测试、service-sms 4 文件 39 测试全绿。

(check:i18n / check:i18n-coverage 首轮红是新 worktree 的前置未满足 —— 未构建 CLI、未全量 build,AGENTS.md §9 那一类;补上 turbo run build 后两者均绿,与本 diff 无关。)

⛔ 无 changeset

最终 diff = 新测试 + 一段源码注释 + 一条 devDependency + lockfile + 一个 ledger 计数,不含任何运行时行为变化,按 issue 要求不写空 changeset。已自行打 skip-changeset 标签。


Generated by Claude Code

mail 侧自 #5094 起有一道可执行的闸门把 `mail.manifest.ts` 的 provider 取值与
`EMAIL_TRANSPORT_PROVIDERS` / `makeTransport` 双向锁死;sms 侧一直只是两份独立
维护的字面量「恰好一致」,任一方向漂移都不会有任何东西报警。

PR #5771(#5713)把 sms transports 的词表提成具名导出 `SMS_TRANSPORT_PROVIDERS`
后,原料齐备,本次补上对应的契约测试:

- 新增 `packages/services/service-sms/src/sms-manifest-providers.contract.test.ts`
  —— 两个方向各自独立成 `it`(⊆ 提供了没有 transport 的选项 / ⊇ 有 transport 却
  选不到),断言信息点名漂移的具体取值;再叠一层可构建性比对(每个选项都要
  `makeSmsTransport` 出正确的 transport 类)以及 `isSmsTransportProvider` 与下拉框
  同词表。
- 落点按依赖图判定:测试住 transport owner(`service-sms`),`service-settings`
  作 devDependency —— 与 mail 先例(`plugin-email` 持测试)同向,两个方向均不成环。
- `sms.manifest.ts` 的 provider 表处加「a CONTRACT, not a menu of aspirations」注释,
  指向这道测试。
- `check-type-check-coverage.mjs` 的 service-sms TEST_DEBT 条目 tests 3 → 4(实测:
  解除 exclusion 后错误数仍是 1,新文件本身零类型错误)。

无运行时行为变更,不写 changeset。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015a5qkLzpGXhLL2F5gvJ7dD
@vercel

vercel Bot commented Aug 6, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 6, 2026 1:51pm

Request Review

@github-actions github-actions Bot added the size/m label Aug 6, 2026
@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/service-settings, @objectstack/service-sms.

8 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/kernel/runtime-services/audit-service.mdx (via packages/services/service-settings)
  • content/docs/kernel/runtime-services/index.mdx (via packages/services/service-settings)
  • content/docs/kernel/runtime-services/settings-service.mdx (via packages/services/service-settings)
  • content/docs/permissions/authentication.mdx (via @objectstack/service-sms)
  • content/docs/plugins/packages.mdx (via @objectstack/service-settings, @objectstack/service-sms)
  • content/docs/releases/implementation-status.mdx (via @objectstack/service-settings)
  • content/docs/releases/v14.mdx (via @objectstack/service-sms)
  • content/docs/releases/v9.mdx (via @objectstack/service-settings)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@github-actions github-actions Bot added dependencies Pull requests that update a dependency file tests labels Aug 6, 2026
@hotlong hotlong added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 6, 2026 — with Claude
@hotlong
hotlong marked this pull request as ready for review August 6, 2026 14:05
@hotlong
hotlong added this pull request to the merge queue Aug 6, 2026
Merged via the queue into main with commit 8faa067 Aug 6, 2026
30 of 31 checks passed
@hotlong
hotlong deleted the claude/issue-5773-sms-manifest-provider-contract branch August 6, 2026 14:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

sms settings 的 provider options 表与 SMS transports 之间没有契约测试 —— mail 有,sms 没有(#5094 同形,目前两端恰好一致)

2 participants