package controller import ( "context" "net/http" "net/http/httptest" "reflect" "testing" "ezops/model" "github.com/glebarez/sqlite" "gorm.io/gorm" ) func TestParseLLMModelList(t *testing.T) { raw := []byte(`{"data":[{"id":"model-b"},{"id":"model-a"},{"id":"model-b"}],"models":[{"name":"model-c"}]}`) got, err := parseLLMModelList(raw) if err != nil { t.Fatalf("parseLLMModelList() error = %v", err) } want := []string{"model-a", "model-b", "model-c"} if !reflect.DeepEqual(got, want) { t.Fatalf("parseLLMModelList() = %#v, want %#v", got, want) } } func TestRequestLLMModelListForOpenAICompatibleGateway(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/v1/models" { t.Errorf("request path = %q, want /v1/models", r.URL.Path) } if got := r.Header.Get("Authorization"); got != "Bearer test-key" { t.Errorf("Authorization = %q", got) } w.Header().Set("Content-Type", "application/json") _, _ = w.Write([]byte(`{"data":[{"id":"gpt-test"}]}`)) })) defer server.Close() models, err := requestLLMModelList(context.Background(), "new-api", server.URL+"/v1", "test-key") if err != nil { t.Fatalf("requestLLMModelList() error = %v", err) } if !reflect.DeepEqual(models, []string{"gpt-test"}) { t.Fatalf("requestLLMModelList() = %#v", models) } } func TestRequestLLMModelListForAnthropic(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/v1/models" { t.Errorf("request path = %q, want /v1/models", r.URL.Path) } if got := r.Header.Get("x-api-key"); got != "anthropic-key" { t.Errorf("x-api-key = %q", got) } if got := r.Header.Get("anthropic-version"); got != "2023-06-01" { t.Errorf("anthropic-version = %q", got) } w.Header().Set("Content-Type", "application/json") _, _ = w.Write([]byte(`{"data":[{"id":"claude-test"}]}`)) })) defer server.Close() models, err := requestLLMModelList(context.Background(), "anthropic", server.URL, "anthropic-key") if err != nil { t.Fatalf("requestLLMModelList() error = %v", err) } if !reflect.DeepEqual(models, []string{"claude-test"}) { t.Fatalf("requestLLMModelList() = %#v", models) } } func TestResolveModelListCredentialDoesNotReuseKeyAcrossProviders(t *testing.T) { db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{}) if err != nil { t.Fatalf("open database: %v", err) } if err := db.AutoMigrate(&model.LLMConfig{}); err != nil { t.Fatalf("migrate database: %v", err) } if err := db.Create(&model.LLMConfig{Provider: "deepseek", APIKey: "deepseek-secret"}).Error; err != nil { t.Fatalf("create config: %v", err) } handler := NewLLMHandler(db) _, _, errMsg := handler.resolveModelListCredential(model.SaveLLMConfigRequest{ Provider: "new-api", Endpoint: "https://new-api.example.com/v1", APIKey: "****cret", }) if errMsg != "提供商或 Endpoint 已变化,请重新填写 API Key" { t.Fatalf("provider mismatch error = %q", errMsg) } }