fix: resolve 5 audit findings in channel/credits/scheduling
P0-1: Credits degraded response retry + fail-open - Add isAntigravityDegradedResponse() to detect transient API failures - Retry up to 3 times with exponential backoff (500ms/1s/2s) - Invalidate singleflight cache between retries - Fail-open after exhausting retries instead of 5h circuit break P1-1: Fix channel restriction pre-check timing conflict - Swap checkClaudeCodeRestriction before checkChannelPricingRestriction - Ensures channel restriction is checked against final fallback groupID P1-2: Add interval pricing validation (frontend + backend) - Backend: ValidateIntervals() with boundary, price, overlap checks - Frontend: validateIntervals() with Chinese error messages - Rules: MinTokens>=0, MaxTokens>MinTokens, prices>=0, no overlap P2: Fix cross-platform same-model pricing/mapping override - Store cache keys using original platform instead of group platform - Lookup across matching platforms (antigravity→anthropic→gemini) - Prevents anthropic/gemini same-name models from overwriting each other
This commit is contained in:
@@ -307,3 +307,129 @@ func TestChannelClone_EdgeCases(t *testing.T) {
|
||||
require.Equal(t, "gpt-4-turbo", original.ModelMapping["openai"]["gpt-4"])
|
||||
})
|
||||
}
|
||||
|
||||
// --- ValidateIntervals ---
|
||||
|
||||
func TestValidateIntervals_Empty(t *testing.T) {
|
||||
require.NoError(t, ValidateIntervals(nil))
|
||||
require.NoError(t, ValidateIntervals([]PricingInterval{}))
|
||||
}
|
||||
|
||||
func TestValidateIntervals_ValidIntervals(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
intervals []PricingInterval
|
||||
}{
|
||||
{
|
||||
name: "single bounded interval",
|
||||
intervals: []PricingInterval{
|
||||
{MinTokens: 0, MaxTokens: testPtrInt(128000), InputPrice: testPtrFloat64(1e-6)},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "two intervals with gap",
|
||||
intervals: []PricingInterval{
|
||||
{MinTokens: 0, MaxTokens: testPtrInt(100000), InputPrice: testPtrFloat64(1e-6)},
|
||||
{MinTokens: 128000, MaxTokens: nil, InputPrice: testPtrFloat64(2e-6)},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "two contiguous intervals",
|
||||
intervals: []PricingInterval{
|
||||
{MinTokens: 0, MaxTokens: testPtrInt(128000), InputPrice: testPtrFloat64(1e-6)},
|
||||
{MinTokens: 128000, MaxTokens: nil, InputPrice: testPtrFloat64(2e-6)},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "unsorted input (auto-sorted by validator)",
|
||||
intervals: []PricingInterval{
|
||||
{MinTokens: 128000, MaxTokens: nil, InputPrice: testPtrFloat64(2e-6)},
|
||||
{MinTokens: 0, MaxTokens: testPtrInt(128000), InputPrice: testPtrFloat64(1e-6)},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "single unbounded interval",
|
||||
intervals: []PricingInterval{
|
||||
{MinTokens: 0, MaxTokens: nil, InputPrice: testPtrFloat64(1e-6)},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
require.NoError(t, ValidateIntervals(tt.intervals))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateIntervals_NegativeMinTokens(t *testing.T) {
|
||||
intervals := []PricingInterval{
|
||||
{MinTokens: -1, MaxTokens: testPtrInt(100), InputPrice: testPtrFloat64(1e-6)},
|
||||
}
|
||||
err := ValidateIntervals(intervals)
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "min_tokens")
|
||||
require.Contains(t, err.Error(), ">= 0")
|
||||
}
|
||||
|
||||
func TestValidateIntervals_MaxTokensZero(t *testing.T) {
|
||||
intervals := []PricingInterval{
|
||||
{MinTokens: 0, MaxTokens: testPtrInt(0), InputPrice: testPtrFloat64(1e-6)},
|
||||
}
|
||||
err := ValidateIntervals(intervals)
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "max_tokens")
|
||||
require.Contains(t, err.Error(), "> 0")
|
||||
}
|
||||
|
||||
func TestValidateIntervals_MaxLessThanMin(t *testing.T) {
|
||||
intervals := []PricingInterval{
|
||||
{MinTokens: 100, MaxTokens: testPtrInt(50), InputPrice: testPtrFloat64(1e-6)},
|
||||
}
|
||||
err := ValidateIntervals(intervals)
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "max_tokens")
|
||||
require.Contains(t, err.Error(), "> min_tokens")
|
||||
}
|
||||
|
||||
func TestValidateIntervals_MaxEqualsMin(t *testing.T) {
|
||||
intervals := []PricingInterval{
|
||||
{MinTokens: 100, MaxTokens: testPtrInt(100), InputPrice: testPtrFloat64(1e-6)},
|
||||
}
|
||||
err := ValidateIntervals(intervals)
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "max_tokens")
|
||||
require.Contains(t, err.Error(), "> min_tokens")
|
||||
}
|
||||
|
||||
func TestValidateIntervals_NegativePrice(t *testing.T) {
|
||||
negPrice := -0.01
|
||||
intervals := []PricingInterval{
|
||||
{MinTokens: 0, MaxTokens: testPtrInt(100), InputPrice: &negPrice},
|
||||
}
|
||||
err := ValidateIntervals(intervals)
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "input_price")
|
||||
require.Contains(t, err.Error(), ">= 0")
|
||||
}
|
||||
|
||||
func TestValidateIntervals_OverlappingIntervals(t *testing.T) {
|
||||
intervals := []PricingInterval{
|
||||
{MinTokens: 0, MaxTokens: testPtrInt(200), InputPrice: testPtrFloat64(1e-6)},
|
||||
{MinTokens: 100, MaxTokens: testPtrInt(300), InputPrice: testPtrFloat64(2e-6)},
|
||||
}
|
||||
err := ValidateIntervals(intervals)
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "overlap")
|
||||
}
|
||||
|
||||
func TestValidateIntervals_UnboundedNotLast(t *testing.T) {
|
||||
intervals := []PricingInterval{
|
||||
{MinTokens: 0, MaxTokens: nil, InputPrice: testPtrFloat64(1e-6)},
|
||||
{MinTokens: 128000, MaxTokens: testPtrInt(256000), InputPrice: testPtrFloat64(2e-6)},
|
||||
}
|
||||
err := ValidateIntervals(intervals)
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "unbounded")
|
||||
require.Contains(t, err.Error(), "last")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user