test_rate_limiter.py 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. import time
  2. import unittest
  3. from utils.rate_limiter import SimpleRateLimiter
  4. class TestSimpleRateLimiter(unittest.TestCase):
  5. def test_allows_requests_within_limit(self):
  6. limiter = SimpleRateLimiter(max_requests=5, window_seconds=10)
  7. key = 'test_key'
  8. for i in range(5):
  9. self.assertTrue(limiter.is_allowed(key), f"Request {i+1} should be allowed")
  10. def test_blocks_requests_exceeding_limit(self):
  11. limiter = SimpleRateLimiter(max_requests=3, window_seconds=10)
  12. key = 'test_key'
  13. # First 3 requests should pass
  14. for i in range(3):
  15. self.assertTrue(limiter.is_allowed(key))
  16. # 4th request should be blocked
  17. self.assertFalse(limiter.is_allowed(key))
  18. def test_different_keys_are_independent(self):
  19. limiter = SimpleRateLimiter(max_requests=2, window_seconds=10)
  20. self.assertTrue(limiter.is_allowed('key_a'))
  21. self.assertTrue(limiter.is_allowed('key_a'))
  22. self.assertFalse(limiter.is_allowed('key_a'))
  23. # key_b should still be allowed
  24. self.assertTrue(limiter.is_allowed('key_b'))
  25. self.assertTrue(limiter.is_allowed('key_b'))
  26. self.assertFalse(limiter.is_allowed('key_b'))
  27. def test_window_sliding(self):
  28. limiter = SimpleRateLimiter(max_requests=2, window_seconds=1)
  29. key = 'test_key'
  30. # Use up the limit
  31. self.assertTrue(limiter.is_allowed(key))
  32. self.assertTrue(limiter.is_allowed(key))
  33. self.assertFalse(limiter.is_allowed(key))
  34. # Wait for window to expire
  35. time.sleep(1.1)
  36. # Should be allowed again
  37. self.assertTrue(limiter.is_allowed(key))
  38. def test_cleanup_removes_old_entries(self):
  39. limiter = SimpleRateLimiter(max_requests=5, window_seconds=10)
  40. limiter.is_allowed('key_1')
  41. limiter.is_allowed('key_2')
  42. limiter.is_allowed('key_3')
  43. self.assertEqual(len(limiter._requests), 3)
  44. # Cleanup with very short max_age should remove nothing (requests are recent)
  45. limiter.cleanup(max_age_seconds=10)
  46. self.assertEqual(len(limiter._requests), 3)
  47. # Wait and cleanup
  48. time.sleep(0.1)
  49. limiter.cleanup(max_age_seconds=0.05)
  50. self.assertEqual(len(limiter._requests), 0)
  51. def test_in_flight_slot_is_reusable_after_release(self):
  52. limiter = SimpleRateLimiter(
  53. max_requests=0,
  54. window_seconds=60,
  55. max_in_flight=2,
  56. )
  57. key = 'GWS_A:query_order_detail'
  58. self.assertTrue(limiter.try_acquire(key))
  59. self.assertTrue(limiter.try_acquire(key))
  60. self.assertFalse(limiter.try_acquire(key))
  61. limiter.release(key)
  62. self.assertTrue(limiter.try_acquire(key))
  63. limiter.release(key)
  64. limiter.release(key)
  65. self.assertNotIn(key, limiter._in_flight)
  66. def test_zero_window_limit_disables_request_count_limit(self):
  67. limiter = SimpleRateLimiter(
  68. max_requests=0,
  69. window_seconds=60,
  70. max_in_flight=1,
  71. )
  72. for _ in range(100):
  73. self.assertTrue(limiter.is_allowed('GWS_A:query_order_detail'))
  74. def test_zero_in_flight_limit_is_unbounded_and_unknown_release_is_safe(self):
  75. limiter = SimpleRateLimiter(
  76. max_requests=0,
  77. window_seconds=60,
  78. max_in_flight=0,
  79. )
  80. self.assertTrue(limiter.try_acquire('GWS_A:query_order_detail'))
  81. limiter.release('GWS_A:query_order_detail')
  82. limiter.release('unknown')
  83. if __name__ == '__main__':
  84. unittest.main()