Summary
On 2.0-maintenance, tests registered through @fedify/fixture's test() never execute under Node.js. The registration call throws, the failure is swallowed by an empty catch, and node --test then reports each file as a single passing entry. The suite is counted green while the tests inside it have not run.
229 registrations across 7 packages go through this path. 228 of them do not execute on Node. (40 files import test from the fixture; 39 of them register at least one test.)
| Package |
Files |
Registrations |
Run on Node |
@fedify/fedify |
28 |
149 |
none |
@fedify/vocab |
4 |
53 |
none |
@fedify/testing |
1 |
15 |
none |
@fedify/redis |
3 |
9 |
none |
@fedify/postgres |
1 |
1 |
none |
@fedify/sqlite |
1 |
1 |
none |
@fedify/webfinger |
1 |
1 |
yes (see below) |
@fedify/fedify's Node run finishing in ~3s with # tests 28 / # pass 28 — one entry per file, no individual test names — is the visible symptom.
Cause
packages/fixture/src/test.ts calls require("node:test") in the Node branch. The file is ESM, where require is not defined, so the call throws and the empty catch discards it:
try {
const { test: nodeTest } = require("node:test");
nodeTest(def.name, { only: def.only, skip: def.ignore }, async (t) => { … });
} catch {
// Fallback for environments without `node:test`
}
main does not have this problem: it uses createRequire(process.cwd() + "/")("node:test"), with no catch, behind a guard that checks process.release?.name === "node" and excludes Cloudflare Workers. The change landed in fab1c07, “Upgrade Deno to 2.9.5” (2026-08-24), whose message notes it is meant to “[e]nsure bundled Node tests register correctly”. It was never backported to 2.0-maintenance: that commit is not an ancestor of the branch, and 096becf is still the only commit touching this path there.
That commit does not cherry-pick onto 2.0-maintenance. It changes 23 files as part of the Deno upgrade, and git cherry-pick leaves 14 of them conflicted — deno.json, deno.lock, pnpm-lock.yaml, mise.toml, packages/lint/package.json, packages/vocab/src/actor.test.ts, the setup-deno action, and seven modify/delete conflicts against paths that do not exist on this branch (packages/uri-template/, test/smoke/, packages/fedify/src/federation/tasks/codec.ts, …). packages/fixture/src/test.ts itself is among the nine that apply cleanly, so the relevant change can be taken on its own, but not by cherry-picking the commit as it stands.
Why @fedify/webfinger is the exception
It builds both lookup.test.cjs and lookup.test.js into dist/, and node --test picks up both. require works in the CJS copy, so those tests register there; the ESM copy still fails silently and shows up as a bare file entry. @fedify/fedify and @fedify/vocab build .mjs only, and @fedify/redis, @fedify/postgres, @fedify/sqlite and @fedify/testing run ESM sources directly via --experimental-transform-types, so every one of them takes the failing path.
Reproduction
Add a test that must fail, registered through the fixture test(), and run each runtime:
deno: PROBE: this must fail ... FAILED
bun : (fail) PROBE: this must fail 0 pass
node: ok 1 - src/probe.test.ts # pass 1, # fail 0
Node reports a pass for a test that cannot pass.
Note on fixing this
Restoring registration will make these 228 tests run on Node for the first time on this branch, and some may need work before they are green.
On main the same tests do run on Node, by name, and they pass. From the test-node job of the most recent main workflow run (34687149404, 2026-09-12):
| Package |
Node tests on main |
on 2.0-maintenance |
@fedify/fedify |
934 passed, 0 failed, 1 skipped |
28 bare file entries |
@fedify/vocab |
32492 passed, 0 failed, 1 skipped |
4 bare file entries |
@fedify/webfinger |
72 passed, 0 failed |
18 (1 real test + 16 steps, via the CJS copy) |
@fedify/testing |
60 passed, 0 failed |
1 bare file entry |
@fedify/postgres |
33 passed, 0 failed |
24, none of them the fixture-registered one |
@fedify/sqlite |
15 passed, 0 failed |
14, none of them the fixture-registered one |
@fedify/redis |
13 passed, 0 failed |
3 bare file entries |
@fedify/redis on main lists RedisKvStore.get(), RedisKvStore.set(), RedisMessageQueue and the rest by name; the same three files on 2.0-maintenance report only src/kv.test.ts, src/codec.test.ts and src/mq.test.ts. So the exposure is bounded by what main already tolerates, though the two branches have diverged enough that this is an indication rather than a guarantee.
The unit to port is the file, not the commit, and the only option that disappears with it is unused on this branch: in source, the only two .only references anywhere in the tree are packages/fixture/src/test.ts's own Bun and Node branches, and no caller passes only to test(). main dropped the option from the TestDefinition type entirely, Bun branch included.
I'm happy to take this on if you'd like it backported; I'd rather check first, since the blast radius is larger than the diff.
Summary
On
2.0-maintenance, tests registered through@fedify/fixture'stest()never execute under Node.js. The registration call throws, the failure is swallowed by an emptycatch, andnode --testthen reports each file as a single passing entry. The suite is counted green while the tests inside it have not run.229 registrations across 7 packages go through this path. 228 of them do not execute on Node. (40 files import
testfrom the fixture; 39 of them register at least one test.)@fedify/fedify@fedify/vocab@fedify/testing@fedify/redis@fedify/postgres@fedify/sqlite@fedify/webfinger@fedify/fedify's Node run finishing in ~3s with# tests 28 / # pass 28— one entry per file, no individual test names — is the visible symptom.Cause
packages/fixture/src/test.tscallsrequire("node:test")in the Node branch. The file is ESM, whererequireis not defined, so the call throws and the emptycatchdiscards it:maindoes not have this problem: it usescreateRequire(process.cwd() + "/")("node:test"), with nocatch, behind a guard that checksprocess.release?.name === "node"and excludes Cloudflare Workers. The change landed in fab1c07, “Upgrade Deno to 2.9.5” (2026-08-24), whose message notes it is meant to “[e]nsure bundled Node tests register correctly”. It was never backported to2.0-maintenance: that commit is not an ancestor of the branch, and 096becf is still the only commit touching this path there.That commit does not cherry-pick onto
2.0-maintenance. It changes 23 files as part of the Deno upgrade, andgit cherry-pickleaves 14 of them conflicted —deno.json,deno.lock,pnpm-lock.yaml,mise.toml,packages/lint/package.json,packages/vocab/src/actor.test.ts, thesetup-denoaction, and seven modify/delete conflicts against paths that do not exist on this branch (packages/uri-template/,test/smoke/,packages/fedify/src/federation/tasks/codec.ts, …).packages/fixture/src/test.tsitself is among the nine that apply cleanly, so the relevant change can be taken on its own, but not by cherry-picking the commit as it stands.Why
@fedify/webfingeris the exceptionIt builds both
lookup.test.cjsandlookup.test.jsintodist/, andnode --testpicks up both.requireworks in the CJS copy, so those tests register there; the ESM copy still fails silently and shows up as a bare file entry.@fedify/fedifyand@fedify/vocabbuild.mjsonly, and@fedify/redis,@fedify/postgres,@fedify/sqliteand@fedify/testingrun ESM sources directly via--experimental-transform-types, so every one of them takes the failing path.Reproduction
Add a test that must fail, registered through the fixture
test(), and run each runtime:Node reports a pass for a test that cannot pass.
Note on fixing this
Restoring registration will make these 228 tests run on Node for the first time on this branch, and some may need work before they are green.
On
mainthe same tests do run on Node, by name, and they pass. From thetest-nodejob of the most recentmainworkflow run (34687149404, 2026-09-12):main2.0-maintenance@fedify/fedify@fedify/vocab@fedify/webfinger@fedify/testing@fedify/postgres@fedify/sqlite@fedify/redis@fedify/redisonmainlistsRedisKvStore.get(),RedisKvStore.set(),RedisMessageQueueand the rest by name; the same three files on2.0-maintenancereport onlysrc/kv.test.ts,src/codec.test.tsandsrc/mq.test.ts. So the exposure is bounded by whatmainalready tolerates, though the two branches have diverged enough that this is an indication rather than a guarantee.The unit to port is the file, not the commit, and the
onlyoption that disappears with it is unused on this branch: in source, the only two.onlyreferences anywhere in the tree arepackages/fixture/src/test.ts's own Bun and Node branches, and no caller passesonlytotest().maindropped the option from theTestDefinitiontype entirely, Bun branch included.I'm happy to take this on if you'd like it backported; I'd rather check first, since the blast radius is larger than the diff.