package providers import ( "os" "path/filepath" "testing" ) func writeTempJSON(t *testing.T, content string) string { t.Helper() dir := t.TempDir() path := filepath.Join(dir, "provider_types.json") if err := os.WriteFile(path, []byte(content), 0644); err != nil { t.Fatalf("failed to write temp file: %v", err) } return path } func TestLoadCustomTypes_Valid(t *testing.T) { path := writeTempJSON(t, `[ { "id": "test-ollama", "name": "Test Ollama", "description": "Local test", "default_endpoint": "http://localhost:11434/v1", "api": "openai" }, { "id": "test-litellm", "name": "Test LiteLLM", "description": "LiteLLM proxy", "default_endpoint": "http://litellm:4000/v1", "api": "openai" } ]`) n, err := LoadCustomTypes(path) if err != nil { t.Fatalf("unexpected error: %v", err) } if n != 2 { t.Errorf("registered = %d, want 2", n) } // Verify they're in the registry p, err := Get("test-ollama") if err != nil { t.Errorf("test-ollama not registered: %v", err) } if p == nil { t.Error("test-ollama provider is nil") } // Verify type metadata types := ListTypes() found := false for _, m := range types { if m.ID == "test-litellm" { found = true if m.DefaultEndpoint != "http://litellm:4000/v1" { t.Errorf("endpoint = %q", m.DefaultEndpoint) } } } if !found { t.Error("test-litellm not in ListTypes()") } } func TestLoadCustomTypes_SkipsBuiltins(t *testing.T) { path := writeTempJSON(t, `[ { "id": "openai", "name": "Override OpenAI", "description": "Should be skipped", "default_endpoint": "http://evil.com", "api": "openai" }, { "id": "test-custom-ok", "name": "Valid Custom", "description": "Should register", "default_endpoint": "http://ok.com", "api": "openai" } ]`) n, err := LoadCustomTypes(path) if err != nil { t.Fatalf("unexpected error: %v", err) } if n != 1 { t.Errorf("registered = %d, want 1 (openai skipped)", n) } } func TestLoadCustomTypes_SkipsInvalid(t *testing.T) { path := writeTempJSON(t, `[ {"id": "", "name": "No ID", "api": "openai"}, {"id": "test-no-name", "name": "", "api": "openai"}, {"id": "test-no-api", "name": "No API", "api": ""}, {"id": "test-bad-api", "name": "Bad API", "api": "grpc", "default_endpoint": "http://x"}, {"id": "test-valid-entry", "name": "Valid", "description": "ok", "default_endpoint": "http://ok", "api": "openai"} ]`) n, err := LoadCustomTypes(path) if err != nil { t.Fatalf("unexpected error: %v", err) } if n != 1 { t.Errorf("registered = %d, want 1 (4 invalid skipped)", n) } } func TestLoadCustomTypes_EmptyArray(t *testing.T) { path := writeTempJSON(t, `[]`) n, err := LoadCustomTypes(path) if err != nil { t.Fatalf("unexpected error: %v", err) } if n != 0 { t.Errorf("registered = %d, want 0", n) } } func TestLoadCustomTypes_InvalidJSON(t *testing.T) { path := writeTempJSON(t, `not json`) _, err := LoadCustomTypes(path) if err == nil { t.Error("expected error for invalid JSON") } } func TestLoadCustomTypes_FileNotFound(t *testing.T) { _, err := LoadCustomTypes("/nonexistent/path/providers.json") if err == nil { t.Error("expected error for missing file") } } func TestLoadCustomTypes_NoDefaultEndpoint(t *testing.T) { // default_endpoint is optional — should still register path := writeTempJSON(t, `[ { "id": "test-no-endpoint", "name": "No Endpoint", "description": "User must fill in endpoint", "api": "openai" } ]`) n, err := LoadCustomTypes(path) if err != nil { t.Fatalf("unexpected error: %v", err) } if n != 1 { t.Errorf("registered = %d, want 1", n) } }