package data_test import ( "math" "testing" "time" "cpa-ext/internal/data" ) func validUsage() data.Usage { return data.Usage{ Provider: "codex", Model: "gpt-5.5", RequestedAt: time.Date(2026, time.August, 14, 8, 0, 0, 0, time.UTC), Generated: true, Quality: data.UsageQualityComplete, Tokens: data.TokenUsage{ UncachedInputTokens: 10, CacheReadTokens: 20, CacheCreationTokens: 30, NonReasoningTokens: 40, ReasoningTokens: 50, TotalTokens: 150, }, } } func TestUsageValidateAcceptsCompleteBreakdown(t *testing.T) { t.Parallel() if err := validUsage().Validate(); err != nil { t.Fatalf("Validate() error = %v", err) } } func TestUsageDistinguishesMissingFromMeasuredZero(t *testing.T) { t.Parallel() measured := validUsage() measured.Tokens = data.TokenUsage{} if err := measured.Validate(); err != nil { t.Fatalf("measured zero Validate() error = %v", err) } missing := measured missing.Quality = data.UsageQualityMissing if err := missing.Validate(); err != nil { t.Fatalf("missing Validate() error = %v", err) } missing.Tokens.TotalTokens = 1 if err := missing.Validate(); err == nil { t.Fatal("missing usage with tokens unexpectedly passed validation") } } func TestUsageValidateRejectsInvalidIdentityAndQuality(t *testing.T) { t.Parallel() tests := []struct { name string mutate func(*data.Usage) }{ {"missing provider", func(u *data.Usage) { u.Provider = " " }}, {"missing model", func(u *data.Usage) { u.Model = "" }}, {"missing requested time", func(u *data.Usage) { u.RequestedAt = time.Time{} }}, {"unknown quality", func(u *data.Usage) { u.Quality = "other" }}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { t.Parallel() usage := validUsage() test.mutate(&usage) if err := usage.Validate(); err == nil { t.Fatal("Validate() unexpectedly succeeded") } }) } } func TestUsageValidateRejectsInvalidTokenBreakdown(t *testing.T) { t.Parallel() tests := []struct { name string mutate func(*data.Usage) }{ {"negative token", func(u *data.Usage) { u.Tokens.CacheReadTokens = -1 }}, {"wrong total", func(u *data.Usage) { u.Tokens.TotalTokens++ }}, {"unclassified complete", func(u *data.Usage) { u.Tokens.UnclassifiedTokens = 1 u.Tokens.TotalTokens++ }}, {"overflow", func(u *data.Usage) { u.Tokens = data.TokenUsage{ UncachedInputTokens: math.MaxInt64, CacheReadTokens: 1, TotalTokens: math.MaxInt64, } }}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { t.Parallel() usage := validUsage() test.mutate(&usage) if err := usage.Validate(); err == nil { t.Fatal("Validate() unexpectedly succeeded") } }) } } func TestUsageValidatePreservesExplicitQualityStates(t *testing.T) { t.Parallel() normalized := validUsage() normalized.Quality = data.UsageQualityNormalized if err := normalized.Validate(); err != nil { t.Fatalf("normalized Validate() error = %v", err) } partial := validUsage() partial.Quality = data.UsageQualityPartial if err := partial.Validate(); err != nil { t.Fatalf("partial Validate() error = %v", err) } unclassified := validUsage() unclassified.Quality = data.UsageQualityUnclassified unclassified.Tokens.UnclassifiedTokens = 5 unclassified.Tokens.TotalTokens += 5 if err := unclassified.Validate(); err != nil { t.Fatalf("unclassified Validate() error = %v", err) } inconsistent := validUsage() inconsistent.Quality = data.UsageQualityInconsistent inconsistent.Tokens.TotalTokens++ if err := inconsistent.Validate(); err != nil { t.Fatalf("inconsistent Validate() error = %v", err) } }