Skip to content

Flaky test: maybeSetUpTunnelRouteRewriteRules times out on slow CI runners #25591

Open

Description

@isaacs

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');
  // ...
}
  1. 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.

  2. 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.

  3. That late run calls console.log. The second test's beforeEach restored the first test's spies and installed new ones, so the log goes into the second test's logSpy. 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

  1. self-assigned this
    on Oct 21, 2026
  2. linear-code commented on Oct 21, 2026

    @linear-code
  3. added
    javascriptPull requests that update javascript code
    on Oct 21, 2026
  4. added a commit that references this issue on Oct 21, 2026
    6109bda
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

TestsjavascriptPull requests that update javascript code

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions