package openai import ( "context" "encoding/json" "net/http" "net/http/httptest" "testing" ) func TestFetchModels(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/models" { http.NotFound(w, r) return } if r.Header.Get("Authorization") != "Bearer test-key" { http.Error(w, `{"error":"unauthorized"}`, http.StatusUnauthorized) return } json.NewEncoder(w).Encode(map[string]any{ "object": "list", "data": []map[string]string{ {"id": "model-b", "object": "model"}, {"id": "model-a", "object": "model"}, }, }) })) defer srv.Close() models, err := FetchModels(context.Background(), srv.URL, "test-key", nil) if err != nil { t.Fatalf("unexpected error: %v", err) } if len(models) != 2 { t.Fatalf("want 2 models, got %d", len(models)) } if models[0] != "model-a" || models[1] != "model-b" { t.Errorf("want sorted [model-a model-b], got %v", models) } } func TestFetchModelsSendsCustomHeaders(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Header.Get("HTTP-Referer") != "https://app.example" || r.Header.Get("X-Title") != "Reasonix" { http.Error(w, `{"error":"missing headers"}`, http.StatusForbidden) return } json.NewEncoder(w).Encode(map[string]any{ "data": []map[string]string{{"id": "model-a"}}, }) })) defer srv.Close() models, err := FetchModels(context.Background(), srv.URL, "key", map[string]string{ "HTTP-Referer": "https://app.example", "X-Title": "Reasonix", }) if err != nil { t.Fatalf("FetchModels: %v", err) } if len(models) != 1 || models[0] != "model-a" { t.Fatalf("models = %v, want [model-a]", models) } } func TestFetchModelsWithOptionsUsesXAPIKey(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if got := r.Header.Get("x-api-key"); got != "anthropic-key" { http.Error(w, `{"error":"missing x-api-key"}`, http.StatusUnauthorized) return } if got := r.Header.Get("Authorization"); got != "" { http.Error(w, `{"error":"unexpected bearer"}`, http.StatusUnauthorized) return } json.NewEncoder(w).Encode(map[string]any{ "data": []map[string]string{{"id": "anthropic-model"}}, }) })) defer srv.Close() models, err := FetchModelsWithOptions(context.Background(), srv.URL, "anthropic-key", FetchModelsOptions{ AuthMode: ModelFetchAuthXAPIKey, }) if err != nil { t.Fatalf("FetchModelsWithOptions: %v", err) } if len(models) != 1 || models[0] != "anthropic-model" { t.Fatalf("models = %v, want [anthropic-model]", models) } } func TestApplyAPIKeyHeaderUsesMiMoAPIKeyHeader(t *testing.T) { h := http.Header{} applyAPIKeyHeader(h, "https://api.xiaomimimo.com/v1", "mimo-key") if got := h.Get("api-key"); got != "mimo-key" { t.Fatalf("api-key = %q, want mimo-key", got) } if got := h.Get("Authorization"); got != "" { t.Fatalf("Authorization = %q, want omitted for MiMo", got) } h = http.Header{} applyAPIKeyHeader(h, "https://api.deepseek.com", "deepseek-key") if got := h.Get("Authorization"); got != "Bearer deepseek-key" { t.Fatalf("Authorization = %q, want Bearer deepseek-key", got) } if got := h.Get("api-key"); got != "" { t.Fatalf("api-key = %q, want omitted for standard OpenAI-compatible providers", got) } } func TestFetchModelsAuthError(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { http.Error(w, `{"error":{"message":"invalid key"}}`, http.StatusUnauthorized) })) defer srv.Close() _, err := FetchModels(context.Background(), srv.URL, "bad-key", nil) if err == nil { t.Fatal("expected error for bad key") } } func TestFetchModelsEmptyResponse(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(map[string]any{"object": "list", "data": nil}) })) defer srv.Close() models, err := FetchModels(context.Background(), srv.URL, "key", nil) if err != nil { t.Fatalf("unexpected error: %v", err) } if len(models) != 0 { t.Errorf("want empty list, got %v", models) } }