package builder import ( "fmt" "strings" "testing" ) func TestTruncateOutput(t *testing.T) { tests := []struct { name string input string keepLines int wantLines int wantOmit bool }{ { name: "short output unchanged", input: "line1\nline2\nline3", keepLines: 50, wantLines: 3, wantOmit: false, }, { name: "exactly at threshold unchanged", input: strings.Join(makeLines(100), "\n"), keepLines: 50, wantLines: 100, wantOmit: false, }, { name: "over threshold truncated", input: strings.Join(makeLines(150), "\n"), keepLines: 50, wantLines: 103, // 50 + 1 (empty) + 1 (omitted msg) + 1 (empty) + 50 wantOmit: true, }, { name: "large output truncated", input: strings.Join(makeLines(1000), "\n"), keepLines: 50, wantLines: 103, wantOmit: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := truncateOutput(tt.input, tt.keepLines) gotLines := strings.Split(got, "\n") if len(gotLines) != tt.wantLines { t.Errorf("got %d lines, want %d", len(gotLines), tt.wantLines) } hasOmit := strings.Contains(got, "lines omitted") if hasOmit != tt.wantOmit { t.Errorf("got omit marker = %v, want %v", hasOmit, tt.wantOmit) } if tt.wantOmit { // Verify first and last lines are preserved inputLines := strings.Split(tt.input, "\n") firstLine := inputLines[0] lastLine := inputLines[len(inputLines)-1] if !strings.HasPrefix(got, firstLine+"\n") { t.Errorf("first line not preserved, got prefix %q, want %q", gotLines[0], firstLine) } if !strings.HasSuffix(got, lastLine) { t.Errorf("last line not preserved, got suffix %q, want %q", gotLines[len(gotLines)-1], lastLine) } } }) } } func makeLines(n int) []string { lines := make([]string, n) for i := range lines { lines[i] = "line " + strings.Repeat("x", i%80) } return lines } func TestTruncateOutputLines(t *testing.T) { t.Run("short output returns all lines", func(t *testing.T) { input := "line1\nline2\nline3" got := truncateOutputLines(input, 50) if len(got) != 3 { t.Errorf("got %d lines, want 3", len(got)) } if got[0] != "line1" || got[1] != "line2" || got[2] != "line3" { t.Errorf("unexpected lines: %v", got) } }) t.Run("over threshold returns head + marker + tail", func(t *testing.T) { lines := makeLines(200) input := strings.Join(lines, "\n") got := truncateOutputLines(input, 50) // Should be 50 head + 1 marker + 50 tail = 101 if len(got) != 101 { t.Errorf("got %d lines, want 101", len(got)) } // Check first and last lines preserved if got[0] != lines[0] { t.Errorf("first line = %q, want %q", got[0], lines[0]) } if got[len(got)-1] != lines[len(lines)-1] { t.Errorf("last line = %q, want %q", got[len(got)-1], lines[len(lines)-1]) } // Check omitted marker marker := got[50] expected := fmt.Sprintf("... (%d lines omitted) ...", 100) if marker != expected { t.Errorf("marker = %q, want %q", marker, expected) } }) t.Run("exactly at threshold returns all lines", func(t *testing.T) { lines := makeLines(100) input := strings.Join(lines, "\n") got := truncateOutputLines(input, 50) if len(got) != 100 { t.Errorf("got %d lines, want 100", len(got)) } }) } func TestTruncateOutputPreservesContent(t *testing.T) { // Create input with distinct first and last lines lines := make([]string, 200) for i := range lines { lines[i] = "middle" } lines[0] = "FIRST" lines[49] = "LAST_OF_HEAD" lines[150] = "FIRST_OF_TAIL" lines[199] = "LAST" input := strings.Join(lines, "\n") got := truncateOutput(input, 50) if !strings.Contains(got, "FIRST") { t.Error("missing FIRST") } if !strings.Contains(got, "LAST_OF_HEAD") { t.Error("missing LAST_OF_HEAD") } if !strings.Contains(got, "FIRST_OF_TAIL") { t.Error("missing FIRST_OF_TAIL") } if !strings.Contains(got, "LAST") { t.Error("missing LAST") } if !strings.Contains(got, "(100 lines omitted)") { t.Errorf("wrong omitted count, got: %s", got) } }