package billingexpr_test import ( "math" "math/rand" "testing" "github.com/QuantumNous/new-api/pkg/billingexpr" ) // --------------------------------------------------------------------------- // Claude-style: fixed tiers, input > 200K changes both input & output price // --------------------------------------------------------------------------- const claudeExpr = `p <= 200000 ? tier("standard", p * 1.5 + c * 7.5) : tier("long_context", p * 3.0 + c * 11.25)` func TestClaude_StandardTier(t *testing.T) { cost, trace, err := billingexpr.RunExpr(claudeExpr, billingexpr.TokenParams{P: 100000, C: 5000}) if err != nil { t.Fatal(err) } want := 100000*1.5 + 5000*7.5 if math.Abs(cost-want) > 1e-6 { t.Errorf("cost = %f, want %f", cost, want) } if trace.MatchedTier != "standard" { t.Errorf("tier = %q, want %q", trace.MatchedTier, "standard") } } func TestClaude_LongContextTier(t *testing.T) { cost, trace, err := billingexpr.RunExpr(claudeExpr, billingexpr.TokenParams{P: 300000, C: 10000}) if err != nil { t.Fatal(err) } want := 300000*3.0 + 10000*11.25 if math.Abs(cost-want) > 1e-6 { t.Errorf("cost = %f, want %f", cost, want) } if trace.MatchedTier != "long_context" { t.Errorf("tier = %q, want %q", trace.MatchedTier, "long_context") } } func TestClaude_BoundaryExact(t *testing.T) { cost, trace, err := billingexpr.RunExpr(claudeExpr, billingexpr.TokenParams{P: 200000, C: 1000}) if err != nil { t.Fatal(err) } want := 200000*1.5 + 1000*7.5 if math.Abs(cost-want) > 1e-6 { t.Errorf("cost = %f, want %f", cost, want) } if trace.MatchedTier != "standard" { t.Errorf("tier = %q, want %q", trace.MatchedTier, "standard") } } // --------------------------------------------------------------------------- // GLM-style: multi-condition tiers with both input and output dimensions // --------------------------------------------------------------------------- const glmExpr = ` ( p < 32000 && c < 200 ? tier("tier1_short", (p)*2 + c*8) : p < 32000 && c >= 200 ? tier("tier2_long_output", (p)*3 + c*14) : tier("tier3_long_input", (p)*4 + c*16) ) / 1000000 ` func TestGLM_Tier1(t *testing.T) { cost, trace, err := billingexpr.RunExpr(glmExpr, billingexpr.TokenParams{P: 15000, C: 100}) if err != nil { t.Fatal(err) } want := (15000.0*2 + 100.0*8) / 1000000 if math.Abs(cost-want) > 1e-10 { t.Errorf("cost = %f, want %f", cost, want) } if trace.MatchedTier != "tier1_short" { t.Errorf("tier = %q, want %q", trace.MatchedTier, "tier1_short") } } func TestGLM_Tier2(t *testing.T) { cost, trace, err := billingexpr.RunExpr(glmExpr, billingexpr.TokenParams{P: 15000, C: 500}) if err != nil { t.Fatal(err) } want := (15000.0*3 + 500.0*14) / 1000000 if math.Abs(cost-want) > 1e-10 { t.Errorf("cost = %f, want %f", cost, want) } if trace.MatchedTier != "tier2_long_output" { t.Errorf("tier = %q, want %q", trace.MatchedTier, "tier2_long_output") } } func TestGLM_Tier3(t *testing.T) { cost, trace, err := billingexpr.RunExpr(glmExpr, billingexpr.TokenParams{P: 50000, C: 100}) if err != nil { t.Fatal(err) } want := (50000.0*4 + 100.0*16) / 1000000 if math.Abs(cost-want) > 1e-10 { t.Errorf("cost = %f, want %f", cost, want) } if trace.MatchedTier != "tier3_long_input" { t.Errorf("tier = %q, want %q", trace.MatchedTier, "tier3_long_input") } } // --------------------------------------------------------------------------- // Simple flat-rate (no tier() call) // --------------------------------------------------------------------------- func TestSimpleExpr_NoTier(t *testing.T) { cost, trace, err := billingexpr.RunExpr("p * 0.5 + c * 1.0", billingexpr.TokenParams{P: 1000, C: 500}) if err != nil { t.Fatal(err) } want := 1000*0.5 + 500*1.0 if math.Abs(cost-want) > 1e-6 { t.Errorf("cost = %f, want %f", cost, want) } if trace.MatchedTier != "" { t.Errorf("tier should be empty, got %q", trace.MatchedTier) } } // --------------------------------------------------------------------------- // Math helper functions // --------------------------------------------------------------------------- func TestMathHelpers(t *testing.T) { cost, _, err := billingexpr.RunExpr("max(p, c) * 0.5 + min(p, c) * 0.1", billingexpr.TokenParams{P: 300, C: 500}) if err != nil { t.Fatal(err) } want := 500*0.5 + 300*0.1 if math.Abs(cost-want) > 1e-6 { t.Errorf("cost = %f, want %f", cost, want) } } func TestRequestProbeHelpers(t *testing.T) { cost, _, err := billingexpr.RunExprWithRequest( `p * 0.5 + c * 1.0 * (param("service_tier") == "fast" ? 2 : 1)`, billingexpr.TokenParams{P: 1000, C: 500}, billingexpr.RequestInput{ Body: []byte(`{"service_tier":"fast"}`), }, ) if err != nil { t.Fatal(err) } want := 1000*0.5 + 500*1.0*2 if math.Abs(cost-want) > 1e-6 { t.Errorf("cost = %f, want %f", cost, want) } } func TestHeaderProbeHelper(t *testing.T) { cost, _, err := billingexpr.RunExprWithRequest( `p * 0.5 + c * 1.0 * (has(header("anthropic-beta"), "fast-mode") ? 2 : 1)`, billingexpr.TokenParams{P: 1000, C: 500}, billingexpr.RequestInput{ Headers: map[string]string{ "Anthropic-Beta": "fast-mode-2026-02-01", }, }, ) if err != nil { t.Fatal(err) } want := 1000*0.5 + 500*1.0*2 if math.Abs(cost-want) > 1e-6 { t.Errorf("cost = %f, want %f", cost, want) } } func TestParamProbeNestedBool(t *testing.T) { cost, _, err := billingexpr.RunExprWithRequest( `p * (param("stream_options.fast_mode") == true ? 1.5 : 1.0)`, billingexpr.TokenParams{P: 100}, billingexpr.RequestInput{ Body: []byte(`{"stream_options":{"fast_mode":true}}`), }, ) if err != nil { t.Fatal(err) } want := 150.0 if math.Abs(cost-want) > 1e-6 { t.Errorf("cost = %f, want %f", cost, want) } } func TestParamProbeArrayLength(t *testing.T) { cost, _, err := billingexpr.RunExprWithRequest( `p * (param("messages.#") > 20 ? 1.2 : 1.0)`, billingexpr.TokenParams{P: 100}, billingexpr.RequestInput{ Body: []byte(`{"messages":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21]}`), }, ) if err != nil { t.Fatal(err) } want := 120.0 if math.Abs(cost-want) > 1e-6 { t.Errorf("cost = %f, want %f", cost, want) } } func TestRequestProbeMissingFieldReturnsNil(t *testing.T) { cost, _, err := billingexpr.RunExprWithRequest( `param("missing.value") == nil ? 2 : 1`, billingexpr.TokenParams{}, billingexpr.RequestInput{ Body: []byte(`{"service_tier":"standard"}`), }, ) if err != nil { t.Fatal(err) } if cost != 2 { t.Errorf("cost = %f, want 2", cost) } } func TestRequestProbeMultipleRulesMultiply(t *testing.T) { cost, _, err := billingexpr.RunExprWithRequest( `(param("service_tier") == "fast" ? 2 : 1) * (has(header("anthropic-beta"), "fast-mode-2026-02-01") ? 2.5 : 1)`, billingexpr.TokenParams{}, billingexpr.RequestInput{ Headers: map[string]string{ "Anthropic-Beta": "fast-mode-2026-02-01", }, Body: []byte(`{"service_tier":"fast"}`), }, ) if err != nil { t.Fatal(err) } if math.Abs(cost-5) > 1e-6 { t.Errorf("cost = %f, want 5", cost) } } func TestCeilFloor(t *testing.T) { cost, _, err := billingexpr.RunExpr("ceil(p / 1000) * 0.5", billingexpr.TokenParams{P: 1500}) if err != nil { t.Fatal(err) } want := math.Ceil(1500.0/1000) * 0.5 if math.Abs(cost-want) > 1e-6 { t.Errorf("cost = %f, want %f", cost, want) } } // --------------------------------------------------------------------------- // Zero tokens // --------------------------------------------------------------------------- func TestZeroTokens(t *testing.T) { cost, _, err := billingexpr.RunExpr(claudeExpr, billingexpr.TokenParams{}) if err != nil { t.Fatal(err) } if cost != 0 { t.Errorf("cost should be 0 for zero tokens, got %f", cost) } } // --------------------------------------------------------------------------- // Rounding // --------------------------------------------------------------------------- func TestQuotaRound(t *testing.T) { tests := []struct { in float64 want int }{ {0, 0}, {0.4, 0}, {0.5, 1}, {0.6, 1}, {1.5, 2}, {-0.5, -1}, {-0.6, -1}, {999.4999, 999}, {999.5, 1000}, {1e9 + 0.5, 1e9 + 1}, } for _, tt := range tests { got := billingexpr.QuotaRound(tt.in) if got != tt.want { t.Errorf("QuotaRound(%f) = %d, want %d", tt.in, got, tt.want) } } } // --------------------------------------------------------------------------- // Settlement // --------------------------------------------------------------------------- func TestComputeTieredQuota_Basic(t *testing.T) { snap := &billingexpr.BillingSnapshot{ BillingMode: "tiered_expr", ExprString: claudeExpr, ExprHash: billingexpr.ExprHashString(claudeExpr), GroupRatio: 1.0, EstimatedPromptTokens: 100000, EstimatedCompletionTokens: 5000, EstimatedQuotaBeforeGroup: (100000*1.5 + 5000*7.5) / 1_000_000 * 500_000, EstimatedQuotaAfterGroup: billingexpr.QuotaRound((100000*1.5 + 5000*7.5) / 1_000_000 * 500_000), EstimatedTier: "standard", QuotaPerUnit: 500_000, } result, err := billingexpr.ComputeTieredQuota(snap, billingexpr.TokenParams{P: 300000, C: 10000}) if err != nil { t.Fatal(err) } wantBefore := (300000*3.0 + 10000*11.25) / 1_000_000 * 500_000 if math.Abs(result.ActualQuotaBeforeGroup-wantBefore) > 1e-6 { t.Errorf("before group: got %f, want %f", result.ActualQuotaBeforeGroup, wantBefore) } if result.MatchedTier != "long_context" { t.Errorf("tier = %q, want %q", result.MatchedTier, "long_context") } if !result.CrossedTier { t.Error("expected crossed_tier=true (estimated standard, actual long_context)") } } func TestComputeTieredQuota_SameTier(t *testing.T) { snap := &billingexpr.BillingSnapshot{ BillingMode: "tiered_expr", ExprString: claudeExpr, ExprHash: billingexpr.ExprHashString(claudeExpr), GroupRatio: 1.5, EstimatedPromptTokens: 50000, EstimatedCompletionTokens: 1000, EstimatedQuotaBeforeGroup: (50000*1.5 + 1000*7.5) / 1_000_000 * 500_000, EstimatedQuotaAfterGroup: billingexpr.QuotaRound((50000*1.5 + 1000*7.5) / 1_000_000 * 500_000 * 1.5), EstimatedTier: "standard", QuotaPerUnit: 500_000, } result, err := billingexpr.ComputeTieredQuota(snap, billingexpr.TokenParams{P: 80000, C: 2000}) if err != nil { t.Fatal(err) } wantBefore := (80000*1.5 + 2000*7.5) / 1_000_000 * 500_000 wantAfter := billingexpr.QuotaRound(wantBefore * 1.5) if result.ActualQuotaAfterGroup != wantAfter { t.Errorf("after group: got %d, want %d", result.ActualQuotaAfterGroup, wantAfter) } if result.CrossedTier { t.Error("expected crossed_tier=false (both standard)") } } // --------------------------------------------------------------------------- // Compile errors // --------------------------------------------------------------------------- func TestCompileError(t *testing.T) { _, _, err := billingexpr.RunExpr("invalid +-+ syntax", billingexpr.TokenParams{}) if err == nil { t.Error("expected compile error") } } // --------------------------------------------------------------------------- // Compile Cache // --------------------------------------------------------------------------- func TestCompileCache_SameResult(t *testing.T) { r1, _, err := billingexpr.RunExpr("p * 0.5", billingexpr.TokenParams{P: 100}) if err != nil { t.Fatal(err) } r2, _, err := billingexpr.RunExpr("p * 0.5", billingexpr.TokenParams{P: 100}) if err != nil { t.Fatal(err) } if r1 != r2 { t.Errorf("cached and uncached results differ: %f != %f", r1, r2) } } func TestInvalidateCache(t *testing.T) { billingexpr.InvalidateCache() r1, _, _ := billingexpr.RunExpr("p * 0.5", billingexpr.TokenParams{P: 100}) billingexpr.InvalidateCache() r2, _, _ := billingexpr.RunExpr("p * 0.5", billingexpr.TokenParams{P: 100}) if r1 != r2 { t.Errorf("post-invalidate results differ: %f != %f", r1, r2) } } // --------------------------------------------------------------------------- // Hash // --------------------------------------------------------------------------- func TestExprHashString_Deterministic(t *testing.T) { h1 := billingexpr.ExprHashString("p * 0.5") h2 := billingexpr.ExprHashString("p * 0.5") if h1 != h2 { t.Error("hash should be deterministic") } h3 := billingexpr.ExprHashString("p * 0.6") if h1 == h3 { t.Error("different expressions should have different hashes") } } // --------------------------------------------------------------------------- // Cache variables: present // --------------------------------------------------------------------------- const claudeWithCacheExpr = `p <= 200000 ? tier("standard", p * 1.5 + c * 7.5 + cr * 0.15 + cc * 1.875) : tier("long_context", p * 3.0 + c * 11.25 + cr * 0.3 + cc * 3.75)` func TestCachePresent_StandardTier(t *testing.T) { params := billingexpr.TokenParams{P: 100000, C: 5000, CR: 50000, CC: 10000} cost, trace, err := billingexpr.RunExpr(claudeWithCacheExpr, params) if err != nil { t.Fatal(err) } want := 100000*1.5 + 5000*7.5 + 50000*0.15 + 10000*1.875 if math.Abs(cost-want) > 1e-6 { t.Errorf("cost = %f, want %f", cost, want) } if trace.MatchedTier != "standard" { t.Errorf("tier = %q, want %q", trace.MatchedTier, "standard") } } func TestCachePresent_LongContextTier(t *testing.T) { params := billingexpr.TokenParams{P: 300000, C: 10000, CR: 100000, CC: 20000} cost, trace, err := billingexpr.RunExpr(claudeWithCacheExpr, params) if err != nil { t.Fatal(err) } want := 300000*3.0 + 10000*11.25 + 100000*0.3 + 20000*3.75 if math.Abs(cost-want) > 1e-6 { t.Errorf("cost = %f, want %f", cost, want) } if trace.MatchedTier != "long_context" { t.Errorf("tier = %q, want %q", trace.MatchedTier, "long_context") } } // --------------------------------------------------------------------------- // Cache variables: absent (all zero) — same expression still works // --------------------------------------------------------------------------- func TestCacheAbsent_ZeroCacheTokens(t *testing.T) { params := billingexpr.TokenParams{P: 100000, C: 5000} cost, trace, err := billingexpr.RunExpr(claudeWithCacheExpr, params) if err != nil { t.Fatal(err) } want := 100000*1.5 + 5000*7.5 if math.Abs(cost-want) > 1e-6 { t.Errorf("cost = %f, want %f (cache terms should be 0)", cost, want) } if trace.MatchedTier != "standard" { t.Errorf("tier = %q, want %q", trace.MatchedTier, "standard") } } // --------------------------------------------------------------------------- // Mixed cache fields: cc and cc1h non-zero // --------------------------------------------------------------------------- const claudeCacheSplitExpr = `tier("default", p * 1.5 + c * 7.5 + cr * 0.15 + cc * 2.0 + cc1h * 3.0)` func TestMixedCacheFields(t *testing.T) { params := billingexpr.TokenParams{P: 100000, C: 5000, CR: 10000, CC: 5000, CC1h: 2000} cost, _, err := billingexpr.RunExpr(claudeCacheSplitExpr, params) if err != nil { t.Fatal(err) } want := 100000*1.5 + 5000*7.5 + 10000*0.15 + 5000*2.0 + 2000*3.0 if math.Abs(cost-want) > 1e-6 { t.Errorf("cost = %f, want %f", cost, want) } } func TestMixedCacheFields_AllCacheZero(t *testing.T) { params := billingexpr.TokenParams{P: 100000, C: 5000} cost, _, err := billingexpr.RunExpr(claudeCacheSplitExpr, params) if err != nil { t.Fatal(err) } want := 100000*1.5 + 5000*7.5 if math.Abs(cost-want) > 1e-6 { t.Errorf("cost = %f, want %f (all cache zero)", cost, want) } } // --------------------------------------------------------------------------- // Backward compatibility: p+c only expressions still work with TokenParams // --------------------------------------------------------------------------- func TestBackwardCompat_OldExprWithTokenParams(t *testing.T) { params := billingexpr.TokenParams{P: 100000, C: 5000, CR: 99999, CC: 88888} cost, trace, err := billingexpr.RunExpr(claudeExpr, params) if err != nil { t.Fatal(err) } want := 100000*1.5 + 5000*7.5 if math.Abs(cost-want) > 1e-6 { t.Errorf("cost = %f, want %f (old expr ignores cache fields)", cost, want) } if trace.MatchedTier != "standard" { t.Errorf("tier = %q, want %q", trace.MatchedTier, "standard") } } // --------------------------------------------------------------------------- // Settlement with cache tokens // --------------------------------------------------------------------------- func TestComputeTieredQuota_WithCache(t *testing.T) { snap := &billingexpr.BillingSnapshot{ BillingMode: "tiered_expr", ExprString: claudeWithCacheExpr, ExprHash: billingexpr.ExprHashString(claudeWithCacheExpr), GroupRatio: 1.0, EstimatedPromptTokens: 100000, EstimatedCompletionTokens: 5000, EstimatedQuotaBeforeGroup: (100000*1.5 + 5000*7.5) / 1_000_000 * 500_000, EstimatedQuotaAfterGroup: billingexpr.QuotaRound((100000*1.5 + 5000*7.5) / 1_000_000 * 500_000), EstimatedTier: "standard", QuotaPerUnit: 500_000, } params := billingexpr.TokenParams{P: 100000, C: 5000, CR: 50000, CC: 10000} result, err := billingexpr.ComputeTieredQuota(snap, params) if err != nil { t.Fatal(err) } wantBefore := (100000*1.5 + 5000*7.5 + 50000*0.15 + 10000*1.875) / 1_000_000 * 500_000 if math.Abs(result.ActualQuotaBeforeGroup-wantBefore) > 1e-6 { t.Errorf("before group: got %f, want %f", result.ActualQuotaBeforeGroup, wantBefore) } if result.MatchedTier != "standard" { t.Errorf("tier = %q, want %q", result.MatchedTier, "standard") } if result.CrossedTier { t.Error("expected crossed_tier=false (same tier)") } } func TestComputeTieredQuota_WithCacheCrossTier(t *testing.T) { snap := &billingexpr.BillingSnapshot{ BillingMode: "tiered_expr", ExprString: claudeWithCacheExpr, ExprHash: billingexpr.ExprHashString(claudeWithCacheExpr), GroupRatio: 2.0, EstimatedPromptTokens: 100000, EstimatedCompletionTokens: 5000, EstimatedQuotaBeforeGroup: (100000*1.5 + 5000*7.5) / 1_000_000 * 500_000, EstimatedQuotaAfterGroup: billingexpr.QuotaRound((100000*1.5 + 5000*7.5) / 1_000_000 * 500_000 * 2.0), EstimatedTier: "standard", QuotaPerUnit: 500_000, } params := billingexpr.TokenParams{P: 300000, C: 10000, CR: 50000, CC: 10000} result, err := billingexpr.ComputeTieredQuota(snap, params) if err != nil { t.Fatal(err) } wantBefore := (300000*3.0 + 10000*11.25 + 50000*0.3 + 10000*3.75) / 1_000_000 * 500_000 wantAfter := billingexpr.QuotaRound(wantBefore * 2.0) if math.Abs(result.ActualQuotaBeforeGroup-wantBefore) > 1e-6 { t.Errorf("before group: got %f, want %f", result.ActualQuotaBeforeGroup, wantBefore) } if result.ActualQuotaAfterGroup != wantAfter { t.Errorf("after group: got %d, want %d", result.ActualQuotaAfterGroup, wantAfter) } if !result.CrossedTier { t.Error("expected crossed_tier=true (estimated standard, actual long_context)") } } // --------------------------------------------------------------------------- // Fuzz: random p/c/cache, verify non-negative result // --------------------------------------------------------------------------- func TestFuzz_NonNegativeResults(t *testing.T) { exprs := []string{ claudeExpr, claudeWithCacheExpr, glmExpr, "p * 0.5 + c * 1.0", "max(p, c) * 0.1", "p * 0.5 + cr * 0.1 + cc * 0.2", } rng := rand.New(rand.NewSource(42)) for _, exprStr := range exprs { for i := 0; i < 500; i++ { params := billingexpr.TokenParams{ P: math.Round(rng.Float64() * 1000000), C: math.Round(rng.Float64() * 500000), CR: math.Round(rng.Float64() * 200000), CC: math.Round(rng.Float64() * 50000), CC1h: math.Round(rng.Float64() * 10000), } cost, _, err := billingexpr.RunExpr(exprStr, params) if err != nil { t.Fatalf("expr=%q params=%+v: %v", exprStr, params, err) } if cost < 0 { t.Errorf("expr=%q params=%+v: negative cost %f", exprStr, params, cost) } } } } func TestFuzz_SettlementConsistency(t *testing.T) { rng := rand.New(rand.NewSource(99)) for i := 0; i < 200; i++ { estParams := billingexpr.TokenParams{ P: math.Round(rng.Float64() * 500000), C: math.Round(rng.Float64() * 100000), CR: math.Round(rng.Float64() * 100000), CC: math.Round(rng.Float64() * 30000), } actParams := billingexpr.TokenParams{ P: math.Round(rng.Float64() * 500000), C: math.Round(rng.Float64() * 100000), CR: math.Round(rng.Float64() * 100000), CC: math.Round(rng.Float64() * 30000), } groupRatio := 0.5 + rng.Float64()*2.0 estCost, estTrace, _ := billingexpr.RunExpr(claudeWithCacheExpr, estParams) const qpu = 500_000.0 snap := &billingexpr.BillingSnapshot{ BillingMode: "tiered_expr", ExprString: claudeWithCacheExpr, ExprHash: billingexpr.ExprHashString(claudeWithCacheExpr), GroupRatio: groupRatio, EstimatedPromptTokens: int(estParams.P), EstimatedCompletionTokens: int(estParams.C), EstimatedQuotaBeforeGroup: estCost / 1_000_000 * qpu, EstimatedQuotaAfterGroup: billingexpr.QuotaRound(estCost / 1_000_000 * qpu * groupRatio), EstimatedTier: estTrace.MatchedTier, QuotaPerUnit: qpu, } result, err := billingexpr.ComputeTieredQuota(snap, actParams) if err != nil { t.Fatalf("iter %d: %v", i, err) } directCost, _, _ := billingexpr.RunExpr(claudeWithCacheExpr, actParams) directQuota := billingexpr.QuotaRound(directCost / 1_000_000 * qpu * groupRatio) if result.ActualQuotaAfterGroup != directQuota { t.Errorf("iter %d: settlement %d != direct %d", i, result.ActualQuotaAfterGroup, directQuota) } } } // --------------------------------------------------------------------------- // Settlement-level tests for ComputeTieredQuota // --------------------------------------------------------------------------- func TestComputeTieredQuota_BasicSettlement(t *testing.T) { exprStr := `tier("default", p + c)` snap := &billingexpr.BillingSnapshot{ BillingMode: "tiered_expr", ExprString: exprStr, ExprHash: billingexpr.ExprHashString(exprStr), GroupRatio: 1.0, QuotaPerUnit: 500_000, } result, err := billingexpr.ComputeTieredQuota(snap, billingexpr.TokenParams{P: 3000, C: 2000}) if err != nil { t.Fatal(err) } // exprOutput = 5000; quota = 5000 / 1M * 500K = 2500 if math.Abs(result.ActualQuotaBeforeGroup-2500) > 1e-6 { t.Errorf("before group = %f, want 2500", result.ActualQuotaBeforeGroup) } if result.ActualQuotaAfterGroup != 2500 { t.Errorf("after group = %d, want 2500", result.ActualQuotaAfterGroup) } if result.MatchedTier != "default" { t.Errorf("tier = %q, want default", result.MatchedTier) } } func TestComputeTieredQuota_WithGroupRatio(t *testing.T) { exprStr := `tier("default", p + c)` snap := &billingexpr.BillingSnapshot{ BillingMode: "tiered_expr", ExprString: exprStr, ExprHash: billingexpr.ExprHashString(exprStr), GroupRatio: 2.0, QuotaPerUnit: 500_000, } result, err := billingexpr.ComputeTieredQuota(snap, billingexpr.TokenParams{P: 1000, C: 500}) if err != nil { t.Fatal(err) } // exprOutput = 1500; quotaBeforeGroup = 750; afterGroup = round(750 * 2.0) = 1500 if result.ActualQuotaAfterGroup != 1500 { t.Errorf("after group = %d, want 1500", result.ActualQuotaAfterGroup) } } func TestComputeTieredQuota_ZeroTokens(t *testing.T) { exprStr := `tier("default", p * 2 + c * 10)` snap := &billingexpr.BillingSnapshot{ BillingMode: "tiered_expr", ExprString: exprStr, ExprHash: billingexpr.ExprHashString(exprStr), GroupRatio: 1.0, QuotaPerUnit: 500_000, } result, err := billingexpr.ComputeTieredQuota(snap, billingexpr.TokenParams{}) if err != nil { t.Fatal(err) } if result.ActualQuotaAfterGroup != 0 { t.Errorf("after group = %d, want 0", result.ActualQuotaAfterGroup) } } func TestComputeTieredQuota_RoundingEdge(t *testing.T) { exprStr := `tier("default", p * 0.5)` // 3 * 0.5 = 1.5 (expr); 1.5 / 1M * 500K = 0.75; round(0.75) = 1 snap := &billingexpr.BillingSnapshot{ BillingMode: "tiered_expr", ExprString: exprStr, ExprHash: billingexpr.ExprHashString(exprStr), GroupRatio: 1.0, QuotaPerUnit: 500_000, } result, err := billingexpr.ComputeTieredQuota(snap, billingexpr.TokenParams{P: 3}) if err != nil { t.Fatal(err) } // 3 * 0.5 = 1.5 (expr); quota = 1.5 / 1M * 500K = 0.75; round(0.75) = 1 if result.ActualQuotaAfterGroup != 1 { t.Errorf("after group = %d, want 1 (round 0.75 up)", result.ActualQuotaAfterGroup) } } func TestComputeTieredQuota_RoundingEdgeDown(t *testing.T) { exprStr := `tier("default", p * 0.4)` // 3 * 0.4 = 1.2 (expr); 1.2 / 1M * 500K = 0.6; round(0.6) = 1 snap := &billingexpr.BillingSnapshot{ BillingMode: "tiered_expr", ExprString: exprStr, ExprHash: billingexpr.ExprHashString(exprStr), GroupRatio: 1.0, QuotaPerUnit: 500_000, } result, err := billingexpr.ComputeTieredQuota(snap, billingexpr.TokenParams{P: 3}) if err != nil { t.Fatal(err) } // 3 * 0.4 = 1.2 (expr); quota = 1.2 / 1M * 500K = 0.6; round(0.6) = 1 if result.ActualQuotaAfterGroup != 1 { t.Errorf("after group = %d, want 1 (round 0.6 up)", result.ActualQuotaAfterGroup) } } func TestComputeTieredQuotaWithRequest_ProbeAffectsQuota(t *testing.T) { exprStr := `param("fast") == true ? tier("fast", p * 4) : tier("normal", p * 2)` snap := &billingexpr.BillingSnapshot{ BillingMode: "tiered_expr", ExprString: exprStr, ExprHash: billingexpr.ExprHashString(exprStr), GroupRatio: 1.0, EstimatedTier: "normal", QuotaPerUnit: 500_000, } // Without request: normal tier r1, err := billingexpr.ComputeTieredQuota(snap, billingexpr.TokenParams{P: 1000}) if err != nil { t.Fatal(err) } // normal: p*2 = 2000; quota = 2000 / 1M * 500K = 1000 if r1.ActualQuotaAfterGroup != 1000 { t.Errorf("normal = %d, want 1000", r1.ActualQuotaAfterGroup) } // With request: fast tier r2, err := billingexpr.ComputeTieredQuotaWithRequest(snap, billingexpr.TokenParams{P: 1000}, billingexpr.RequestInput{ Body: []byte(`{"fast":true}`), }) if err != nil { t.Fatal(err) } // fast: p*4 = 4000; quota = 4000 / 1M * 500K = 2000 if r2.ActualQuotaAfterGroup != 2000 { t.Errorf("fast = %d, want 2000", r2.ActualQuotaAfterGroup) } if !r2.CrossedTier { t.Error("expected CrossedTier = true when probe changes tier") } } func TestComputeTieredQuota_BoundaryTierCrossing(t *testing.T) { exprStr := `p <= 100000 ? tier("small", p * 1) : tier("large", p * 2)` snap := &billingexpr.BillingSnapshot{ BillingMode: "tiered_expr", ExprString: exprStr, ExprHash: billingexpr.ExprHashString(exprStr), GroupRatio: 1.0, EstimatedTier: "small", QuotaPerUnit: 500_000, } // At boundary: small, p*1 = 100000; quota = 100000 / 1M * 500K = 50000 r1, err := billingexpr.ComputeTieredQuota(snap, billingexpr.TokenParams{P: 100000}) if err != nil { t.Fatal(err) } if r1.MatchedTier != "small" { t.Errorf("at boundary: tier = %s, want small", r1.MatchedTier) } if r1.ActualQuotaAfterGroup != 50000 { t.Errorf("at boundary: quota = %d, want 50000", r1.ActualQuotaAfterGroup) } // Past boundary: large, p*2 = 200002; quota = 200002 / 1M * 500K = 100001 r2, err := billingexpr.ComputeTieredQuota(snap, billingexpr.TokenParams{P: 100001}) if err != nil { t.Fatal(err) } if r2.MatchedTier != "large" { t.Errorf("past boundary: tier = %s, want large", r2.MatchedTier) } if r2.ActualQuotaAfterGroup != 100001 { t.Errorf("past boundary: quota = %d, want 100001", r2.ActualQuotaAfterGroup) } if !r2.CrossedTier { t.Error("expected CrossedTier = true") } } // --------------------------------------------------------------------------- // Time function tests // --------------------------------------------------------------------------- func TestTimeFunctions_ValidTimezone(t *testing.T) { exprStr := `tier("default", p) * (hour("UTC") >= 0 ? 1 : 1)` cost, _, err := billingexpr.RunExpr(exprStr, billingexpr.TokenParams{P: 100}) if err != nil { t.Fatal(err) } if cost != 100 { t.Errorf("cost = %f, want 100", cost) } } func TestTimeFunctions_AllFunctionsCompile(t *testing.T) { exprStr := `tier("default", p) * (hour("Asia/Shanghai") >= 0 ? 1 : 1) * (minute("UTC") >= 0 ? 1 : 1) * (weekday("UTC") >= 0 ? 1 : 1) * (month("UTC") >= 1 ? 1 : 1) * (day("UTC") >= 1 ? 1 : 1)` cost, _, err := billingexpr.RunExpr(exprStr, billingexpr.TokenParams{P: 500}) if err != nil { t.Fatal(err) } if cost != 500 { t.Errorf("cost = %f, want 500", cost) } } func TestTimeFunctions_InvalidTimezone(t *testing.T) { exprStr := `tier("default", p) * (hour("Invalid/Zone") >= 0 ? 1 : 2)` cost, _, err := billingexpr.RunExpr(exprStr, billingexpr.TokenParams{P: 100}) if err != nil { t.Fatal(err) } // Invalid timezone falls back to UTC; hour is 0-23, so condition is always true if cost != 100 { t.Errorf("cost = %f, want 100 (fallback to UTC)", cost) } } func TestTimeFunctions_EmptyTimezone(t *testing.T) { exprStr := `tier("default", p) * (hour("") >= 0 ? 1 : 2)` cost, _, err := billingexpr.RunExpr(exprStr, billingexpr.TokenParams{P: 100}) if err != nil { t.Fatal(err) } if cost != 100 { t.Errorf("cost = %f, want 100 (empty tz -> UTC)", cost) } } func TestTimeFunctions_NightDiscountPattern(t *testing.T) { exprStr := `tier("default", p * 2 + c * 10) * (hour("UTC") >= 21 || hour("UTC") < 6 ? 0.5 : 1)` cost, _, err := billingexpr.RunExpr(exprStr, billingexpr.TokenParams{P: 1000, C: 500}) if err != nil { t.Fatal(err) } // Base = 1000*2 + 500*10 = 7000; multiplier is either 0.5 or 1 depending on current UTC hour if cost != 7000 && cost != 3500 { t.Errorf("cost = %f, want 7000 or 3500", cost) } } func TestTimeFunctions_WeekdayRange(t *testing.T) { exprStr := `tier("default", p) * (weekday("UTC") >= 0 && weekday("UTC") <= 6 ? 1 : 999)` cost, _, err := billingexpr.RunExpr(exprStr, billingexpr.TokenParams{P: 100}) if err != nil { t.Fatal(err) } // weekday is always 0-6, so multiplier is always 1 if cost != 100 { t.Errorf("cost = %f, want 100", cost) } } func TestTimeFunctions_MonthDayPattern(t *testing.T) { exprStr := `tier("default", p) * (month("Asia/Shanghai") == 1 && day("Asia/Shanghai") == 1 ? 0.5 : 1)` cost, _, err := billingexpr.RunExpr(exprStr, billingexpr.TokenParams{P: 1000}) if err != nil { t.Fatal(err) } // Either 1000 (not Jan 1) or 500 (Jan 1) — both are valid if cost != 1000 && cost != 500 { t.Errorf("cost = %f, want 1000 or 500", cost) } } // --------------------------------------------------------------------------- // Image and audio token tests // --------------------------------------------------------------------------- func TestImageTokenVariable(t *testing.T) { exprStr := `tier("base", p * 2 + c * 10 + img * 5)` cost, _, err := billingexpr.RunExpr(exprStr, billingexpr.TokenParams{P: 1000, C: 500, Img: 200}) if err != nil { t.Fatal(err) } // 1000*2 + 500*10 + 200*5 = 2000 + 5000 + 1000 = 8000 if math.Abs(cost-8000) > 1e-6 { t.Errorf("cost = %f, want 8000", cost) } } func TestAudioTokenVariables(t *testing.T) { exprStr := `tier("base", p * 2 + c * 10 + ai * 50 + ao * 100)` cost, _, err := billingexpr.RunExpr(exprStr, billingexpr.TokenParams{P: 1000, C: 500, AI: 100, AO: 50}) if err != nil { t.Fatal(err) } // 1000*2 + 500*10 + 100*50 + 50*100 = 2000 + 5000 + 5000 + 5000 = 17000 if math.Abs(cost-17000) > 1e-6 { t.Errorf("cost = %f, want 17000", cost) } } func TestImageAudioVariables(t *testing.T) { exprStr := `tier("base", p * 1 + img * 3 + ai * 5 + ao * 10)` cost, _, err := billingexpr.RunExpr(exprStr, billingexpr.TokenParams{P: 100, Img: 50, AI: 20, AO: 10}) if err != nil { t.Fatal(err) } // 100*1 + 50*3 + 20*5 + 10*10 = 100 + 150 + 100 + 100 = 450 if math.Abs(cost-450) > 1e-6 { t.Errorf("cost = %f, want 450", cost) } } func TestImageAudioZero(t *testing.T) { exprStr := `tier("base", p * 2 + img * 5 + ai * 50 + ao * 100)` cost, _, err := billingexpr.RunExpr(exprStr, billingexpr.TokenParams{P: 1000}) if err != nil { t.Fatal(err) } // img, ai, ao default to 0 if math.Abs(cost-2000) > 1e-6 { t.Errorf("cost = %f, want 2000", cost) } } // --------------------------------------------------------------------------- // Benchmarks: compile vs cached execution // --------------------------------------------------------------------------- const benchComplexExpr = `p <= 200000 ? tier("standard", p * 3 + c * 15 + cr * 0.3 + cc * 3.75 + cc1h * 6 + img * 3 + img_o * 30 + ai * 10 + ao * 40) : tier("long_context", p * 6 + c * 22.5 + cr * 0.6 + cc * 7.5 + cc1h * 12 + img * 6 + img_o * 60 + ai * 20 + ao * 80)` func BenchmarkExprCompile(b *testing.B) { for i := 0; i < b.N; i++ { billingexpr.InvalidateCache() billingexpr.CompileFromCache(benchComplexExpr) } } func BenchmarkExprRunCached(b *testing.B) { billingexpr.CompileFromCache(benchComplexExpr) params := billingexpr.TokenParams{P: 150000, C: 10000, CR: 30000, CC: 5000, Img: 2000, AI: 1000, AO: 500} b.ResetTimer() for i := 0; i < b.N; i++ { billingexpr.RunExpr(benchComplexExpr, params) } }