Files
dockhand-upstream/tests/stacks-collect-process.test.ts

66 lines
2.7 KiB
TypeScript

import { describe, test, expect } from 'bun:test';
import { spawn } from 'child_process';
import { EventEmitter } from 'node:events';
import { collectProcess } from '../src/lib/server/process-output-core';
/** A minimal ChildProcess stand-in that lets a test drive the EXACT chunk
* boundaries and stdout/stderr interleaving a real spawn can't guarantee. */
function fakeProc() {
const proc = new EventEmitter() as any;
proc.stdout = new EventEmitter();
proc.stderr = new EventEmitter();
proc.out = (s: string) => proc.stdout.emit('data', Buffer.from(s));
proc.err = (s: string) => proc.stderr.emit('data', Buffer.from(s));
proc.close = (code = 0) => proc.emit('close', code);
return proc;
}
describe('collectProcess with a line callback', () => {
test('emits each line separately and in order', async () => {
const lines: string[] = [];
const child = spawn('sh', ['-c', 'printf "eins\\nzwei\\ndrei\\n" >&2']);
await collectProcess(child, (line) => lines.push(line));
expect(lines).toEqual(['eins', 'zwei', 'drei']);
});
test('emits a trailing line that has no newline', async () => {
const lines: string[] = [];
const child = spawn('sh', ['-c', 'printf "ohne-umbruch" >&2']);
await collectProcess(child, (line) => lines.push(line));
expect(lines).toEqual(['ohne-umbruch']);
});
test('behaves exactly as before when no callback is given', async () => {
const child = spawn('sh', ['-c', 'printf "unveraendert\\n" >&2']);
const result = await collectProcess(child);
expect(result.stderr).toContain('unveraendert');
});
test('does NOT splice a partial stdout line onto an interleaved stderr chunk', async () => {
// stdout emits a partial line (no newline), stderr chunks arrive before the
// stdout line is completed. With a shared buffer these would concatenate into
// one corrupted line ("stdout-partSTDERR ..."); with per-stream buffers they
// stay separate.
const proc = fakeProc();
const lines: string[] = [];
const done = collectProcess(proc, (line) => lines.push(line));
proc.out('stdout-part'); // partial, no '\n'
proc.err('an error line\n'); // complete stderr line arrives mid-stdout-line
proc.out(' rest\n'); // completes the stdout line
proc.close(0);
await done;
expect(lines).toEqual(['an error line', 'stdout-part rest']);
});
test('flushes each stream trailing partial separately (stdout before stderr)', async () => {
const proc = fakeProc();
const lines: string[] = [];
const done = collectProcess(proc, (line) => lines.push(line));
proc.out('out-tail'); // partial stdout, never newline-terminated
proc.err('err-tail'); // partial stderr, never newline-terminated
proc.close(0);
await done;
expect(lines).toEqual(['out-tail', 'err-tail']);
});
});