| 1 | package cli |
| 2 | |
| 3 | import ( |
| 4 | "errors" |
| 5 | "strings" |
| 6 | "testing" |
| 7 | ) |
| 8 | |
| 9 | // A failed run must say why. -p/--print builds a result sink, and text output |
| 10 | // carries only the final response, so the failure had no path to the user at |
| 11 | // all: exit 1, empty stderr. |
| 12 | func TestReportRunFailureAlwaysExplainsTextRuns(t *testing.T) { |
| 13 | runErr := errors.New("context maintenance is blocked for the current view") |
| 14 | cases := []struct { |
| 15 | name string |
| 16 | format runOutputFormat |
| 17 | structured bool |
| 18 | want string |
| 19 | }{ |
| 20 | {"print mode builds a sink", runOutputText, true, "context maintenance is blocked"}, |
| 21 | {"plain text without a sink", runOutputText, false, "context maintenance is blocked"}, |
| 22 | {"json carries it in the payload", runOutputJSON, true, ""}, |
| 23 | {"json without a sink still reports", runOutputJSON, false, "context maintenance is blocked"}, |
| 24 | } |
| 25 | for _, tc := range cases { |
| 26 | t.Run(tc.name, func(t *testing.T) { |
| 27 | var out strings.Builder |
| 28 | reportRunFailure(&out, tc.format, tc.structured, classifyRunCompletion(runErr), runErr) |
| 29 | if tc.want == "" { |
| 30 | if out.Len() != 0 { |
| 31 | t.Fatalf("wrote %q, want the structured payload to carry it alone", out.String()) |
| 32 | } |
| 33 | return |
| 34 | } |
| 35 | if !strings.Contains(out.String(), tc.want) { |
| 36 | t.Fatalf("wrote %q, want it to contain %q", out.String(), tc.want) |
| 37 | } |
| 38 | }) |
| 39 | } |
| 40 | } |
| 41 | |
| 42 | func TestReportRunFailureStaysSilentOnSuccess(t *testing.T) { |
| 43 | var out strings.Builder |
| 44 | reportRunFailure(&out, runOutputText, true, classifyRunCompletion(nil), nil) |
| 45 | if out.Len() != 0 { |
| 46 | t.Fatalf("successful run wrote %q", out.String()) |
| 47 | } |
| 48 | } |
| 49 |