package main import ( "context" "database/sql" "encoding/json" "net/http" "net/http/httptest" "os" "path/filepath" "strings" "testing" ) func TestSubscriptionPlansHandlerReturnsEnvelope(t *testing.T) { mux := newMux( &sql.DB{}, func(context.Context, *sql.DB) ([]modelResponse, error) { return nil, nil }, func(context.Context, *sql.DB) ([]subscriptionPlanResponse, error) { return []subscriptionPlanResponse{ { PlanFamily: "token_plan", PlanCode: "token-plan-lite", PlanName: "General Token Plan Lite", Tier: "Lite", Provider: "Tencent", ProviderCN: "腾讯", Operator: "Tencent Cloud", OperatorCN: "腾讯云", Currency: "CNY", ListPrice: 39, PriceUnit: "CNY/month", QuotaValue: 35000000, QuotaUnit: "tokens/month", ContextWindow: 0, ModelScope: []string{"tc-code-latest", "glm-5", "glm-5.1"}, SourceURL: "https://cloud.tencent.com/document/product/1823/130060", EffectiveDate: "2026-04-27", }, }, nil }, "", ) req := httptest.NewRequest(http.MethodGet, "/api/v1/subscription-plans", nil) rec := httptest.NewRecorder() mux.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Fatalf("expected status 200, got %d", rec.Code) } var payload struct { Data []subscriptionPlanResponse `json:"data"` } if err := json.Unmarshal(rec.Body.Bytes(), &payload); err != nil { t.Fatalf("unmarshal response: %v", err) } if len(payload.Data) != 1 { t.Fatalf("expected 1 plan, got %d", len(payload.Data)) } got := payload.Data[0] if got.PlanCode != "token-plan-lite" { t.Fatalf("unexpected plan code: %q", got.PlanCode) } if got.ProviderCN != "腾讯" { t.Fatalf("unexpected providerCN: %q", got.ProviderCN) } if got.OperatorCN != "腾讯云" { t.Fatalf("unexpected operatorCN: %q", got.OperatorCN) } if got.ListPrice != 39 { t.Fatalf("unexpected list price: %v", got.ListPrice) } if len(got.ModelScope) != 3 { t.Fatalf("unexpected model scope length: %d", len(got.ModelScope)) } } func TestFrontendHandlerServesIndexAssetsAndSpaFallback(t *testing.T) { distDir := t.TempDir() writeTestFile(t, filepath.Join(distDir, "index.html"), "dashboard") writeTestFile(t, filepath.Join(distDir, "assets", "app.js"), "console.log('ok');") mux := newMux(&sql.DB{}, noOpModelsFetcher, noOpPlansFetcher, distDir) t.Run("root serves index", func(t *testing.T) { req := httptest.NewRequest(http.MethodGet, "/", nil) rec := httptest.NewRecorder() mux.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Fatalf("expected status 200, got %d", rec.Code) } if !strings.Contains(rec.Body.String(), "dashboard") { t.Fatalf("expected index response, got %q", rec.Body.String()) } }) t.Run("asset serves file", func(t *testing.T) { req := httptest.NewRequest(http.MethodGet, "/assets/app.js", nil) rec := httptest.NewRecorder() mux.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Fatalf("expected status 200, got %d", rec.Code) } if !strings.Contains(rec.Body.String(), "console.log") { t.Fatalf("expected asset response, got %q", rec.Body.String()) } }) t.Run("spa route falls back to index", func(t *testing.T) { req := httptest.NewRequest(http.MethodGet, "/explorer/detail", nil) rec := httptest.NewRecorder() mux.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Fatalf("expected status 200, got %d", rec.Code) } if !strings.Contains(rec.Body.String(), "dashboard") { t.Fatalf("expected SPA fallback, got %q", rec.Body.String()) } }) t.Run("missing asset returns not found", func(t *testing.T) { req := httptest.NewRequest(http.MethodGet, "/assets/missing.js", nil) rec := httptest.NewRecorder() mux.ServeHTTP(rec, req) if rec.Code != http.StatusNotFound { t.Fatalf("expected status 404, got %d", rec.Code) } }) t.Run("api routes keep precedence", func(t *testing.T) { req := httptest.NewRequest(http.MethodGet, "/api/v1/models", nil) rec := httptest.NewRecorder() mux.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Fatalf("expected status 200, got %d", rec.Code) } }) } func noOpModelsFetcher(context.Context, *sql.DB) ([]modelResponse, error) { return []modelResponse{}, nil } func noOpPlansFetcher(context.Context, *sql.DB) ([]subscriptionPlanResponse, error) { return []subscriptionPlanResponse{}, nil } func writeTestFile(t *testing.T, path string, contents string) { t.Helper() if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil { t.Fatalf("mkdir %s: %v", path, err) } if err := os.WriteFile(path, []byte(contents), 0o644); err != nil { t.Fatalf("write %s: %v", path, err) } }