TestCmdDoFreshSpawnFailureRollsBackSessionID pins the rollback invariant: when a fresh-bootstrap spawn fails (e.g. Terminal.app Accessibility denied), BOTH the session_id pre-allocation AND the status flip must be undone so the next `flow do` retries bootstrap fresh. Under the session-id invariant (
(t *testing.T)
| 351 | stubPS(t, |
| 352 | " PID COMMAND\n"+ |
| 353 | "12345 /bin/claude --session-id "+pinnedSID+"\n"+ |
| 354 | "67890 /bin/claude --resume "+pinnedSID+"\n", |
| 355 | ) |
| 356 | |
| 357 | // iterm focus succeeds against the first match. |
| 358 | oldFocusPS := iterm.PSRunner |
| 359 | iterm.PSRunner = func() ([]byte, error) { |
| 360 | return []byte( |
| 361 | " PID TTY COMMAND\n" + |
| 362 | "12345 ttys012 /bin/claude --session-id " + pinnedSID + "\n" + |
| 363 | "67890 ttys013 /bin/claude --resume " + pinnedSID + "\n", |
| 364 | ), nil |
| 365 | } |
| 366 | t.Cleanup(func() { iterm.PSRunner = oldFocusPS }) |
| 367 | |
| 368 | oldRunnerOut := iterm.RunnerOutput |
| 369 | iterm.RunnerOutput = func(args []string) ([]byte, error) { return []byte("ok\n"), nil } |
| 370 | t.Cleanup(func() { iterm.RunnerOutput = oldRunnerOut }) |
| 371 | |
| 372 | stderr := captureStderr(t) |
| 373 | count, _ := stubITerm(t) |
| 374 | if rc := cmdDo([]string{"dup-task"}); rc != 0 { |
| 375 | t.Errorf("cmdDo with duplicates: rc=%d, want 0 (focus should still succeed)", rc) |
| 376 | } |
| 377 | if *count != 0 { |
| 378 | t.Errorf("iterm spawn count = %d, want 0 (focus should not spawn)", *count) |
| 379 | } |
| 380 | got := stderr() |
| 381 | for _, want := range []string{"2 claude processes", pinnedSID, "may race"} { |
| 382 | if !strings.Contains(got, want) { |
| 383 | t.Errorf("warning missing %q\n--- stderr ---\n%s", want, got) |
| 384 | } |
| 385 | } |
| 386 | } |
| 387 | |
| 388 | // captureStderr redirects os.Stderr through an os.Pipe for the duration |
| 389 | // of the test and returns a closure that drains and returns whatever |
| 390 | // was written. The original stderr is restored on Cleanup. |
| 391 | func captureStderr(t *testing.T) func() string { |
| 392 | t.Helper() |
| 393 | origStderr := os.Stderr |
| 394 | r, w, err := os.Pipe() |
| 395 | if err != nil { |
| 396 | t.Fatalf("pipe: %v", err) |
nothing calls this directly
no test coverage detected