package httpapi import ( "context" "io" "log/slog" "net/http" "net/http/httptest" "net/url" "testing" "github.com/easyai/easyai-ai-gateway/apps/api/internal/auth" "github.com/easyai/easyai-ai-gateway/apps/api/internal/config" "github.com/easyai/easyai-ai-gateway/apps/api/internal/store" ) func TestParseModelUsageSceneQuery(t *testing.T) { t.Parallel() for _, testCase := range []struct { name string query url.Values want string wantErr bool }{ {name: "missing", query: url.Values{}, want: ""}, {name: "desktop", query: url.Values{"usage_scene": {"desktop"}}, want: "desktop"}, {name: "canvas", query: url.Values{"usage_scene": {"canvas_model_node"}}, want: "canvas_model_node"}, {name: "empty", query: url.Values{"usage_scene": {""}}, wantErr: true}, {name: "unknown", query: url.Values{"usage_scene": {"mobile"}}, wantErr: true}, {name: "duplicate", query: url.Values{"usage_scene": {"desktop", "canvas_model_node"}}, wantErr: true}, } { testCase := testCase t.Run(testCase.name, func(t *testing.T) { t.Parallel() got, err := parseModelUsageSceneQuery(testCase.query) if testCase.wantErr { if err == nil { t.Fatal("expected usage_scene validation error") } return } if err != nil { t.Fatalf("parse usage_scene: %v", err) } if got != testCase.want { t.Fatalf("usage_scene = %q, want %q", got, testCase.want) } }) } } func TestListServerMainUsageSceneModelIDs(t *testing.T) { t.Parallel() serverMain := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/api/integration-platform/models/strict/all" { t.Errorf("path = %q", r.URL.Path) } if got := r.URL.Query().Get("usage_scene"); got != "desktop" { t.Errorf("usage_scene = %q", got) } w.Header().Set("Content-Type", "application/json") _, _ = io.WriteString(w, `{"data":[{"alias":"Qwen3.7-Plus"},{"invocationName":"GPT-5"},{"legacyAliases":["gpt-latest"]}]}`) })) defer serverMain.Close() authenticator := auth.New("secret", serverMain.URL, "") gateway := &Server{ cfg: config.Config{ServerMainBaseURL: serverMain.URL}, auth: authenticator, logger: slog.New(slog.NewTextHandler(io.Discard, nil)), } ids, err := gateway.listServerMainUsageSceneModelIDs(context.Background(), "desktop") if err != nil { t.Fatalf("list server-main usage scene models: %v", err) } for _, id := range []string{"qwen3.7-plus", "gpt-5", "gpt-latest"} { if _, ok := ids[id]; !ok { t.Errorf("missing normalized model ID %q in %#v", id, ids) } } } func TestFilterPlatformModelsByUsageSceneIDs(t *testing.T) { t.Parallel() models := []store.PlatformModel{ {ID: "allowed-by-invocation", ModelName: "Qwen3.7-Plus", ProviderModelName: "qwen3.7-plus-2026-07-01"}, {ID: "allowed-by-provider-name", ModelName: "GPT-5", ProviderModelName: "gpt-5-2026-07-01"}, {ID: "allowed-by-legacy-alias", ModelName: "renamed-model", LegacyAliases: store.StringList{"old-model"}}, {ID: "desktop-restricted", ModelName: "Qwen3.6-Plus", ProviderModelName: "qwen3.6-plus-2026-04-02"}, } allowed := map[string]struct{}{ "qwen3.7-plus": {}, "gpt-5-2026-07-01": {}, "old-model": {}, } filtered := filterPlatformModelsByUsageSceneIDs(models, allowed) if len(filtered) != 3 { t.Fatalf("filtered model count = %d, want 3: %#v", len(filtered), filtered) } for _, model := range filtered { if model.ID == "desktop-restricted" { t.Fatalf("desktop-restricted model remained in filtered catalog: %#v", filtered) } } } func TestListServerMainUsageSceneModelIDsRejectsMalformedPayload(t *testing.T) { t.Parallel() serverMain := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { w.Header().Set("Content-Type", "application/json") _, _ = io.WriteString(w, `{"success":true}`) })) defer serverMain.Close() gateway := &Server{ cfg: config.Config{ServerMainBaseURL: serverMain.URL}, auth: auth.New("secret", serverMain.URL, ""), } if _, err := gateway.listServerMainUsageSceneModelIDs(context.Background(), "desktop"); err == nil { t.Fatal("expected malformed upstream payload error") } } func TestListServerMainUsageSceneModelIDsFallsBackToDirectServerMainRoute(t *testing.T) { t.Parallel() requestedPaths := make([]string, 0, 2) serverMain := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { requestedPaths = append(requestedPaths, r.URL.Path) if r.URL.Path == "/api/integration-platform/models/strict/all" { http.NotFound(w, r) return } w.Header().Set("Content-Type", "application/json") _, _ = io.WriteString(w, `[{"alias":"Qwen3.7-Plus"}]`) })) defer serverMain.Close() gateway := &Server{ cfg: config.Config{ServerMainBaseURL: serverMain.URL}, auth: auth.New("secret", serverMain.URL, ""), } ids, err := gateway.listServerMainUsageSceneModelIDs(context.Background(), "desktop") if err != nil { t.Fatalf("list direct server-main usage scene models: %v", err) } if _, ok := ids["qwen3.7-plus"]; !ok { t.Fatalf("missing direct server-main model ID: %#v", ids) } if len(requestedPaths) != 2 || requestedPaths[0] != "/api/integration-platform/models/strict/all" || requestedPaths[1] != "/integration-platform/models/strict/all" { t.Fatalf("unexpected server-main route probes: %#v", requestedPaths) } }