Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { tmpdir } from "node:os";
import { join } from "node:path";
import { claudeInterceptCaCertPath, claudeInterceptStateDir, ensureLocalInterceptCa } from "../../src/claude/intercept/local-ca";
import { setLocalCaFileHooksForTests } from "../../src/claude/intercept/local-ca-files";
import { hardenSecretDir } from "../../src/lib/windows-secret-acl";

const roots: string[] = [];
function fixture(): string {
Expand Down Expand Up @@ -87,6 +88,7 @@ test("local CA ignores predictable predecessor temp names and leaves external fi
const root = fixture();
const dir = claudeInterceptStateDir(root);
mkdirSync(dir, { mode: 0o700 });
hardenSecretDir(dir, { required: true });
const oldTemp = join(dir, `ca.key.${process.pid}.tmp`);
writeFileSync(oldTemp, "reserved predecessor", { mode: 0o600 });
const ca = ensureLocalInterceptCa(root);
Expand Down
8 changes: 8 additions & 0 deletions tests/codex-integration/codex-affinity-debug.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,20 @@ function diagnosticPayload(): Record<string, unknown> {
return JSON.parse(line.slice(prefix.length)) as Record<string, unknown>;
}

let savedDebug: string | undefined;
let savedFramesDebug: string | undefined;
beforeEach(() => {
savedDebug = process.env.OCX_DEBUG;
savedFramesDebug = process.env.OCX_DEBUG_FRAMES;
delete process.env.OCX_DEBUG;
delete process.env.OCX_DEBUG_FRAMES;
resetDebugSettingsForTests();
resetDebugLogBufferForTests();
});

afterEach(() => {
if (savedDebug === undefined) delete process.env.OCX_DEBUG; else process.env.OCX_DEBUG = savedDebug;
if (savedFramesDebug === undefined) delete process.env.OCX_DEBUG_FRAMES; else process.env.OCX_DEBUG_FRAMES = savedFramesDebug;
resetDebugSettingsForTests();
resetDebugLogBufferForTests();
});
Expand Down
60 changes: 33 additions & 27 deletions tests/codex-integration/codex-app-server-processes.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, test } from "bun:test";
import { spawn } from "node:child_process";
import { execFileSync, spawn } from "node:child_process";
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { setTrustedWindowsElevationExecutablesForTests } from "../../src/lib/windows-elevation";
import { resolveTrustedWindowsPowerShellExe, setTrustedWindowsElevationExecutablesForTests } from "../../src/lib/windows-elevation";
import {
createWindowsPowerShellFixture,
probeWindowsPowerShellFixture,
Expand Down Expand Up @@ -956,39 +956,45 @@ describe("Windows Win32_Process owner enumeration (#476)", () => {
() => {
// Keep a live process whose CommandLine contains a Codex basename token.
const child = spawn(
"powershell.exe",
resolveTrustedWindowsPowerShellExe(),
[
"-NoProfile", "-NoLogo", "-NonInteractive",
"-Command",
"Start-Sleep -Seconds 45 # codex app-server integration-probe",
"Start-Sleep -Seconds 90 # codex app-server integration-probe",
],
{ stdio: "ignore", windowsHide: true },
);
try {
expect(child.pid).toBeGreaterThan(1);
// Brief settle so Win32_Process can observe the child. A loaded Windows
// runner can also exhaust one CIM enumeration deadline, so tolerate one
// transient empty result OR one thrown deadline (ETIMEDOUT propagates by
// design) while keeping the production timeout unchanged.
Bun.sleepSync(250);
const enumerate = (): ReturnType<typeof listWindowsSnapshots> | undefined => {
try {
return listWindowsSnapshots();
} catch {
return undefined; // transient CIM deadline on a contended runner
}
};
let snapshots = enumerate() ?? [];
// This verifies native enumeration, not the product's 8s latency bound.
// Limit the native query to this fixture; host job count must not set its cost.
// Keep the trusted executable, owner/candidate checks and parser unchanged;
// the fixture's explicit finally owns its lifetime, and lookup errors propagate.
const deadline = performance.now() + 55_000;
const enumerate = () => listWindowsSnapshots(psCommand => {
const query = "Get-CimInstance Win32_Process -ErrorAction Stop";
expect(psCommand).toContain(query);
const fixtureCommand = psCommand.replace(query,
`Get-CimInstance Win32_Process -Filter "ProcessId=${child.pid}" -ErrorAction Stop`);
const executable = resolveTrustedWindowsPowerShellExe();
const remaining = Math.floor(deadline - performance.now());
if (remaining <= 0) throw new Error("native enumeration fixture deadline expired");
return execFileSync(
executable,
["-NoProfile", "-NoLogo", "-NonInteractive", "-Command", fixtureCommand],
{ encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"], timeout: Math.min(45_000, remaining), windowsHide: true },
);
});
let snapshots = enumerate();
let match = snapshots.find(snapshot => snapshot.pid === child.pid);
if (!match) {
Bun.sleepSync(250);
snapshots = enumerate() ?? [];
match = snapshots.find(snapshot => snapshot.pid === child.pid);
}
if (!match) {
Bun.sleepSync(1_000);
snapshots = enumerate() ?? [];
match = snapshots.find(snapshot => snapshot.pid === child.pid);
// Only a successful scan missing this PID is retried; errors propagate.
// Both scans and the visibility delay share the same monotonic budget.
if (!match && performance.now() < deadline) {
Bun.sleepSync(Math.min(250, Math.max(0, Math.floor(deadline - performance.now()))));
if (performance.now() < deadline) {
snapshots = enumerate();
match = snapshots.find(snapshot => snapshot.pid === child.pid);
}
}
expect(match).toBeDefined();
expect(match!.owner).toMatch(/\\/);
Expand All @@ -1002,7 +1008,7 @@ describe("Windows Win32_Process owner enumeration (#476)", () => {
}
}
},
{ timeout: 35_000 },
{ timeout: 60_000 },
);
});

Expand Down
14 changes: 11 additions & 3 deletions tests/codex-integration/project-config-warnings.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -352,11 +352,11 @@ describe("collectProjectCodexConfigWarnings", () => {
});

test("skips untrusted projects even when they define bypass config", () => {
const escaped = testDir.replace(/\\/g, "\\\\");
const projectDir = join(testDir, "proj");
const codexConfigPath = join(process.env.CODEX_HOME!, "config.toml");
// A TOML literal string keeps the platform path verbatim, so the key matches on every OS.
writeGlobalRoutingConfig(`
[projects.'${escaped}\\proj']
[projects.'${projectDir}']
trust_level = "untrusted"
`);
mkdirSync(join(projectDir, ".codex"), { recursive: true });
Expand All @@ -365,7 +365,15 @@ model_provider = "anthropic"
[model_providers.anthropic]
name = "anthropic"
`);
expect(collectProjectCodexConfigWarnings({ cwd: testDir, codexConfigPath })).toEqual([]);
const warnings = collectProjectCodexConfigWarnings({ cwd: testDir, codexConfigPath });
expect(warnings.filter(warning => warning.path === join(projectDir, ".codex", "config.toml"))).toEqual([]);
writeGlobalRoutingConfig(`
[projects.'${projectDir}']
trust_level = "trusted"
`);
expect(collectProjectCodexConfigWarnings({ cwd: testDir, codexConfigPath })
.filter(warning => warning.path === join(projectDir, ".codex", "config.toml"))
.map(warning => warning.code)).toEqual(["model_providers_table"]);
});

test("uncached collection reflects project config changes", () => {
Expand Down
Loading