fix(test): strip every inherited CODEMAN_* var and move quick-start off 3099 (#479)

Split out of #476. A Docker Compose deployment exports CODEMAN_CASES_PATH,
which bypasses the temp HOME, so route tests wrote into the real case root.


Claude-Session: https://claude.ai/code/session_01GuHtuPiHXdykq9T6rKQJ9n

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Devvyn
2026-09-24 01:35:23 +02:00
committed by GitHub
co-authored by Claude Opus 5.5
parent 120d780267
commit dd230b0b6e
4 changed files with 17 additions and 3 deletions
+2 -2
View File
@@ -4,7 +4,7 @@ import { existsSync, rmSync, mkdirSync, mkdtempSync } from 'node:fs';
import { join } from 'node:path';
import { tmpdir } from 'node:os';
const TEST_PORT = 3099;
const TEST_PORT = 3299;
const ORIGINAL_HOME = process.env.HOME;
const TEST_HOME = mkdtempSync(join(tmpdir(), 'codeman-quick-start-'));
const CASES_DIR = join(TEST_HOME, 'codeman-cases');
@@ -33,7 +33,7 @@ describe('Quick Start API', () => {
server = await createTestServer(TEST_PORT);
await server.start();
baseUrl = `http://localhost:${TEST_PORT}`;
});
}, 30000);
afterEach(() => {
// Clean up cases created during this test
+1 -1
View File
@@ -183,7 +183,7 @@ Integration tests that spawn web servers use unique ports to avoid conflicts.
| Port | Test File | Notes |
|-------|------------------------------|----------------------------------|
| 3099 | quick-start.test.ts | Basic startup tests |
| 3299 | quick-start.test.ts | Basic startup tests |
| 3102 | session.test.ts | Session lifecycle tests |
| 3105 | scheduled-runs.test.ts | Scheduled task tests |
| 3107 | sse-events.test.ts | Server-Sent Events tests |
+9
View File
@@ -35,6 +35,12 @@ process.env.HOME = testHome;
process.env.USERPROFILE = testHome;
process.env.VITEST = 'true';
for (const key of Object.keys(process.env)) {
if (key.startsWith('CODEMAN_')) delete process.env[key];
}
// Explicitly document the most consequential inherited settings below. The
// loop above intentionally also catches future container/deployment variables.
delete process.env.CODEMAN_PASSWORD;
delete process.env.CODEMAN_USERNAME;
// Gesture availability changes renderIndexHtml output (injects the
@@ -84,6 +90,9 @@ delete process.env.CLAUDE_CONFIG_DIR;
delete process.env.CODEMAN_INSTANCE;
delete process.env.CODEMAN_DATA_DIR;
delete process.env.CODEMAN_TMUX_SOCKET;
// Docker Compose binds cases outside HOME. Leaving this set makes route tests
// write into the deployment's real case root instead of their temp HOME.
delete process.env.CODEMAN_CASES_PATH;
afterEach(() => {
vi.clearAllMocks();
+5
View File
@@ -35,6 +35,7 @@ const STRIPPED_ENV_VARS: Array<[name: string, why: string]> = [
['CODEMAN_DATA_DIR', 'ABSOLUTE override: bypasses the temp HOME and points the suite at a real data dir'],
['CODEMAN_TMUX_SOCKET', 'renames the socket resolveTmuxSocketName() returns'],
['CLAUDE_CONFIG_DIR', 'relocates the Claude tree, so transcript fixtures under the temp HOME read as missing'],
['CODEMAN_CASES_PATH', 'bypasses the temporary HOME and points case routes at a deployment bind mount'],
];
const SETUP_SOURCE = readFileSync(fileURLToPath(new URL('./setup.ts', import.meta.url)), 'utf-8');
@@ -49,6 +50,10 @@ const SETUP_SOURCE = readFileSync(fileURLToPath(new URL('./setup.ts', import.met
const SETUP_STRIP_SECTION = SETUP_SOURCE.split(/^afterEach\(/m)[0];
describe('test environment isolation', () => {
it('removes every inherited CODEMAN_* environment variable', () => {
expect(Object.keys(process.env).filter((key) => key.startsWith('CODEMAN_'))).toEqual([]);
});
it.each(STRIPPED_ENV_VARS)('%s is unset while the suite runs', (name) => {
expect(process.env[name], `${name} leaked into the test environment`).toBeUndefined();
});