api_key_rate_limit_test.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. package service
  2. import (
  3. "testing"
  4. "time"
  5. )
  6. func TestIsWindowExpired(t *testing.T) {
  7. now := time.Now()
  8. tests := []struct {
  9. name string
  10. start *time.Time
  11. duration time.Duration
  12. want bool
  13. }{
  14. {
  15. name: "nil window start (treated as expired)",
  16. start: nil,
  17. duration: RateLimitWindow5h,
  18. want: true,
  19. },
  20. {
  21. name: "active window (started 1h ago, 5h window)",
  22. start: rateLimitTimePtr(now.Add(-1 * time.Hour)),
  23. duration: RateLimitWindow5h,
  24. want: false,
  25. },
  26. {
  27. name: "expired window (started 6h ago, 5h window)",
  28. start: rateLimitTimePtr(now.Add(-6 * time.Hour)),
  29. duration: RateLimitWindow5h,
  30. want: true,
  31. },
  32. {
  33. name: "exactly at boundary (started 5h ago, 5h window)",
  34. start: rateLimitTimePtr(now.Add(-5 * time.Hour)),
  35. duration: RateLimitWindow5h,
  36. want: true,
  37. },
  38. {
  39. name: "active 1d window (started 12h ago)",
  40. start: rateLimitTimePtr(now.Add(-12 * time.Hour)),
  41. duration: RateLimitWindow1d,
  42. want: false,
  43. },
  44. {
  45. name: "expired 1d window (started 25h ago)",
  46. start: rateLimitTimePtr(now.Add(-25 * time.Hour)),
  47. duration: RateLimitWindow1d,
  48. want: true,
  49. },
  50. {
  51. name: "active 7d window (started 3d ago)",
  52. start: rateLimitTimePtr(now.Add(-3 * 24 * time.Hour)),
  53. duration: RateLimitWindow7d,
  54. want: false,
  55. },
  56. {
  57. name: "expired 7d window (started 8d ago)",
  58. start: rateLimitTimePtr(now.Add(-8 * 24 * time.Hour)),
  59. duration: RateLimitWindow7d,
  60. want: true,
  61. },
  62. }
  63. for _, tt := range tests {
  64. t.Run(tt.name, func(t *testing.T) {
  65. got := IsWindowExpired(tt.start, tt.duration)
  66. if got != tt.want {
  67. t.Errorf("IsWindowExpired() = %v, want %v", got, tt.want)
  68. }
  69. })
  70. }
  71. }
  72. func TestAPIKey_EffectiveUsage(t *testing.T) {
  73. now := time.Now()
  74. tests := []struct {
  75. name string
  76. key APIKey
  77. want5h float64
  78. want1d float64
  79. want7d float64
  80. }{
  81. {
  82. name: "all windows active",
  83. key: APIKey{
  84. Usage5h: 5.0,
  85. Usage1d: 10.0,
  86. Usage7d: 50.0,
  87. Window5hStart: rateLimitTimePtr(now.Add(-1 * time.Hour)),
  88. Window1dStart: rateLimitTimePtr(now.Add(-12 * time.Hour)),
  89. Window7dStart: rateLimitTimePtr(now.Add(-3 * 24 * time.Hour)),
  90. },
  91. want5h: 5.0,
  92. want1d: 10.0,
  93. want7d: 50.0,
  94. },
  95. {
  96. name: "all windows expired",
  97. key: APIKey{
  98. Usage5h: 5.0,
  99. Usage1d: 10.0,
  100. Usage7d: 50.0,
  101. Window5hStart: rateLimitTimePtr(now.Add(-6 * time.Hour)),
  102. Window1dStart: rateLimitTimePtr(now.Add(-25 * time.Hour)),
  103. Window7dStart: rateLimitTimePtr(now.Add(-8 * 24 * time.Hour)),
  104. },
  105. want5h: 0,
  106. want1d: 0,
  107. want7d: 0,
  108. },
  109. {
  110. name: "nil window starts return 0 (stale usage reset)",
  111. key: APIKey{
  112. Usage5h: 5.0,
  113. Usage1d: 10.0,
  114. Usage7d: 50.0,
  115. Window5hStart: nil,
  116. Window1dStart: nil,
  117. Window7dStart: nil,
  118. },
  119. want5h: 0,
  120. want1d: 0,
  121. want7d: 0,
  122. },
  123. {
  124. name: "mixed: 5h expired, 1d active, 7d nil",
  125. key: APIKey{
  126. Usage5h: 5.0,
  127. Usage1d: 10.0,
  128. Usage7d: 50.0,
  129. Window5hStart: rateLimitTimePtr(now.Add(-6 * time.Hour)),
  130. Window1dStart: rateLimitTimePtr(now.Add(-12 * time.Hour)),
  131. Window7dStart: nil,
  132. },
  133. want5h: 0,
  134. want1d: 10.0,
  135. want7d: 0,
  136. },
  137. {
  138. name: "zero usage with active windows",
  139. key: APIKey{
  140. Usage5h: 0,
  141. Usage1d: 0,
  142. Usage7d: 0,
  143. Window5hStart: rateLimitTimePtr(now.Add(-1 * time.Hour)),
  144. Window1dStart: rateLimitTimePtr(now.Add(-1 * time.Hour)),
  145. Window7dStart: rateLimitTimePtr(now.Add(-1 * time.Hour)),
  146. },
  147. want5h: 0,
  148. want1d: 0,
  149. want7d: 0,
  150. },
  151. }
  152. for _, tt := range tests {
  153. t.Run(tt.name, func(t *testing.T) {
  154. if got := tt.key.EffectiveUsage5h(); got != tt.want5h {
  155. t.Errorf("EffectiveUsage5h() = %v, want %v", got, tt.want5h)
  156. }
  157. if got := tt.key.EffectiveUsage1d(); got != tt.want1d {
  158. t.Errorf("EffectiveUsage1d() = %v, want %v", got, tt.want1d)
  159. }
  160. if got := tt.key.EffectiveUsage7d(); got != tt.want7d {
  161. t.Errorf("EffectiveUsage7d() = %v, want %v", got, tt.want7d)
  162. }
  163. })
  164. }
  165. }
  166. func TestAPIKeyRateLimitData_EffectiveUsage(t *testing.T) {
  167. now := time.Now()
  168. tests := []struct {
  169. name string
  170. data APIKeyRateLimitData
  171. want5h float64
  172. want1d float64
  173. want7d float64
  174. }{
  175. {
  176. name: "all windows active",
  177. data: APIKeyRateLimitData{
  178. Usage5h: 3.0,
  179. Usage1d: 8.0,
  180. Usage7d: 40.0,
  181. Window5hStart: rateLimitTimePtr(now.Add(-2 * time.Hour)),
  182. Window1dStart: rateLimitTimePtr(now.Add(-10 * time.Hour)),
  183. Window7dStart: rateLimitTimePtr(now.Add(-2 * 24 * time.Hour)),
  184. },
  185. want5h: 3.0,
  186. want1d: 8.0,
  187. want7d: 40.0,
  188. },
  189. {
  190. name: "all windows expired",
  191. data: APIKeyRateLimitData{
  192. Usage5h: 3.0,
  193. Usage1d: 8.0,
  194. Usage7d: 40.0,
  195. Window5hStart: rateLimitTimePtr(now.Add(-10 * time.Hour)),
  196. Window1dStart: rateLimitTimePtr(now.Add(-48 * time.Hour)),
  197. Window7dStart: rateLimitTimePtr(now.Add(-10 * 24 * time.Hour)),
  198. },
  199. want5h: 0,
  200. want1d: 0,
  201. want7d: 0,
  202. },
  203. {
  204. name: "nil window starts return 0 (stale usage reset)",
  205. data: APIKeyRateLimitData{
  206. Usage5h: 3.0,
  207. Usage1d: 8.0,
  208. Usage7d: 40.0,
  209. Window5hStart: nil,
  210. Window1dStart: nil,
  211. Window7dStart: nil,
  212. },
  213. want5h: 0,
  214. want1d: 0,
  215. want7d: 0,
  216. },
  217. }
  218. for _, tt := range tests {
  219. t.Run(tt.name, func(t *testing.T) {
  220. if got := tt.data.EffectiveUsage5h(); got != tt.want5h {
  221. t.Errorf("EffectiveUsage5h() = %v, want %v", got, tt.want5h)
  222. }
  223. if got := tt.data.EffectiveUsage1d(); got != tt.want1d {
  224. t.Errorf("EffectiveUsage1d() = %v, want %v", got, tt.want1d)
  225. }
  226. if got := tt.data.EffectiveUsage7d(); got != tt.want7d {
  227. t.Errorf("EffectiveUsage7d() = %v, want %v", got, tt.want7d)
  228. }
  229. })
  230. }
  231. }
  232. func rateLimitTimePtr(t time.Time) *time.Time {
  233. return &t
  234. }