1package subagents
2
3import (
4 "sync"
5 "testing"
6)
7
8func TestAgentAttemptValueAndTranscriptAccounting(t *testing.T) {
9 a := &Agent{maxOutputBytes: 6, maxOutputLines: 10}
10 if got := a.AttemptValue(); got != 0 {
11 t.Fatalf("initial attempt = %d, want 0", got)
12 }
13 if got := a.incrementAttempt(); got != 1 || a.AttemptValue() != 1 {
14 t.Fatalf("attempt after increment = %d, value = %d; want 1", got, a.AttemptValue())
15 }
16
17 a.appendTranscript("a\nbb")
18 if got := a.outputBytes; got != 5 {
19 t.Fatalf("output bytes after append = %d, want 5", got)
20 }
21 if got := a.outputLines; got != 2 {
22 t.Fatalf("output lines after append = %d, want 2", got)
23 }
24 a.appendTranscript("ccc")
25 if got, want := a.Transcript(), []string{"ccc"}; len(got) != 1 || got[0] != want[0] {
26 t.Fatalf("transcript after byte eviction = %q, want %q", got, want)
27 }
28 if a.outputBytes != 4 || a.outputLines != 1 {
29 t.Fatalf("accounting after byte eviction = (%d bytes, %d lines), want (4, 1)", a.outputBytes, a.outputLines)
30 }
31
32 a.maxOutputBytes = 1
33 a.appendTranscript("z")
34 if got := a.Transcript(); len(got) != 0 {
35 t.Fatalf("transcript after evicting every line = %q, want empty", got)
36 }
37 if a.outputBytes != 0 || a.outputLines != 0 {
38 t.Fatalf("empty accounting = (%d bytes, %d lines), want (0, 0)", a.outputBytes, a.outputLines)
39 }
40}
41
42func TestAgentTranscriptLineCapAccounting(t *testing.T) {
43 a := &Agent{maxOutputBytes: 100, maxOutputLines: 2}
44 a.appendTranscript("one\ntwo\nthree")
45
46 if got, want := a.Transcript(), []string{"two", "three"}; len(got) != len(want) || got[0] != want[0] || got[1] != want[1] {
47 t.Fatalf("transcript = %q, want %q", got, want)
48 }
49 if a.outputBytes != len("two")+1+len("three")+1 || a.outputLines != 2 {
50 t.Fatalf("line-cap accounting = (%d bytes, %d lines), want (10, 2)", a.outputBytes, a.outputLines)
51 }
52}
53
54func TestAgentSnapshotConcurrentTranscriptTruncation(t *testing.T) {
55 a := &Agent{maxOutputBytes: 8, maxOutputLines: 1}
56 start := make(chan struct{})
57 var wg sync.WaitGroup
58 wg.Add(2)
59 go func() {
60 defer wg.Done()
61 <-start
62 for range 10_000 {
63 a.appendTranscript("overflow")
64 }
65 }()
66 go func() {
67 defer wg.Done()
68 <-start
69 for range 10_000 {
70 _ = a.Snapshot()
71 }
72 }()
73 close(start)
74 wg.Wait()
75
76 if !a.Snapshot().OutputTruncated {
77 t.Fatal("snapshot did not report transcript truncation")
78 }
79}