Spaces:
Configuration error
Configuration error
| import type { ChildProcessWithoutNullStreams } from "node:child_process"; | |
| import { beforeEach, describe, expect, it } from "vitest"; | |
| import type { ProcessSession } from "./bash-process-registry.js"; | |
| import { | |
| addSession, | |
| appendOutput, | |
| drainSession, | |
| listFinishedSessions, | |
| markBackgrounded, | |
| markExited, | |
| resetProcessRegistryForTests, | |
| } from "./bash-process-registry.js"; | |
| describe("bash process registry", () => { | |
| beforeEach(() => { | |
| resetProcessRegistryForTests(); | |
| }); | |
| it("captures output and truncates", () => { | |
| const session: ProcessSession = { | |
| id: "sess", | |
| command: "echo test", | |
| child: { pid: 123 } as ChildProcessWithoutNullStreams, | |
| startedAt: Date.now(), | |
| cwd: "/tmp", | |
| maxOutputChars: 10, | |
| pendingMaxOutputChars: 30_000, | |
| totalOutputChars: 0, | |
| pendingStdout: [], | |
| pendingStderr: [], | |
| pendingStdoutChars: 0, | |
| pendingStderrChars: 0, | |
| aggregated: "", | |
| tail: "", | |
| exited: false, | |
| exitCode: undefined, | |
| exitSignal: undefined, | |
| truncated: false, | |
| backgrounded: false, | |
| }; | |
| addSession(session); | |
| appendOutput(session, "stdout", "0123456789"); | |
| appendOutput(session, "stdout", "abcdef"); | |
| expect(session.aggregated).toBe("6789abcdef"); | |
| expect(session.truncated).toBe(true); | |
| }); | |
| it("caps pending output to avoid runaway polls", () => { | |
| const session: ProcessSession = { | |
| id: "sess", | |
| command: "echo test", | |
| child: { pid: 123 } as ChildProcessWithoutNullStreams, | |
| startedAt: Date.now(), | |
| cwd: "/tmp", | |
| maxOutputChars: 100_000, | |
| pendingMaxOutputChars: 20_000, | |
| totalOutputChars: 0, | |
| pendingStdout: [], | |
| pendingStderr: [], | |
| pendingStdoutChars: 0, | |
| pendingStderrChars: 0, | |
| aggregated: "", | |
| tail: "", | |
| exited: false, | |
| exitCode: undefined, | |
| exitSignal: undefined, | |
| truncated: false, | |
| backgrounded: true, | |
| }; | |
| addSession(session); | |
| const payload = `${"a".repeat(70_000)}${"b".repeat(20_000)}`; | |
| appendOutput(session, "stdout", payload); | |
| const drained = drainSession(session); | |
| expect(drained.stdout).toBe("b".repeat(20_000)); | |
| expect(session.pendingStdout).toHaveLength(0); | |
| expect(session.pendingStdoutChars).toBe(0); | |
| expect(session.truncated).toBe(true); | |
| }); | |
| it("respects max output cap when pending cap is larger", () => { | |
| const session: ProcessSession = { | |
| id: "sess", | |
| command: "echo test", | |
| child: { pid: 123 } as ChildProcessWithoutNullStreams, | |
| startedAt: Date.now(), | |
| cwd: "/tmp", | |
| maxOutputChars: 5_000, | |
| pendingMaxOutputChars: 30_000, | |
| totalOutputChars: 0, | |
| pendingStdout: [], | |
| pendingStderr: [], | |
| pendingStdoutChars: 0, | |
| pendingStderrChars: 0, | |
| aggregated: "", | |
| tail: "", | |
| exited: false, | |
| exitCode: undefined, | |
| exitSignal: undefined, | |
| truncated: false, | |
| backgrounded: true, | |
| }; | |
| addSession(session); | |
| appendOutput(session, "stdout", "x".repeat(10_000)); | |
| const drained = drainSession(session); | |
| expect(drained.stdout.length).toBe(5_000); | |
| expect(session.truncated).toBe(true); | |
| }); | |
| it("caps stdout and stderr independently", () => { | |
| const session: ProcessSession = { | |
| id: "sess", | |
| command: "echo test", | |
| child: { pid: 123 } as ChildProcessWithoutNullStreams, | |
| startedAt: Date.now(), | |
| cwd: "/tmp", | |
| maxOutputChars: 100, | |
| pendingMaxOutputChars: 10, | |
| totalOutputChars: 0, | |
| pendingStdout: [], | |
| pendingStderr: [], | |
| pendingStdoutChars: 0, | |
| pendingStderrChars: 0, | |
| aggregated: "", | |
| tail: "", | |
| exited: false, | |
| exitCode: undefined, | |
| exitSignal: undefined, | |
| truncated: false, | |
| backgrounded: true, | |
| }; | |
| addSession(session); | |
| appendOutput(session, "stdout", "a".repeat(6)); | |
| appendOutput(session, "stdout", "b".repeat(6)); | |
| appendOutput(session, "stderr", "c".repeat(12)); | |
| const drained = drainSession(session); | |
| expect(drained.stdout).toBe("a".repeat(4) + "b".repeat(6)); | |
| expect(drained.stderr).toBe("c".repeat(10)); | |
| expect(session.truncated).toBe(true); | |
| }); | |
| it("only persists finished sessions when backgrounded", () => { | |
| const session: ProcessSession = { | |
| id: "sess", | |
| command: "echo test", | |
| child: { pid: 123 } as ChildProcessWithoutNullStreams, | |
| startedAt: Date.now(), | |
| cwd: "/tmp", | |
| maxOutputChars: 100, | |
| pendingMaxOutputChars: 30_000, | |
| totalOutputChars: 0, | |
| pendingStdout: [], | |
| pendingStderr: [], | |
| pendingStdoutChars: 0, | |
| pendingStderrChars: 0, | |
| aggregated: "", | |
| tail: "", | |
| exited: false, | |
| exitCode: undefined, | |
| exitSignal: undefined, | |
| truncated: false, | |
| backgrounded: false, | |
| }; | |
| addSession(session); | |
| markExited(session, 0, null, "completed"); | |
| expect(listFinishedSessions()).toHaveLength(0); | |
| markBackgrounded(session); | |
| markExited(session, 0, null, "completed"); | |
| expect(listFinishedSessions()).toHaveLength(1); | |
| }); | |
| }); | |