99 lines
3.3 KiB
TypeScript
99 lines
3.3 KiB
TypeScript
import { expect, it, vi } from "vitest";
|
|
import type { Socket } from "node:net";
|
|
import { EventEmitter } from "node:events";
|
|
import { PassThrough } from "node:stream";
|
|
import {
|
|
ProviderProcessSupervisor,
|
|
type ProviderProcessDiagnostic,
|
|
} from "../../../src/process/ProviderProcess.js";
|
|
import {
|
|
spawnProviderProcessFixture,
|
|
stopProviderProcessFixture,
|
|
} from "../../fixtures/providerProcess.js";
|
|
|
|
it("unreferences inherited output streams without closing them", async () => {
|
|
const child = spawnProviderProcessFixture("stubborn");
|
|
const supervisor = new ProviderProcessSupervisor({
|
|
process: child,
|
|
ownsProcessLifetime: true,
|
|
});
|
|
const stdoutUnref = vi.spyOn(child.stdout as Socket, "unref");
|
|
const stderrUnref = vi.spyOn(child.stderr as Socket, "unref");
|
|
try {
|
|
supervisor.unrefOutputStreams();
|
|
expect(stdoutUnref).toHaveBeenCalledOnce();
|
|
expect(stderrUnref).toHaveBeenCalledOnce();
|
|
expect(child.stdout!.destroyed).toBe(false);
|
|
expect(child.stderr!.destroyed).toBe(false);
|
|
} finally {
|
|
await supervisor.stop();
|
|
await stopProviderProcessFixture(child);
|
|
}
|
|
});
|
|
|
|
it("bounds capture before diagnostic callbacks while draining both output streams", async () => {
|
|
const child = spawnProviderProcessFixture("burst", 70_000);
|
|
const diagnostics: ProviderProcessDiagnostic[] = [];
|
|
const supervisor = new ProviderProcessSupervisor(
|
|
{ process: child, ownsProcessLifetime: true },
|
|
{
|
|
maxDiagnosticBytes: 1024,
|
|
onDiagnostic: (event) => {
|
|
const snapshot = supervisor.snapshot();
|
|
expect(
|
|
snapshot.stdout.bytes + snapshot.stderr.bytes,
|
|
).toBeLessThanOrEqual(1024);
|
|
diagnostics.push(event);
|
|
},
|
|
},
|
|
);
|
|
try {
|
|
expect(await supervisor.waitForOutputClose(2_000)).toBe(true);
|
|
expect(
|
|
diagnostics.some((event) => event.type === "output" && event.truncated),
|
|
).toBe(true);
|
|
const snapshot = supervisor.snapshot();
|
|
expect(snapshot.stdout.observedBytes).toBe(70_000);
|
|
expect(snapshot.stderr.observedBytes).toBe(70_000);
|
|
expect(snapshot.stdout.bytes + snapshot.stderr.bytes).toBeLessThanOrEqual(
|
|
1024,
|
|
);
|
|
for (const stream of ["stdout", "stderr"])
|
|
expect(
|
|
diagnostics
|
|
.filter((event) => event.type === "output" && event.stream === stream)
|
|
.at(-1),
|
|
).toMatchObject({ totalBytes: 70_000 });
|
|
} finally {
|
|
await supervisor.stop();
|
|
await stopProviderProcessFixture(child);
|
|
}
|
|
});
|
|
|
|
it("bounds additional late output while retaining observed byte counts", () => {
|
|
const child = Object.assign(new EventEmitter(), {
|
|
stdout: new PassThrough(),
|
|
stderr: new PassThrough(),
|
|
exitCode: null as number | null,
|
|
signalCode: null as NodeJS.Signals | null,
|
|
kill: vi.fn(() => true),
|
|
});
|
|
const supervisor = new ProviderProcessSupervisor({
|
|
process: child,
|
|
ownsProcessLifetime: true,
|
|
});
|
|
|
|
supervisor.limitAdditionalOutputBytes(5);
|
|
child.stdout.write("abc");
|
|
supervisor.limitAdditionalOutputBytes(5);
|
|
child.stdout.write("defgh");
|
|
child.stderr.write("ijkl");
|
|
|
|
const snapshot = supervisor.snapshot();
|
|
expect(snapshot.stdout.text + snapshot.stderr.text).toHaveLength(5);
|
|
expect(snapshot.stdout.observedBytes).toBe(8);
|
|
expect(snapshot.stderr.observedBytes).toBe(4);
|
|
expect(snapshot.diagnosticTruncated).toBe(true);
|
|
supervisor.dispose();
|
|
});
|