mirror of
https://github.com/cexll/myclaude.git
synced 2026-03-03 15:33:14 +08:00
maxLen=0 in extractKeyOutputFromLines now means unlimited, so the orchestrator receives the agent's full concluding summary instead of a 150-character stub. Negative maxLen still returns empty. Structured summary format and log-path-only behaviour are preserved. Generated with SWE-Agent.ai Co-Authored-By: SWE-Agent.ai <noreply@swe-agent.ai>
171 lines
5.4 KiB
Go
171 lines
5.4 KiB
Go
package wrapper
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestExtractCoverage(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
in string
|
|
want string
|
|
}{
|
|
{"bare int", "92%", "92%"},
|
|
{"bare float", "92.5%", "92.5%"},
|
|
{"coverage prefix", "coverage: 92%", "92%"},
|
|
{"total prefix", "TOTAL 92%", "92%"},
|
|
{"all files", "All files 92%", "92%"},
|
|
{"empty", "", ""},
|
|
{"no number", "coverage: N/A", ""},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if got := extractCoverage(tt.in); got != tt.want {
|
|
t.Fatalf("extractCoverage(%q) = %q, want %q", tt.in, got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestExtractTestResults(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
in string
|
|
wantPassed int
|
|
wantFailed int
|
|
}{
|
|
{"pytest one line", "12 passed, 2 failed", 12, 2},
|
|
{"pytest split lines", "12 passed\n2 failed", 12, 2},
|
|
{"jest format", "Tests: 2 failed, 12 passed, 14 total", 12, 2},
|
|
{"go test style count", "ok\texample.com/foo\t0.12s\t12 tests", 12, 0},
|
|
{"zero counts", "0 passed, 0 failed", 0, 0},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
passed, failed := extractTestResults(tt.in)
|
|
if passed != tt.wantPassed || failed != tt.wantFailed {
|
|
t.Fatalf("extractTestResults(%q) = (%d, %d), want (%d, %d)", tt.in, passed, failed, tt.wantPassed, tt.wantFailed)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestExtractFilesChanged(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
in string
|
|
want []string
|
|
}{
|
|
{"root file", "Modified: main.go\n", []string{"main.go"}},
|
|
{"path file", "Created: codeagent-wrapper/utils.go\n", []string{"codeagent-wrapper/utils.go"}},
|
|
{"at prefix", "Updated: @codeagent-wrapper/main.go\n", []string{"codeagent-wrapper/main.go"}},
|
|
{"token scan", "Files: @main.go, @codeagent-wrapper/utils.go\n", []string{"main.go", "codeagent-wrapper/utils.go"}},
|
|
{"space path", "Modified: dir/with space/file.go\n", []string{"dir/with space/file.go"}},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if got := extractFilesChanged(tt.in); !reflect.DeepEqual(got, tt.want) {
|
|
t.Fatalf("extractFilesChanged(%q) = %#v, want %#v", tt.in, got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
|
|
t.Run("limits to first 10", func(t *testing.T) {
|
|
var b strings.Builder
|
|
for i := 0; i < 12; i++ {
|
|
fmt.Fprintf(&b, "Modified: file%d.go\n", i)
|
|
}
|
|
got := extractFilesChanged(b.String())
|
|
if len(got) != 10 {
|
|
t.Fatalf("len(files)=%d, want 10: %#v", len(got), got)
|
|
}
|
|
for i := 0; i < 10; i++ {
|
|
want := fmt.Sprintf("file%d.go", i)
|
|
if got[i] != want {
|
|
t.Fatalf("files[%d]=%q, want %q", i, got[i], want)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestSafeTruncate(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
in string
|
|
maxLen int
|
|
want string
|
|
}{
|
|
{"empty", "", 4, ""},
|
|
{"zero maxLen", "hello", 0, ""},
|
|
{"one rune", "你好", 1, "你"},
|
|
{"two runes no truncate", "你好", 2, "你好"},
|
|
{"three runes no truncate", "你好", 3, "你好"},
|
|
{"two runes truncates long", "你好世界", 2, "你"},
|
|
{"three runes truncates long", "你好世界", 3, "你"},
|
|
{"four with ellipsis", "你好世界啊", 4, "你..."},
|
|
{"emoji", "🙂🙂🙂🙂🙂", 4, "🙂..."},
|
|
{"no truncate", "你好世界", 4, "你好世界"},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if got := safeTruncate(tt.in, tt.maxLen); got != tt.want {
|
|
t.Fatalf("safeTruncate(%q, %d) = %q, want %q", tt.in, tt.maxLen, got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestExtractKeyOutputFromLines(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
lines []string
|
|
maxLen int
|
|
want string
|
|
}{
|
|
{"empty lines returns empty", []string{}, 0, ""},
|
|
{"negative maxLen returns empty", []string{"hello world"}, -1, ""},
|
|
{"unlimited: summary prefix returned in full", []string{"Summary: all done with a very long description that exceeds 150 characters and should not be cut off at all"}, 0, "all done with a very long description that exceeds 150 characters and should not be cut off at all"},
|
|
{"unlimited: first meaningful line returned in full", []string{"this is a meaningful line that is longer than twenty chars and should not be truncated"}, 0, "this is a meaningful line that is longer than twenty chars and should not be truncated"},
|
|
{"unlimited: skips noise lines", []string{"---", "# heading", "short", "this is a meaningful line longer than twenty chars"}, 0, "this is a meaningful line longer than twenty chars"},
|
|
{"limited: summary prefix truncated", []string{"Summary: hello world truncated"}, 10, "hello w..."},
|
|
{"limited: first meaningful line truncated", []string{"this is a long meaningful line that will be truncated"}, 20, "this is a long me..."},
|
|
{"unlimited: fallback joins all lines", []string{"short", "also short"}, 0, "short\nalso short"},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got := extractKeyOutputFromLines(tt.lines, tt.maxLen)
|
|
if got != tt.want {
|
|
t.Fatalf("extractKeyOutputFromLines(%v, %d) = %q, want %q", tt.lines, tt.maxLen, got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSanitizeOutput(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
in string
|
|
want string
|
|
}{
|
|
{"ansi", "\x1b[31mred\x1b[0m", "red"},
|
|
{"control chars", "a\x07b\r\nc\t", "ab\nc\t"},
|
|
{"normal", "hello\nworld\t!", "hello\nworld\t!"},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if got := sanitizeOutput(tt.in); got != tt.want {
|
|
t.Fatalf("sanitizeOutput(%q) = %q, want %q", tt.in, got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|