1package agent
2
3import (
4 "encoding/json"
5 "os"
6 "testing"
7
8 "github.com/bnema/zut/packages/provider"
9 providerauth "github.com/bnema/zut/packages/provider/auth"
10)
11
12func TestResolveCredentialOllamaEnv(t *testing.T) {
13 t.Setenv("ZUT_HOME", t.TempDir())
14 t.Setenv("OLLAMA_API_KEY", "ollama-cloud-key")
15
16 cred, method, _, err := ResolveCredentialFull("ollama", "")
17 if err != nil {
18 t.Fatalf("ResolveCredentialFull failed: %v", err)
19 }
20 if cred != "ollama-cloud-key" {
21 t.Errorf("cred = %q, want %q", cred, "ollama-cloud-key")
22 }
23 if method != "apikey" {
24 t.Errorf("method = %q, want apikey", method)
25 }
26 if !CredentialAvailable("ollama") {
27 t.Error("CredentialAvailable(ollama) = false, want true with OLLAMA_API_KEY set")
28 }
29}
30
31func TestResolveCredentialOllamaExplicitWinsOverEnv(t *testing.T) {
32 t.Setenv("ZUT_HOME", t.TempDir())
33 t.Setenv("OLLAMA_API_KEY", "env-key")
34
35 cred, _, _, err := ResolveCredentialFull("ollama", "explicit-key")
36 if err != nil {
37 t.Fatalf("ResolveCredentialFull failed: %v", err)
38 }
39 if cred != "explicit-key" {
40 t.Errorf("cred = %q, want explicit-key", cred)
41 }
42}
43
44func TestResolveOllamaCloudDefaultWithKey(t *testing.T) {
45 t.Setenv("ZUT_HOME", t.TempDir())
46 t.Setenv("OLLAMA_API_KEY", "ollama-cloud-key")
47
48 resolved, err := Resolve(Args{Provider: "ollama", Model: "gemma4:31b"}, false)
49 if err != nil {
50 t.Fatal(err)
51 }
52 if resolved.Credential != "ollama-cloud-key" {
53 t.Errorf("credential = %q, want real key", resolved.Credential)
54 }
55 if resolved.BaseURL != "https://ollama.com/v1" {
56 t.Errorf("baseURL = %q, want https://ollama.com/v1", resolved.BaseURL)
57 }
58}
59
60func TestResolveOllamaLocalDefaultWithoutKey(t *testing.T) {
61 t.Setenv("ZUT_HOME", t.TempDir())
62 t.Setenv("OLLAMA_API_KEY", "")
63
64 resolved, err := Resolve(Args{Provider: "ollama", Model: "qwen3.5:4b"}, false)
65 if err != nil {
66 t.Fatal(err)
67 }
68 if resolved.Credential != "ollama" {
69 t.Errorf("credential = %q, want dummy ollama", resolved.Credential)
70 }
71 if resolved.BaseURL != "http://localhost:11434" {
72 t.Errorf("baseURL = %q, want http://localhost:11434", resolved.BaseURL)
73 }
74}
75
76func TestResolveOllamaExplicitBaseURLWinsOverCloud(t *testing.T) {
77 t.Setenv("ZUT_HOME", t.TempDir())
78 t.Setenv("OLLAMA_API_KEY", "ollama-cloud-key")
79
80 resolved, err := Resolve(Args{Provider: "ollama", Model: "qwen3.5:4b", BaseURL: "http://localhost:11434"}, false)
81 if err != nil {
82 t.Fatal(err)
83 }
84 if resolved.BaseURL != "http://localhost:11434" {
85 t.Errorf("baseURL = %q, want explicit local URL", resolved.BaseURL)
86 }
87 if resolved.Credential != "ollama-cloud-key" {
88 t.Errorf("credential = %q, want real key (local server ignores it)", resolved.Credential)
89 }
90}
91
92func TestResolveOllamaExplicitKeyTriggersCloud(t *testing.T) {
93 t.Setenv("ZUT_HOME", t.TempDir())
94 t.Setenv("OLLAMA_API_KEY", "")
95
96 resolved, err := Resolve(Args{Provider: "ollama", Model: "gemma4:31b", APIKey: "explicit-key"}, false)
97 if err != nil {
98 t.Fatal(err)
99 }
100 if resolved.Credential != "explicit-key" {
101 t.Errorf("credential = %q, want explicit-key", resolved.Credential)
102 }
103 if resolved.BaseURL != "https://ollama.com/v1" {
104 t.Errorf("baseURL = %q, want https://ollama.com/v1", resolved.BaseURL)
105 }
106}
107
108func TestResolveOllamaModelBaseURLWinsOverCloud(t *testing.T) {
109 t.Setenv("ZUT_HOME", t.TempDir())
110 t.Setenv("OLLAMA_API_KEY", "ollama-cloud-key")
111 provider.SetUserModels([]provider.Model{{
112 Provider: "ollama", ID: "custom-local", DisplayName: "custom-local",
113 ContextWindow: 32768, MaxOutput: 8192,
114 BaseURL: "http://mylocal:11434", Source: "user",
115 }})
116 t.Cleanup(func() { provider.SetUserModels(nil) })
117
118 resolved, err := Resolve(Args{Provider: "ollama", Model: "custom-local"}, false)
119 if err != nil {
120 t.Fatal(err)
121 }
122 if resolved.BaseURL != "http://mylocal:11434" {
123 t.Errorf("baseURL = %q, want model-level URL", resolved.BaseURL)
124 }
125 if resolved.Credential != "ollama-cloud-key" {
126 t.Errorf("credential = %q, want real key", resolved.Credential)
127 }
128}
129
130func TestResolveOllamaStoredCredTriggersCloud(t *testing.T) {
131 t.Setenv("ZUT_HOME", t.TempDir())
132 t.Setenv("OLLAMA_API_KEY", "")
133 encoded, err := json.Marshal(providerauth.Credentials{AdditionalAPIKeyCreds: map[string]providerauth.ProviderCreds{
134 "ollama": {APIKey: "stored-key"},
135 }})
136 if err != nil {
137 t.Fatal(err)
138 }
139 if err := os.WriteFile(AuthPath(), encoded, 0o600); err != nil {
140 t.Fatal(err)
141 }
142
143 cred, _, _, err := ResolveCredentialFull("ollama", "")
144 if err != nil {
145 t.Fatalf("ResolveCredentialFull failed: %v", err)
146 }
147 if cred != "stored-key" {
148 t.Errorf("cred = %q, want stored-key", cred)
149 }
150
151 resolved, err := Resolve(Args{Provider: "ollama", Model: "gemma4:31b"}, false)
152 if err != nil {
153 t.Fatal(err)
154 }
155 if resolved.Credential != "stored-key" {
156 t.Errorf("credential = %q, want stored-key", resolved.Credential)
157 }
158 if resolved.BaseURL != "https://ollama.com/v1" {
159 t.Errorf("baseURL = %q, want https://ollama.com/v1", resolved.BaseURL)
160 }
161}