Skip to content

Commit acd190b

Browse files
ci(test-source-alias): follow a changed test into the source it loads, and red a cold literal import() paid there (#30969)
<!-- claude-projects-attribution: {"github_login":"os-elon-musk"} --> _Requested by **Elon** · [project thread](https://claude.ai/code/project/chan_01WedDzV26nniwxv7jC88B9D?thread=cmsg_01WedDzV26nniwxv7jC88B9DWeevjnj6yeVVrex8nUuQRA)_ domain:devx#1 · os-elon-musk · session_016WV4d1Nibc5bxtbQZv6ssS Fixes #30909 Clause-②: no Metadata-protocol: none ## 维护者速读 - **改了什么**:`check:test-source-alias` 的时钟窗口规则新增第三个读取器:顺着测试收集期的导入链走进同包被测源码,找出藏在函数体里的字面量 `import()`(该包经 `dist/` 解析的那批 specifier),要求测试在模块顶部预热或 mock;只判本次改动新增或修改的测试文件。 - **为什么改**:#30873 的测试经 `rest-server.ts` 走到回填路由里的 `await import('@objectstack/objectql')`,冷加载 3.2 秒落在 5000 毫秒窗口里,把无关 PR 的 Test Core 打红;门禁当时 exit 0。 - **风险与回滚**:只改一个门禁脚本,不动产品代码与测试;误报方向是让作者多写一行模块顶部 import。回滚即 revert。全量扫描耗时 32 秒 → 24 秒(加了解析缓存)。 - **先看**:① `sourceReachFindings` 与 `reachEdgesOf`(可达与预热的判定);② 入口处 `changedFilesSinceMergeBase`(只判改动测试、找不到 merge base 判红);③ 头部新节 "The source reach" 里为什么不对全部测试判红的实测。 - **两分钟验证**:`git show 98f640d:packages/rest/src/meta-write-door-capability-enumeration.test.ts > packages/rest/src/meta-write-door-capability-enumeration.test.ts && node scripts/check-test-source-alias.mjs`,预期 exit 1 并点名 `meta-record-currency-backfill-route.ts:177`;之后 `git checkout HEAD -- packages/rest/src/meta-write-door-capability-enumeration.test.ts`。 - **需要你做的**:无,席位落地。 ## What changes One file, `scripts/check-test-source-alias.mjs`. - **A third reader for the clocked-window rule, "the source reach".** From each test file it follows every same-package file the test loads during COLLECTION (a static import or a module-scope dynamic one), transitively, never across a bare specifier. A literal dynamic load of a specifier the package resolves through `dist/` (the rule's existing population, unchanged) inside a function body of a reached file is a finding against the test, unless a file in the same reach loads that specifier at module scope or the test `vi.mock`s / `vi.doMock`s it. The remedy is the line the rule already prints: a module-top `import 'SPEC'` in the test file. The failure names the specifier, every site of it in the reach, and the import chain to the first one. - **Scope: the test files a change adds or edits.** Measured from the merge base with `origin/main` (committed, staged, unstaged and untracked alike); `--changed-from REF` names another base and `--reach-all` runs the census over every test file. No merge base is a failure, never a pass. On a `main` push the base is HEAD and the half judges nothing; on a PR or a queue build it judges that change's test files. The `Lint · CI, release & engine gates` job already checks out with `fetch-depth: 0`, so no workflow edit. - **Why not every test file.** Static reach is not execution: `rest-server.ts` registers every route, so every rest test that builds a `RestServer` reaches every handler's guarded import while only the cases that call that handler pay it. Over every test file at `main` 0e88208, the reach flags 2307 test files / 5491 (test, specifier) pairs, from only 89 source sites in 53 files. The header's new section "The source reach" records this and the boundaries. - **Speed.** Each file's edges are read once per scan, a file with no dynamic form skips masking, and relative resolution is cached; the full scan measured 32.0 s before and 24.3 s after on this container (4 vCPU), the census over every file 34.8 s. - **Self-test:** a new battery, "the source reach: a load the source under test pays (#30909)", 12 cases over six fixtures: the 98f640d shape (test → hub → route, two collection-time hops) reported with its site and chain; the remedy, a load warm elsewhere in the reach, a `vi.mock`, and a lazy relative hop all silent; one finding per specifier when the direct reader already reports it; the scope judged in and out; the direct reader left unscoped. `SELF_TEST_BATTERY_FLOOR` 11 → 12. ## Tests - `node scripts/check-test-source-alias.mjs --self-test` → `check-test-source-alias --self-test OK`. - Three mutations, each committed-then-restored with `git checkout HEAD --` and `git diff HEAD` empty: dropping the warm-set check reds two cases (remedy, warm-in-reach); dropping the scope filter reds both scope cases; dropping the mock read reds the mock case. - **Negative control (the card's ask):** with `packages/rest/src/meta-write-door-capability-enumeration.test.ts` at its `98f640d5` blob, `node scripts/check-test-source-alias.mjs` exits 1: ```text packages/rest/src/meta-write-door-capability-enumeration.test.ts:60: reaches `import('@objectstack/objectql')` in the source under test, at packages/rest/src/meta-object-state-routes.ts:235, packages/rest/src/meta-record-currency-backfill-route.ts:177 — inside a function body, a CLOCKED window for whichever case first calls it. Reached during collection through (the first site): packages/rest/src/meta-write-door-capability-enumeration.test.ts → packages/rest/src/rest-server.ts → packages/rest/src/meta-object-state-routes.ts ``` Restored with `git checkout HEAD --`, `git status --porcelain` clean of it; the same command then exits 0. ## Acceptance notes - **How many existing test files the extended rule flags:** 0 under the CI scope (no test file changes here). Over every test file (`--reach-all`, `main` 0e88208): 2307. By package: `ui/packages/app-shell` 932, `packages/runtime` 387, `ui/packages/components` 287, `packages/metadata-protocol` 200, `packages/rest` 187, `packages/cli` 179, `packages/services/service-datasource` 64, `packages/verify` 37, `packages/metadata` 18, `packages/cloud-connection` 6, `ui/scripts/__tests__` 6, `packages/plugins/plugin-dev` 4. Reproduce the full list with `node scripts/check-test-source-alias.mjs --reach-all`. They are not fixed here (over the card's "fix if small" bound): each is fixed with one module-top line when a change next touches it. - In `packages/rest` only two specifiers stay cold, `@objectstack/objectql` and `@objectstack/spec/ui`. The card's other latent sites (`@objectstack/spec/api` in `batch-routes.ts` / `crud-routes.ts`, `@objectstack/spec/system` in `form-routes.ts` / `meta-item-read-gate.ts`) are not flagged: another file in the same reach already imports those subpaths statically, so they are paid during collection today. - Boundaries (header): a lazy relative hop is not followed; a module-top import of another workspace package whose `dist/` loads the specifier is not credited (the loud direction); a mock counts whether or not it calls `importOriginal`; a source edit that adds a cold site reds no unchanged test. - No timeout raised; no test file touched, so #30901's lane (PR #30919, `packages/formula` only) does not overlap. ## Verification All on head `efd622a9` (this branch merged with `origin/main` 6ddf827), this container, 4 vCPU. - `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` derived 31 families for the one changed path; all 31 ran sequentially, each exit code captured before any pipe; 31 exit 0. `--ran`: "31 derived famil(ies) accounted for — 31 run, 0 NOT-MEASURED". - `pnpm exec eslint scripts/check-test-source-alias.mjs` exit 0. - `node scripts/check-test-source-alias.mjs`: exit 0, "the source reach judged 0 test file(s) changed since 6ddf827", 24.3 s. `--reach-all`: exit 1, 5491 findings over 2307 test files (the same counts as at 0e88208), 34.8 s. - Negative control repeated on this head: the `98f640d5` blob (hash 76ae2b8) exits 1 with the finding quoted under Tests; restored blob hash equals the HEAD blob. <details><summary>gates-ran-efd622a9</summary> ```text node scripts/check-closing-keyword-parity.mjs :: exit 0 node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0 node scripts/check-comment-mask-corpus.mjs :: exit 0 node scripts/check-declaration-mirrors.mjs :: exit 0 node scripts/check-declaration-mirrors.mjs --self-test :: exit 0 node scripts/check-self-test-wired.mjs :: exit 0 node scripts/check-self-test-wired.mjs --self-test :: exit 0 node scripts/check-self-test-workflow-commands.mjs :: exit 0 node scripts/check-self-test-workflow-commands.mjs --self-test :: exit 0 node scripts/check-whole-set-label-write.mjs :: exit 0 node scripts/check-whole-set-label-write.mjs --self-test :: exit 0 node scripts/ci/plan-test-core-shards.mjs --self-test :: exit 0 node scripts/pm/bare-root-worklist.mjs --self-test :: exit 0 pnpm check:agent-test-spelling :: exit 0 pnpm check:bash32-floor :: exit 0 pnpm check:ci-filter-parity :: exit 0 pnpm check:cli-command-ids :: exit 0 pnpm check:cross-package-test-inputs :: exit 0 pnpm check:driver-memory-census :: exit 0 pnpm check:entry-guard :: exit 0 pnpm check:gitlink-declared :: exit 0 pnpm check:nul-bytes :: exit 0 pnpm check:parse-guard :: exit 0 pnpm check:pm-dispatch-gates :: exit 0 pnpm check:pnpm-filter-targets :: exit 0 pnpm check:pnpm-flag-spellings :: exit 0 pnpm check:ratchet-remedy-authority :: exit 0 pnpm check:refd-timer-probe :: exit 0 pnpm check:scripts-symbol-anchors :: exit 0 pnpm check:test-source-alias :: exit 0 pnpm check:watch-hint-literal :: exit 0 ``` </details> --- _Generated by [Claude Code](https://claude.ai/code/session_016WV4d1Nibc5bxtbQZv6ssS)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 40ecdf9 commit acd190b

1 file changed

Lines changed: 472 additions & 9 deletions

File tree

0 commit comments

Comments
 (0)