mirror of
https://github.com/ollama/ollama.git
synced 2026-03-09 03:12:11 -05:00
* Revert "Revert "Reapply "don't require pulling stubs for cloud models"" (#14606)"
This reverts commit 39982a954e.
* fix test + do cloud lookup only when seeing cloud models
---------
Co-authored-by: ParthSareen <parth.sareen@ollama.com>
269 lines
6.8 KiB
Go
269 lines
6.8 KiB
Go
package modelref
|
|
|
|
import (
|
|
"errors"
|
|
"testing"
|
|
)
|
|
|
|
func TestParseRef(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
input string
|
|
wantBase string
|
|
wantSource ModelSource
|
|
wantErr error
|
|
wantCloud bool
|
|
wantLocal bool
|
|
wantStripped string
|
|
wantStripOK bool
|
|
}{
|
|
{
|
|
name: "cloud suffix",
|
|
input: "gpt-oss:20b:cloud",
|
|
wantBase: "gpt-oss:20b",
|
|
wantSource: ModelSourceCloud,
|
|
wantCloud: true,
|
|
wantStripped: "gpt-oss:20b",
|
|
wantStripOK: true,
|
|
},
|
|
{
|
|
name: "legacy cloud suffix",
|
|
input: "gpt-oss:20b-cloud",
|
|
wantBase: "gpt-oss:20b",
|
|
wantSource: ModelSourceCloud,
|
|
wantCloud: true,
|
|
wantStripped: "gpt-oss:20b",
|
|
wantStripOK: true,
|
|
},
|
|
{
|
|
name: "local suffix",
|
|
input: "qwen3:8b:local",
|
|
wantBase: "qwen3:8b",
|
|
wantSource: ModelSourceLocal,
|
|
wantLocal: true,
|
|
wantStripped: "qwen3:8b:local",
|
|
},
|
|
{
|
|
name: "no source suffix",
|
|
input: "llama3.2",
|
|
wantBase: "llama3.2",
|
|
wantSource: ModelSourceUnspecified,
|
|
wantStripped: "llama3.2",
|
|
},
|
|
{
|
|
name: "bare cloud name is not explicit cloud",
|
|
input: "my-cloud-model",
|
|
wantBase: "my-cloud-model",
|
|
wantSource: ModelSourceUnspecified,
|
|
wantStripped: "my-cloud-model",
|
|
},
|
|
{
|
|
name: "slash in suffix blocks legacy cloud parsing",
|
|
input: "foo:bar-cloud/baz",
|
|
wantBase: "foo:bar-cloud/baz",
|
|
wantSource: ModelSourceUnspecified,
|
|
wantStripped: "foo:bar-cloud/baz",
|
|
},
|
|
{
|
|
name: "conflicting source suffixes",
|
|
input: "foo:cloud:local",
|
|
wantErr: ErrConflictingSourceSuffix,
|
|
wantSource: ModelSourceUnspecified,
|
|
},
|
|
{
|
|
name: "empty input",
|
|
input: " ",
|
|
wantErr: ErrModelRequired,
|
|
wantSource: ModelSourceUnspecified,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got, err := ParseRef(tt.input)
|
|
if tt.wantErr != nil {
|
|
if !errors.Is(err, tt.wantErr) {
|
|
t.Fatalf("ParseRef(%q) error = %v, want %v", tt.input, err, tt.wantErr)
|
|
}
|
|
return
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("ParseRef(%q) returned error: %v", tt.input, err)
|
|
}
|
|
|
|
if got.Base != tt.wantBase {
|
|
t.Fatalf("base = %q, want %q", got.Base, tt.wantBase)
|
|
}
|
|
|
|
if got.Source != tt.wantSource {
|
|
t.Fatalf("source = %v, want %v", got.Source, tt.wantSource)
|
|
}
|
|
|
|
if HasExplicitCloudSource(tt.input) != tt.wantCloud {
|
|
t.Fatalf("HasExplicitCloudSource(%q) = %v, want %v", tt.input, HasExplicitCloudSource(tt.input), tt.wantCloud)
|
|
}
|
|
|
|
if HasExplicitLocalSource(tt.input) != tt.wantLocal {
|
|
t.Fatalf("HasExplicitLocalSource(%q) = %v, want %v", tt.input, HasExplicitLocalSource(tt.input), tt.wantLocal)
|
|
}
|
|
|
|
stripped, ok := StripCloudSourceTag(tt.input)
|
|
if ok != tt.wantStripOK {
|
|
t.Fatalf("StripCloudSourceTag(%q) ok = %v, want %v", tt.input, ok, tt.wantStripOK)
|
|
}
|
|
if stripped != tt.wantStripped {
|
|
t.Fatalf("StripCloudSourceTag(%q) base = %q, want %q", tt.input, stripped, tt.wantStripped)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNormalizePullName(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
input string
|
|
wantName string
|
|
wantCloud bool
|
|
wantErr error
|
|
}{
|
|
{
|
|
name: "explicit local strips source",
|
|
input: "gpt-oss:20b:local",
|
|
wantName: "gpt-oss:20b",
|
|
},
|
|
{
|
|
name: "explicit cloud with size maps to legacy dash cloud tag",
|
|
input: "gpt-oss:20b:cloud",
|
|
wantName: "gpt-oss:20b-cloud",
|
|
wantCloud: true,
|
|
},
|
|
{
|
|
name: "legacy cloud with size remains stable",
|
|
input: "gpt-oss:20b-cloud",
|
|
wantName: "gpt-oss:20b-cloud",
|
|
wantCloud: true,
|
|
},
|
|
{
|
|
name: "explicit cloud without tag maps to cloud tag",
|
|
input: "qwen3:cloud",
|
|
wantName: "qwen3:cloud",
|
|
wantCloud: true,
|
|
},
|
|
{
|
|
name: "host port without tag keeps host port and appends cloud tag",
|
|
input: "localhost:11434/library/foo:cloud",
|
|
wantName: "localhost:11434/library/foo:cloud",
|
|
wantCloud: true,
|
|
},
|
|
{
|
|
name: "conflicting source suffixes fail",
|
|
input: "foo:cloud:local",
|
|
wantErr: ErrConflictingSourceSuffix,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
gotName, gotCloud, err := NormalizePullName(tt.input)
|
|
if tt.wantErr != nil {
|
|
if !errors.Is(err, tt.wantErr) {
|
|
t.Fatalf("NormalizePullName(%q) error = %v, want %v", tt.input, err, tt.wantErr)
|
|
}
|
|
return
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("NormalizePullName(%q) returned error: %v", tt.input, err)
|
|
}
|
|
|
|
if gotName != tt.wantName {
|
|
t.Fatalf("normalized name = %q, want %q", gotName, tt.wantName)
|
|
}
|
|
if gotCloud != tt.wantCloud {
|
|
t.Fatalf("cloud = %v, want %v", gotCloud, tt.wantCloud)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestParseSourceSuffix(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
input string
|
|
wantBase string
|
|
wantSource ModelSource
|
|
wantExplicit bool
|
|
}{
|
|
{
|
|
name: "explicit cloud suffix",
|
|
input: "gpt-oss:20b:cloud",
|
|
wantBase: "gpt-oss:20b",
|
|
wantSource: ModelSourceCloud,
|
|
wantExplicit: true,
|
|
},
|
|
{
|
|
name: "explicit local suffix",
|
|
input: "qwen3:8b:local",
|
|
wantBase: "qwen3:8b",
|
|
wantSource: ModelSourceLocal,
|
|
wantExplicit: true,
|
|
},
|
|
{
|
|
name: "legacy cloud suffix on tag",
|
|
input: "gpt-oss:20b-cloud",
|
|
wantBase: "gpt-oss:20b",
|
|
wantSource: ModelSourceCloud,
|
|
wantExplicit: true,
|
|
},
|
|
{
|
|
name: "legacy cloud suffix does not match model segment",
|
|
input: "my-cloud-model",
|
|
wantBase: "my-cloud-model",
|
|
wantSource: ModelSourceUnspecified,
|
|
wantExplicit: false,
|
|
},
|
|
{
|
|
name: "legacy cloud suffix blocked when suffix includes slash",
|
|
input: "foo:bar-cloud/baz",
|
|
wantBase: "foo:bar-cloud/baz",
|
|
wantSource: ModelSourceUnspecified,
|
|
wantExplicit: false,
|
|
},
|
|
{
|
|
name: "unknown suffix is not explicit source",
|
|
input: "gpt-oss:clod",
|
|
wantBase: "gpt-oss:clod",
|
|
wantSource: ModelSourceUnspecified,
|
|
wantExplicit: false,
|
|
},
|
|
{
|
|
name: "uppercase suffix is accepted",
|
|
input: "gpt-oss:20b:CLOUD",
|
|
wantBase: "gpt-oss:20b",
|
|
wantSource: ModelSourceCloud,
|
|
wantExplicit: true,
|
|
},
|
|
{
|
|
name: "no suffix",
|
|
input: "llama3.2",
|
|
wantBase: "llama3.2",
|
|
wantSource: ModelSourceUnspecified,
|
|
wantExplicit: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
gotBase, gotSource, gotExplicit := parseSourceSuffix(tt.input)
|
|
if gotBase != tt.wantBase {
|
|
t.Fatalf("base = %q, want %q", gotBase, tt.wantBase)
|
|
}
|
|
if gotSource != tt.wantSource {
|
|
t.Fatalf("source = %v, want %v", gotSource, tt.wantSource)
|
|
}
|
|
if gotExplicit != tt.wantExplicit {
|
|
t.Fatalf("explicit = %v, want %v", gotExplicit, tt.wantExplicit)
|
|
}
|
|
})
|
|
}
|
|
}
|