package runner import ( "strings" "testing" ) // The repair channel against the REAL shell contract. `status --json` is what settles a run's money // and what the pre-spawn meter reads, and the engine exits 2 from it for any book that has ever // flagged a unit — with the report already printed. These run a real process for the same reason the // manifest tests do: what is being pinned is a disposition of os/exec, not a value we choose. // Bare decimals, as the engine writes them (pipeline.StatusReport binds both figures to float64). const flaggedReport = `{"book_id":"gu","total_units":10,"done":9,"flagged":1, "committed_usd":1.25,"reserved_usd":0.1}` func TestAFlaggedBookStillAnswersAboutItsMoney(t *testing.T) { // Exactly what the engine does: the whole document on stdout, the sentinel's message on stderr, // exit 2 (backend/cmd/tmctl/render.go renderStatusJSON). bin := fakeEngine(t, `cat <<'JSON' `+flaggedReport+` JSON echo "tmctl: 1 of 10 unit(s) flagged" >&2 exit 2`) rep, err := New(nil).Status(t.Context(), bin, t.TempDir()) if err != nil { t.Fatalf("a flagged book could not be read: %v", err) } if rep.Spend == nil || rep.Reserved == nil { t.Fatalf("the money of a flagged book did not survive the exit code: %+v", rep) } if *rep.Spend != 1_250_000 || rep.Flagged != 1 { t.Errorf("report %+v, want committed 1.25 and one flagged unit", rep) } } func TestExitTwoWithNothingToReadIsStillARefusal(t *testing.T) { bin := fakeEngine(t, `echo "=== STATUS: gu ==="; exit 2`) if _, err := New(nil).Status(t.Context(), bin, t.TempDir()); err == nil { t.Fatal("a human dashboard was accepted as a report because the code was 2") } else if !strings.Contains(err.Error(), "tmctl status") { t.Errorf("the error does not name what failed: %v", err) } } // The boundary in the other direction: only 2 is a completed command. A refusal that happens to // print a document is still a refusal — the report it printed describes a book the engine did not // finish reading. func TestAReportPrintedByAFailedCallIsNotAnAnswer(t *testing.T) { for _, code := range []string{"1", "11", "13"} { bin := fakeEngine(t, `cat <<'JSON' `+flaggedReport+` JSON exit `+code) if _, err := New(nil).Status(t.Context(), bin, t.TempDir()); err == nil { t.Errorf("exit %s with a document on stdout was read as an answer", code) } } } // Exit 2 is taken only when the report AGREES that the book has a flagged unit — the one condition // under which the engine emits that code from `status --json`. Without it, anything at the pinned // path that prints a JSON object with a committed figure and exits 2 is CHARGED rather than // deferred: a wrapper, a version directory replaced in place, a half-finished deploy. Measured by // the money lens of the dofix review, which settled 2.5 USD against three such answers. // // Mutation caught: taking exit 2 on the code and a parse alone. func TestExitTwoIsTakenOnlyFromAReportThatAgreesWithIt(t *testing.T) { for name, body := range map[string]string{ "a report with nothing flagged": `{"book_id":"gu","total_units":10,"done":10,"flagged":0,"committed_usd":2.5,"reserved_usd":0}`, "a document that is not one": `{"committed_usd":2.5}`, "an empty object": `{}`, "the JSON literal null": `null`, } { t.Run(name, func(t *testing.T) { bin := fakeEngine(t, `cat <<'JSON' `+body+` JSON exit 2`) if _, err := New(nil).Status(t.Context(), bin, t.TempDir()); err == nil { t.Error("exit 2 was taken from a report that does not carry a flagged unit") } }) } } // The cap is on the same threat as the manifest's — a process at the configured path that is not the // engine — and it must not be reachable by a real book: a 2283-chapter report measures 1.1 MB. func TestAnEndlessStatusIsRefusedRatherThanRead(t *testing.T) { bin := fakeEngine(t, `exec yes '{"book_id":"gu"}'`) _, err := New(nil).Status(t.Context(), bin, t.TempDir()) if err == nil { t.Fatal("an endless report was read to the end") } if !strings.Contains(err.Error(), "more than this platform will read") { t.Errorf("the refusal does not name the cap: %v", err) } }