test(process): compare progress with retained PTY chunks

This commit is contained in:
morluto
2026-10-24 06:29:39 +08:00
parent dbf8e1eacc
commit 2886cc1efe
2 changed files with 58 additions and 7 deletions
@@ -138,3 +138,42 @@ it.each([false, true])(
}
},
);
it("retains PTY chunk boundaries when two output messages arrive fragmented", () => {
let onData: ((data: string) => void) | undefined;
const terminal = {
onData: (listener: (data: string) => void) => {
onData = listener;
return { dispose: () => undefined };
},
} as unknown as IPty;
const scenario = parseProcessScenario({
executable: "unused",
normalization: { paths: false, pids: false, ports: false },
});
const frames: TerminalFrame[] = [];
const captureRetention = captureTerminalFrames({
terminal,
scenario,
frames,
started: 0,
onOutput: () => undefined,
renderer: { write: () => undefined } as never,
normalizeText: createBoundedProcessTextNormalizer(scenario, "/tmp", -1),
recordEvent: () => undefined,
identifiers: () => [],
});
// A PTY transports chunks, independently of the child's write boundaries.
const chunks = ["SPARSE_CHILD_", "STARTED\r\n", "SPARSE_CHILD_", "DONE\r\n"];
for (const chunk of chunks) onData?.(chunk);
expect(frames.map(({ data }) => data)).toEqual(chunks);
expect(frames.map(({ sequence }) => sequence)).toEqual([0, 1, 2, 3]);
expect(frames.map(({ data }) => data).join("")).toBe(
"SPARSE_CHILD_STARTED\r\nSPARSE_CHILD_DONE\r\n",
);
expect(captureRetention().raw_terminal).toMatchObject({
observed_frames: 4,
retained_frames: 4,
});
});
@@ -260,9 +260,9 @@ itWithCaptureCapability(
operation: "capture_process_scenario",
predicate_type: "rea.process-capture",
});
expect(recorded.frames.map(({ data }) => data).join("")).toContain(
"SPARSE_CHILD_STARTED",
);
const terminalText = recorded.frames.map(({ data }) => data).join("");
expect(terminalText).toContain("SPARSE_CHILD_STARTED");
expect(terminalText).toContain("SPARSE_CHILD_DONE");
expect(capture.stderr).not.toContain("SPARSE_CHILD_STARTED");
expect(capture.stderr).not.toContain("SPARSE_CHILD_DONE");
const updates = capture.lines.map(({ update }) => update);
@@ -273,15 +273,27 @@ itWithCaptureCapability(
expect(Math.max(...elapsed) - Math.min(...elapsed)).toBeGreaterThanOrEqual(
1_000,
);
expect(
running.every(({ details }) => details.frames <= 2),
`running progress details: ${JSON.stringify(running.map(({ details }) => details))}`,
).toBe(true);
// Frames count retained PTY chunks; child writes may be split or coalesced.
for (const [index, update] of running.entries()) {
expect(update.details.frames).toBeLessThanOrEqual(recorded.frames.length);
const previous = running[index - 1];
if (previous !== undefined)
expect(update.details.frames).toBeGreaterThanOrEqual(
previous.details.frames,
);
}
assertRateBound(
updates.filter(({ terminal }) => terminal !== true).length,
updates.at(-1)?.details.elapsed_ms ?? 0,
);
expect(updates.at(-1)).toMatchObject({
phase: "cleanup",
terminal: true,
details: {
disposition: "exited",
frames: recorded.frames.length,
samples: recorded.process_samples.length,
interactions: recorded.interaction_events.length,
owned_process_group: "cleaned",
terminal_renderer: "cleaned",
temporary_root: "cleaned",