@@ -7044,3 +7044,53 @@ def test_create_evaluation_set_with_agent_data(
70447044 candidate_response = candidate_responses [0 ]
70457045 assert candidate_response ["candidate" ] == "test-candidate"
70467046 assert candidate_response ["agent_data" ] == agent_data
7047+
7048+
7049+ class TestRateLimiter :
7050+ """Tests for the RateLimiter class in _evals_utils."""
7051+
7052+ def test_rate_limiter_init (self ):
7053+ """Tests that RateLimiter initializes correctly."""
7054+ limiter = _evals_utils .RateLimiter (rate = 10.0 )
7055+ assert limiter .seconds_per_event == pytest .approx (0.1 )
7056+
7057+ def test_rate_limiter_invalid_rate (self ):
7058+ """Tests that RateLimiter raises ValueError for non-positive rate."""
7059+ with pytest .raises (ValueError , match = "Rate must be a positive number" ):
7060+ _evals_utils .RateLimiter (rate = 0 )
7061+ with pytest .raises (ValueError , match = "Rate must be a positive number" ):
7062+ _evals_utils .RateLimiter (rate = - 1 )
7063+
7064+ @mock .patch ("time.sleep" , return_value = None )
7065+ @mock .patch ("time.monotonic" )
7066+ def test_rate_limiter_sleep_and_advance (self , mock_monotonic , mock_sleep ):
7067+ """Tests that sleep_and_advance properly throttles calls."""
7068+ # With rate=10 (0.1s interval):
7069+ # - __init__ at t=0: _next_allowed = 0.0
7070+ # - first call at t=0: no delay, _next_allowed = 0.1
7071+ # - second call at t=0.01: delay = 0.1 - 0.01 = 0.09
7072+ mock_monotonic .side_effect = [
7073+ 0.0 , # __init__: time.monotonic()
7074+ 0.0 , # first sleep_and_advance: now
7075+ 0.01 , # second sleep_and_advance: now
7076+ ]
7077+ limiter = _evals_utils .RateLimiter (rate = 10.0 )
7078+ limiter .sleep_and_advance () # First call - should not sleep
7079+ limiter .sleep_and_advance () # Second call - should sleep
7080+ assert mock_sleep .call_count == 1
7081+ # Verify sleep was called with approximately the right delay
7082+ sleep_delay = mock_sleep .call_args [0 ][0 ]
7083+ assert 0.08 < sleep_delay <= 0.1
7084+
7085+ def test_rate_limiter_no_sleep_when_enough_time_passed (self ):
7086+ """Tests that no sleep occurs when enough time has passed."""
7087+ import time as real_time
7088+
7089+ limiter = _evals_utils .RateLimiter (rate = 1000.0 ) # Very high rate
7090+ # With rate=1000, interval is 0.001s - should not sleep
7091+ start = real_time .time ()
7092+ for _ in range (5 ):
7093+ limiter .sleep_and_advance ()
7094+ elapsed = real_time .time () - start
7095+ # 5 calls at 1000 QPS should take ~0.005s, certainly under 1s
7096+ assert elapsed < 1.0
0 commit comments