Skip to content

[Test] CoreStaticImports walks a 148-file import closure inside a test body, so bun's 5 s per-test default kills it under a loaded coverage run #1392

Description

@pathosDev

Problem

tests/unit/CoreStaticImports.test.ts > core static import closure (#1005) > ActorSystem never statically reaches fastify failed inside a full gated run on 2026-08-29 (Windows, bun 1.4.0, ACTOR_TS_SKIP_FLAKY_MNS=1 bun run test:coverage:gate):

(fail) core static import closure (#1005) > ActorSystem never statically reaches fastify [6723.89ms]
  ^ this test timed out after 5000ms.

It passed in a full gated run earlier the same day and passes standalone, so this is load-dependence, not a regression. The invariant it guards — that import { ActorSystem } never statically reaches Fastify — is the #1005 gate, and an intermittently red gate is one people learn to re-run.

The deadline is not declared anywhere in the file. Neither test() takes a third argument, and bunfig.toml raises nothing, so the 5 000 ms is bun's per-test default — the same number tests/unit/ci/AwaitConditionBudgets.test.ts pins as BUN_DEFAULT_TEST_TIMEOUT_MS and re-measures against a spawned child run.

What runs against that cap is staticClosure('src/ActorSystem.ts'), called inside the test body: a breadth-first walk that reads every statically reachable source file and runs three matchAll passes over each.

Evidence

Measured in this worktree at 7fb9ca08, bun 1.4.0, Windows 11:

what cost
the walk itself, src/ActorSystem.ts entry 26.2 ms — 148 files, 383 queue pops, 1.28 MiB of source
the walk itself, FastifyBackend.ts entry 1.6 ms — 7 files, 69 KiB
whole file, standalone bun test 276 ms
whole file, standalone bun test --coverage 82 ms
the failing test, in a full gated run 6 723.89 ms

So the walk is not slow: 26 ms of real work was observed taking 6.7 s, ~250x, because the whole suite was running around it. The failure reports the machine, not the invariant.

That this is the runner's cap and not something in the test is measurable directly. A fixture with 6 s of synchronous work at module scope and the identical 6 s inside a test body, one bun test run:

(fail) the same work inside a test body is killed at 5000ms [6000.16ms]
  ^ this test timed out after 5000ms.

 1 pass
 1 fail

The module-scope half passes — bun applies no per-test timeout to module load.

Why not simply declare a bigger third argument

That is the remedy AwaitConditionBudgets prescribes, and it is the wrong one here. A third argument is the right fix when the number being protected is a failure budget — a wait that is supposed to expire and print a label. This walk has no budget; it is bounded work that always completes. Any literal cap would be one more encoded assumption about machine speed, which is exactly the shared defect #1376 is open to remove — and it would have to be picked against a 250x observed spread.

The three sibling repo-file guards already do the right thing: tests/unit/ci/AwaitConditionBudgets.test.ts, tests/unit/ci/WorkflowHygiene.test.ts and tests/unit/config/NoDeadConfigKeys.test.ts all scan the tree at module scope and keep their test() bodies to assertions. CoreStaticImports.test.ts is the odd one out.

Related

Noted while reading, not fixed here: tests/unit/TreeShaking.test.ts runs Bun.build in three test bodies with no explicit timeout, which is the same shape and considerably more expensive per call. It has not been observed failing; recording it so it is not rediscovered from scratch.

Acceptance criteria

  • The closure walk no longer runs against bun's per-test default.
  • Both assertions are unchanged — ActorSystem must still be proven never to statically reach FastifyBackend.ts or a bare fastify, and the canary must still prove the walker is not blind.
  • The reason is written down at the site, with the measured numbers, so the walk is not moved back into a test body later.
  • Green under ACTOR_TS_SKIP_FLAKY_MNS=1 bun run test:coverage:gate and under bun run test:stress scoped to the file.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinginfrastructureCI / build / live-integration testspriority: mediumUseful, not urgent

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions