Skip to content

test(driver-mongodb): 依赖 mongod 二进制的套件改为 opt-in,默认响亮跳过(#5517) - #5538

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-5517-mongodb-tests-optin
Aug 5, 2026
Merged

test(driver-mongodb): 依赖 mongod 二进制的套件改为 opt-in,默认响亮跳过(#5517)#5538
os-zhuang merged 1 commit into
mainfrom
claude/issue-5517-mongodb-tests-optin

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Fixes #5517

按维护者 2026-08-05 拍板的「作废相关测试」执行:依赖 mongodb-memory-server 二进制的套件改为 opt-in,默认跳过并打印原因 —— 响亮作废,不是静默删除。不为该驱动族建下载单飞 / 预热基建(#5499 冻结令)。

一、机制:issue 说的「两种触发形态」其实是同一件事(已核到上游源码)

mongodb-memory-server-core@11.2.0lib/util/MongoBinaryDownload.js:405-415:

fileStream.on('finish', async () => {          // async 监听器,没有任何人 await 它
  ...
  await fs.promises.rename(tempDownloadLocation, downloadLocation);   // :413
  resolve(downloadLocation);
});

冷缓存下两个 worker 同时下载:赢家先把 .tgz.downloading rename 掉,输家的 rename 抛 ENOENT。因为这个 async 监听器是 fire-and-forget,该 rejection 谁都收不到,并且外层 new Promise 既不 resolve 也不 reject。于是同一个事件产生两个症状:

  1. 输家的 MongoMemoryServer.create() 永不 settle —— 结束等待的是本包 120s 的 deadline,这正是现场看到的「timed out after 120s 跳过」(issue 里的形态 ②);
  2. ENOENT 以进程级 unhandled rejection 冒出,vitest 记为 Errors 1 error,于是全部测试绿仍 exit 1(形态 ①)。

推论一条,对实现是关键的:create() 返回的 promise 上挂 .catch() 治不了这个 —— 错误根本不经过那个 promise(Promise.race 早已给它挂了 handler,它自己的 rejection 一直是被处理的)。所以「接住弃流 rejection」只能在进程级监听器上做。

二、改法

  • 门:OS_TEST_MONGODB_MEMORY_SERVER_ENABLED=1。命名照 Prime Directive [WIP] Create a new release version #9 的 test/CI-only 形状 OS_TEST_* + 布尔开关 _ENABLED,与既有 OS_TEST_MULTI_ORG_ENABLED(packages/qa/dogfood/test/enterprise-organizations.ts)同一套房规;live 后端那半是用 URL 提供能力(OS_TEST_POSTGRES_URL),不适用于布尔 opt-in。
  • 门在 import 之前:test-mongod.tsmongodb-memory-server 只保留 import type(编译期擦除),值导入放进 createTestMongod 里的动态 import(),在门后面。默认跑一次下载都不会开始。新套件 mongodb-memory-server-gate.test.ts 用 mock 工厂数库被求值的次数把这条性质钉住(反向验证见下)。
  • 默认跳过要能看见:提示走 process.stderr.write,不走 console.warn。实测 vitest 4.1.10:默认 reporter 对「通过/跳过」文件里 console.* 的输出一行都不渲染(只有 --reporter=verbose 才带 stderr | file 头打出来),而直写 stderr 总是到日志。若照原有 console.warn 惯例写,这次作废就会变成静默跳过 —— 恰好是本单要避免的东西。该测量结论写在 printMongodNotice 的注释里。
  • 接住弃流 rejection(维护者要求 3,opt-in 本地跑仍需要):installAbandonedDownloadGuard 只在 opt-in 路径安装一个进程监听器,只吞 ENOENT + syscall: rename + 源路径以 .downloading 结尾这一种签名;其它一律 re-raise。必须 re-raise 的原因:vitest 的 worker handler 只要发现存在第二个 unhandledRejection 监听器就整体让位(源码 vitest/dist/chunks/init.*.jsprocessListeners(event).length 大于 1 即 return),所以只吞不抬会把该 worker 里所有别的 unhandled rejection 一起消音 —— 那正是本单在治的那类隐藏失败。
  • mongodb-findone-querymongodb-pagination-conformance 原先各自直接 MongoMemoryServer.create(),既绕过门也绕过本包的 deadline,一并收敛到 createTestMongod
  • 未动 .github/workflows/**:核过,没有任何 workflow 硬要求这些套件必须跑(Temporal Conformance 的非 SQL 步骤只是 pnpm --filter @objectstack/driver-mongodb test,现在照跑,binary 套件跳过)。也未动包的任何非测试源码。

三、验证(真实输出)

冷缓存 + 默认(无 env),~/.cache/mongodb-binaries 事前不存在:

TEST EXIT=0
=== cache state AFTER ===  ls: cannot access '/root/.cache/mongodb-binaries': No such file or directory
=== Downloading MongoDB occurrences ===  0
[driver-mongodb] SKIP MongoDBDriver — needs a real mongod, ... retired from default test runs by #5517 ... Set OS_TEST_MONGODB_MEMORY_SERVER_ENABLED=1 to run it.
(7 个套件各打印一行,此处略)
 Test Files  10 passed | 5 skipped (15)
      Tests  202 passed | 133 skipped (335)
   Duration  8.55s

opt-in(从冷缓存起,单套件):EXIT=0Tests 37 passed,缓存目录被创建并落下 mongod-x64-ubuntu-8.2.6(221 MB)—— 即门开则下载真的会发生。随后整包 opt-in:

 Test Files  15 passed (15)
      Tests  335 passed (335)

暖缓存 + 默认:仍然 7 行 SKIP、exit 0 —— 决定权在 env,不在缓存。

反向验证(方向事先预判为「红」):把删掉的静态值导入加回 test-mongod.ts,门测试里两条「库未被求值」断言立刻红:

× skips without touching the library at all when the switch is unset
× treats a set-but-not-"1" value as OFF and says so instead of skipping quietly
AssertionError: expected 1 to be +0 // Object.is equality

其它:pnpm --filter @objectstack/driver-mongodb typecheck EXIT=0;check:driver-conformance(含 --self-test)OK,20 covered cells;check-type-check-coverage OK;check-nul-bytes OK;改动文件另做了一次 grep -naP 控制字符自扫,干净;eslint 对改动文件零输出。

四、台账与诚实记账

  • scripts/check-test-completeness.mjs 不受影响:skipped 计入「已交代」,不是洞。
  • scripts/check-driver-conformance.mjs 的判定是按 import,所以 mongo 的 cell 仍然 CONSUMED —— 诚实,但覆盖的东西变少了。不能加 ledger entry(为已覆盖的 cell 加条目会被 RECONCILED 判红),因此把事实写进该 ledger 的注释,逐 marker 记明:FILTER_LOGIC_CASES 默认仍全量跑(server-free 那半);PAGINATION_CASES / PAGINATION_UNORDERED_CASES 只剩 opt-in(server-free 那半断言的是 sort spec,不是分区性质);TEMPORAL_CASES / TEMPORAL_TIME_CASES 只剩 opt-in(无 server-free 半边)。
  • 包 README 增加 opt-in 说明与命令。
  • 测试-only 改动,按仓规走 skip-changeset 标签(已挂到本 PR);README 与脚本注释不带任何行为变化,不加 changeset。

五、顺手发现(未在本 PR 修)

docs/adr/0053-date-and-datetime-semantics.md 的 coverage note 写着 mongo 端到端扫描「runs in CI and skips where no binary is reachable」—— 本单落地后它在 CI 里不再跑。属跨面(ADR)的文档准确性问题,另开 observation-class finding,不夹带进本 PR。


Generated by Claude Code

…env var (#5517)

The seven suites that need a real MongoDB now skip by default and print why.
`mongodb-memory-server` downloads a ~123 MB binary on first use, and on a cold
cache two vitest workers raced that download: the winner renamed
`<archive>.tgz.downloading`, the loser's `rename` threw ENOENT inside an
unawaited `async` listener (`MongoBinaryDownload.js:413`), so the enclosing
promise never settled AND the ENOENT surfaced as a process-level unhandled
rejection. That is one event, not the two the issue describes: the never-settling
promise is the "timed out after 120s" skip, and the unhandled rejection is what
turned all-green runs into `exit 1` and ejected unrelated PRs from the merge
queue.

Per the maintainer's decision, the download is retired rather than made
single-flight (#5499 freezes investment in this family): no globalSetup
pre-download, no cross-worker lock, no workflow cache warming.

- `OS_TEST_MONGODB_MEMORY_SERVER_ENABLED=1` opts in (`OS_TEST_*` + `_ENABLED`,
  the `OS_TEST_MULTI_ORG_ENABLED` shape, Prime Directive #9).
- The gate is checked BEFORE `mongodb-memory-server` is imported at all, so a
  default run starts zero downloads; the new gate suite pins that by counting
  library evaluations.
- Notices go to `process.stderr`, not `console.warn`: measured on vitest 4.1.10,
  the default reporter renders nothing a passing/skipped file logs through
  `console`, which would have made the retirement a silent skip.
- `installAbandonedDownloadGuard` swallows the abandoned-download ENOENT — and
  only it — on the opt-in path, re-raising everything else because vitest steps
  aside once another `unhandledRejection` listener exists.
- `mongodb-findone-query` / `mongodb-pagination-conformance` called
  `MongoMemoryServer.create()` directly (bypassing the gate and the deadline);
  both now go through `createTestMongod`.
- Coverage consequence recorded in `scripts/check-driver-conformance.mjs`: the
  mongo cells stay CONSUMED on their imports, but the temporal and pagination
  case-sets now run only opt-in.

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

vercel Bot commented Aug 5, 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 5, 2026 4:42pm

Request Review

@os-zhuang os-zhuang added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 5, 2026 — with Claude
@github-actions github-actions Bot added size/l documentation Improvements or additions to documentation tests labels Aug 5, 2026
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/driver-mongodb.

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

  • content/docs/data-modeling/drivers.mdx (via @objectstack/driver-mongodb)
  • content/docs/getting-started/glossary.mdx (via @objectstack/driver-mongodb)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/driver-mongodb)
  • content/docs/plugins/packages.mdx (via @objectstack/driver-mongodb)
  • content/docs/protocol/objectql/query-syntax.mdx (via @objectstack/driver-mongodb)
  • content/docs/releases/implementation-status.mdx (via @objectstack/driver-mongodb)

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.

@os-zhuang os-zhuang removed documentation Improvements or additions to documentation size/l tests labels Aug 5, 2026 — with Claude
@github-actions github-actions Bot added documentation Improvements or additions to documentation tests size/l labels Aug 5, 2026
@os-zhuang
os-zhuang marked this pull request as ready for review August 5, 2026 16:56
@os-zhuang
os-zhuang added this pull request to the merge queue Aug 5, 2026
Merged via the queue into main with commit 4fdf480 Aug 5, 2026
35 of 36 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-5517-mongodb-tests-optin branch August 5, 2026 17:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/l 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.

driver-mongodb 测试:两个套件并发下载 MongoDB 二进制,mongodb-memory-server 的 rename 竞态让全绿测试 exit 1(队列 flaky)

2 participants