Repository navigation
Flaky test: maybeSetUpTunnelRouteRewriteRules times out on slow CI runners #25591
Description
In packages/nextjs/test/config/withSentryConfig/getFinalConfigObjectUtils.test.ts, two tests fail together on the Node 20.19 unit test job:
❯ test/config/withSentryConfig/getFinalConfigObjectUtils.test.ts (8 tests | 2 failed) 8400ms
× sets up the handler rewrite when the project has a tunnel route handler 5016ms
× logs the tunnel route handler file once 2837ms
Seen on PRs #25581 and #25588. Neither PR changes @sentry/nextjs. The job runs the Next.js tests because they depend on @sentry/node, and CI runs the tests of every affected package. On develop, these tests only run when something in the Next.js package changes, so the problem rarely shows there.
The test file came with #25456 (the tunnel route handler).
Cause
Each test calls vi.resetModules() in beforeEach, then dynamically imports getFinalConfigObjectUtils in its setUp() helper:
beforeEach(() => {
vi.restoreAllMocks();
// A fresh module per test, because the function logs each message only once.
vi.resetModules();
delete process.env.__SENTRY_TUNNEL_ROUTE__;
});
async function setUp(files: string[]) {
// ...
const { maybeSetUpTunnelRouteRewriteRules } =
await import('../../../src/config/withSentryConfig/getFinalConfigObjectUtils');
// ...
}
-
The first test pays for the cold transform of the module and its whole dependency tree. Locally this takes about 1 second. On the CI runner it took more than 5 seconds, vitest's default timeout, so the first test fails.
-
Vitest does not stop a timed-out test. Its
import()resolves later, and the rest of the test runs while the second test is in progress. -
That late run calls
console.log. The second test'sbeforeEachrestored the first test's spies and installed new ones, so the log goes into the second test'slogSpy. The second test then sees two calls instead of one:expect(logSpy).toHaveBeenCalledTimes(1);
So one slow import fails two tests. The other six tests pass, because the transforms are warm by then.
Locally, three runs of the file passed, with the first test at about 1050 ms each time.
Proposed fix
Warm the module graph once before the tests run. vi.resetModules() clears the module registry, but not vitest's transform cache, so every later import in the file is fast:
beforeAll(async () => {
// The first import transforms the whole dependency tree, which can take more than the default
// timeout on a slow CI runner. Later imports after `vi.resetModules()` reuse the transforms.
await import('../../../src/config/withSentryConfig/getFinalConfigObjectUtils');
});
The beforeAll hook has its own timeout (10 seconds by default), and the slow work no longer happens inside a test, so a slow runner cannot leave a test running in the background.
A longer timeout for the describe block would also work, but it only hides the cost and keeps the risk that a slow first test breaks the next one.
Done when
- The two tests pass on the Node 20.19 unit test job when CI runs the Next.js tests, for example on a PR that changes
@sentry/node. - The first test in the file takes about as long as the others.
Activity
linear-code commentedon Oct 21, 2026
JS-3977
test(nextjs): Warm the module graph before the tunnel route tests