"""Test the rate_limited decorator functionality.""" import time from unittest.mock import patch from common.utilities import rate_limited class TestRateLimited: """Test rate_limited decorator functionality.""" def test_rate_limited_decorator(self): """Test that rate limiting works correctly.""" call_times = [] @rate_limited(2.0) # 2 calls per second max def test_function(): call_times.append(time.time()) return len(call_times) # First call should execute immediately result1 = test_function() assert result1 == 1 assert len(call_times) == 1 # Second call should be delayed start_time = time.time() result2 = test_function() end_time = time.time() assert result2 == 2 assert len(call_times) == 2 # Should have waited at least close to 0.5 seconds (1/2 calls per second) elapsed = end_time - start_time assert elapsed >= 0.4 # Allow some tolerance for timing def test_rate_limited_with_args(self): """Test rate limited function with arguments.""" @rate_limited(1.0) # 1 call per second def test_function(x, y=1): return x + y # Test that arguments are passed correctly result1 = test_function(5, y=10) assert result1 == 15 # Test positional and keyword args start_time = time.time() result2 = test_function(3, 7) end_time = time.time() assert result2 == 10 # Should have waited about 1 second elapsed = end_time - start_time assert elapsed >= 0.9 def test_rate_limited_logging(self): """Test that rate limiting logs waiting messages.""" @rate_limited(10.0) # 10 calls per second def test_function(): return "called" with patch("common.utilities.log.info") as mock_log: # First call should not log test_function() mock_log.assert_not_called() # Second call should log the wait test_function() mock_log.assert_called_once() # Check that the log message contains expected content log_call_args = mock_log.call_args[0][0] assert "Rate limit exceeded" in log_call_args assert "Waiting for" in log_call_args def test_rate_limited_different_rates(self): """Test different rate limiting values.""" @rate_limited(4.0) # 4 calls per second = 0.25 second intervals def fast_function(): return time.time() @rate_limited(0.5) # 0.5 calls per second = 2 second intervals def slow_function(): return time.time() # Test fast function (should wait ~0.25 seconds) start = time.time() fast_function() fast_function() fast_elapsed = time.time() - start assert 0.2 <= fast_elapsed <= 0.4 # Test slow function (should wait ~2 seconds) start = time.time() slow_function() slow_function() slow_elapsed = time.time() - start assert slow_elapsed >= 1.9 def test_multiple_instances(self): """Test that different decorated functions maintain separate rate limits.""" @rate_limited(2.0) def function_a(): return "A" @rate_limited(2.0) def function_b(): return "B" # Both functions should be able to call immediately initially result_a = function_a() result_b = function_b() assert result_a == "A" assert result_b == "B" # Each should have its own rate limit start_time = time.time() function_a() # Should wait elapsed = time.time() - start_time assert elapsed >= 0.4 # function_b should still be able to call without waiting start_time = time.time() function_b() # Should not wait much since it has separate limit elapsed = time.time() - start_time assert elapsed >= 0.4 # It will also wait since it was called recently