diff options
Diffstat (limited to 'internal/tmuxedit/agentutil_test.go')
| -rw-r--r-- | internal/tmuxedit/agentutil_test.go | 206 |
1 files changed, 206 insertions, 0 deletions
diff --git a/internal/tmuxedit/agentutil_test.go b/internal/tmuxedit/agentutil_test.go new file mode 100644 index 0000000..8bf2e64 --- /dev/null +++ b/internal/tmuxedit/agentutil_test.go @@ -0,0 +1,206 @@ +package tmuxedit + +import ( + "regexp" + "testing" +) + +func TestScopeToLastSection(t *testing.T) { + tests := []struct { + name string + content string + pattern string + want string + }{ + { + name: "no pattern returns full content", + content: "line1\nline2\nline3", + pattern: "", + want: "line1\nline2\nline3", + }, + { + name: "invalid regex returns full content", + content: "line1\nline2", + pattern: "[invalid", + want: "line1\nline2", + }, + { + name: "fewer than two delimiters returns full content", + content: "─────\nhello", + pattern: `^─{5,}`, + want: "─────\nhello", + }, + { + name: "extracts last section between two delimiters", + content: "─────\nold message\n─────\n❯ prompt text\n─────", + pattern: `^─{5,}`, + want: "❯ prompt text", + }, + { + name: "skips earlier sections", + content: "─────\n❯ old msg1\n─────\n" + + "─────\n❯ old msg2\n─────\n" + + "─────\n❯ current prompt\n─────", + pattern: `^─{5,}`, + want: "❯ current prompt", + }, + { + name: "claude multi-line prompt between rules", + content: "previous output\n" + + "─────────────\n" + + "❯ first line\n" + + "\n" + + "❯ second line\n" + + "\n" + + "❯ third line\n" + + "─────────────\n" + + " -- INSERT --", + pattern: `^─{5,}`, + want: "❯ first line\n\n❯ second line\n\n❯ third line", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := scopeToLastSection(tt.content, tt.pattern) + if got != tt.want { + t.Errorf("scopeToLastSection() = %q, want %q", got, tt.want) + } + }) + } +} + +func TestStripNoise(t *testing.T) { + tests := []struct { + name string + text string + patterns []string + want string + }{ + {"no patterns", "hello world", nil, "hello world"}, + {"strip INSERT", "fix the bug INSERT", []string{"INSERT"}, "fix the bug"}, + {"strip multiple", "INSERT fix the bug Add a follow-up", []string{"INSERT", "Add a follow-up"}, "fix the bug"}, + {"strip to empty", "INSERT", []string{"INSERT"}, ""}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := stripNoise(tt.text, tt.patterns) + if got != tt.want { + t.Errorf("stripNoise() = %q, want %q", got, tt.want) + } + }) + } +} + +func TestMatchPromptLines(t *testing.T) { + tests := []struct { + name string + pattern string + content string + want int + }{ + {"no matches", `❯\s*(.+)$`, "no prompt here", 0}, + {"single match", `❯\s*(.+)$`, "❯ hello", 1}, + {"multiple matches", `❯\s*(.+)$`, "❯ first\nother\n❯ second", 2}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + re := mustCompile(t, tt.pattern) + got := matchPromptLines(re, tt.content) + if len(got) != tt.want { + t.Errorf("matchPromptLines() returned %d matches, want %d", len(got), tt.want) + } + }) + } +} + +func TestJoinAllMatches(t *testing.T) { + matches := []promptMatch{ + {lineNum: 0, text: "first"}, + {lineNum: 2, text: "INSERT"}, + {lineNum: 4, text: "third"}, + } + got := joinAllMatches(matches, []string{"INSERT"}) + if got != "first\nthird" { + t.Errorf("joinAllMatches() = %q, want %q", got, "first\nthird") + } +} + +func TestJoinLastContiguousBlock(t *testing.T) { + tests := []struct { + name string + matches []promptMatch + strips []string + want string + }{ + { + name: "single block", + matches: []promptMatch{ + {lineNum: 5, text: "first"}, + {lineNum: 6, text: "second"}, + }, + want: "first\nsecond", + }, + { + name: "two blocks takes last", + matches: []promptMatch{ + {lineNum: 1, text: "old"}, + {lineNum: 2, text: "old2"}, + {lineNum: 10, text: "new"}, + {lineNum: 11, text: "new2"}, + }, + want: "new\nnew2", + }, + { + name: "strips noise", + matches: []promptMatch{ + {lineNum: 0, text: "fix INSERT"}, + }, + strips: []string{"INSERT"}, + want: "fix", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := joinLastContiguousBlock(tt.matches, tt.strips) + if got != tt.want { + t.Errorf("joinLastContiguousBlock() = %q, want %q", got, tt.want) + } + }) + } +} + +func TestParseKeyRepeat(t *testing.T) { + tests := []struct { + token string + wantKey string + wantCount int + }{ + {"BSpace*200", "BSpace", 200}, + {"End", "End", 1}, + {"C-u", "C-u", 1}, + {"BSpace*1", "BSpace", 1}, + {"BSpace*0", "BSpace*0", 1}, // invalid count + {"BSpace*abc", "BSpace*abc", 1}, // non-numeric + {"*200", "*200", 1}, // no key name + {"x*3", "x", 3}, + } + for _, tt := range tests { + t.Run(tt.token, func(t *testing.T) { + key, count := parseKeyRepeat(tt.token) + if key != tt.wantKey || count != tt.wantCount { + t.Errorf("parseKeyRepeat(%q) = (%q, %d), want (%q, %d)", + tt.token, key, count, tt.wantKey, tt.wantCount) + } + }) + } +} + +// mustCompile is a test helper that compiles a regex or fails the test. +func mustCompile(t *testing.T, pattern string) *regexp.Regexp { + t.Helper() + re, err := regexp.Compile(pattern) + if err != nil { + t.Fatalf("regexp.Compile(%q) failed: %v", pattern, err) + } + return re +} |
