Files
sub2api/backend/internal/service/deepseek_pricing_test.go
T
alfadb ab9bd9e877 fix(billing): 适配 DeepSeek V4.1-Flash 新价与 pro→Flash 切换
- flash 低谷价更新为 $0.15/$0.60/$0.003 per MTok(2026-09-10 官方降价)
- deepseek-v4-pro 自 2026-09-14 04:00 UTC 起按 Flash 价计费(上游路由变更)
- 计费时点显式驱动 pro→Flash 切换,定价展示与计费两条路径同源
- deepseek-flash 保留 reasoning_effort=max;补 Codex 模型清单与前端白名单
- 价表 JSON 新增 deepseek-flash 条目并同步 flash 系价格
2026-09-10 22:10:29 +08:00

446 lines
19 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//go:build unit
package service
import (
"context"
"os"
"path/filepath"
"testing"
"time"
"github.com/Wei-Shaw/sub2api/internal/config"
"github.com/Wei-Shaw/sub2api/internal/pkg/timezone"
"github.com/stretchr/testify/require"
)
// ---------------------------------------------------------------------------
// deepseekPeakMultiplierAt:官方峰谷口径(2026-08-23 起生效)
// 高峰时段 01:00–04:00 与 06:00–10:00 UTC(半开区间,仅工作日);
// 北京时间周六/周日全天低谷;高峰价 = 2× 低谷价。
// 2026-08-24 为周一(工作日),2026-08-22 周六、2026-08-23 周日。
// ---------------------------------------------------------------------------
func TestDeepseekPeakMultiplierAt(t *testing.T) {
mon := func(hour, min int) time.Time { return time.Date(2026, 8, 24, hour, min, 0, 0, time.UTC) }
sat := func(hour, min int) time.Time { return time.Date(2026, 8, 22, hour, min, 0, 0, time.UTC) }
sun := func(hour, min int) time.Time { return time.Date(2026, 8, 23, hour, min, 0, 0, time.UTC) }
tests := []struct {
name string
now time.Time
want float64
}{
// 工作日高峰窗口边界(半开区间)
{"weekday 01:00 peak start", mon(1, 0), 2.0},
{"weekday 03:59 peak upper bound", mon(3, 59), 2.0},
{"weekday 04:00 peak end", mon(4, 0), 1.0},
{"weekday 06:00 peak start", mon(6, 0), 2.0},
{"weekday 09:59 peak upper bound", mon(9, 59), 2.0},
{"weekday 10:00 peak end", mon(10, 0), 1.0},
// 工作日低谷时段
{"weekday 00:00 off-peak", mon(0, 0), 1.0},
{"weekday 05:00 off-peak", mon(5, 0), 1.0},
{"weekday 12:00 off-peak", mon(12, 0), 1.0},
{"weekday 23:59 off-peak", mon(23, 59), 1.0},
// 北京时间周末全天低谷(即使 UTC 处于高峰时段)
{"saturday utc 02:00 beijing sat 10:00", sat(2, 0), 1.0},
{"sunday utc 07:00 beijing sun 15:00", sun(7, 0), 1.0},
// 北京时间与 UTC 跨日边界:UTC 周六 16:30 = 北京周日 00:30 → 周末低谷
{"utc saturday 16:30 = beijing sunday 00:30", sat(16, 30), 1.0},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
require.Equal(t, tt.want, deepseekPeakMultiplierAt(tt.now))
})
}
}
func TestIsDeepSeekModel(t *testing.T) {
deepseek := []string{
"deepseek-flash", "deepseek-v4-flash", "deepseek-v4-pro", "deepseek-v4-flash-vision-exp",
"deepseek-chat", "deepseek-reasoner", "deepseek-v3-2-251201",
"deepseek-coder", "deepseek-foo", "deepseek-v4-pro-0813",
"DEEPSEEK-V4-PRO", " deepseek-v4-flash ",
}
for _, m := range deepseek {
require.True(t, isDeepSeekModel(m), "model %q should be deepseek", m)
}
nonDeepseek := []string{
"gpt-5.4", "claude-sonnet-4", "deepseekcoder", // 无连字符不算 deepseek- 前缀
"", " deepseek", // 无连字符后缀
}
for _, m := range nonDeepseek {
require.False(t, isDeepSeekModel(m), "model %q should not be deepseek", m)
}
}
// ---------------------------------------------------------------------------
// 默认价卡(Source=LiteLLM)按官方峰谷倍率计费;分组/渠道自定义定价不叠加
// ---------------------------------------------------------------------------
func TestCalculateCostUnified_DeepseekDefaultCardPeakMultiplier(t *testing.T) {
bs := newTestBillingService()
resolver := NewModelPricingResolver(nil, bs)
tokens := UsageTokens{InputTokens: 1000, OutputTokens: 500, CacheReadTokens: 1000}
// 低谷成本(2026-09-10 官方新价):1000*1.5e-7 + 500*6e-7 + 1000*3e-9 = 4.53e-4
offPeakTotal := 1000*1.5e-7 + 500*6e-7 + 1000*3e-9
offPeak, err := bs.CalculateCostUnified(CostInput{
Ctx: context.Background(), Model: "deepseek-v4-flash", Tokens: tokens,
RateMultiplier: 1.0, Resolver: resolver,
PricingAt: time.Date(2026, 8, 24, 12, 0, 0, 0, time.UTC), // 周一低谷
})
require.NoError(t, err)
require.InDelta(t, offPeakTotal, offPeak.TotalCost, 1e-10)
peak, err := bs.CalculateCostUnified(CostInput{
Ctx: context.Background(), Model: "deepseek-v4-flash", Tokens: tokens,
RateMultiplier: 1.0, Resolver: resolver,
PricingAt: time.Date(2026, 8, 24, 2, 0, 0, 0, time.UTC), // 周一高峰
})
require.NoError(t, err)
require.InDelta(t, offPeakTotal*2, peak.TotalCost, 1e-10)
}
func TestCalculateCostUnified_DeepseekProDefaultCardPeakMultiplier(t *testing.T) {
bs := newTestBillingService()
resolver := NewModelPricingResolver(nil, bs)
tokens := UsageTokens{InputTokens: 1000, OutputTokens: 500, CacheReadTokens: 1000}
offPeakTotal := 1000*6.6e-7 + 500*1.98e-6 + 1000*2.2e-8
offPeak, err := bs.CalculateCostUnified(CostInput{
Ctx: context.Background(), Model: "deepseek-v4-pro", Tokens: tokens,
RateMultiplier: 1.0, Resolver: resolver,
PricingAt: time.Date(2026, 8, 24, 12, 0, 0, 0, time.UTC),
})
require.NoError(t, err)
require.InDelta(t, offPeakTotal, offPeak.TotalCost, 1e-10)
peak, err := bs.CalculateCostUnified(CostInput{
Ctx: context.Background(), Model: "deepseek-v4-pro", Tokens: tokens,
RateMultiplier: 1.0, Resolver: resolver,
PricingAt: time.Date(2026, 8, 24, 6, 30, 0, 0, time.UTC), // 周一高峰
})
require.NoError(t, err)
require.InDelta(t, offPeakTotal*2, peak.TotalCost, 1e-10)
}
func TestCalculateCostUnified_DeepseekVersionedNamePeakMultiplier(t *testing.T) {
bs := newTestBillingService()
resolver := NewModelPricingResolver(nil, bs)
tokens := UsageTokens{InputTokens: 1000, OutputTokens: 500, CacheReadTokens: 1000}
offPeakTotal := 1000*1.5e-7 + 500*6e-7 + 1000*3e-9
offPeak, err := bs.CalculateCostUnified(CostInput{
Ctx: context.Background(), Model: "deepseek-v4-flash-0731", Tokens: tokens,
RateMultiplier: 1.0, Resolver: resolver,
PricingAt: time.Date(2026, 8, 24, 12, 0, 0, 0, time.UTC), // 周一低谷
})
require.NoError(t, err)
require.InDelta(t, offPeakTotal, offPeak.TotalCost, 1e-10)
peak, err := bs.CalculateCostUnified(CostInput{
Ctx: context.Background(), Model: "deepseek-v4-flash-0731", Tokens: tokens,
RateMultiplier: 1.0, Resolver: resolver,
PricingAt: time.Date(2026, 8, 24, 2, 0, 0, 0, time.UTC), // 周一高峰
})
require.NoError(t, err)
require.InDelta(t, offPeakTotal*2, peak.TotalCost, 1e-10)
}
func TestCalculateCostUnified_DeepseekGroupPricingNotScaledByPeak(t *testing.T) {
bs := newTestBillingService()
resolver := NewModelPricingResolver(nil, bs)
inputPrice := 1e-6
outputPrice := 2e-6
group := &Group{
ID: 1, Name: "ds-group", Platform: PlatformDeepseek, Status: StatusActive,
ModelPricing: []ChannelModelPricing{{
Models: []string{"deepseek-v4-flash"}, BillingMode: BillingModeToken,
InputPrice: &inputPrice, OutputPrice: &outputPrice,
}},
}
resolved := resolver.Resolve(context.Background(), PricingInput{Model: "deepseek-v4-flash", Group: group})
require.Equal(t, PricingSourceGroup, resolved.Source)
tokens := UsageTokens{InputTokens: 1000, OutputTokens: 500, CacheReadTokens: 1000}
// 分组自定义价:1000*1e-6 + 500*2e-6 + 1000*3e-9(缓存读沿用官方 flash 价)
groupTotal := 1000*1e-6 + 500*2e-6 + 1000*3e-9
for _, pricingAt := range []time.Time{
time.Date(2026, 8, 24, 12, 0, 0, 0, time.UTC), // 低谷
time.Date(2026, 8, 24, 2, 0, 0, 0, time.UTC), // 高峰
} {
cost, err := bs.CalculateCostUnified(CostInput{
Ctx: context.Background(), Model: "deepseek-v4-flash", Group: group,
Tokens: tokens, RateMultiplier: 1.0, Resolver: resolver, PricingAt: pricingAt,
})
require.NoError(t, err)
require.InDelta(t, groupTotal, cost.TotalCost, 1e-10,
"分组自定义定价不应叠加官方峰谷倍率(pricingAt=%v)", pricingAt)
}
}
func TestCalculateCostUnified_NonDeepseekDefaultCardNotScaledByPeak(t *testing.T) {
bs := newTestBillingService()
resolver := NewModelPricingResolver(nil, bs)
tokens := UsageTokens{InputTokens: 1000, OutputTokens: 500}
total := 1000*3e-6 + 500*15e-6 // claude-sonnet-4 fallback
for _, pricingAt := range []time.Time{
time.Date(2026, 8, 24, 12, 0, 0, 0, time.UTC),
time.Date(2026, 8, 24, 2, 0, 0, 0, time.UTC),
} {
cost, err := bs.CalculateCostUnified(CostInput{
Ctx: context.Background(), Model: "claude-sonnet-4", Tokens: tokens,
RateMultiplier: 1.0, Resolver: resolver, PricingAt: pricingAt,
})
require.NoError(t, err)
require.InDelta(t, total, cost.TotalCost, 1e-10,
"非 DeepSeek 模型不应受官方峰谷倍率影响(pricingAt=%v)", pricingAt)
}
}
func TestCalculateCostUnified_DeepseekPricingAtZeroFallsBackToNow(t *testing.T) {
bs := newTestBillingService()
resolver := NewModelPricingResolver(nil, bs)
tokens := UsageTokens{InputTokens: 1000, OutputTokens: 500}
base := CostInput{
Ctx: context.Background(), Model: "deepseek-v4-flash", Tokens: tokens,
RateMultiplier: 1.0, Resolver: resolver,
}
// PricingAt 零值 → 回退 timezone.Now(),与显式传入当前时刻结果一致。
costZero, err := bs.CalculateCostUnified(base)
require.NoError(t, err)
costNow, err := bs.CalculateCostUnified(CostInput{
Ctx: base.Ctx, Model: base.Model, Tokens: base.Tokens,
RateMultiplier: base.RateMultiplier, Resolver: base.Resolver,
PricingAt: timezone.Now(),
})
require.NoError(t, err)
require.Equal(t, costZero.TotalCost, costNow.TotalCost)
}
// ---------------------------------------------------------------------------
// 官方价强制覆盖(远端旧价兜底)与未知 deepseek-* flash 兜底
// ---------------------------------------------------------------------------
func TestGetModelPricing_DeepseekForcesOfficialRatesOverJSON(t *testing.T) {
// JSON 给任意价(模拟远端旧价/占位价),deepseek-* 必须被强制覆盖为官方低谷价。
pricingSvc := &PricingService{pricingData: map[string]*LiteLLMModelPricing{
"deepseek-flash": {InputCostPerToken: 1e-6, OutputCostPerToken: 2e-6, CacheReadInputTokenCost: 1e-8},
"deepseek-v4-flash": {InputCostPerToken: 1e-6, OutputCostPerToken: 2e-6, CacheReadInputTokenCost: 1e-8},
"deepseek-v4-pro": {InputCostPerToken: 1e-6, OutputCostPerToken: 2e-6, CacheReadInputTokenCost: 1e-8},
"deepseek-v4-flash-vision-exp": {InputCostPerToken: 1e-6, OutputCostPerToken: 2e-6, CacheReadInputTokenCost: 1e-8},
"deepseek-chat": {InputCostPerToken: 1e-6, OutputCostPerToken: 2e-6, CacheReadInputTokenCost: 1e-8},
"deepseek-reasoner": {InputCostPerToken: 1e-6, OutputCostPerToken: 2e-6, CacheReadInputTokenCost: 1e-8},
}}
bs := NewBillingService(&config.Config{}, pricingSvc)
tests := []struct {
model string
input, output, cacheRead float64
}{
// 2026-09-10 官方降价后:deepseek-flash(V4.1-Flash 新名)与旧名
// deepseek-v4-flash 同按 Flash 新价。
{"deepseek-flash", 1.5e-7, 6e-7, 3e-9},
{"deepseek-v4-flash", 1.5e-7, 6e-7, 3e-9},
{"deepseek-v4-flash-vision-exp", 1.5e-7, 6e-7, 3e-9},
// 已停服的 chat/reasoner:即使 JSON 有旧条目也按 flash 价兜底。
{"deepseek-chat", 1.5e-7, 6e-7, 3e-9},
{"deepseek-reasoner", 1.5e-7, 6e-7, 3e-9},
}
for _, tt := range tests {
t.Run(tt.model, func(t *testing.T) {
// flash 档三档价与 pro→Flash 切换无关,GetModelPricing 断言不随时间翻转。
pricing, err := bs.GetModelPricing(tt.model)
require.NoError(t, err)
require.InDelta(t, tt.input, pricing.InputPricePerToken, 1e-15)
require.InDelta(t, tt.output, pricing.OutputPricePerToken, 1e-15)
require.InDelta(t, tt.cacheRead, pricing.CacheReadPricePerToken, 1e-15)
// 固定时点(切换点之后的 2026-10-01)复核:仍走 Flash 新价。
atPricing, err := bs.getModelPricingAt(tt.model, time.Date(2026, 10, 1, 0, 0, 0, 0, time.UTC))
require.NoError(t, err)
require.InDelta(t, tt.input, atPricing.InputPricePerToken, 1e-15)
require.InDelta(t, tt.output, atPricing.OutputPricePerToken, 1e-15)
require.InDelta(t, tt.cacheRead, atPricing.CacheReadPricePerToken, 1e-15)
require.True(t, bs.HasIdentifiedTokenPricing(tt.model))
})
}
// pro 档(含版本化名称):断言经固定时点的 getModelPricingAt,不依赖墙上时钟。
// 2026-08-01 早于切换点 2026-09-14 04:00 UTC → Pro 价。
for _, model := range []string{"deepseek-v4-pro", "deepseek-v4-pro-0813"} {
t.Run(model+"/before-cutoff", func(t *testing.T) {
pricing, err := bs.getModelPricingAt(model, time.Date(2026, 8, 1, 0, 0, 0, 0, time.UTC))
require.NoError(t, err)
require.InDelta(t, 6.6e-7, pricing.InputPricePerToken, 1e-15)
require.InDelta(t, 1.98e-6, pricing.OutputPricePerToken, 1e-15)
require.InDelta(t, 2.2e-8, pricing.CacheReadPricePerToken, 1e-15)
})
}
// 半开边界钉死:2026-09-14 03:59:59 仍 Pro 价,04:00:00 整起 Flash 新价。
proBefore, err := bs.getModelPricingAt("deepseek-v4-pro", time.Date(2026, 9, 14, 3, 59, 59, 0, time.UTC))
require.NoError(t, err)
require.InDelta(t, 6.6e-7, proBefore.InputPricePerToken, 1e-15)
require.InDelta(t, 1.98e-6, proBefore.OutputPricePerToken, 1e-15)
require.InDelta(t, 2.2e-8, proBefore.CacheReadPricePerToken, 1e-15)
proAtCutoff, err := bs.getModelPricingAt("deepseek-v4-pro", time.Date(2026, 9, 14, 4, 0, 0, 0, time.UTC))
require.NoError(t, err)
require.InDelta(t, 1.5e-7, proAtCutoff.InputPricePerToken, 1e-15)
require.InDelta(t, 6e-7, proAtCutoff.OutputPricePerToken, 1e-15)
require.InDelta(t, 3e-9, proAtCutoff.CacheReadPricePerToken, 1e-15)
// 版本化名称(不在 JSON / fallbackPrices 精确表中):按子串归档计价。
// flash-0731 归 flash 档,三档价与切换无关,GetModelPricing 断言稳定。
versioned := []struct {
model string
input, output, cacheRead float64
}{
{"deepseek-v4-flash-0731", 1.5e-7, 6e-7, 3e-9},
}
for _, tt := range versioned {
t.Run(tt.model, func(t *testing.T) {
pricing, err := bs.GetModelPricing(tt.model)
require.NoError(t, err)
require.InDelta(t, tt.input, pricing.InputPricePerToken, 1e-15)
require.InDelta(t, tt.output, pricing.OutputPricePerToken, 1e-15)
require.InDelta(t, tt.cacheRead, pricing.CacheReadPricePerToken, 1e-15)
})
}
}
func TestGetModelPricing_UnknownDeepseekMapsToFlash(t *testing.T) {
// JSON 含 $0 占位条目(如旧 deepseek-v3-2-251201):未知 deepseek-* 不再
// fail-closed,统一按 flash 价兜底(1.5e-7/6e-7/3e-9),不得按 $0 计费。
pricingSvc := &PricingService{pricingData: map[string]*LiteLLMModelPricing{
"deepseek-v3-2-251201": {InputCostPerToken: 0, OutputCostPerToken: 0},
}}
bs := NewBillingService(&config.Config{}, pricingSvc)
for _, m := range []string{"deepseek-v3-2-251201", "deepseek-chat", "deepseek-reasoner", "deepseek-foo"} {
t.Run(m, func(t *testing.T) {
pricing, err := bs.GetModelPricing(m)
require.NoError(t, err)
require.InDelta(t, 1.5e-7, pricing.InputPricePerToken, 1e-15)
require.InDelta(t, 6e-7, pricing.OutputPricePerToken, 1e-15)
require.InDelta(t, 3e-9, pricing.CacheReadPricePerToken, 1e-15)
})
}
}
// ---------------------------------------------------------------------------
// 本地兜底 JSON:无 $0 占位条目,官方模型价格为官方低谷价
// ---------------------------------------------------------------------------
func TestDeepseekPricingFileMatchesOfficialRates(t *testing.T) {
data, err := os.ReadFile(filepath.Join("..", "..", "resources", "model-pricing", "model_prices_and_context_window.json"))
require.NoError(t, err)
pricingSvc := &PricingService{}
pricingData, err := pricingSvc.parsePricingData(data)
require.NoError(t, err)
_, ok := pricingData["deepseek-v3-2-251201"]
require.False(t, ok, "deepseek-v3-2-251201($0 占位条目)必须从价格表中移除")
for _, discontinued := range []string{"deepseek-chat", "deepseek-reasoner"} {
_, ok := pricingData[discontinued]
require.False(t, ok, "%s 已停止服务,必须从价格表中移除", discontinued)
}
tests := []struct {
model string
input, output, cacheRead float64
}{
{"deepseek-flash", 1.5e-7, 6e-7, 3e-9},
{"deepseek-v4-flash", 1.5e-7, 6e-7, 3e-9},
{"deepseek-v4-flash-vision-exp", 1.5e-7, 6e-7, 3e-9},
{"deepseek-v4-pro", 6.6e-7, 1.98e-6, 2.2e-8},
}
for _, tt := range tests {
t.Run(tt.model, func(t *testing.T) {
entry, ok := pricingData[tt.model]
require.True(t, ok, "model %s must exist in pricing file", tt.model)
require.InDelta(t, tt.input, entry.InputCostPerToken, 1e-15)
require.InDelta(t, tt.output, entry.OutputCostPerToken, 1e-15)
require.InDelta(t, tt.cacheRead, entry.CacheReadInputTokenCost, 1e-15)
})
}
}
// ---------------------------------------------------------------------------
// 2026-09-10 官方降价:deepseek-flash(V4.1-Flash 新名)与旧名同价;
// 2026-09-14 04:00 UTC 起 deepseek-v4-pro 按上游路由改按 Flash 价计费
// ---------------------------------------------------------------------------
func TestCalculateCostUnified_DeepseekFlashAndLegacyFlashShareNewRates(t *testing.T) {
bs := newTestBillingService()
resolver := NewModelPricingResolver(nil, bs)
tokens := UsageTokens{InputTokens: 1000, OutputTokens: 500, CacheReadTokens: 1000}
// 2026-09-10 官方新低谷价:1000*1.5e-7 + 500*6e-7 + 1000*3e-9 = 4.53e-4
offPeakTotal := 1000*1.5e-7 + 500*6e-7 + 1000*3e-9
// deepseek-flash 与 deepseek-v4-flash 都取 Flash 新价。
// 时点取切换日 2026-09-14(周一)12:00 UTC 低谷,峰谷倍率不影响断言。
for _, model := range []string{"deepseek-flash", "deepseek-v4-flash"} {
cost, err := bs.CalculateCostUnified(CostInput{
Ctx: context.Background(), Model: model, Tokens: tokens,
RateMultiplier: 1.0, Resolver: resolver,
PricingAt: time.Date(2026, 9, 14, 12, 0, 0, 0, time.UTC),
})
require.NoError(t, err)
require.InDelta(t, offPeakTotal, cost.TotalCost, 1e-10, "model %s must use new flash rates", model)
}
}
func TestCalculateCostUnified_DeepseekProRoutesToFlashAtCutoff(t *testing.T) {
bs := newTestBillingService()
resolver := NewModelPricingResolver(nil, bs)
tokens := UsageTokens{InputTokens: 1000, OutputTokens: 500, CacheReadTokens: 1000}
proTotal := 1000*6.6e-7 + 500*1.98e-6 + 1000*2.2e-8 // 切换前 Pro 价
flashTotal := 1000*1.5e-7 + 500*6e-7 + 1000*3e-9 // 切换后 Flash 价
// 切换时点之前(2026-09-13 周日,北京周末全天低谷):仍按 Pro 价。
before, err := bs.CalculateCostUnified(CostInput{
Ctx: context.Background(), Model: "deepseek-v4-pro", Tokens: tokens,
RateMultiplier: 1.0, Resolver: resolver,
PricingAt: time.Date(2026, 9, 13, 12, 0, 0, 0, time.UTC),
})
require.NoError(t, err)
require.InDelta(t, proTotal, before.TotalCost, 1e-10,
"deepseek-v4-pro must use Pro rates before the 2026-09-14 04:00 UTC cutoff")
// 到达切换时点(2026-09-14 04:00 UTC 整,周一低谷窗口边界外):按 Flash 价。
after, err := bs.CalculateCostUnified(CostInput{
Ctx: context.Background(), Model: "deepseek-v4-pro", Tokens: tokens,
RateMultiplier: 1.0, Resolver: resolver,
PricingAt: time.Date(2026, 9, 14, 4, 0, 0, 0, time.UTC),
})
require.NoError(t, err)
require.InDelta(t, flashTotal, after.TotalCost, 1e-10,
"deepseek-v4-pro must use Flash rates at/after the 2026-09-14 04:00 UTC cutoff")
// 版本化名称同口径:切换后 deepseek-v4-pro-0813 也按 Flash 价。
versioned, err := bs.CalculateCostUnified(CostInput{
Ctx: context.Background(), Model: "deepseek-v4-pro-0813", Tokens: tokens,
RateMultiplier: 1.0, Resolver: resolver,
PricingAt: time.Date(2026, 9, 14, 4, 0, 0, 0, time.UTC),
})
require.NoError(t, err)
require.InDelta(t, flashTotal, versioned.TotalCost, 1e-10,
"versioned pro names must also route to Flash rates after the cutoff")
}