package webdemo import ( "encoding/json" "io" "net/http" "net/http/httptest" "strings" "testing" ) func TestServerServesSplitUIAndFakeInput(t *testing.T) { server := httptest.NewServer(NewServer(NewFakeInput())) defer server.Close() for _, path := range []string{"/ui", "/ui-config.js", "/styles/base.css", "/styles/keys.css", "/styles/usage.css", "/styles/pricing.css", "/app/main.js", "/app/core/runtime.js", "/app/features/keys.js", "/app/features/usage.js", "/app/features/pricing.js", "/v0/resource/plugins/billing/ui-config.js", "/v0/resource/plugins/billing/styles/base.css", "/v0/resource/plugins/billing/app/main.js"} { response, err := http.Get(server.URL + path) if err != nil { t.Fatal(err) } if response.StatusCode != http.StatusOK { response.Body.Close() t.Fatalf("GET %s status = %d", path, response.StatusCode) } response.Body.Close() } configResponse, err := http.Get(server.URL + "/ui-config.js") if err != nil { t.Fatal(err) } defer configResponse.Body.Close() configBody, err := io.ReadAll(configResponse.Body) if err != nil { t.Fatal(err) } if !strings.Contains(string(configBody), "demoPerspectives:true") || !strings.Contains(string(configBody), `readOnlyBase:"/v0/resource/plugins/billing/ui"`) { t.Fatalf("demo config does not enable perspective switch: %s", configBody) } response, err := http.Get(server.URL + managementBase + "/usage?page=2&page_size=100&model=deepseek") if err != nil { t.Fatal(err) } defer response.Body.Close() var usage struct { Records []fakeUsage `json:"records"` Page map[string]any `json:"pagination"` } if err := json.NewDecoder(response.Body).Decode(&usage); err != nil { t.Fatal(err) } if response.StatusCode != http.StatusOK || len(usage.Records) == 0 || usage.Page["page"] != float64(2) { t.Fatalf("unexpected fake usage response: status=%d records=%d page=%v", response.StatusCode, len(usage.Records), usage.Page) } for _, record := range usage.Records { if !strings.Contains(record.Model, "deepseek") { t.Fatalf("unexpected filtered model %q", record.Model) } } response, err = http.Get(server.URL + resourceUI + "?view=usage&page=8&page_size=100") if err != nil { t.Fatal(err) } defer response.Body.Close() var readOnlyUsage struct { Records []fakeUsage `json:"records"` Page struct { Page int `json:"page"` PageSize int `json:"page_size"` } `json:"pagination"` } if err := json.NewDecoder(response.Body).Decode(&readOnlyUsage); err != nil { t.Fatal(err) } if len(readOnlyUsage.Records) != 50 || readOnlyUsage.Page.Page != 1 || readOnlyUsage.Page.PageSize != 50 { t.Fatalf("unexpected read-only usage: records=%d page=%+v", len(readOnlyUsage.Records), readOnlyUsage.Page) } response, err = http.Get(server.URL + managementBase + "/usage?page=1&page_size=1") if err != nil { t.Fatal(err) } defer response.Body.Close() var allUsage struct { Page struct { Total int `json:"total"` } `json:"pagination"` } if err := json.NewDecoder(response.Body).Decode(&allUsage); err != nil { t.Fatal(err) } if allUsage.Page.Total != fakeUsageCount { t.Fatalf("fake usage total = %d, want %d", allUsage.Page.Total, fakeUsageCount) } } func TestFakeInputSupportsKeyMutation(t *testing.T) { server := httptest.NewServer(NewServer(NewFakeInput())) defer server.Close() body := `{"name":"Demo User","route_mode":"auto","all_models":true,"billing":{"quota_usd":"25","reset_period":"none","max_concurrency":4}}` request, err := http.NewRequest(http.MethodPost, server.URL+managementBase+"/keys", strings.NewReader(body)) if err != nil { t.Fatal(err) } request.Header.Set("Content-Type", "application/json") response, err := http.DefaultClient.Do(request) if err != nil { t.Fatal(err) } defer response.Body.Close() var key fakeKey if err := json.NewDecoder(response.Body).Decode(&key); err != nil { t.Fatal(err) } if response.StatusCode != http.StatusOK || key.Name != "Demo User" || key.Billing["balance_usd"] != "25.000000" { t.Fatalf("unexpected created key: status=%d key=%+v", response.StatusCode, key) } } func TestFakeDashboardIncludesSevenDailyCosts(t *testing.T) { server := httptest.NewServer(NewServer(NewFakeInput())) defer server.Close() response, err := http.Get(server.URL + managementBase + "/usage-summary") if err != nil { t.Fatal(err) } defer response.Body.Close() var dashboard struct { Days []struct { Date string `json:"date"` CostUSD float64 `json:"cost_usd"` } `json:"days"` Users []struct { Days []struct { CostUSD float64 `json:"cost_usd"` } `json:"days"` } `json:"users"` } if err := json.NewDecoder(response.Body).Decode(&dashboard); err != nil { t.Fatal(err) } if len(dashboard.Days) != 7 || dashboard.Days[6].CostUSD <= 0 || len(dashboard.Users) == 0 || len(dashboard.Users[0].Days) != 7 { t.Fatalf("unexpected daily costs: %+v", dashboard.Days) } } func TestFakeDashboardHidesArchivedUsers(t *testing.T) { input := NewFakeInput().(*fakeInput) archivedID := input.keys[0].ID input.keys[0].Status = "archived" dashboard := input.dashboard() users := dashboard["users"].([]map[string]any) if len(users) != len(input.keys)-1 { t.Fatalf("dashboard users = %d, want %d", len(users), len(input.keys)-1) } for _, user := range users { if user["key_id"] == archivedID { t.Fatalf("archived user %q remains in dashboard", archivedID) } } if dashboard["today"].(map[string]any)["requests"].(int) == 0 { t.Fatal("archived user usage should remain in overall totals") } } func TestFakeDashboardHidesOptedOutUsers(t *testing.T) { input := NewFakeInput().(*fakeInput) hiddenID := input.keys[0].ID input.keys[0].ShowInStats = false dashboard := input.dashboard() users := dashboard["users"].([]map[string]any) if len(users) != len(input.keys)-1 { t.Fatalf("dashboard users = %d, want %d", len(users), len(input.keys)-1) } for _, user := range users { if user["key_id"] == hiddenID { t.Fatalf("opted-out user %q remains in dashboard", hiddenID) } } if dashboard["today"].(map[string]any)["requests"].(int) == 0 { t.Fatal("opted-out user usage should remain in overall totals") } } func TestDatabaseInputServesStandaloneBillingDatabase(t *testing.T) { input, err := NewDatabaseInput(t.TempDir() + "/billing.db") if err != nil { t.Fatal(err) } defer input.Close() server := httptest.NewServer(NewServer(input)) defer server.Close() response, err := http.Get(server.URL + managementBase + "/keys") if err != nil { t.Fatal(err) } defer response.Body.Close() var payload struct { Keys []fakeKey `json:"keys"` } if err := json.NewDecoder(response.Body).Decode(&payload); err != nil { t.Fatal(err) } if response.StatusCode != http.StatusOK || len(payload.Keys) != 1 { t.Fatalf("database input keys: status=%d keys=%d", response.StatusCode, len(payload.Keys)) } }