82 lines
3.4 KiB
Go
82 lines
3.4 KiB
Go
package readselector
|
|
|
|
import "testing"
|
|
|
|
func TestSplit(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
raw string
|
|
wantPath string
|
|
wantStart int
|
|
wantCount int
|
|
}{
|
|
{"no selector", "config.json", "config.json", 0, 0},
|
|
{"single line", "apps/web/lib/utils.ts:50", "apps/web/lib/utils.ts", 50, 0},
|
|
{"open ended", "main.go:50-", "main.go", 50, 0},
|
|
{"closed range", "apps/backend/internal/sentry/handlers.go:43-94", "apps/backend/internal/sentry/handlers.go", 43, 52},
|
|
{"plus span", "main.go:50+150", "main.go", 50, 150},
|
|
{"anchor", "main.go:20+1", "main.go", 20, 1},
|
|
{"multi range", "main.go:5-16,960-973", "main.go", 5, 12},
|
|
{"raw mode", "main.go:raw", "main.go", 0, 0},
|
|
{"conflicts mode", "main.go:conflicts", "main.go", 0, 0},
|
|
{"range then raw", "main.go:2-4:raw", "main.go", 2, 3},
|
|
{"raw then range", "main.go:raw:2-4", "main.go", 2, 3},
|
|
{"absolute path", "/home/u/.kandev/x/handlers.go:43-94", "/home/u/.kandev/x/handlers.go", 43, 52},
|
|
{"absolute external", "/home/clem/.claude/CLAUDE.md:93-113", "/home/clem/.claude/CLAUDE.md", 93, 21},
|
|
{"no extension single line", "Makefile:10", "Makefile", 10, 0},
|
|
// Real OMP reads observed in the wild (multi-range, zero-length range,
|
|
// range+mode combo, and a "~"-prefixed home path).
|
|
{"omp multi range zero-len", "x/README.md:16-20,32-40,69-69,85-96", "x/README.md", 16, 5},
|
|
{"omp range plus raw combo", "x/README.md:69+1:raw", "x/README.md", 69, 1},
|
|
{"omp tilde multi range", "~/.kandev/x/README.md:27-28,35-36,66-66", "~/.kandev/x/README.md", 27, 2},
|
|
// Windows path: drive-letter colon must not be read as the selector
|
|
// boundary; the trailing "\\" segment + ":43-94" selector is stripped.
|
|
{"windows absolute", `C:\Users\me\handlers.go:43-94`, `C:\Users\me\handlers.go`, 43, 52},
|
|
// "N+0" is intentionally accepted (count 0, anchor at N) and stripped, so
|
|
// the link still opens; rejecting it would leave the selector on the path.
|
|
{"plus zero anchors", "main.go:50+0", "main.go", 50, 0},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
path, start, count := Split(tt.raw)
|
|
if path != tt.wantPath {
|
|
t.Errorf("path = %q, want %q", path, tt.wantPath)
|
|
}
|
|
if start != tt.wantStart {
|
|
t.Errorf("startLine = %d, want %d", start, tt.wantStart)
|
|
}
|
|
if count != tt.wantCount {
|
|
t.Errorf("lineCount = %d, want %d", count, tt.wantCount)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestSplit_NoFalsePositives verifies that paths whose colon suffix is not a
|
|
// valid omp selector are returned untouched, so legitimate filenames and
|
|
// non-omp agent paths never lose data.
|
|
func TestSplit_NoFalsePositives(t *testing.T) {
|
|
unchanged := []string{
|
|
"",
|
|
"src/file.ts",
|
|
"foo.go:bar", // non-numeric, non-keyword suffix
|
|
"foo.go:1.2", // float-like, not a line spec
|
|
`C:\Users\me\a.go`, // windows drive letter: suffix is not a line spec
|
|
"dir:1/file.go", // colon lives in a directory component, not the file
|
|
":43-94", // empty base
|
|
"main.go:", // empty selector
|
|
"main.go:-5", // negative / malformed start
|
|
"main.go:0", // zero is not a valid 1-based line
|
|
"main.go:10-5", // descending range
|
|
"main.go:5,abc", // one invalid segment invalidates the list
|
|
}
|
|
for _, raw := range unchanged {
|
|
t.Run(raw, func(t *testing.T) {
|
|
path, start, count := Split(raw)
|
|
if path != raw || start != 0 || count != 0 {
|
|
t.Errorf("Split(%q) = (%q, %d, %d), want (%q, 0, 0)", raw, path, start, count, raw)
|
|
}
|
|
})
|
|
}
|
|
}
|