Spaces:
Running
Running
| package api | |
| import ( | |
| "context" | |
| "net/http" | |
| "net/http/httptest" | |
| "testing" | |
| "time" | |
| "cpa-usage-keeper/internal/cpa" | |
| "cpa-usage-keeper/internal/redact" | |
| "cpa-usage-keeper/internal/service" | |
| ) | |
| type usageAnalysisStub struct { | |
| analysis *service.UsageAnalysisSnapshot | |
| err error | |
| lastFilter service.UsageFilter | |
| analysisCalls int | |
| } | |
| func (s *usageAnalysisStub) GetUsageWithFilter(context.Context, service.UsageFilter) (*cpa.StatisticsSnapshot, error) { | |
| return nil, nil | |
| } | |
| func (s *usageAnalysisStub) GetUsageOverview(context.Context, service.UsageFilter) (*service.UsageOverviewSnapshot, error) { | |
| return nil, nil | |
| } | |
| func (s *usageAnalysisStub) ListUsageEvents(context.Context, service.UsageFilter) (*service.UsageEventsPage, error) { | |
| return nil, nil | |
| } | |
| func (s *usageAnalysisStub) ListUsageEventFilterOptions(context.Context, service.UsageFilter) (*service.UsageEventFilterOptions, error) { | |
| return nil, nil | |
| } | |
| func (s *usageAnalysisStub) ListUsageCredentialStats(context.Context, service.UsageFilter) ([]service.UsageCredentialStat, error) { | |
| return nil, nil | |
| } | |
| func (s *usageAnalysisStub) GetUsageAnalysis(_ context.Context, filter service.UsageFilter) (*service.UsageAnalysisSnapshot, error) { | |
| s.lastFilter = filter | |
| s.analysisCalls++ | |
| return s.analysis, s.err | |
| } | |
| func TestUsageAnalysisReturnsAggregatedRows(t *testing.T) { | |
| provider := &usageAnalysisStub{analysis: &service.UsageAnalysisSnapshot{ | |
| APIs: []service.UsageAnalysisAPIStat{{ | |
| APIKey: "provider-a", | |
| DisplayName: "provider-a", | |
| TotalRequests: 2, | |
| SuccessCount: 1, | |
| FailureCount: 1, | |
| TotalTokens: 42, | |
| Models: []service.UsageAnalysisModelStat{{ | |
| Model: "claude-sonnet", | |
| TotalRequests: 2, | |
| SuccessCount: 1, | |
| FailureCount: 1, | |
| InputTokens: 30, | |
| OutputTokens: 9, | |
| ReasoningTokens: 2, | |
| CachedTokens: 1, | |
| TotalTokens: 42, | |
| TotalLatencyMS: 350, | |
| LatencySampleCount: 2, | |
| }}, | |
| }}, | |
| Models: []service.UsageAnalysisModelStat{{ | |
| Model: "claude-sonnet", | |
| TotalRequests: 2, | |
| SuccessCount: 1, | |
| FailureCount: 1, | |
| InputTokens: 30, | |
| OutputTokens: 9, | |
| ReasoningTokens: 2, | |
| CachedTokens: 1, | |
| TotalTokens: 42, | |
| TotalLatencyMS: 350, | |
| LatencySampleCount: 2, | |
| }}, | |
| }} | |
| router := NewRouter(nil, nil, provider, nil, AuthConfig{}, nil, "") | |
| req := httptest.NewRequest(http.MethodGet, "/api/v1/usage/analysis?range=24h", nil) | |
| resp := httptest.NewRecorder() | |
| router.ServeHTTP(resp, req) | |
| if resp.Code != http.StatusOK { | |
| t.Fatalf("expected status 200, got %d", resp.Code) | |
| } | |
| body := resp.Body.String() | |
| if !contains(body, `"apis":[`) || !contains(body, `"models":[`) { | |
| t.Fatalf("unexpected response body: %s", body) | |
| } | |
| if !contains(body, `"display_name":"prov**er-a"`) { | |
| t.Fatalf("expected display name in response body: %s", body) | |
| } | |
| if !contains(body, `"api_key":"`+redact.APIAlias("provider-a")+`"`) { | |
| t.Fatalf("expected redacted api key alias in response body: %s", body) | |
| } | |
| if !contains(body, `"model":"claude-sonnet"`) || !contains(body, `"latency_sample_count":2`) || !contains(body, `"total_latency_ms":350`) { | |
| t.Fatalf("expected model latency aggregates in response body: %s", body) | |
| } | |
| if provider.analysisCalls != 1 { | |
| t.Fatalf("expected GetUsageAnalysis to be called once, got %d", provider.analysisCalls) | |
| } | |
| if provider.lastFilter.Range != "24h" { | |
| t.Fatalf("expected range to be passed through, got %+v", provider.lastFilter) | |
| } | |
| if provider.lastFilter.StartTime == nil || provider.lastFilter.EndTime == nil { | |
| t.Fatalf("expected resolved time bounds in filter, got %+v", provider.lastFilter) | |
| } | |
| } | |
| func TestUsageAnalysisRequiresAuthWhenEnabled(t *testing.T) { | |
| router := NewRouter(nil, nil, &usageAnalysisStub{}, nil, AuthConfig{Enabled: true, LoginPassword: "secret", SessionTTL: time.Hour}, nil, "") | |
| req := httptest.NewRequest(http.MethodGet, "/api/v1/usage/analysis", nil) | |
| resp := httptest.NewRecorder() | |
| router.ServeHTTP(resp, req) | |
| if resp.Code != http.StatusUnauthorized { | |
| t.Fatalf("expected status 401, got %d", resp.Code) | |
| } | |
| } | |