From 88581912ba8fad4e48ba9303f811bbf3fd9a248f Mon Sep 17 00:00:00 2001 From: Wesley Liddick Date: Wed, 8 Jul 2026 19:23:38 +0000 Subject: [PATCH] test: add regression tests for fallback pricing pollution --- .../service/model_pricing_resolver_test.go | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/backend/internal/service/model_pricing_resolver_test.go b/backend/internal/service/model_pricing_resolver_test.go index 3b49b15556..d4169e5f0e 100644 --- a/backend/internal/service/model_pricing_resolver_test.go +++ b/backend/internal/service/model_pricing_resolver_test.go @@ -727,3 +727,63 @@ func TestApplyTokenOverrides_IntervalSetsImageOutputPriceExplicit(t *testing.T) require.True(t, pricing.ImageOutputPriceExplicit) require.Equal(t, 0.0, pricing.ImageOutputPricePerToken) } + +// =========================================================================== +// 10. Regression: channel overrides must not pollute fallbackPrices +// =========================================================================== + +// TestApplyTokenOverrides_FlatDoesNotPolluteFallbackPrices verifies that the +// flat-override path in applyTokenOverrides clones the BasePricing struct +// before mutation, so the shared fallbackPrices map entry is not written through. +func TestApplyTokenOverrides_FlatDoesNotPolluteFallbackPrices(t *testing.T) { + r := newResolverWithChannel(t, []ChannelModelPricing{{ + Platform: "anthropic", + Models: []string{"claude-sonnet-4"}, + BillingMode: BillingModeToken, + InputPrice: testPtrFloat64(10e-6), // base is 3e-6 + OutputPrice: testPtrFloat64(50e-6), // base is 15e-6 + }}) + + resolved := r.Resolve(context.Background(), PricingInput{ + Model: "claude-sonnet-4", + GroupID: groupIDPtr(), + }) + + // Resolved pricing should reflect the channel override + require.NotNil(t, resolved) + require.InDelta(t, 10e-6, resolved.BasePricing.InputPricePerToken, 1e-12) + require.InDelta(t, 50e-6, resolved.BasePricing.OutputPricePerToken, 1e-12) + + // Global fallbackPrices must NOT be polluted + fp := r.billingService.fallbackPrices["claude-sonnet-4"] + require.InDelta(t, 3e-6, fp.InputPricePerToken, 1e-12, "fallback InputPricePerToken polluted") + require.InDelta(t, 15e-6, fp.OutputPricePerToken, 1e-12, "fallback OutputPricePerToken polluted") + require.False(t, fp.ImageOutputPriceExplicit, "fallback ImageOutputPriceExplicit polluted") +} + +// TestApplyTokenOverrides_IntervalDoesNotPolluteFallbackPrices verifies that +// the interval-override path also clones before mutation. +func TestApplyTokenOverrides_IntervalDoesNotPolluteFallbackPrices(t *testing.T) { + r := newResolverWithChannel(t, []ChannelModelPricing{{ + Platform: "anthropic", + Models: []string{"claude-sonnet-4"}, + BillingMode: BillingModeToken, + Intervals: []PricingInterval{ + {MinTokens: 0, MaxTokens: testPtrInt(100000), InputPrice: testPtrFloat64(2e-6), OutputPrice: testPtrFloat64(8e-6)}, + }, + }}) + + resolved := r.Resolve(context.Background(), PricingInput{ + Model: "claude-sonnet-4", + GroupID: groupIDPtr(), + }) + + require.NotNil(t, resolved) + require.True(t, resolved.BasePricing.ImageOutputPriceExplicit) + + // Global fallbackPrices must NOT be polluted + fp := r.billingService.fallbackPrices["claude-sonnet-4"] + require.InDelta(t, 3e-6, fp.InputPricePerToken, 1e-12, "fallback InputPricePerToken polluted") + require.InDelta(t, 15e-6, fp.OutputPricePerToken, 1e-12, "fallback OutputPricePerToken polluted") + require.False(t, fp.ImageOutputPriceExplicit, "fallback ImageOutputPriceExplicit polluted") +}