| package sqlite_test |
|
|
| import ( |
| "context" |
| "testing" |
| "time" |
|
|
| "ccLoad/internal/model" |
| "ccLoad/internal/storage" |
| "ccLoad/internal/util" |
| ) |
|
|
| |
| |
| func TestCooldownConsistency_401Error(t *testing.T) { |
| tmpDB := t.TempDir() + "/test-cooldown-consistency.db" |
| store, err := storage.CreateSQLiteStore(tmpDB) |
| if err != nil { |
| t.Fatalf("创建测试数据库失败: %v", err) |
| } |
| defer func() { _ = store.Close() }() |
|
|
| ctx := context.Background() |
| now := time.Now() |
|
|
| |
| t.Run("初始401错误冷却时间一致性", func(t *testing.T) { |
| |
| channelCfg := &model.Config{ |
| Name: "channel-level-test", |
| URL: "https://api.example.com", |
| Enabled: true, |
| } |
| channelCreated, err := store.CreateConfig(ctx, channelCfg) |
| if err != nil { |
| t.Fatalf("创建渠道测试配置失败: %v", err) |
| } |
|
|
| keyCfg := &model.Config{ |
| Name: "key-level-test", |
| URL: "https://api.example.com", |
| Enabled: true, |
| } |
| keyCreated, err := store.CreateConfig(ctx, keyCfg) |
| if err != nil { |
| t.Fatalf("创建Key测试配置失败: %v", err) |
| } |
|
|
| |
| _ = store.CreateAPIKeysBatch(ctx, []*model.APIKey{ |
| {ChannelID: keyCreated.ID, KeyIndex: 0, APIKey: "sk-key1", KeyStrategy: model.KeyStrategySequential}, |
| {ChannelID: keyCreated.ID, KeyIndex: 1, APIKey: "sk-key2", KeyStrategy: model.KeyStrategySequential}, |
| }) |
|
|
| |
| channelDuration, err := store.BumpChannelCooldown(ctx, channelCreated.ID, now, 401) |
| if err != nil { |
| t.Fatalf("渠道级BumpCooldownOnError失败: %v", err) |
| } |
|
|
| |
| keyDuration, err := store.BumpKeyCooldown(ctx, keyCreated.ID, 0, now, 401) |
| if err != nil { |
| t.Fatalf("Key级BumpKeyCooldownOnError失败: %v", err) |
| } |
|
|
| |
| if channelDuration != keyDuration { |
| t.Errorf("❌ 401错误冷却时间不一致:\n 渠道级: %v\n Key级: %v", |
| channelDuration, keyDuration) |
| } |
|
|
| |
| expectedDuration := util.AuthErrorInitialCooldown |
| tolerance := 10 * time.Millisecond |
|
|
| if abs(channelDuration-expectedDuration) > tolerance { |
| t.Errorf("渠道级冷却时间错误: 期望%v,实际%v", expectedDuration, channelDuration) |
| } |
|
|
| if abs(keyDuration-expectedDuration) > tolerance { |
| t.Errorf("Key级冷却时间错误: 期望%v,实际%v", expectedDuration, keyDuration) |
| } |
| }) |
|
|
| |
| t.Run("401错误指数退避序列一致性", func(t *testing.T) { |
| |
| channelCfg := &model.Config{ |
| Name: "channel-backoff-test", |
| URL: "https://api.example.com", |
| Enabled: true, |
| } |
| channelCreated, err := store.CreateConfig(ctx, channelCfg) |
| if err != nil { |
| t.Fatalf("创建渠道测试配置失败: %v", err) |
| } |
|
|
| keyCfg := &model.Config{ |
| Name: "key-backoff-test", |
| URL: "https://api.example.com", |
| Enabled: true, |
| } |
| keyCreated, err := store.CreateConfig(ctx, keyCfg) |
| if err != nil { |
| t.Fatalf("创建Key测试配置失败: %v", err) |
| } |
|
|
| |
| _ = store.CreateAPIKeysBatch(ctx, []*model.APIKey{ |
| {ChannelID: keyCreated.ID, KeyIndex: 0, APIKey: "sk-key1", KeyStrategy: model.KeyStrategySequential}, |
| {ChannelID: keyCreated.ID, KeyIndex: 1, APIKey: "sk-key2", KeyStrategy: model.KeyStrategySequential}, |
| }) |
|
|
| |
| expectedSequence := []time.Duration{ |
| 5 * time.Minute, |
| 10 * time.Minute, |
| 20 * time.Minute, |
| 30 * time.Minute, |
| } |
|
|
| currentTime := now |
| for i, expected := range expectedSequence { |
| |
| channelDuration, err := store.BumpChannelCooldown(ctx, channelCreated.ID, currentTime, 401) |
| if err != nil { |
| t.Fatalf("第%d次渠道级错误失败: %v", i+1, err) |
| } |
|
|
| |
| keyDuration, err := store.BumpKeyCooldown(ctx, keyCreated.ID, 0, currentTime, 401) |
| if err != nil { |
| t.Fatalf("第%d次Key级错误失败: %v", i+1, err) |
| } |
|
|
| |
| if channelDuration != keyDuration { |
| t.Errorf("❌ 第%d次错误冷却时间不一致:\n 渠道级: %v\n Key级: %v", |
| i+1, channelDuration, keyDuration) |
| } |
|
|
| |
| tolerance := 100 * time.Millisecond |
| if abs(channelDuration-expected) > tolerance { |
| t.Errorf("第%d次错误冷却时间错误: 期望%v,渠道级%v,Key级%v", |
| i+1, expected, channelDuration, keyDuration) |
| } |
|
|
| |
| currentTime = currentTime.Add(expected + 1*time.Second) |
| } |
| }) |
|
|
| |
| t.Run("403错误冷却时间一致性", func(t *testing.T) { |
| channelCfg := &model.Config{ |
| Name: "channel-403-test", |
| URL: "https://api.example.com", |
| Enabled: true, |
| } |
| channelCreated, err := store.CreateConfig(ctx, channelCfg) |
| if err != nil { |
| t.Fatalf("创建渠道测试配置失败: %v", err) |
| } |
|
|
| keyCfg := &model.Config{ |
| Name: "key-403-test", |
| URL: "https://api.example.com", |
| Enabled: true, |
| } |
| keyCreated, err := store.CreateConfig(ctx, keyCfg) |
| if err != nil { |
| t.Fatalf("创建Key测试配置失败: %v", err) |
| } |
|
|
| |
| _ = store.CreateAPIKeysBatch(ctx, []*model.APIKey{ |
| {ChannelID: keyCreated.ID, KeyIndex: 0, APIKey: "sk-key1", KeyStrategy: model.KeyStrategySequential}, |
| {ChannelID: keyCreated.ID, KeyIndex: 1, APIKey: "sk-key2", KeyStrategy: model.KeyStrategySequential}, |
| }) |
|
|
| |
| channelDuration, _ := store.BumpChannelCooldown(ctx, channelCreated.ID, now, 403) |
| keyDuration, _ := store.BumpKeyCooldown(ctx, keyCreated.ID, 0, now, 403) |
|
|
| if channelDuration != keyDuration { |
| t.Errorf("❌ 403错误冷却时间不一致: 渠道级=%v, Key级=%v", |
| channelDuration, keyDuration) |
| } |
|
|
| if channelDuration != util.AuthErrorInitialCooldown { |
| t.Errorf("403错误初始冷却时间错误: 期望%v,实际%v", |
| util.AuthErrorInitialCooldown, channelDuration) |
| } |
| }) |
|
|
| |
| t.Run("其他错误码冷却时间一致性", func(t *testing.T) { |
| testCases := []struct { |
| name string |
| statusCode int |
| expected time.Duration |
| }{ |
| {"429限流错误", 429, util.RateLimitErrorCooldown}, |
| {"500服务器错误", 500, util.ServerErrorInitialCooldown}, |
| {"502网关错误", 502, util.ServerErrorInitialCooldown}, |
| } |
|
|
| for _, tc := range testCases { |
| t.Run(tc.name, func(t *testing.T) { |
| channelCfg := &model.Config{ |
| Name: "channel-" + tc.name, |
| URL: "https://api.example.com", |
| Enabled: true, |
| } |
| channelCreated, _ := store.CreateConfig(ctx, channelCfg) |
|
|
| keyCfg := &model.Config{ |
| Name: "key-" + tc.name, |
| URL: "https://api.example.com", |
| Enabled: true, |
| } |
| keyCreated, _ := store.CreateConfig(ctx, keyCfg) |
|
|
| |
| _ = store.CreateAPIKeysBatch(ctx, []*model.APIKey{ |
| {ChannelID: keyCreated.ID, KeyIndex: 0, APIKey: "sk-key1", KeyStrategy: model.KeyStrategySequential}, |
| {ChannelID: keyCreated.ID, KeyIndex: 1, APIKey: "sk-key2", KeyStrategy: model.KeyStrategySequential}, |
| }) |
|
|
| channelDuration, _ := store.BumpChannelCooldown(ctx, channelCreated.ID, now, tc.statusCode) |
| keyDuration, _ := store.BumpKeyCooldown(ctx, keyCreated.ID, 0, now, tc.statusCode) |
|
|
| if channelDuration != keyDuration { |
| t.Errorf("❌ %s冷却时间不一致: 渠道级=%v, Key级=%v", |
| tc.name, channelDuration, keyDuration) |
| } |
|
|
| if channelDuration != tc.expected { |
| t.Errorf("%s初始冷却时间错误: 期望%v,实际%v", |
| tc.name, tc.expected, channelDuration) |
| } |
| }) |
| } |
| }) |
| } |
|
|
| |
| func abs(d time.Duration) time.Duration { |
| if d < 0 { |
| return -d |
| } |
| return d |
| } |
|
|