1package provider
2
3import (
4 "context"
5 "io"
6 "net/http"
7 "net/http/httptest"
8 "testing"
9)
10
11func TestDiscoverOpenAICodex(t *testing.T) {
12 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
13 if r.URL.Path != "/models" {
14 t.Errorf("path = %q, want /models", r.URL.Path)
15 }
16 if got := r.URL.Query().Get("client_version"); got != openaiCodexClientVersion {
17 t.Errorf("client_version = %q, want %q", got, openaiCodexClientVersion)
18 }
19 if got := r.Header.Get("authorization"); got != "Bearer token" {
20 t.Errorf("authorization = %q", got)
21 }
22 if got := r.Header.Get("chatgpt-account-id"); got != "account" {
23 t.Errorf("chatgpt-account-id = %q", got)
24 }
25 if got := r.Header.Get("originator"); got != "codex_cli_rs" {
26 t.Errorf("originator = %q", got)
27 }
28 _, _ = io.WriteString(w, `{"models":[
29 {"slug":"gpt-5.6-luna","display_name":"GPT-5.6 Luna","supported_in_api":true,"context_window":272000,"max_context_window":872000,"supported_reasoning_levels":["low","medium","high"]},
30 {"slug":"gpt-hidden","display_name":"Hidden","supported_in_api":true,"visibility":"hide"},
31 {"slug":"gpt-unsupported","display_name":"Unsupported","supported_in_api":false},
32 {"slug":"gpt-limited","supported_in_api":true,"context_window":272000,"max_context_window":400000},
33 {"slug":"gpt-custom","supported_in_api":true,"max_context_window":100000}
34 ]}`)
35 }))
36 defer srv.Close()
37
38 got, err := DiscoverOpenAICodex(context.Background(), "token", "account", srv.URL)
39 if err != nil {
40 t.Fatal(err)
41 }
42 if len(got) != 3 {
43 t.Fatalf("models = %+v, want 3 models", got)
44 }
45 if got[0].Provider != "openai-codex" || got[0].ID != "gpt-5.6-luna" || got[0].DisplayName != "GPT-5.6 Luna" || got[0].ContextWindow != 500000 || !got[0].Reasoning || got[0].Source != "live" {
46 t.Errorf("first model = %+v", got[0])
47 }
48 if got[1].ID != "gpt-limited" || got[1].ContextWindow != 272000 {
49 t.Errorf("second model = %+v", got[1])
50 }
51 if got[2].ID != "gpt-custom" || got[2].DisplayName != "gpt-custom" || got[2].ContextWindow != 100000 {
52 t.Errorf("third model = %+v", got[2])
53 }
54}
55
56func TestDiscoverOllamaKeepsCloudModelIDs(t *testing.T) {
57 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
58 if r.URL.Path != "/v1/models" {
59 t.Errorf("path = %q, want /v1/models", r.URL.Path)
60 }
61 if got := r.Header.Get("authorization"); got != "Bearer ollama-key" {
62 t.Errorf("authorization = %q", got)
63 }
64 _, _ = io.WriteString(w, `{"data":[{"id":"qwen3.5:397b-cloud"},{"id":"gemma3:27b"}]}`)
65 }))
66 defer srv.Close()
67
68 got, err := DiscoverOllama(context.Background(), "ollama-key", srv.URL)
69 if err != nil {
70 t.Fatal(err)
71 }
72 if len(got) != 2 || got[0].Provider != "ollama" || got[0].ID != "qwen3.5:397b-cloud" || got[1].ID != "gemma3:27b" {
73 t.Fatalf("models = %+v", got)
74 }
75}
76
77func TestDiscoverOllamaAcceptsVersionedBaseURL(t *testing.T) {
78 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
79 if r.URL.Path != "/v1/models" {
80 t.Errorf("path = %q, want /v1/models", r.URL.Path)
81 }
82 _, _ = io.WriteString(w, `{"data":[]}`)
83 }))
84 defer srv.Close()
85
86 if _, err := DiscoverOllama(context.Background(), "ollama-key", srv.URL+"/v1"); err != nil {
87 t.Fatal(err)
88 }
89}
90
91func TestDiscoverOpenAICodexRejectsHTTPError(t *testing.T) {
92 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
93 http.Error(w, "denied", http.StatusUnauthorized)
94 }))
95 defer srv.Close()
96
97 if _, err := DiscoverOpenAICodex(context.Background(), "token", "", srv.URL); err == nil {
98 t.Fatal("DiscoverOpenAICodex error = nil, want HTTP error")
99 }
100}