test(tools): make write_stdin "\x03" the trigger in the interrupt test (#273)

The de-flake infra (resilientTempDir + deterministic <-session.done wait) is
already on main, but the test still called session.terminate() directly before
write_stdin "\x03" — so the session was already reaped when the code under test
(exec_command.go's Ctrl-C branch) ran, and the assertion no longer guarded it:
the test would pass even if that branch were deleted (caught in review).

Capture the session handle before the interrupt (write_stdin removes a finished
session from the manager), then make write_stdin "\x03" the operation that
terminates, then wait on session.done. Keeps the de-flake; restores the
coverage. Verified by mutation: removing session.terminate() from the Ctrl-C
branch makes this test fail. Runs 15x locally without flaking.
This commit is contained in:
Vasanth T authored and GitHub committed 2026-06-20 23:12:57 +05:30
1 parent b2bb6c4b41
commit bfbdbb10f6
1 file changed
+19 -13
+19 -13
View File
@@ -296,29 +296,35 @@ func TestWriteStdinInterruptTerminatesSession(t *testing.T) {
t.Fatalf("session_id is not numeric: %v", err)
}
// Send the interrupt, then wait deterministically for the process to be
// reaped (session.done closes) before asserting. The previous version relied
// on the write_stdin yield window (1000ms) being long enough for the async
// SIGKILL + reap to land, which flaked on slow CI (notably Windows smoke).
// Waiting on done removes the timing dependence: a generous safety timeout
// fails loudly if the kill genuinely hangs, but the common case returns the
// instant the process exits.
// Capture the live session BEFORE the interrupt so we can wait on its done
// channel afterwards (write_stdin removes a finished session from the
// manager, so manager.get would miss it post-interrupt).
session, ok := manager.get(sessionID)
if !ok {
t.Fatalf("session %d not found after start", sessionID)
}
session.terminate()
// The operation under test: write_stdin "\x03" must itself terminate the
// session (exec_command.go's Ctrl-C branch). This is what the regression
// guards — terminating the session here directly would let the test pass even
// if that branch were deleted.
interrupted := writeTool.Run(context.Background(), map[string]any{
"session_id": sessionID,
"chars": "\x03",
"yield_time_ms": 1000,
})
// De-flake: wait deterministically for the process to be reaped rather than
// relying on the 1000ms yield window being long enough for the async
// SIGKILL + reap to land (which flaked on slow CI, notably Windows smoke). A
// generous safety timeout fails loudly if the kill genuinely hangs; the
// common case returns the instant the process exits.
select {
case <-session.done:
case <-time.After(30 * time.Second):
t.Fatalf("interrupted session %d was not reaped within 30s", sessionID)
}
interrupted := writeTool.Run(context.Background(), map[string]any{
"session_id": sessionID,
"chars": "\x03",
"yield_time_ms": 1000,
})
if interrupted.Status != StatusOK {
t.Fatalf("interrupted session status = %s: %s", interrupted.Status, interrupted.Output)
}