test(driver-mongodb): 依赖 mongod 二进制的套件改为 opt-in,默认响亮跳过(#5517) - #5538
Merged
Conversation
…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
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 1 package(s): 6 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
os-zhuang
marked this pull request as ready for review
August 5, 2026 16:56
This was referenced Aug 5, 2026
Merged
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 #5517
按维护者 2026-08-05 拍板的「作废相关测试」执行:依赖 mongodb-memory-server 二进制的套件改为 opt-in,默认跳过并打印原因 —— 响亮作废,不是静默删除。不为该驱动族建下载单飞 / 预热基建(#5499 冻结令)。
一、机制:issue 说的「两种触发形态」其实是同一件事(已核到上游源码)
mongodb-memory-server-core@11.2.0的lib/util/MongoBinaryDownload.js:405-415:冷缓存下两个 worker 同时下载:赢家先把
.tgz.downloadingrename 掉,输家的rename抛 ENOENT。因为这个async监听器是 fire-and-forget,该 rejection 谁都收不到,并且外层new Promise既不 resolve 也不 reject。于是同一个事件产生两个症状:MongoMemoryServer.create()永不 settle —— 结束等待的是本包 120s 的 deadline,这正是现场看到的「timed out after 120s 跳过」(issue 里的形态 ②);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。test-mongod.ts对mongodb-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的注释里。installAbandonedDownloadGuard只在 opt-in 路径安装一个进程监听器,只吞ENOENT+syscall: rename+ 源路径以.downloading结尾这一种签名;其它一律 re-raise。必须 re-raise 的原因:vitest 的 worker handler 只要发现存在第二个unhandledRejection监听器就整体让位(源码vitest/dist/chunks/init.*.js里processListeners(event).length大于 1 即 return),所以只吞不抬会把该 worker 里所有别的 unhandled rejection 一起消音 —— 那正是本单在治的那类隐藏失败。mongodb-findone-query与mongodb-pagination-conformance原先各自直接MongoMemoryServer.create(),既绕过门也绕过本包的 deadline,一并收敛到createTestMongod。.github/workflows/**:核过,没有任何 workflow 硬要求这些套件必须跑(Temporal Conformance 的非 SQL 步骤只是pnpm --filter @objectstack/driver-mongodb test,现在照跑,binary 套件跳过)。也未动包的任何非测试源码。三、验证(真实输出)
冷缓存 + 默认(无 env),
~/.cache/mongodb-binaries事前不存在:opt-in(从冷缓存起,单套件):
EXIT=0、Tests 37 passed,缓存目录被创建并落下mongod-x64-ubuntu-8.2.6(221 MB)—— 即门开则下载真的会发生。随后整包 opt-in:暖缓存 + 默认:仍然 7 行 SKIP、exit 0 —— 决定权在 env,不在缓存。
反向验证(方向事先预判为「红」):把删掉的静态值导入加回
test-mongod.ts,门测试里两条「库未被求值」断言立刻红:其它:
pnpm --filter @objectstack/driver-mongodb typecheckEXIT=0;check:driver-conformance(含--self-test)OK,20 covered cells;check-type-check-coverageOK;check-nul-bytesOK;改动文件另做了一次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 半边)。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