From 25e430d55a6c0b04af35c34883a2b5120ec53d3d Mon Sep 17 00:00:00 2001 From: mohammed naji Date: Wed, 22 Jul 2026 11:29:15 +0400 Subject: [PATCH] test: stabilize POSIX descendant containment --- tests/fixtures/posix-descendant-provider.sh | 42 ++++ tests/secret-providers.test.ts | 207 ++++++++++++++++---- 2 files changed, 212 insertions(+), 37 deletions(-) create mode 100644 tests/fixtures/posix-descendant-provider.sh diff --git a/tests/fixtures/posix-descendant-provider.sh b/tests/fixtures/posix-descendant-provider.sh new file mode 100644 index 00000000..9722fc96 --- /dev/null +++ b/tests/fixtures/posix-descendant-provider.sh @@ -0,0 +1,42 @@ +#!/bin/sh +# Test-only provider fixture. It is executed directly by spawn(..., { shell: false }) +# after the test copies it to an executable sandbox path. +set -eu + +record_descendant() { + printf '{"descendantPid":%s}\n' "$1" > "${MIFTAH_FAKE_RECORD_PATH:?}" +} + +keep_streams_open() { + while :; do + sleep 60 + done +} + +case "${MIFTAH_FAKE_MODE:-}" in + descendant) + keep_streams_open & + descendant_pid=$! + record_descendant "$descendant_pid" + wait "$descendant_pid" + ;; + early-exit-descendant) + keep_streams_open & + record_descendant "$!" + exit 0 + ;; + early-exit-stubborn-descendant) + ( + ready_path="${MIFTAH_FAKE_DESCENDANT_READY_PATH:?}" + signal_path="${MIFTAH_FAKE_DESCENDANT_SIGNAL_PATH:?}" + trap 'printf "SIGTERM" > "$signal_path"' TERM + printf 'ready' > "$ready_path" + keep_streams_open + ) & + record_descendant "$!" + exit 0 + ;; + *) + exit 2 + ;; +esac diff --git a/tests/secret-providers.test.ts b/tests/secret-providers.test.ts index 2e59bb63..12467351 100644 --- a/tests/secret-providers.test.ts +++ b/tests/secret-providers.test.ts @@ -19,8 +19,15 @@ import { MiftahError } from "../src/utils/errors.js"; const testRoot = join(process.cwd(), ".miftah-secret-provider-tests"); const fakeProviderPath = join(process.cwd(), "tests", "fixtures", "fake-secret-provider.mjs"); +const posixDescendantProviderFixturePath = join( + process.cwd(), + "tests", + "fixtures", + "posix-descendant-provider.sh" +); const embeddedWindowsJobCSharpPattern = /const windowsJobHelper = String\.raw`[\s\S]*?\$source = @'\r?\n([\s\S]*?)\r?\n'@\r?\n {2}Add-Type -TypeDefinition \$source/; +const realSetTimeout = globalThis.setTimeout; afterAll(async () => { await rm(testRoot, { recursive: true, force: true }); @@ -68,6 +75,13 @@ async function installFakeProviderExecutable(directory: string, name: string): P return executable; } +async function installPosixDescendantProviderExecutable(directory: string): Promise { + const executable = join(directory, "posix-descendant-provider"); + await copyFile(posixDescendantProviderFixturePath, executable); + await chmod(executable, 0o700); + return executable; +} + async function readFakeRecord(directory: string): Promise<{ argv: string[]; mode: string; @@ -178,8 +192,19 @@ async function createProviderReadinessBarrier(): Promise<{ } async function readDescendantPid(directory: string): Promise { + return readDescendantPidWithWait(directory, waitForCondition); +} + +async function readDescendantPidWithWait( + directory: string, + wait: ( + condition: () => Promise | boolean, + description: string, + timeoutMs?: number + ) => Promise +): Promise { let descendantPid: number | undefined; - await waitForCondition(async () => { + await wait(async () => { try { const candidate = (await readFakeRecord(directory)).descendantPid; if (!Number.isSafeInteger(candidate) || candidate === undefined || candidate <= 0) return false; @@ -193,6 +218,39 @@ async function readDescendantPid(directory: string): Promise { return descendantPid!; } +async function readPosixDescendantPid(directory: string, observed: Promise): Promise { + let commandSettled = false; + void observed.then(() => { + commandSettled = true; + }); + for (;;) { + try { + const candidate = (await readFakeRecord(directory)).descendantPid; + if (Number.isSafeInteger(candidate) && candidate !== undefined && candidate > 0) return candidate; + } catch (error) { + if (errorCode(error) !== "ENOENT" && !(error instanceof SyntaxError)) throw error; + } + if (commandSettled) throw new Error("Provider command settled before recording its descendant PID"); + await new Promise((resolve) => realSetTimeout(resolve, 10)); + } +} + +async function waitForPosixCondition( + condition: () => Promise | boolean, + observed: Promise, + description: string +): Promise { + let commandSettled = false; + void observed.then(() => { + commandSettled = true; + }); + for (;;) { + if (await condition()) return; + if (commandSettled) throw new Error(`Provider command settled before ${description}`); + await new Promise((resolve) => realSetTimeout(resolve, 10)); + } +} + function isProcessRunning(pid: number): boolean { try { process.kill(pid, 0); @@ -219,6 +277,41 @@ async function terminateTestProcess(pid: number): Promise { await waitForProcessExit(pid); } +function observeCommand(pending: Promise): Promise<{ value: T } | { error: unknown }> { + return pending.then( + (value) => ({ value }), + (error: unknown) => ({ error }) + ); +} + +/** Test-only gate for the initial runner deadline; later termination timers remain real. */ +function holdNextTimeout(timeoutMs: number): { restore: () => void; trigger: () => void } { + const originalSetTimeout = globalThis.setTimeout; + let heldCallback: (() => void) | undefined; + const heldSetTimeout = ((handler: (...args: unknown[]) => void, delay?: number, ...args: unknown[]) => { + if (heldCallback === undefined && delay === timeoutMs) { + heldCallback = () => handler(...args); + const inertTimer = originalSetTimeout(() => undefined, 0); + globalThis.clearTimeout(inertTimer); + return inertTimer; + } + return originalSetTimeout(handler as (...args: never[]) => void, delay, ...(args as never[])); + }) as typeof globalThis.setTimeout; + globalThis.setTimeout = heldSetTimeout; + + return { + restore: () => { + globalThis.setTimeout = originalSetTimeout; + }, + trigger: () => { + const callback = heldCallback; + heldCallback = undefined; + if (callback === undefined) throw new Error("Expected secret command to schedule its timeout"); + callback(); + } + }; +} + const fakeCommand: SecretCommandDescriptor = { executable: process.execPath, prefixArgs: [fakeProviderPath] @@ -1246,33 +1339,54 @@ exit 0`); const controller = new AbortController(); const inheritedSecret = "descendant-secret-that-must-not-leak"; const mode = termination === "timeout" && process.platform === "win32" ? "slow-descendant" : "descendant"; - const pending = runSecretCommand( - { - executable: process.execPath, - args: [fakeProviderPath], - environment: fakeProviderEnvironment(directory, mode, inheritedSecret) - }, - termination === "timeout" - ? { timeoutMs: process.platform === "win32" ? 2_000 : 200 } - : { signal: controller.signal } - ); - const descendantPid = await readDescendantPid(directory); - + const executable = + process.platform === "win32" ? process.execPath : await installPosixDescendantProviderExecutable(directory); + const args = process.platform === "win32" ? [fakeProviderPath] : []; + const timeoutMs = process.platform === "win32" ? 2_000 : 200; + const timeoutGate = termination === "timeout" && process.platform !== "win32" ? holdNextTimeout(timeoutMs) : undefined; + let timeoutTriggered = false; + let pending: Promise<{ stdout: Buffer }>; try { - if (termination === "cancellation") controller.abort(); - const error = await pending.then( - () => { - throw new Error("Expected secret command to reject"); + pending = runSecretCommand( + { + executable, + args, + environment: fakeProviderEnvironment(directory, mode, inheritedSecret) }, - (reason: unknown) => reason + termination === "timeout" ? { timeoutMs } : { signal: controller.signal } ); + } finally { + timeoutGate?.restore(); + } + const observed = observeCommand(pending); + let descendantPid: number | undefined; + + try { + descendantPid = + process.platform === "win32" + ? await readDescendantPid(directory) + : await readPosixDescendantPid(directory, observed); + if (termination === "cancellation") controller.abort(); + if (timeoutGate !== undefined) { + timeoutGate.trigger(); + timeoutTriggered = true; + } + const outcome = await observed; + if ("value" in outcome) throw new Error("Expected secret command to reject"); + const { error } = outcome; expect(error).toBeInstanceOf(SecretProcessError); expect(error).toMatchObject({ kind: expectedKind }); expect(`${error}`).not.toContain(inheritedSecret); await waitForProcessExit(descendantPid); } finally { - await terminateTestProcess(descendantPid); + if (termination === "cancellation") controller.abort(); + timeoutGate?.restore(); + if (!timeoutTriggered) { + timeoutGate?.trigger(); + } + await observed; + if (descendantPid !== undefined) await terminateTestProcess(descendantPid); } }); }); @@ -1328,22 +1442,32 @@ exit 0`); await inSandbox(async (directory) => { const readyPath = join(directory, "descendant-ready"); const signalPath = join(directory, "descendant-signal"); - const pending = runSecretCommand( - { - executable: process.execPath, - args: [fakeProviderPath], - environment: { - ...fakeProviderEnvironment(directory, "early-exit-stubborn-descendant"), - MIFTAH_FAKE_DESCENDANT_READY_PATH: readyPath, - MIFTAH_FAKE_DESCENDANT_SIGNAL_PATH: signalPath - } - }, - { timeoutMs: 1_000 } - ); - const descendantPid = await readDescendantPid(directory); + const executable = await installPosixDescendantProviderExecutable(directory); + const timeoutGate = holdNextTimeout(1_000); + let timeoutTriggered = false; + let pending: Promise<{ stdout: Buffer }>; + try { + pending = runSecretCommand( + { + executable, + args: [], + environment: { + ...fakeProviderEnvironment(directory, "early-exit-stubborn-descendant"), + MIFTAH_FAKE_DESCENDANT_READY_PATH: readyPath, + MIFTAH_FAKE_DESCENDANT_SIGNAL_PATH: signalPath + } + }, + { timeoutMs: 1_000 } + ); + } finally { + timeoutGate.restore(); + } + const observed = observeCommand(pending); + let descendantPid: number | undefined; try { - await waitForCondition( + descendantPid = await readPosixDescendantPid(directory, observed); + await waitForPosixCondition( async () => { try { return (await readFile(readyPath, "utf8")) === "ready"; @@ -1352,15 +1476,24 @@ exit 0`); throw error; } }, - "stubborn descendant to start", - 500 + observed, + "the stubborn descendant to start" ); - await expect(pending).rejects.toEqual(expect.objectContaining>({ kind: "timeout" })); + timeoutGate.trigger(); + timeoutTriggered = true; + const outcome = await observed; + if ("value" in outcome) throw new Error("Expected secret command to reject"); + expect(outcome.error).toEqual(expect.objectContaining>({ kind: "timeout" })); await expect(readFile(signalPath, "utf8")).rejects.toMatchObject({ code: "ENOENT" }); await waitForProcessExit(descendantPid); } finally { - await terminateTestProcess(descendantPid); + timeoutGate.restore(); + if (!timeoutTriggered) { + timeoutGate.trigger(); + } + await observed; + if (descendantPid !== undefined) await terminateTestProcess(descendantPid); } }); }