Skip to content

Commit d53d23d

Browse files
codeslakeclaude
andcommitted
test: give each launcher-spawning file its own TMPDIR
The launcher writes cache-fix-proxy-<port>.sha256 under os.tmpdir() on every spawn, and the tests spawn it with `{ ...process.env }`. So each run left those records in the shared /tmp: measured 17 per run of proxy-held-port alone, 23 across the five files, and 2,145 accumulated on this host — the largest single family left in /tmp after a box-wide sweep, at roughly 1,400 an hour while the suite was being run repeatedly. That is the rate a 7-day reaper cannot win against, and it is not a production rate at all: a production host spawns a proxy when a session starts. Set once per file rather than at each of the 26 spawn sites, because the env is inherited — this also covers sites added later, which is what went wrong the first time. proxy-held-port already had a private TMPDIR at one of its eleven spawn sites, with a comment explaining exactly why ("Asserting a global path can only ever measure the machine's history"); the other ten never got it. Measured per file, before -> after: proxy-held-port 17 -> 0, proxy-holder-handover 4 -> 0, proxy-shutdown-once 1 -> 0, stdio-epipe-survival 1 -> 0, proxy-server 0 -> 0. All five green, and the scratch dirs are removed in after(). Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 78ca948 commit d53d23d

5 files changed

Lines changed: 55 additions & 7 deletions

test/proxy-held-port.test.mjs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,23 @@ import net from "node:net";
55
import { execFileSync, spawn } from "node:child_process";
66
import { fileURLToPath } from "node:url";
77
import { writeFile, rm } from "node:fs/promises";
8-
import { readdirSync, readFileSync, existsSync, mkdirSync, mkdtempSync, writeFileSync, rmSync, utimesSync } from "node:fs";
8+
import { existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, utimesSync, writeFileSync } from "node:fs";
99
import { createHash } from "node:crypto";
1010
import { tmpdir, availableParallelism } from "node:os";
1111
import { join, dirname } from "node:path";
1212

1313
import { sourceFingerprintSync } from "../proxy/source-fingerprint.mjs";
1414
import { HOP_ENV, OURS, cmdOf, freePort as takePort, listeners, onPort } from "./proc-helpers.mjs";
1515

16+
// A PRIVATE TMPDIR FOR THE WHOLE FILE. Every launcher spawned here inherits
17+
// process.env, and the launcher writes cache-fix-proxy-<port>.sha256 under
18+
// os.tmpdir() on each spawn — so without this the run leaves those records in
19+
// the shared /tmp, one per spawn. Set once rather than at each spawn site: the
20+
// env is inherited, so this also covers sites added later.
21+
const FILE_TMP = mkdtempSync(join(tmpdir(), "ccf-hp-"));
22+
process.env.TMPDIR = FILE_TMP;
23+
after(() => { try { rmSync(FILE_TMP, { recursive: true, force: true }); } catch { /* gone */ } });
24+
1625
const launcherPath = join(dirname(fileURLToPath(import.meta.url)), "..", "bin", "claude-via-proxy.mjs");
1726

1827
// WHAT A PROBE RESULT MEANS. One definition, because four hand-rolled ones is

test/proxy-holder-handover.test.mjs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,19 @@ import { dirname, join } from "node:path";
88
import { tmpdir } from "node:os";
99
import { createHash } from "node:crypto";
1010
import { EventEmitter } from "node:events";
11-
import { mkdirSync, mkdtempSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
11+
import { mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
1212
import { OURS, cmdOf, freePort as takePort, listeners, onPort } from "./proc-helpers.mjs";
1313

14+
// A PRIVATE TMPDIR FOR THE WHOLE FILE. Every launcher spawned here inherits
15+
// process.env, and the launcher writes cache-fix-proxy-<port>.sha256 under
16+
// os.tmpdir() on each spawn — so without this the run leaves those records in
17+
// the shared /tmp, one per spawn, for every box the suite has ever run on.
18+
// Set once here rather than at each spawn site: the env is inherited, so this
19+
// also covers spawn sites added later.
20+
const FILE_TMP = mkdtempSync(join(tmpdir(), "ccf-hh-"));
21+
process.env.TMPDIR = FILE_TMP;
22+
after(() => { try { rmSync(FILE_TMP, { recursive: true, force: true }); } catch { /* gone */ } });
23+
1424
const launcherPath = join(dirname(fileURLToPath(import.meta.url)), "..", "bin", "claude-via-proxy.mjs");
1525

1626
// Its own file, and that is the point rather than tidiness. This case samples a

test/proxy-server.test.mjs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,23 @@ import net from "node:net";
55
import { execFileSync, spawn } from "node:child_process";
66
import { fileURLToPath } from "node:url";
77
import { mkdir, writeFile, rm } from "node:fs/promises";
8-
import { readdirSync, readFileSync } from "node:fs";
8+
import { mkdtempSync, readFileSync, readdirSync, rmSync } from "node:fs";
99
import { tmpdir } from "node:os";
1010
import { join, dirname } from "node:path";
1111
import { startProxy, upstreamPointsAtSelf } from "../proxy/server.mjs";
1212
import { startWatcher } from "../proxy/watcher.mjs";
1313
import { loadExtensions, getRegistry } from "../proxy/pipeline.mjs";
1414
import { OURS, cmdOf, freePort as takePort, listeners, onPort } from "./proc-helpers.mjs";
1515

16+
// A PRIVATE TMPDIR FOR THE WHOLE FILE. Every launcher spawned here inherits
17+
// process.env, and the launcher writes cache-fix-proxy-<port>.sha256 under
18+
// os.tmpdir() on each spawn — so without this the run leaves those records in
19+
// the shared /tmp, one per spawn. Set once rather than at each spawn site: the
20+
// env is inherited, so this also covers sites added later.
21+
const FILE_TMP = mkdtempSync(join(tmpdir(), "ccf-ps-"));
22+
process.env.TMPDIR = FILE_TMP;
23+
after(() => { try { rmSync(FILE_TMP, { recursive: true, force: true }); } catch { /* gone */ } });
24+
1625
const serverPath = join(dirname(fileURLToPath(import.meta.url)), "..", "proxy", "server.mjs");
1726
const launcherPath = join(dirname(fileURLToPath(import.meta.url)), "..", "bin", "claude-via-proxy.mjs");
1827

test/proxy-shutdown-once.test.mjs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { tmpdir } from "node:os";
12
// ONE CASE, ITS OWN FILE — the same remedy proxy-holder-handover.test.mjs
23
// applies to itself, and for the same measured reason.
34
//
@@ -10,16 +11,25 @@
1011
// mutation-checked; it needed isolating, not deleting.
1112
//
1213
// node gives each FILE its own process, which is the whole mechanism.
13-
import { describe, it } from "node:test";
14+
import { after, describe, it } from "node:test";
1415
import assert from "node:assert/strict";
1516
import http from "node:http";
1617
import net from "node:net";
1718
import { spawn } from "node:child_process";
18-
import { readFileSync } from "node:fs";
19+
import { mkdtempSync, readFileSync, rmSync } from "node:fs";
1920
import { fileURLToPath } from "node:url";
2021
import { dirname, join } from "node:path";
2122
import { OURS, cmdOf, freePort, listeners } from "./proc-helpers.mjs";
2223

24+
// A PRIVATE TMPDIR FOR THE WHOLE FILE. Every launcher spawned here inherits
25+
// process.env, and the launcher writes cache-fix-proxy-<port>.sha256 under
26+
// os.tmpdir() on each spawn — so without this the run leaves those records in
27+
// the shared /tmp, one per spawn. Set once rather than at each spawn site: the
28+
// env is inherited, so this also covers sites added later.
29+
const FILE_TMP = mkdtempSync(join(tmpdir(), "ccf-so-"));
30+
process.env.TMPDIR = FILE_TMP;
31+
after(() => { try { rmSync(FILE_TMP, { recursive: true, force: true }); } catch { /* gone */ } });
32+
2333
const here = dirname(fileURLToPath(import.meta.url));
2434
const launcherPath = join(here, "..", "bin", "claude-via-proxy.mjs");
2535
const serverPath = join(here, "..", "proxy", "server.mjs");

test/stdio-epipe-survival.test.mjs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { tmpdir } from "node:os";
12
// A DEAD LOG READER MUST NOT TAKE THE PROCESS THAT SERVES THE PORT.
23
//
34
// Measured outage, 27 minutes: a leftover `… | tee <file>` was killed, that tee
@@ -22,15 +23,24 @@
2223
// mode, and the holder through BOTH of its dispatch doors — `server` with
2324
// CACHE_FIX_HOLD_PORT=on was the one an earlier fix missed while the other
2425
// passed, so a single holder row would have called that fixed.
25-
import { describe, it } from "node:test";
26+
import { after, describe, it } from "node:test";
2627
import assert from "node:assert/strict";
2728
import net from "node:net";
2829
import { spawn } from "node:child_process";
29-
import { readFileSync } from "node:fs";
30+
import { mkdtempSync, readFileSync, rmSync } from "node:fs";
3031
import { withDeadline } from "./child-deadline.mjs";
3132
import { fileURLToPath } from "node:url";
3233
import { dirname, join } from "node:path";
3334

35+
// A PRIVATE TMPDIR FOR THE WHOLE FILE. Every launcher spawned here inherits
36+
// process.env, and the launcher writes cache-fix-proxy-<port>.sha256 under
37+
// os.tmpdir() on each spawn — so without this the run leaves those records in
38+
// the shared /tmp, one per spawn. Set once rather than at each spawn site: the
39+
// env is inherited, so this also covers sites added later.
40+
const FILE_TMP = mkdtempSync(join(tmpdir(), "ccf-se-"));
41+
process.env.TMPDIR = FILE_TMP;
42+
after(() => { try { rmSync(FILE_TMP, { recursive: true, force: true }); } catch { /* gone */ } });
43+
3444
const root = join(dirname(fileURLToPath(import.meta.url)), "..");
3545
const reap = (p) => { try { process.kill(-p.pid, "SIGKILL"); } catch {} try { p.kill("SIGKILL"); } catch {} };
3646
const cleanEnv = () => {

0 commit comments

Comments
 (0)