package core import ( "errors" "strings" "testing" ) func TestValidateOwner(t *testing.T) { tests := []struct { in string ok bool }{ {"bigbes", true}, {"user_name", true}, {"user-name", true}, {"user.name", true}, {"u123", true}, {"a", true}, {strings.Repeat("a", MaxOwnerLen), true}, {"", false}, {strings.Repeat("a", MaxOwnerLen+1), false}, {"-leading", false}, {"~bigbes", false}, // caller must strip the '~' first {"has/slash", false}, {"has..dots", false}, {"..", false}, {"Upper", false}, {"has space", false}, {"tilde~", false}, {"emojiπŸ˜€", false}, {"ΠΏΡ€ΠΈΠ²Π΅Ρ‚", false}, {"nul\x00byte", false}, } for _, tc := range tests { err := ValidateOwner(tc.in) if (err == nil) != tc.ok { t.Errorf("ValidateOwner(%q) = %v, want ok=%v", tc.in, err, tc.ok) } if err != nil && !errors.Is(err, ErrInvalidName) { t.Errorf("ValidateOwner(%q) error %v is not ErrInvalidName", tc.in, err) } } } func TestValidateSpaceName(t *testing.T) { tests := []struct { in string ok bool }{ {"rfcs", true}, {"tarantool-rfcs", true}, {"home_ops", true}, {"v1.0", true}, {strings.Repeat("a", MaxSpaceNameLen), true}, {"", false}, {strings.Repeat("a", MaxSpaceNameLen+1), false}, {"-dashfirst", false}, {"a/b", false}, {"a..b", false}, {".", false}, // single dot is a legal byte set but "." alone is a directory {"..", false}, // traversal {"CamelCase", false}, {"+everything", false}, // project namespace, not a space name } for _, tc := range tests { err := ValidateSpaceName(tc.in) if (err == nil) != tc.ok { t.Errorf("ValidateSpaceName(%q) = %v, want ok=%v", tc.in, err, tc.ok) } } } func TestValidateSpaceNameRejectsBareDot(t *testing.T) { // "." passes the byte-set rule, so it must be caught somewhere: a space // literally named "." would resolve to the repos root. if err := ValidateSpaceName("."); err == nil { t.Fatal(`ValidateSpaceName(".") must fail: it would resolve to the repos root`) } } func TestParseSpaceRef(t *testing.T) { tests := []struct { name string in string wantOwner string wantName string ok bool }{ {"tilde form", "~bigbes/rfcs", "bigbes", "rfcs", true}, {"bare form", "bigbes/rfcs", "bigbes", "rfcs", true}, {"leading slash", "/~bigbes/rfcs", "bigbes", "rfcs", true}, {"trailing slash", "~bigbes/rfcs/", "bigbes", "rfcs", true}, {"empty", "", "", "", false}, {"slashes only", "///", "", "", false}, {"one segment", "~bigbes", "", "", false}, {"three segments", "~bigbes/rfcs/docs", "", "", false}, {"empty owner", "~/rfcs", "", "", false}, {"empty name", "~bigbes/", "", "", false}, {"traversal owner", "~../rfcs", "", "", false}, {"traversal name", "~bigbes/..", "", "", false}, {"uppercase owner", "~BigBes/rfcs", "", "", false}, {"space in name", "~bigbes/my rfcs", "", "", false}, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ref, err := ParseSpaceRef(tc.in) if (err == nil) != tc.ok { t.Fatalf("ParseSpaceRef(%q) = %v, %v, want ok=%v", tc.in, ref, err, tc.ok) } if !tc.ok { if !errors.Is(err, ErrInvalidName) { t.Fatalf("error %v is not ErrInvalidName", err) } return } if ref.Owner != tc.wantOwner || ref.Name != tc.wantName { t.Fatalf("ParseSpaceRef(%q) = %+v, want {%q %q}", tc.in, ref, tc.wantOwner, tc.wantName) } if got, want := ref.String(), "~"+tc.wantOwner+"/"+tc.wantName; got != want { t.Fatalf("String() = %q, want %q", got, want) } }) } } func TestValidatePath(t *testing.T) { tests := []struct { name string in string ok bool }{ {"simple", "specs/0007-storage.md", true}, {"root file", "README.md", true}, {"dotfile", ".spec.yml", true}, {"deep", "a/b/c/d/e.md", true}, {"cyrillic", "спСки/Ρ…Ρ€Π°Π½ΠΈΠ»ΠΈΡ‰Π΅.md", true}, {"attachment", "assets/diagram.png", true}, {"dot in name", "v1.2.3.md", true}, {"leading dot component", "notes/.private.md", true}, {"max length", strings.Repeat("a", MaxPathLen), true}, {"empty", "", false}, {"too long", strings.Repeat("a", MaxPathLen+1), false}, {"absolute", "/etc/passwd", false}, {"trailing slash", "specs/", false}, {"parent traversal", "../etc/passwd", false}, {"embedded traversal", "specs/../../etc/passwd", false}, {"interior traversal", "specs/../notes/a.md", false}, {"dot component", "./a.md", false}, {"interior dot component", "a/./b.md", false}, {"empty component", "specs//a.md", false}, {"git dir", ".git/config", false}, {"nested git dir", "specs/.git/HEAD", false}, {"backslash", `specs\a.md`, false}, {"windows traversal", `..\a.md`, false}, {"nul byte", "specs/a\x00.md", false}, {"newline", "specs/a\nb.md", false}, {"tab", "specs/a\tb.md", false}, {"del", "specs/a\x7f.md", false}, {"rtl override", "specs/exe\u202egnp.md", false}, {"rtl mark", "specs/a\u200fb.md", false}, {"pop directional isolate", "specs/a\u2069b.md", false}, {"invalid utf-8", "specs/\xff\xfe.md", false}, {"trailing dot component", "specs/a./b.md", false}, {"trailing space component", "specs/a /b.md", false}, {"trailing space at end", "specs/a.md ", false}, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { err := ValidatePath(tc.in) if (err == nil) != tc.ok { t.Fatalf("ValidatePath(%q) = %v, want ok=%v", tc.in, err, tc.ok) } if err != nil && !errors.Is(err, ErrInvalidPath) { t.Fatalf("error %v is not ErrInvalidPath", err) } }) } } func TestValidateDocPath(t *testing.T) { tests := []struct { name string in string ok bool }{ {"spec", "specs/0007-storage.md", true}, {"root", "README.md", true}, {"hidden doc", "notes/.draft.md", true}, {"cyrillic", "спСки/Ρ…Ρ€Π°Π½ΠΈΠ»ΠΈΡ‰Π΅.md", true}, {"no extension", "specs/0007-storage", false}, {"wrong extension", "specs/0007-storage.markdown", false}, {"uppercase extension", "specs/0007-storage.MD", false}, {"extension only", ".md", false}, {"extension only in dir", "specs/.md", false}, {"extension in middle", "specs/a.md.txt", false}, {"traversal wins", "../a.md", false}, {"empty", "", false}, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { err := ValidateDocPath(tc.in) if (err == nil) != tc.ok { t.Fatalf("ValidateDocPath(%q) = %v, want ok=%v", tc.in, err, tc.ok) } if err != nil && !errors.Is(err, ErrInvalidPath) { t.Fatalf("error %v is not ErrInvalidPath", err) } }) } }