Files

213 lines
6.7 KiB
Go

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))
}
}