mirror of
https://github.com/dogkeeper886/ollama37.git
synced 2025-12-16 02:37:06 +00:00
This commit is a step towards a goal to make names less ceremonial outside of the registry client. Clients of the registry package can treat names as opaque strings, and the registry package will handle parsing, validating, and normalizing names. Ideally we end up with the names package tucked away in an internal package for good. We'll see how things go. Also, this package name is not permanent. This another step in the on-going process of refactoring the server code, and at some point it will most likely be renamed/moved.
221 lines
5.1 KiB
Go
221 lines
5.1 KiB
Go
package names
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestParseName(t *testing.T) {
|
|
cases := []struct {
|
|
in string
|
|
want Name
|
|
}{
|
|
{"", Name{}},
|
|
{"m:t", Name{m: "m", t: "t"}},
|
|
{"m", Name{m: "m"}},
|
|
{"/m", Name{m: "m"}},
|
|
{"/n/m:t", Name{n: "n", m: "m", t: "t"}},
|
|
{"n/m", Name{n: "n", m: "m"}},
|
|
{"n/m:t", Name{n: "n", m: "m", t: "t"}},
|
|
{"n/m", Name{n: "n", m: "m"}},
|
|
{"n/m", Name{n: "n", m: "m"}},
|
|
{strings.Repeat("m", MaxNameLength+1), Name{}},
|
|
{"h/n/m:t", Name{h: "h", n: "n", m: "m", t: "t"}},
|
|
{"ollama.com/library/_:latest", Name{h: "ollama.com", n: "library", m: "_", t: "latest"}},
|
|
|
|
// Invalids
|
|
// TODO: {"n:t/m:t", Name{}},
|
|
// TODO: {"/h/n/m:t", Name{}},
|
|
}
|
|
|
|
for _, tt := range cases {
|
|
t.Run(tt.in, func(t *testing.T) {
|
|
got := Parse(tt.in)
|
|
if got.Compare(tt.want) != 0 {
|
|
t.Errorf("parseName(%q) = %#v, want %q", tt.in, got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestString(t *testing.T) {
|
|
cases := []string{
|
|
"",
|
|
"m:t",
|
|
"m:t",
|
|
"m",
|
|
"n/m",
|
|
"n/m:t",
|
|
"n/m",
|
|
"n/m",
|
|
"h/n/m:t",
|
|
"ollama.com/library/_:latest",
|
|
|
|
// Special cased to "round trip" without the leading slash.
|
|
"/m",
|
|
"/n/m:t",
|
|
}
|
|
for _, s := range cases {
|
|
t.Run(s, func(t *testing.T) {
|
|
s = strings.TrimPrefix(s, "/")
|
|
if g := Parse(s).String(); g != s {
|
|
t.Errorf("parse(%q).String() = %q", s, g)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestParseExtended(t *testing.T) {
|
|
cases := []struct {
|
|
in string
|
|
|
|
wantScheme string
|
|
wantName Name
|
|
wantDigest string
|
|
}{
|
|
{"", "", Name{}, ""},
|
|
{"m", "", Name{m: "m"}, ""},
|
|
{"http://m", "http", Name{m: "m"}, ""},
|
|
{"http+insecure://m", "http+insecure", Name{m: "m"}, ""},
|
|
{"http://m@sha256:deadbeef", "http", Name{m: "m"}, "sha256:deadbeef"},
|
|
}
|
|
for _, tt := range cases {
|
|
t.Run(tt.in, func(t *testing.T) {
|
|
scheme, name, digest := Split(tt.in)
|
|
n := Parse(name)
|
|
if scheme != tt.wantScheme || n.Compare(tt.wantName) != 0 || digest != tt.wantDigest {
|
|
t.Errorf("ParseExtended(%q) = %q, %#v, %q, want %q, %#v, %q", tt.in, scheme, name, digest, tt.wantScheme, tt.wantName, tt.wantDigest)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMerge(t *testing.T) {
|
|
cases := []struct {
|
|
a, b string
|
|
want string
|
|
}{
|
|
{"", "", ""},
|
|
{"m", "", "m"},
|
|
{"", "m", ""},
|
|
{"x", "y", "x"},
|
|
{"o.com/n/m:t", "o.com/n/m:t", "o.com/n/m:t"},
|
|
{"o.com/n/m:t", "o.com/n/_:t", "o.com/n/m:t"},
|
|
|
|
{"bmizerany/smol", "ollama.com/library/_:latest", "ollama.com/bmizerany/smol:latest"},
|
|
{"localhost:8080/bmizerany/smol", "ollama.com/library/_:latest", "localhost:8080/bmizerany/smol:latest"},
|
|
}
|
|
for _, tt := range cases {
|
|
t.Run("", func(t *testing.T) {
|
|
a, b := Parse(tt.a), Parse(tt.b)
|
|
got := Merge(a, b)
|
|
if got.Compare(Parse(tt.want)) != 0 {
|
|
t.Errorf("merge(%q, %q) = %#v, want %q", tt.a, tt.b, got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestParseStringRoundTrip(t *testing.T) {
|
|
cases := []string{
|
|
"",
|
|
"m",
|
|
"m:t",
|
|
"n/m",
|
|
"n/m:t",
|
|
"n/m:t",
|
|
"n/m",
|
|
"n/m",
|
|
"h/n/m:t",
|
|
"ollama.com/library/_:latest",
|
|
}
|
|
for _, s := range cases {
|
|
t.Run(s, func(t *testing.T) {
|
|
if got := Parse(s).String(); got != s {
|
|
t.Errorf("parse(%q).String() = %q", s, got)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
var junkName Name
|
|
|
|
func BenchmarkParseName(b *testing.B) {
|
|
b.ReportAllocs()
|
|
for range b.N {
|
|
junkName = Parse("h/n/m:t")
|
|
}
|
|
}
|
|
|
|
const (
|
|
part80 = "88888888888888888888888888888888888888888888888888888888888888888888888888888888"
|
|
part350 = "33333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333"
|
|
)
|
|
|
|
var testCases = map[string]bool{ // name -> valid
|
|
"": false,
|
|
|
|
"_why/_the/_lucky:_stiff": true,
|
|
|
|
// minimal
|
|
"h/n/m:t": true,
|
|
|
|
"host/namespace/model:tag": true,
|
|
"host/namespace/model": true,
|
|
"namespace/model": true,
|
|
"model": true,
|
|
|
|
// long (but valid)
|
|
part80 + "/" + part80 + "/" + part80 + ":" + part80: true,
|
|
part350 + "/" + part80 + "/" + part80 + ":" + part80: true,
|
|
|
|
// too long
|
|
part80 + "/" + part80 + "/" + part80 + ":" + part350: false,
|
|
"x" + part350 + "/" + part80 + "/" + part80 + ":" + part80: false,
|
|
|
|
"h/nn/mm:t": true, // bare minimum part sizes
|
|
|
|
// unqualified
|
|
"m": true,
|
|
"n/m:": true,
|
|
"h/n/m": true,
|
|
"@t": false,
|
|
"m@d": false,
|
|
|
|
// invalids
|
|
"^": false,
|
|
"mm:": true,
|
|
"/nn/mm": true,
|
|
"//": false, // empty model
|
|
"//mm": true,
|
|
"hh//": false, // empty model
|
|
"//mm:@": false,
|
|
"00@": false,
|
|
"@": false,
|
|
|
|
// not starting with alphanum
|
|
"-hh/nn/mm:tt": false,
|
|
"hh/-nn/mm:tt": false,
|
|
"hh/nn/-mm:tt": false,
|
|
"hh/nn/mm:-tt": false,
|
|
|
|
// smells like a flag
|
|
"-h": false,
|
|
|
|
// hosts
|
|
"host:https/namespace/model:tag": true,
|
|
|
|
// colon in non-host part before tag
|
|
"host/name:space/model:tag": false,
|
|
}
|
|
|
|
func TestParseNameValidation(t *testing.T) {
|
|
for s, valid := range testCases {
|
|
got := Parse(s)
|
|
if got.IsValid() != valid {
|
|
t.Logf("got: %v", got)
|
|
t.Errorf("Parse(%q).IsValid() = %v; want !%[2]v", s, got.IsValid())
|
|
}
|
|
}
|
|
}
|