test(coding-agent): restored pipe-based probes in telemetry and snapshot suites
- The earlier EBADF 'hardening' was chasing a Bun 1.4-canary quirk that only triggers when bun test runs workspace-package files from the repo root; the canonical runner (scripts/ci-test-ts.ts) always uses the package cwd, where the original PR-authored pipe-based assertions pass. - Restores the stronger stdout contracts from PRs #7135 and #7141.
This commit is contained in:
@@ -106,50 +106,9 @@ describe("sanitizeSnapshotForBrush", () => {
|
||||
// function but discard the sidecar var, so the replay shell ran
|
||||
// `command "" "$@"` and died with `command: command not found:` (issue #3470).
|
||||
|
||||
async function runBashWithCapturedOutput(
|
||||
script: string,
|
||||
options: { env?: Record<string, string | undefined>; stdin?: string } = {},
|
||||
): Promise<{ exitCode: number | null; stdout: string; stderr: string }> {
|
||||
const ioDir = await fs.mkdtemp(path.join(os.tmpdir(), "omp-snap-stdio-"));
|
||||
const stdinPath = path.join(ioDir, "stdin");
|
||||
const stdoutPath = path.join(ioDir, "stdout");
|
||||
const stderrPath = path.join(ioDir, "stderr");
|
||||
await Promise.all([
|
||||
fs.writeFile(stdinPath, options.stdin ?? ""),
|
||||
fs.writeFile(stdoutPath, ""),
|
||||
fs.writeFile(stderrPath, ""),
|
||||
]);
|
||||
|
||||
try {
|
||||
// Bun 1.4's test worker can inherit an invalid IPC descriptor on macOS,
|
||||
// which makes posix_spawn reject pipe-backed stdio with EBADF. Redirect
|
||||
// inside bash and keep the spawn's own stdio detached from that descriptor.
|
||||
const child = Bun.spawn(
|
||||
[
|
||||
REAL_BASH,
|
||||
"--noprofile",
|
||||
"--norc",
|
||||
"-c",
|
||||
`{ ${script}\n} < "$OMP_TEST_STDIN" > "$OMP_TEST_STDOUT" 2> "$OMP_TEST_STDERR"`,
|
||||
],
|
||||
{
|
||||
env: {
|
||||
...options.env,
|
||||
OMP_TEST_STDIN: stdinPath,
|
||||
OMP_TEST_STDOUT: stdoutPath,
|
||||
OMP_TEST_STDERR: stderrPath,
|
||||
},
|
||||
stdin: "ignore",
|
||||
stdout: "ignore",
|
||||
stderr: "ignore",
|
||||
},
|
||||
);
|
||||
await child.exited;
|
||||
const [stdout, stderr] = await Promise.all([fs.readFile(stdoutPath, "utf8"), fs.readFile(stderrPath, "utf8")]);
|
||||
return { exitCode: child.exitCode, stdout, stderr };
|
||||
} finally {
|
||||
await fs.rm(ioDir, { recursive: true, force: true });
|
||||
}
|
||||
async function readStream(stream: ReadableStream<Uint8Array> | null): Promise<string> {
|
||||
if (!stream) return "";
|
||||
return await new Response(stream).text();
|
||||
}
|
||||
|
||||
describe("shell-snapshot fn-env helper", () => {
|
||||
@@ -164,17 +123,22 @@ describe("shell-snapshot fn-env helper", () => {
|
||||
``,
|
||||
].join("\n");
|
||||
|
||||
const result = await runBashWithCapturedOutput(`${fnEnvHelper}\n__omp_emit_referenced_exports`, {
|
||||
const child = Bun.spawn(["bash", "-c", `${fnEnvHelper}\n__omp_emit_referenced_exports`], {
|
||||
env: {
|
||||
PATH: process.env.PATH ?? "/usr/bin:/bin",
|
||||
__MISE_EXE: "/opt/echo",
|
||||
FOO_TEST_DIR: "/opt/dir",
|
||||
LC_ALL: "C",
|
||||
},
|
||||
stdin: funcs,
|
||||
stdin: "pipe",
|
||||
stdout: "pipe",
|
||||
stderr: "pipe",
|
||||
});
|
||||
const out = result.stdout;
|
||||
expect(result.exitCode).toBe(0);
|
||||
child.stdin.write(funcs);
|
||||
await child.stdin.end();
|
||||
const out = await readStream(child.stdout as ReadableStream<Uint8Array> | null);
|
||||
await child.exited;
|
||||
expect(child.exitCode).toBe(0);
|
||||
|
||||
expect(out).toContain("export __MISE_EXE='/opt/echo'");
|
||||
expect(out).toContain("export FOO_TEST_DIR='/opt/dir'");
|
||||
@@ -200,7 +164,7 @@ describe("shell-snapshot fn-env helper", () => {
|
||||
``,
|
||||
].join("\n");
|
||||
|
||||
const result = await runBashWithCapturedOutput(`${fnEnvHelper}\n__omp_emit_referenced_exports`, {
|
||||
const child = Bun.spawn(["bash", "-c", `${fnEnvHelper}\n__omp_emit_referenced_exports`], {
|
||||
env: {
|
||||
PATH: process.env.PATH ?? "/usr/bin:/bin",
|
||||
GITHUB_TOKEN: "ghp_REDACTED",
|
||||
@@ -213,10 +177,15 @@ describe("shell-snapshot fn-env helper", () => {
|
||||
AZURE_CREDENTIAL: "xyz",
|
||||
__MISE_EXE: "/opt/echo",
|
||||
},
|
||||
stdin: funcs,
|
||||
stdin: "pipe",
|
||||
stdout: "pipe",
|
||||
stderr: "ignore",
|
||||
});
|
||||
const out = result.stdout;
|
||||
expect(result.exitCode).toBe(0);
|
||||
child.stdin.write(funcs);
|
||||
await child.stdin.end();
|
||||
const out = await readStream(child.stdout as ReadableStream<Uint8Array> | null);
|
||||
await child.exited;
|
||||
expect(child.exitCode).toBe(0);
|
||||
|
||||
for (const secret of [
|
||||
"GITHUB_TOKEN",
|
||||
@@ -241,25 +210,32 @@ describe("shell-snapshot fn-env helper", () => {
|
||||
|
||||
it("single-quote-escapes values containing apostrophes and preserves newlines", async () => {
|
||||
const funcs = `shout () { echo "$TRICKY_VAL $NL_VAL"; }\n`;
|
||||
const result = await runBashWithCapturedOutput(`${fnEnvHelper}\n__omp_emit_referenced_exports`, {
|
||||
const child = Bun.spawn(["bash", "-c", `${fnEnvHelper}\n__omp_emit_referenced_exports`], {
|
||||
env: {
|
||||
PATH: process.env.PATH ?? "/usr/bin:/bin",
|
||||
TRICKY_VAL: "it's 'tricky'",
|
||||
NL_VAL: "line1\nline2",
|
||||
},
|
||||
stdin: funcs,
|
||||
stdin: "pipe",
|
||||
stdout: "pipe",
|
||||
stderr: "ignore",
|
||||
});
|
||||
const out = result.stdout;
|
||||
child.stdin.write(funcs);
|
||||
await child.stdin.end();
|
||||
const out = await readStream(child.stdout as ReadableStream<Uint8Array> | null);
|
||||
await child.exited;
|
||||
|
||||
expect(out).toContain(`export TRICKY_VAL='it'\\''s '\\''tricky'\\'''`);
|
||||
expect(out).toContain(`export NL_VAL='line1\nline2'`);
|
||||
|
||||
// Eval the emitted lines and verify the round-trip values match.
|
||||
const round = await runBashWithCapturedOutput(
|
||||
`__omp_exports=$(cat); eval "$__omp_exports"; printf '%s\\n' "$TRICKY_VAL"; printf '%s\\n' "$NL_VAL"`,
|
||||
{ env: { PATH: process.env.PATH ?? "/usr/bin:/bin" }, stdin: out },
|
||||
const round = Bun.spawn(
|
||||
["bash", "-c", `eval "$1"; printf '%s\\n' "$TRICKY_VAL"; printf '%s\\n' "$NL_VAL"`, "_", out],
|
||||
{ stdout: "pipe", stderr: "ignore" },
|
||||
);
|
||||
expect(round.stdout).toBe("it's 'tricky'\nline1\nline2\n");
|
||||
const echoed = await readStream(round.stdout as ReadableStream<Uint8Array> | null);
|
||||
await round.exited;
|
||||
expect(echoed).toBe("it's 'tricky'\nline1\nline2\n");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -298,11 +274,15 @@ describe("getOrCreateSnapshot", () => {
|
||||
|
||||
// Replay the snapshot in a fresh bash with `set -u` and confirm the
|
||||
// mise() function resolves cleanly instead of dying on the empty var.
|
||||
const replay = await runBashWithCapturedOutput(`set -u; source "$OMP_TEST_SNAPSHOT"; mise hello world; shout`, {
|
||||
env: { ...process.env, OMP_TEST_SNAPSHOT: snapshotPath! },
|
||||
});
|
||||
expect({ exitCode: replay.exitCode, stderr: replay.stderr }).toEqual({ exitCode: 0, stderr: "" });
|
||||
expect(replay.stdout).toBe("hello world\n/opt/foo\n");
|
||||
const replay = Bun.spawn(
|
||||
[realBash, "--noprofile", "--norc", "-c", `set -u; source "$1"; mise hello world; shout`, "_", snapshotPath!],
|
||||
{ stdout: "pipe", stderr: "pipe" },
|
||||
);
|
||||
const stdout = await readStream(replay.stdout as ReadableStream<Uint8Array> | null);
|
||||
const stderr = await readStream(replay.stderr as ReadableStream<Uint8Array> | null);
|
||||
await replay.exited;
|
||||
expect({ exitCode: replay.exitCode, stderr }).toEqual({ exitCode: 0, stderr: "" });
|
||||
expect(stdout).toBe("hello world\n/opt/foo\n");
|
||||
|
||||
// PR-review hardening: snapshot file must be group/world-unreadable since
|
||||
// it now inlines env-var values. Directory must be 0700 for the same
|
||||
|
||||
@@ -100,10 +100,10 @@ describe("initTelemetryExport signals export path", () => {
|
||||
// singleton into every later test. The probe stands up its own loopback
|
||||
// receiver and exits 0 only when a protobuf trace export actually lands.
|
||||
const probe = fileURLToPath(new URL("./otel-export-probe.ts", import.meta.url));
|
||||
// Ignored stdio sidesteps Bun's intermittent pipe EBADF in test workers;
|
||||
// the probe exits 0 only after it observes the export land.
|
||||
const proc = Bun.spawn([process.execPath, probe], { stdin: "ignore", stdout: "ignore", stderr: "ignore" });
|
||||
expect(await proc.exited).toBe(0);
|
||||
const proc = Bun.spawn(["bun", probe], { stdout: "pipe", stderr: "pipe" });
|
||||
const [code, stdout] = await Promise.all([proc.exited, new Response(proc.stdout).text()]);
|
||||
expect(stdout).toContain("PROBE: RECEIVED");
|
||||
expect(code).toBe(0);
|
||||
}, 20_000);
|
||||
|
||||
it("exports log records and metrics to OTLP/proto receivers", async () => {
|
||||
@@ -111,10 +111,10 @@ describe("initTelemetryExport signals export path", () => {
|
||||
// drives the bridged logger and the agent telemetry metric hooks, then
|
||||
// asserts protobuf POSTs landed at both /v1/logs and /v1/metrics.
|
||||
const probe = fileURLToPath(new URL("./otel-signals-probe.ts", import.meta.url));
|
||||
// Ignored stdio sidesteps Bun's intermittent pipe EBADF in test workers;
|
||||
// the probe exits 0 only after it observes the export land.
|
||||
const proc = Bun.spawn([process.execPath, probe], { stdin: "ignore", stdout: "ignore", stderr: "ignore" });
|
||||
expect(await proc.exited).toBe(0);
|
||||
const proc = Bun.spawn(["bun", probe], { stdout: "pipe", stderr: "pipe" });
|
||||
const [code, stdout] = await Promise.all([proc.exited, new Response(proc.stdout).text()]);
|
||||
expect(stdout).toContain("PROBE: RECEIVED");
|
||||
expect(code).toBe(0);
|
||||
}, 20_000);
|
||||
|
||||
it("merges OTEL_RESOURCE_ATTRIBUTES into the exported resource", async () => {
|
||||
@@ -123,9 +123,9 @@ describe("initTelemetryExport signals export path", () => {
|
||||
// asserts the merged attributes land and that OTEL_SERVICE_NAME wins
|
||||
// service.name over an OTEL_RESOURCE_ATTRIBUTES entry.
|
||||
const probe = fileURLToPath(new URL("./otel-resource-probe.ts", import.meta.url));
|
||||
// Ignored stdio sidesteps Bun's intermittent pipe EBADF in test workers;
|
||||
// the probe exits 0 only after it observes the export land.
|
||||
const proc = Bun.spawn([process.execPath, probe], { stdin: "ignore", stdout: "ignore", stderr: "ignore" });
|
||||
expect(await proc.exited).toBe(0);
|
||||
const proc = Bun.spawn(["bun", probe], { stdout: "pipe", stderr: "pipe" });
|
||||
const [code, stdout] = await Promise.all([proc.exited, new Response(proc.stdout).text()]);
|
||||
expect(stdout).toContain("PROBE: RECEIVED");
|
||||
expect(code).toBe(0);
|
||||
}, 20_000);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user