validate_test.go
package main
import (
"errors"
"testing"
)
func TestValidateRepoName(t *testing.T) {
t.Parallel()
runs(t, validateRepoName, []vcase{
{"plain", "parnass", true},
{"nested", "work/infra/deploy", true},
{"dots and dashes inside", "a-b.c_d", true},
{"digits", "repo2", true},
{"empty", "", false},
{"leading slash", "/foo", false},
{"trailing slash", "foo/", false},
{"double slash", "foo//bar", false},
{"dot segment", "foo/./bar", false},
{"dotdot segment", "foo/../bar", false},
{"leading dot", ".hidden", false},
{"leading dash", "-flag", false},
{"leading dash in segment", "ok/-flag", false},
{"git suffix", "foo.git", false},
{"git suffix in segment", "foo.git/bar", false},
{"bare .git segment", "foo/.git", false},
{"space", "fo o", false},
{"colon", "fo:o", false},
{"unicode", "caf\u00e9", false},
})
}
func TestValidateRef(t *testing.T) {
t.Parallel()
runs(t, validateRef, []vcase{
{"branch", "main", true},
{"HEAD", "HEAD", true},
{"tag", "v1.0", true},
{"slashed branch", "feature/shiny", true},
{"full sha", "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", true},
{"empty", "", false},
{"leading dash", "-option", false},
{"range", "main..dev", false},
{"triple dot range", "main...dev", false},
{"reflog syntax", "HEAD@{1}", false},
{"parent suffix", "HEAD^", false},
{"tilde suffix", "HEAD~2", false},
{"colon path", "main:README.md", false},
{"space", "ma in", false},
{"dot-led segment", "foo/.bar", false},
{"lock suffix", "main.lock", false},
{"empty segment", "foo//bar", false},
{"leading slash", "/main", false},
{"trailing slash", "main/", false},
{"overlong", string(make([]byte, 256)), false},
})
}
func TestValidateTreePath(t *testing.T) {
t.Parallel()
runs(t, validateTreePath, []vcase{
{"root", "", true},
{"file", "README.md", true},
{"nested", "docs/notes.md", true},
{"dotfile", ".gitignore", true},
{"space in name", "docs/with space.txt", true},
{"unicode", "caf\u00e9/menu.md", true},
{"leading dash", "-dashed", true}, // safe: paths ride after the colon of <sha>:<path>
{"dot segment", "./README.md", false},
{"dotdot segment", "docs/../secret", false},
{"bare dotdot", "..", false},
{"leading slash", "/etc/passwd", false},
{"trailing slash", "docs/", false},
{"double slash", "docs//notes.md", false},
{"newline", "do\ncs", false},
{"nul", "do\x00cs", false},
})
}
type vcase struct {
name string
input string
ok bool
}
// runs drives one validator through a case table,
// checking both acceptance and that every rejection is an errInvalid.
func runs(t *testing.T, validate func(string) error, cases []vcase) {
t.Helper()
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
err := validate(c.input)
if c.ok && err != nil {
t.Errorf("%q: expected acceptance, got %v", c.input, err)
}
if !c.ok {
if err == nil {
t.Errorf("%q: expected rejection, got acceptance", c.input)
} else if !errors.Is(err, errInvalid) {
t.Errorf("%q: rejection should wrap errInvalid, got %v", c.input, err)
}
}
})
}
}
validate_test.go · 3.0 KiB · 2f10e1d4cf