package core import ( "errors" "testing" ) func TestParseCompareSpec(t *testing.T) { tests := []struct { name string raw string wantBase string wantHead string want3Dot bool wantErr bool }{ // Happy paths. {name: "three-dot", raw: "main...feature", wantBase: "main", wantHead: "feature", want3Dot: true}, {name: "two-dot", raw: "main..feature", wantBase: "main", wantHead: "feature", want3Dot: false}, {name: "slash branch head", raw: "main...feature/foo", wantBase: "main", wantHead: "feature/foo", want3Dot: true}, {name: "slash branch both", raw: "feature/with-slash...release/v1", wantBase: "feature/with-slash", wantHead: "release/v1", want3Dot: true}, {name: "two-dot slash", raw: "feature/a..feature/b", wantBase: "feature/a", wantHead: "feature/b", want3Dot: false}, {name: "sha40 vs branch", raw: "0123456789abcdef0123456789abcdef01234567...main", wantBase: "0123456789abcdef0123456789abcdef01234567", wantHead: "main", want3Dot: true}, {name: "sha256 head", raw: "main.." + "0000000000000000000000000000000000000000000000000000000000000000", wantBase: "main", wantHead: "0000000000000000000000000000000000000000000000000000000000000000"}, {name: "percent-escaped slash", raw: "feature%2Ffoo...main", wantBase: "feature/foo", wantHead: "main", want3Dot: true}, {name: "tag with dot", raw: "v1.0.0...v1.1.0", wantBase: "v1.0.0", wantHead: "v1.1.0", want3Dot: true}, // Separator handling. {name: "no separator", raw: "mainfeature", wantErr: true}, {name: "empty", raw: "", wantErr: true}, {name: "empty base", raw: "...main", wantErr: true}, {name: "empty head", raw: "main...", wantErr: true}, {name: "both empty two-dot", raw: "..", wantErr: true}, {name: "four dots splits on three then leading dot", raw: "a....b", wantErr: true}, // Hostile input — must be rejected via ValidRef. {name: "option injection head", raw: "main...--upload-pack=x", wantErr: true}, {name: "option injection base", raw: "--output=/etc/passwd...main", wantErr: true}, {name: "revision at-brace", raw: "main...@{u}", wantErr: true}, {name: "trailing .lock", raw: "main...foo.lock", wantErr: true}, {name: "leading dot head", raw: "main....hidden", wantErr: true}, {name: "caret ref", raw: "main...HEAD^", wantErr: true}, {name: "tilde ref", raw: "main...HEAD~3", wantErr: true}, {name: "colon ref", raw: "main...refs:x", wantErr: true}, {name: "space in ref", raw: "main...my branch", wantErr: true}, {name: "control char", raw: "main...he\x00ad", wantErr: true}, {name: "escaped dotdot becomes traversal", raw: "main...%2E%2E", wantErr: true}, {name: "escaped traversal path", raw: "main...%2e%2e%2Fetc", wantErr: true}, {name: "bad percent-encoding", raw: "main...%zz", wantErr: true}, {name: "double slash", raw: "main...a//b", wantErr: true}, {name: "at shorthand", raw: "main...@", wantErr: true}, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { got, err := ParseCompareSpec(tc.raw) if tc.wantErr { if err == nil { t.Fatalf("ParseCompareSpec(%q) = %+v, want error", tc.raw, got) } if !errors.Is(err, ErrBadRef) { t.Fatalf("ParseCompareSpec(%q) error = %v, want wrapping ErrBadRef", tc.raw, err) } return } if err != nil { t.Fatalf("ParseCompareSpec(%q) unexpected error: %v", tc.raw, err) } if got.Base != tc.wantBase || got.Head != tc.wantHead || got.ThreeDot != tc.want3Dot { t.Fatalf("ParseCompareSpec(%q) = {Base:%q Head:%q ThreeDot:%v}, want {Base:%q Head:%q ThreeDot:%v}", tc.raw, got.Base, got.Head, got.ThreeDot, tc.wantBase, tc.wantHead, tc.want3Dot) } }) } }