subscription_tier_test.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. //go:build unit
  2. package xai
  3. import (
  4. "encoding/base64"
  5. "encoding/json"
  6. "testing"
  7. "time"
  8. "github.com/stretchr/testify/require"
  9. )
  10. func TestMapJWTSubscriptionTierNumber(t *testing.T) {
  11. t.Parallel()
  12. require.Equal(t, "free", MapJWTSubscriptionTier(0))
  13. require.Equal(t, "supergrok", MapJWTSubscriptionTier(1))
  14. require.Equal(t, "x_basic", MapJWTSubscriptionTier(2))
  15. require.Equal(t, "x_premium", MapJWTSubscriptionTier(3))
  16. require.Equal(t, "x_premium_plus", MapJWTSubscriptionTier(4))
  17. require.Equal(t, "supergrok_heavy", MapJWTSubscriptionTier(5))
  18. require.Equal(t, "supergrok_lite", MapJWTSubscriptionTier(6))
  19. require.Equal(t, "supergrok_plus", MapJWTSubscriptionTier(7))
  20. require.Equal(t, "9", MapJWTSubscriptionTier(9))
  21. }
  22. func TestNormalizeSubscriptionTierAliases(t *testing.T) {
  23. t.Parallel()
  24. require.Equal(t, "free", NormalizeSubscriptionTier("Free"))
  25. require.Equal(t, "free", NormalizeSubscriptionTier(" FREE "))
  26. require.Equal(t, "supergrok", NormalizeSubscriptionTier("SuperGrok"))
  27. require.Equal(t, "supergrok_heavy", NormalizeSubscriptionTier("SuperGrok Heavy"))
  28. require.Equal(t, "supergrok_pro", NormalizeSubscriptionTier("SuperGrokPro"))
  29. require.Equal(t, "supergrok_lite", NormalizeSubscriptionTier("SuperGrok Lite"))
  30. require.Equal(t, "supergrok_lite", NormalizeSubscriptionTier("SuperGrokLite"))
  31. require.Equal(t, "x_basic", NormalizeSubscriptionTier("X Basic"))
  32. require.Equal(t, "free", NormalizeSubscriptionTier("free-tier"))
  33. require.Equal(t, "free", NormalizeSubscriptionTier("free_tier"))
  34. require.Equal(t, "free", NormalizeSubscriptionTier("grok-basic"))
  35. require.Equal(t, "free", NormalizeSubscriptionTier("grok_basic"))
  36. require.Equal(t, "supergrok_lite", NormalizeSubscriptionTier("supergrok_lite"))
  37. }
  38. func TestSubscriptionTierFromJWTUsesNumericClaim(t *testing.T) {
  39. t.Parallel()
  40. require.Equal(t, "supergrok_heavy", SubscriptionTierFromJWT(jwtWithClaims(t, map[string]any{"tier": 5})))
  41. require.Equal(t, "free", SubscriptionTierFromJWT(jwtWithClaims(t, map[string]any{"tier": 0})))
  42. require.Equal(t, "supergrok_lite", SubscriptionTierFromJWT(jwtWithClaims(t, map[string]any{"tier": 6})))
  43. require.Empty(t, SubscriptionTierFromJWT(jwtWithClaims(t, map[string]any{"sub": "user"})))
  44. require.Empty(t, SubscriptionTierFromJWT("not-a-jwt"))
  45. }
  46. func TestCanonicalGrokPlanUsesOnlyGrok45ResponsesWindow(t *testing.T) {
  47. t.Parallel()
  48. zero := float64(0)
  49. heavyReq, heavyTok := int64(8300), int64(53_000_000)
  50. superReq, superTok := int64(900), int64(15_000_000)
  51. fresh := time.Now().UTC().Format(time.RFC3339)
  52. stale := time.Now().Add(-GrokQuotaSignalMaxAge - time.Hour).UTC().Format(time.RFC3339)
  53. require.Equal(t, "supergrok", CanonicalGrokPlan(&zero, "SuperGrokPro", nil))
  54. require.Equal(t, "supergrok", CanonicalGrokPlan(&zero, "SuperGrok", nil))
  55. require.Equal(t, "supergrok_heavy", CanonicalGrokPlan(&zero, "SuperGrok Heavy", nil))
  56. require.Empty(t, CanonicalGrokPlan(&zero, "", nil))
  57. require.Equal(t, "free", CanonicalGrokPlan(&zero, "free", nil))
  58. require.Equal(t, "supergrok_heavy", CanonicalGrokPlan(&zero, "SuperGrokPro", &QuotaSnapshot{
  59. Model: "grok-4.5",
  60. Requests: &QuotaWindow{Limit: &heavyReq},
  61. Tokens: &QuotaWindow{Limit: &heavyTok},
  62. LastHeadersSeenAt: fresh,
  63. }))
  64. require.Equal(t, "supergrok", CanonicalGrokPlan(&zero, "SuperGrokPro", &QuotaSnapshot{
  65. Model: "grok-4.6",
  66. Requests: &QuotaWindow{Limit: &heavyReq},
  67. Tokens: &QuotaWindow{Limit: &heavyTok},
  68. LastHeadersSeenAt: fresh,
  69. }))
  70. require.Equal(t, "supergrok", CanonicalGrokPlan(&zero, "SuperGrokPro", &QuotaSnapshot{
  71. Requests: &QuotaWindow{Limit: &heavyReq},
  72. Tokens: &QuotaWindow{Limit: &heavyTok},
  73. LastHeadersSeenAt: fresh,
  74. }))
  75. require.Equal(t, "supergrok", CanonicalGrokPlan(&zero, "SuperGrokPro", &QuotaSnapshot{
  76. Model: "grok-4.5",
  77. Requests: &QuotaWindow{Limit: &superReq},
  78. Tokens: &QuotaWindow{Limit: &superTok},
  79. LastHeadersSeenAt: fresh,
  80. }))
  81. require.Equal(t, "supergrok", CanonicalGrokPlan(&zero, "SuperGrokPro", &QuotaSnapshot{
  82. Model: "grok-4.5",
  83. Requests: &QuotaWindow{Limit: &heavyReq},
  84. LastHeadersSeenAt: stale,
  85. }))
  86. require.Equal(t, "supergrok_heavy", CanonicalGrokPlan(&zero, "SuperGrokPro", &QuotaSnapshot{
  87. Model: "grok-4.6",
  88. Requests: &QuotaWindow{Limit: &superReq},
  89. PlanFrom45Responses: "supergrok_heavy",
  90. PlanFrom45ResponsesAt: fresh,
  91. }))
  92. require.Equal(t, "free", CanonicalGrokPlan(&zero, "free", &QuotaSnapshot{
  93. Model: "grok-4.5",
  94. Requests: &QuotaWindow{Limit: &heavyReq},
  95. LastHeadersSeenAt: fresh,
  96. }))
  97. heavyCents := float64(SuperGrokHeavyLimitCents)
  98. require.Equal(t, "supergrok_heavy", CanonicalGrokPlan(&heavyCents, "SuperGrokPro", nil))
  99. }
  100. func TestApplyGrok45ResponsesPlanSignalCarriesHint(t *testing.T) {
  101. t.Parallel()
  102. heavyReq := int64(8300)
  103. fresh := time.Now().UTC().Format(time.RFC3339)
  104. prev := &QuotaSnapshot{
  105. Model: "grok-4.5",
  106. Requests: &QuotaWindow{Limit: &heavyReq},
  107. LastHeadersSeenAt: fresh,
  108. }
  109. prev.ApplyGrok45ResponsesPlanSignal(nil)
  110. require.Equal(t, "supergrok_heavy", prev.PlanFrom45Responses)
  111. next := &QuotaSnapshot{
  112. Model: "grok-4.6",
  113. Requests: &QuotaWindow{Limit: int64Ptr(100)},
  114. LastHeadersSeenAt: fresh,
  115. }
  116. next.ApplyGrok45ResponsesPlanSignal(prev)
  117. require.Equal(t, "supergrok_heavy", next.PlanFrom45Responses)
  118. require.Equal(t, prev.PlanFrom45ResponsesAt, next.PlanFrom45ResponsesAt)
  119. }
  120. func int64Ptr(v int64) *int64 { return &v }
  121. func jwtWithClaims(t *testing.T, claims map[string]any) string {
  122. t.Helper()
  123. payload, err := json.Marshal(claims)
  124. require.NoError(t, err)
  125. return "header." + base64.RawURLEncoding.EncodeToString(payload) + ".sig"
  126. }