fix(tests): use global hookTimeout for integration test beforeAll hooks (#125)

* fix(tests): remove explicit beforeAll timeouts that override global hookTimeout

Integration tests passed 60s timeouts to beforeAll, overriding the
120s hookTimeout in vitest.smoke.config.ts. On CI the dev server
startup can consume the full 60s, leaving no time for setup + seeding.

Also bumps createTestServer's default waitForServer timeout from 60s
to 90s, leaving 30s margin within the 120s hook budget.

* fix(tests): don't remove shared node_modules symlink during cleanup

Multiple integration test suites run concurrently and share the
fixture/node_modules symlink. When the suite that created it finishes
first, its cleanup deletes the symlink, causing other suites to fail
with MODULE_NOT_FOUND when their server process tries to resolve astro.

The symlink is gitignored so it's safe to leave in place.
This commit is contained in:
Matt Kane
2026-04-02 20:33:07 +01:00
committed by GitHub
parent 7924d54072
commit 953815969a
5 changed files with 9 additions and 20 deletions

View File

@@ -19,7 +19,6 @@ import { assertNodeVersion, createTestServer } from "../server.js";
const exec = promisify(execFile);
const PORT = 4398; // Different port from client integration tests
const TIMEOUT = 60_000;
// Path to the built CLI binary
const CLI_BIN = resolve(import.meta.dirname, "../../../dist/cli/index.mjs");
@@ -30,7 +29,7 @@ describe("CLI Integration", () => {
beforeAll(async () => {
assertNodeVersion();
ctx = await createTestServer({ port: PORT });
}, TIMEOUT);
});
afterAll(async () => {
await ctx?.cleanup();

View File

@@ -15,7 +15,6 @@ import type { TestServerContext } from "../server.js";
import { assertNodeVersion, createTestServer } from "../server.js";
const PORT = 4399;
const TIMEOUT = 60_000;
describe("EmDashClient Integration", () => {
let ctx: TestServerContext;
@@ -23,7 +22,7 @@ describe("EmDashClient Integration", () => {
beforeAll(async () => {
assertNodeVersion();
ctx = await createTestServer({ port: PORT });
}, TIMEOUT);
});
afterAll(async () => {
await ctx?.cleanup();

View File

@@ -15,7 +15,6 @@ import type { TestServerContext } from "../server.js";
import { assertNodeVersion, createTestServer } from "../server.js";
const PORT = 4396;
const TIMEOUT = 60_000;
/** Helper: raw fetch with auth headers */
async function adminFetch(
@@ -88,7 +87,7 @@ describe("Comments Integration", () => {
const body = await res.text().catch(() => "");
throw new Error(`Failed to enable comments on posts (${res.status}): ${body}`);
}
}, TIMEOUT);
});
afterAll(async () => {
await ctx?.cleanup();

View File

@@ -17,7 +17,6 @@ import type { TestServerContext } from "../server.js";
import { assertNodeVersion, createTestServer } from "../server.js";
const PORT = 4397;
const TIMEOUT = 90_000;
describe("Field Widgets Integration", () => {
let ctx: TestServerContext;
@@ -25,7 +24,7 @@ describe("Field Widgets Integration", () => {
beforeAll(async () => {
assertNodeVersion();
ctx = await createTestServer({ port: PORT });
}, TIMEOUT);
});
afterAll(async () => {
await ctx?.cleanup();

View File

@@ -16,7 +16,7 @@
*/
import { execFile, spawn } from "node:child_process";
import { existsSync, mkdtempSync, rmSync, symlinkSync, unlinkSync } from "node:fs";
import { existsSync, mkdtempSync, rmSync, symlinkSync } from "node:fs";
import { tmpdir } from "node:os";
import { join, resolve } from "node:path";
import { promisify } from "node:util";
@@ -43,7 +43,7 @@ const DONOR_NODE_MODULES = resolve(import.meta.dirname, "../../../../demos/simpl
export interface TestServerOptions {
port: number;
/** Server startup timeout in ms (default: 30_000) */
/** Server startup timeout in ms (default: 90_000) */
timeout?: number;
/** Seed test data after setup (default: true) */
seed?: boolean;
@@ -148,7 +148,7 @@ async function waitForServer(url: string, timeoutMs: number): Promise<void> {
* database file — source files stay at their real paths.
*/
export async function createTestServer(options: TestServerOptions): Promise<TestServerContext> {
const { port, timeout = 60_000, seed = true } = options;
const { port, timeout = 90_000, seed = true } = options;
const baseUrl = `http://localhost:${port}`;
// --- 0. Ensure workspace is built ---
@@ -161,12 +161,12 @@ export async function createTestServer(options: TestServerOptions): Promise<Test
// Ensure node_modules symlink exists in the fixture dir.
// Multiple test suites may race to create this — handle EEXIST gracefully.
// The symlink is intentionally never removed: it's shared across concurrent
// test suites and gitignored, so cleanup of one suite must not break others.
const fixtureNodeModules = join(FIXTURE_DIR, "node_modules");
let createdSymlink = false;
if (!existsSync(fixtureNodeModules)) {
try {
symlinkSync(DONOR_NODE_MODULES, fixtureNodeModules);
createdSymlink = true;
} catch (err: unknown) {
if ((err as NodeJS.ErrnoException).code !== "EEXIST") throw err;
}
@@ -215,13 +215,6 @@ export async function createTestServer(options: TestServerOptions): Promise<Test
// Remove temp data directory
rmSync(tempDataDir, { recursive: true, force: true });
// Remove symlink if we created it
if (createdSymlink && existsSync(fixtureNodeModules)) {
try {
unlinkSync(fixtureNodeModules);
} catch {}
}
}
try {