package trim import ( "strings" "testing" ) func TestParseSelectorSingleID(t *testing.T) { selector, err := ParseSelector("1") if err != nil { t.Fatalf("parse failed: %v", err) } assertIDs(t, selector, []int{1}) assertContains(t, selector, map[int]bool{1: true, 2: false, 0: false, -1: false}) } func TestParseSelectorInclusiveRange(t *testing.T) { selector, err := ParseSelector("1-3") if err != nil { t.Fatalf("parse failed: %v", err) } assertIDs(t, selector, []int{1, 2, 3}) } func TestParseSelectorCommaSeparatedCombination(t *testing.T) { selector, err := ParseSelector("1-3,8,10-12") if err != nil { t.Fatalf("parse failed: %v", err) } assertIDs(t, selector, []int{1, 2, 3, 8, 10, 11, 12}) } func TestParseSelectorWhitespaceTolerance(t *testing.T) { selector, err := ParseSelector(" 1 - 3 , 8 , 10 - 12 ") if err != nil { t.Fatalf("parse failed: %v", err) } assertIDs(t, selector, []int{1, 2, 3, 8, 10, 11, 12}) } func TestParseSelectorDuplicatesAndOverlapsNormalizeUnion(t *testing.T) { selector, err := ParseSelector("1-4,2,4,3-6,6") if err != nil { t.Fatalf("parse failed: %v", err) } assertIDs(t, selector, []int{1, 2, 3, 4, 5, 6}) assertContains(t, selector, map[int]bool{1: true, 5: true, 6: true, 7: false}) } func TestParseSelectorDeterministicNormalizedOutput(t *testing.T) { left, err := ParseSelector("8,1-3,2,10-12") if err != nil { t.Fatalf("parse left failed: %v", err) } right, err := ParseSelector("10-12,3,2,1,8") if err != nil { t.Fatalf("parse right failed: %v", err) } leftIDs := left.IDs() rightIDs := right.IDs() if !equalInts(leftIDs, rightIDs) { t.Fatalf("normalized IDs mismatch: %v vs %v", leftIDs, rightIDs) } } func TestParseSelectorFailures(t *testing.T) { tests := []struct { name string selector string wantError string }{ {name: "empty", selector: "", wantError: "cannot be empty"}, {name: "whitespace only", selector: " ", wantError: "cannot be empty"}, {name: "zero", selector: "0", wantError: "must be positive"}, {name: "negative", selector: "-1", wantError: "must be positive"}, {name: "range includes zero", selector: "0-2", wantError: "must be positive"}, {name: "descending range", selector: "10-1", wantError: "descending range"}, {name: "empty element", selector: "1,,2", wantError: "cannot be empty"}, {name: "trailing comma", selector: "1,", wantError: "cannot be empty"}, {name: "malformed alpha", selector: "abc", wantError: "malformed element"}, {name: "malformed range", selector: "1-2-3", wantError: "malformed element"}, {name: "missing end", selector: "1-", wantError: "malformed element"}, {name: "missing start", selector: "-2", wantError: "must be positive"}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { _, err := ParseSelector(test.selector) if err == nil { t.Fatalf("expected error for %q", test.selector) } if !strings.Contains(err.Error(), test.wantError) { t.Fatalf("error = %q, want substring %q", err.Error(), test.wantError) } }) } } func assertIDs(t *testing.T, selector Selector, want []int) { t.Helper() got := selector.IDs() if !equalInts(got, want) { t.Fatalf("IDs = %v, want %v", got, want) } } func assertContains(t *testing.T, selector Selector, checks map[int]bool) { t.Helper() for id, want := range checks { if got := selector.Contains(id); got != want { t.Fatalf("Contains(%d) = %t, want %t", id, got, want) } } } func equalInts(left []int, right []int) bool { if len(left) != len(right) { return false } for index := range left { if left[index] != right[index] { return false } } return true }