Key changes: - Upgrade model mapping: Opus 4.5 → Opus 4.6-thinking with precise matching - Unified rate limiting: scope-level → model-level with Redis snapshot sync - Load-balanced scheduling by call count with smart retry mechanism - Force cache billing support - Model identity injection in prompts with leak prevention - Thinking mode auto-handling (max_tokens/budget_tokens fix) - Frontend: whitelist mode toggle, model mapping validation, status indicators - Gemini session fallback with Redis Trie O(L) matching - Ops: enhanced concurrency monitoring, account availability, retry logic - Migration scripts: 049-051 for model mapping unification
113 lines
4.0 KiB
Go
113 lines
4.0 KiB
Go
//go:build unit
|
||
|
||
// Package service 提供 API 网关核心服务。
|
||
// 本文件包含 shouldClearStickySession 函数的单元测试,
|
||
// 验证粘性会话清理逻辑在各种账号状态下的正确行为。
|
||
//
|
||
// This file contains unit tests for the shouldClearStickySession function,
|
||
// verifying correct sticky session clearing behavior under various account states.
|
||
package service
|
||
|
||
import (
|
||
"testing"
|
||
"time"
|
||
|
||
"github.com/stretchr/testify/require"
|
||
)
|
||
|
||
// TestShouldClearStickySession 测试粘性会话清理判断逻辑。
|
||
// 验证在以下情况下是否正确判断需要清理粘性会话:
|
||
// - nil 账号:不清理(返回 false)
|
||
// - 状态为错误或禁用:清理
|
||
// - 不可调度:清理
|
||
// - 临时不可调度且未过期:清理
|
||
// - 临时不可调度已过期:不清理
|
||
// - 正常可调度状态:不清理
|
||
// - 模型限流超过阈值:清理
|
||
// - 模型限流未超过阈值:不清理
|
||
//
|
||
// TestShouldClearStickySession tests the sticky session clearing logic.
|
||
// Verifies correct behavior for various account states including:
|
||
// nil account, error/disabled status, unschedulable, temporary unschedulable,
|
||
// and model rate limiting scenarios.
|
||
func TestShouldClearStickySession(t *testing.T) {
|
||
now := time.Now()
|
||
future := now.Add(1 * time.Hour)
|
||
past := now.Add(-1 * time.Hour)
|
||
|
||
// 短限流时间(低于阈值,不应清除粘性会话)
|
||
shortRateLimitReset := now.Add(5 * time.Second).Format(time.RFC3339)
|
||
// 长限流时间(超过阈值,应清除粘性会话)
|
||
longRateLimitReset := now.Add(30 * time.Second).Format(time.RFC3339)
|
||
|
||
tests := []struct {
|
||
name string
|
||
account *Account
|
||
requestedModel string
|
||
want bool
|
||
}{
|
||
{name: "nil account", account: nil, requestedModel: "", want: false},
|
||
{name: "status error", account: &Account{Status: StatusError, Schedulable: true}, requestedModel: "", want: true},
|
||
{name: "status disabled", account: &Account{Status: StatusDisabled, Schedulable: true}, requestedModel: "", want: true},
|
||
{name: "schedulable false", account: &Account{Status: StatusActive, Schedulable: false}, requestedModel: "", want: true},
|
||
{name: "temp unschedulable", account: &Account{Status: StatusActive, Schedulable: true, TempUnschedulableUntil: &future}, requestedModel: "", want: true},
|
||
{name: "temp unschedulable expired", account: &Account{Status: StatusActive, Schedulable: true, TempUnschedulableUntil: &past}, requestedModel: "", want: false},
|
||
{name: "active schedulable", account: &Account{Status: StatusActive, Schedulable: true}, requestedModel: "", want: false},
|
||
// 模型限流测试
|
||
{
|
||
name: "model rate limited short duration",
|
||
account: &Account{
|
||
Status: StatusActive,
|
||
Schedulable: true,
|
||
Extra: map[string]any{
|
||
"model_rate_limits": map[string]any{
|
||
"claude-sonnet-4": map[string]any{
|
||
"rate_limit_reset_at": shortRateLimitReset,
|
||
},
|
||
},
|
||
},
|
||
},
|
||
requestedModel: "claude-sonnet-4",
|
||
want: false, // 低于阈值,不清除
|
||
},
|
||
{
|
||
name: "model rate limited long duration",
|
||
account: &Account{
|
||
Status: StatusActive,
|
||
Schedulable: true,
|
||
Extra: map[string]any{
|
||
"model_rate_limits": map[string]any{
|
||
"claude-sonnet-4": map[string]any{
|
||
"rate_limit_reset_at": longRateLimitReset,
|
||
},
|
||
},
|
||
},
|
||
},
|
||
requestedModel: "claude-sonnet-4",
|
||
want: true, // 超过阈值,清除
|
||
},
|
||
{
|
||
name: "model rate limited different model",
|
||
account: &Account{
|
||
Status: StatusActive,
|
||
Schedulable: true,
|
||
Extra: map[string]any{
|
||
"model_rate_limits": map[string]any{
|
||
"claude-sonnet-4": map[string]any{
|
||
"rate_limit_reset_at": longRateLimitReset,
|
||
},
|
||
},
|
||
},
|
||
},
|
||
requestedModel: "claude-opus-4", // 请求不同模型
|
||
want: false, // 不同模型不受影响
|
||
},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.name, func(t *testing.T) {
|
||
require.Equal(t, tt.want, shouldClearStickySession(tt.account, tt.requestedModel))
|
||
})
|
||
}
|
||
}
|