grok_p2_test.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. //go:build unit
  2. package service
  3. import (
  4. "context"
  5. "testing"
  6. "time"
  7. "github.com/stretchr/testify/require"
  8. )
  9. func TestGrokModelQuotaBlock_FiltersOnlyNamedModel(t *testing.T) {
  10. id := time.Now().UnixNano()%1_000_000 + 5000
  11. markGrokModelQuotaBlock(id, "grok-4.5", time.Now().Add(time.Hour))
  12. now := time.Now()
  13. require.True(t, isGrokModelQuotaBlocked(id, "grok-4.5", now))
  14. require.False(t, isGrokModelQuotaBlocked(id, "grok-4.3", now))
  15. accounts := []Account{
  16. {ID: id, Platform: PlatformGrok, Type: AccountTypeOAuth},
  17. {ID: id + 1, Platform: PlatformGrok, Type: AccountTypeOAuth},
  18. }
  19. filtered := filterGrokModelQuotaBlockedAccounts(accounts, "grok-4.5", now)
  20. require.Len(t, filtered, 1)
  21. require.Equal(t, id+1, filtered[0].ID)
  22. }
  23. func TestGrokModelQuotaBlockFiltersMappedUpstreamModel(t *testing.T) {
  24. id := time.Now().UnixNano()%1_000_000 + 7000
  25. markGrokModelQuotaBlock(id, "grok-4.5", time.Now().Add(time.Hour))
  26. account := Account{
  27. ID: id,
  28. Platform: PlatformGrok,
  29. Type: AccountTypeOAuth,
  30. Credentials: map[string]any{
  31. "model_mapping": map[string]any{"gpt-*": "grok-4.5"},
  32. },
  33. }
  34. require.Empty(t, filterGrokModelQuotaBlockedAccounts([]Account{account}, "gpt-5", time.Now()))
  35. }
  36. func TestIsGrokModelSpecificFreeUsage(t *testing.T) {
  37. require.True(t, isGrokModelSpecificFreeUsage(
  38. "you've used all the included free usage for model grok-4.5", "grok-4.5"))
  39. require.True(t, isGrokModelSpecificFreeUsage("模型额度用完 grok-4.3", "grok-4.3"))
  40. require.False(t, isGrokModelSpecificFreeUsage("free usage exhausted", "grok-4.5"))
  41. }
  42. func TestGrokStickyAffinitySeed_ScopesByModel(t *testing.T) {
  43. a := grokStickyAffinitySeed("session-1", []byte(`{"model":"grok-4.5"}`))
  44. b := grokStickyAffinitySeed("session-1", []byte(`{"model":"grok-4.3"}`))
  45. c := grokStickyAffinitySeed("session-1", []byte(`{"model":"grok-4.5"}`))
  46. require.NotEqual(t, a, b)
  47. require.Equal(t, a, c)
  48. require.Contains(t, a, "grok-affinity:v1:")
  49. }
  50. func TestExtractGrokModelIDsFromModelsBody(t *testing.T) {
  51. body := []byte(`{"object":"list","data":[{"id":"grok-4.5"},{"id":"grok-4.3"},{"id":"grok-4.5"}]}`)
  52. ids := extractGrokModelIDsFromModelsBody(body)
  53. require.Equal(t, []string{"grok-4.5", "grok-4.3"}, ids)
  54. }
  55. func TestAccountGrokNeedsReauth(t *testing.T) {
  56. require.False(t, accountGrokNeedsReauth(nil))
  57. require.True(t, accountGrokNeedsReauth(&Account{
  58. Extra: map[string]any{"grok_needs_reauth": true},
  59. }))
  60. require.True(t, accountGrokNeedsReauth(&Account{
  61. Status: StatusError,
  62. ErrorMessage: "Grok spending limit reached; reauthorize or wait for billing reset",
  63. }))
  64. }
  65. func TestApplyGrokUpstreamFailure_ModelSpecificFreeUsage(t *testing.T) {
  66. repo := &grokQuotaAccountRepo{}
  67. svc := &OpenAIGatewayService{accountRepo: repo}
  68. account := &Account{ID: 9109, Platform: PlatformGrok, Type: AccountTypeOAuth}
  69. body := []byte(`{"error":{"code":"subscription:free-usage-exhausted","message":"You've used all the included free usage for model grok-4.5. Usage resets over a rolling 24-hour window."}}`)
  70. svc.handleGrokAccountUpstreamError(context.Background(), account, 400, nil, body)
  71. require.Zero(t, repo.tempUnschedCalls, "model-scoped free usage must not cool sibling models")
  72. require.True(t, isGrokModelQuotaBlocked(account.ID, "grok-4.5", time.Now()))
  73. require.False(t, isGrokModelQuotaBlocked(account.ID, "grok-4.3", time.Now()))
  74. }
  75. func TestApplyGrokUpstreamFailure_SpendingLimitRemainsRecoverable(t *testing.T) {
  76. repo := &grokQuotaAccountRepo{}
  77. svc := &OpenAIGatewayService{accountRepo: repo}
  78. account := &Account{ID: 9110, Platform: PlatformGrok, Type: AccountTypeOAuth}
  79. body := []byte(`{"code":"personal-team-blocked:spending-limit","error":"spending limit reached"}`)
  80. svc.handleGrokAccountUpstreamError(context.Background(), account, 403, nil, body)
  81. require.Equal(t, 1, repo.rateLimitedCalls)
  82. require.Zero(t, repo.tempUnschedCalls)
  83. // Without a billing-period snapshot, use a short recoverable probe cooldown.
  84. require.WithinDuration(t, time.Now().Add(grokSpendingLimitProbeCooldown), repo.lastRateLimitResetAt, 2*time.Second)
  85. }