- 使用 SQLite 保存关联后的请求生命周期和用量记录 - 支持长上下文阶梯价格和可配置的 Fast 计费倍率 - 添加管理接口和可配置列的用量面板 - 保留失败、取消、重试和 compact 请求,便于计费核对
147 lines
3.6 KiB
Go
147 lines
3.6 KiB
Go
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)
|
|
}
|
|
}
|