diff --git a/packages/gateway/__tests__/integration/app.test.ts b/packages/gateway/__tests__/integration/app.test.ts index b8d7cf0..0c873b6 100644 --- a/packages/gateway/__tests__/integration/app.test.ts +++ b/packages/gateway/__tests__/integration/app.test.ts @@ -7,18 +7,28 @@ import { spawn } from "node:child_process"; import request from "supertest"; -import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { + afterAll, + afterEach, + beforeEach, + describe, + expect, + it, + vi, +} from "vitest"; import { afterSpawnCalled, createCliResultJson, createMockChildProcess, } from "../helpers.js"; -// Set required environment variable BEFORE any imports +// Set required environment variables BEFORE any imports // Using vi.hoisted to ensure this runs at the earliest possible time const TEST_API_KEY = vi.hoisted(() => { const key = "test-api-key-12345"; process.env.CLAUDE_CODE_GATEWAY_API_KEY = key; + // Use port 0 to let OS assign an available port (avoids conflicts in parallel runs) + process.env.PORT = "0"; return key; }); @@ -29,12 +39,19 @@ vi.mock("node:child_process", () => ({ const mockSpawn = vi.mocked(spawn); -// Import app after env var is set -import app from "../../src/index.js"; +// Import app and server after env var is set +import app, { server } from "../../src/index.js"; describe("Claude Code Wrapper App (Integration)", () => { const validAuthHeader = `Bearer ${TEST_API_KEY}`; + afterAll(async () => { + // Close the server to prevent resource leaks and port conflicts + await new Promise((resolve, reject) => { + server.close((err) => (err ? reject(err) : resolve())); + }); + }); + beforeEach(() => { vi.clearAllMocks(); }); diff --git a/packages/gateway/vitest.config.ts b/packages/gateway/vitest.config.ts index 14f165f..4855f6a 100644 --- a/packages/gateway/vitest.config.ts +++ b/packages/gateway/vitest.config.ts @@ -8,6 +8,16 @@ export default defineConfig({ setupFiles: ["./__tests__/setup.ts"], include: ["__tests__/**/*.test.ts"], exclude: ["__tests__/e2e/**/*.test.ts"], + // Use forks pool for tests with shared state (concurrency module, server ports) + // to isolate them from parallel execution. Other tests run in threads pool. + // This fixes flaky tests from #70 and #72. + pool: "threads", + poolMatchGlobs: [ + // Integration tests start actual servers and need isolation + ["**/__tests__/integration/**", "forks"], + // Concurrency tests modify shared module state + ["**/__tests__/concurrency.test.ts", "forks"], + ], coverage: { provider: "v8", reporter: ["text", "lcov"],