mirror of
https://github.com/multica-ai/multica.git
synced 2026-08-03 19:20:07 +02:00
test(agent): stop the cursor fixtures racing their own prompt write (MUL-5536) (#6174)
TestCursorExecuteFailsOnCleanEOFWithoutResult failed on main with "cursor-agent prompt write failed: write |1: broken pipe" where it expects "stream ended without terminal result". The fake cursor-agent exits without reading stdin, so the prompt write races the child's exit: win and the pipe buffer swallows it, lose and the read end is gone and the write returns EPIPE. writeErr outranks both exitErr and the generic no-terminal-result error in cursor.go, so a lost race replaces the asserted failure with the EPIPE one. A real cursor-agent reads stdin to EOF, so the fixtures now do too. That removes the race rather than reordering production error precedence, which is deliberate. Only the two fixtures whose expected error ranks below writeErr need the drain. Co-authored-by: Bohan-J <bohan@devv.ai> Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
@@ -11,6 +11,23 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// A real cursor-agent reads the prompt from stdin to EOF (see buildCursorArgs).
|
||||
// A fake that exits without reading it leaves the prompt write racing the
|
||||
// child's exit: win and the ~64 KiB pipe buffer swallows it, lose and the read
|
||||
// end is already closed and the write fails with EPIPE.
|
||||
//
|
||||
// That matters because writeErr outranks both `exitErr` and the generic
|
||||
// "stream ended without terminal result" when finalizing the error (cursor.go),
|
||||
// so a lost race replaces the failure the test is asserting on with
|
||||
// "cursor-agent prompt write failed: broken pipe" — the flake that turned main
|
||||
// red on 2026-07-30 (MUL-5536).
|
||||
//
|
||||
// Draining stdin first makes the fake honour the same contract as the real CLI,
|
||||
// which removes the race instead of papering over it. Only fixtures whose
|
||||
// expected error ranks below writeErr need it; the scanner-overflow and
|
||||
// structured-stream-error cases rank above it and are unaffected.
|
||||
const drainStdin = "cat > /dev/null"
|
||||
|
||||
func TestCursorExecuteStopsAfterTerminalResult(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -108,6 +125,7 @@ func TestCursorExecuteReportsSanitizedStderrOnProcessFailure(t *testing.T) {
|
||||
}
|
||||
|
||||
script := `#!/bin/sh
|
||||
` + drainStdin + `
|
||||
dd if=/dev/zero bs=4096 count=1 2>/dev/null | tr '\000' x >&2
|
||||
printf '\nAuthorization: Bearer cursor-secret-token-value\npath=%s/private\n' "$HOME" >&2
|
||||
exit 1
|
||||
@@ -196,6 +214,7 @@ printf '\n'
|
||||
|
||||
func TestCursorExecuteFailsOnCleanEOFWithoutResult(t *testing.T) {
|
||||
script := `#!/bin/sh
|
||||
` + drainStdin + `
|
||||
printf '%s\n' '{"type":"system","subtype":"init","session_id":"sess-no-result"}'
|
||||
printf '%s\n' '{"type":"assistant","message":{"content":[{"type":"text","text":"partial answer"}]}}'
|
||||
`
|
||||
|
||||
Reference in New Issue
Block a user