openai_profit_veto_budget_test.go 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
  1. package handler
  2. import (
  3. "testing"
  4. "github.com/stretchr/testify/require"
  5. )
  6. // TestRecordOpenAIProfitVetoBounded 钉死 OpenAI 侧选号循环的利润否决预算:
  7. // 账号被加入排除集,且第 maxProfitVetoAttempts 次否决返回 false 要求终止。
  8. //
  9. // 上限是必需的:acquireResponsesAccountSlot 的 WaitPlan 分支先阻塞排队
  10. // (sticky 45s / fallback 30s)拿到槽位才做利润终检,无上限重选会把单次请求
  11. // 的延迟放大到 N × WaitPlan.Timeout。
  12. func TestRecordOpenAIProfitVetoBounded(t *testing.T) {
  13. failed := make(map[int64]struct{})
  14. count := 0
  15. for i := int64(1); i < int64(maxProfitVetoAttempts); i++ {
  16. require.True(t, recordOpenAIProfitVeto(failed, i, &count), "第 %d 次否决应允许继续重选", i)
  17. require.Contains(t, failed, i, "否决的账号必须进入本请求排除集")
  18. }
  19. require.False(t,
  20. recordOpenAIProfitVeto(failed, int64(maxProfitVetoAttempts), &count),
  21. "第 %d 次否决应耗尽预算并要求终止", maxProfitVetoAttempts)
  22. require.Equal(t, maxProfitVetoAttempts, count)
  23. require.Len(t, failed, maxProfitVetoAttempts)
  24. }
  25. // TestOpenAIProfitVetoLoopTerminates 模拟「选号 → 抢槽 → 利润终检否决 → 重选」
  26. // 循环:整池越线时必须在常数步内终止,而不是把候选池逐个排队一遍。
  27. func TestOpenAIProfitVetoLoopTerminates(t *testing.T) {
  28. failed := make(map[int64]struct{})
  29. count := 0
  30. terminated := false
  31. iterations := 0
  32. // 候选池远大于否决上限:无上限时会对每个账号各排队一次。
  33. for accountID := int64(1); accountID <= 500; accountID++ {
  34. iterations++
  35. if _, excluded := failed[accountID]; excluded {
  36. continue
  37. }
  38. if !recordOpenAIProfitVeto(failed, accountID, &count) {
  39. terminated = true
  40. break
  41. }
  42. }
  43. require.True(t, terminated, "整池越线时循环必须由否决预算终止")
  44. require.Equal(t, maxProfitVetoAttempts, iterations)
  45. }
  46. // TestProfitVetoBudgetSharedWithFailoverState 钉死两条路径(FailoverState 与
  47. // OpenAI 独立计数器)使用同一上限语义,避免日后单边漂移。
  48. func TestProfitVetoBudgetSharedWithFailoverState(t *testing.T) {
  49. fs := NewFailoverState(10, false)
  50. failed := make(map[int64]struct{})
  51. count := 0
  52. var fsStoppedAt, openAIStoppedAt int
  53. for i := int64(1); i <= int64(maxProfitVetoAttempts)+5; i++ {
  54. if fsStoppedAt == 0 && fs.RecordProfitVeto(i) == FailoverExhausted {
  55. fsStoppedAt = fs.ProfitVetoCount()
  56. }
  57. if openAIStoppedAt == 0 && !recordOpenAIProfitVeto(failed, i, &count) {
  58. openAIStoppedAt = count
  59. }
  60. }
  61. require.Equal(t, maxProfitVetoAttempts, fsStoppedAt)
  62. require.Equal(t, fsStoppedAt, openAIStoppedAt, "两条路径的利润否决上限必须一致")
  63. }