TestBashBoundsRunawayOutput: a firehose command must not grow an unbounded buffer (the old CombinedOutput OOM-killed the whole TUI on `cat big.iso` well before the timeout could react). The capture keeps head+tail with an OMITTED marker between, mirroring ctx.Truncate's framing, and preserves the ve
(t *testing.T)
| 50 | // OMITTED marker between, mirroring ctx.Truncate's framing, and preserves the |
| 51 | // very first and very last bytes so the model still sees both ends. |
| 52 | func TestBashBoundsRunawayOutput(t *testing.T) { |
| 53 | // ~3x the tail cap so the ring provably wraps. |
| 54 | n := 3 * bashOutputTail |
| 55 | out := Bash(context.Background(), |
| 56 | "printf 'START'; head -c "+strconv.Itoa(n)+" /dev/zero | tr '\\0' 'a'; printf 'END'", |
| 57 | 30*time.Second) |
| 58 | if len(out) > bashOutputHead+bashOutputTail+300 { |
| 59 | t.Fatalf("output not bounded: %d bytes", len(out)) |
| 60 | } |
| 61 | if !strings.HasPrefix(out, "START") { |
| 62 | t.Fatalf("head lost, output starts %q", out[:16]) |
| 63 | } |
| 64 | if !strings.Contains(out, "END\n(output capped at capture:") { |
| 65 | t.Fatalf("tail or capture note lost, output ends %q", out[len(out)-80:]) |
| 66 | } |
| 67 | if !strings.Contains(out, "OMITTED") { |
| 68 | t.Fatal("dropped middle must carry the seam marker") |
| 69 | } |
| 70 | } |
| 71 | |
| 72 | // TestHeadTailBufferSmallOutputUntouched: output under the head cap must come |
| 73 | // back byte-identical - the bounding must be invisible in the normal case. |