@@ -252,6 +252,86 @@ def test_ratelimit_callback_error(client, httpx2_mock, mock_auth, mock_gp_predic
252252 client .gp ()
253253
254254
255+ def test_ratelimit_rechecked_after_wait (
256+ client , httpx2_mock , mock_auth , mock_gp_predicates
257+ ):
258+ # With one request per window, every query after the model definition
259+ # request has to wait, which is only the case if requests sent after a
260+ # wait are recorded.
261+ client ._per_minute_throttle .rate = Quota (
262+ period = dt .timedelta (milliseconds = 50 ), count = 1
263+ )
264+
265+ url = api_url ("basicspacedata/query/class/gp" )
266+ httpx2_mock .add_response (method = "GET" , url = url , json = {"a" : 1 }, is_reusable = True )
267+
268+ waits = []
269+ client .callback = waits .append
270+
271+ for _ in range (3 ):
272+ assert client .gp () == {"a" : 1 }
273+
274+ assert len (waits ) >= 3
275+
276+ # Waiting on the per-minute quota must not have charged the per-hour one
277+ # more than once per request sent (the model definition plus 3 queries).
278+ hour_limit = client ._per_hour_throttle .peek (client ._per_hour_key )
279+ assert hour_limit .limit - hour_limit .remaining == 4
280+
281+
282+ def test_ratelimit_per_hour (client , httpx2_mock , mock_auth , mock_gp_predicates ):
283+ # Trip the per-hour throttle instead of the per-minute one, with a short
284+ # period so that the real wait is brief.
285+ client ._per_hour_throttle .rate = Quota (
286+ period = dt .timedelta (milliseconds = 50 ), count = 2
287+ )
288+
289+ url = api_url ("basicspacedata/query/class/gp" )
290+ httpx2_mock .add_response (method = "GET" , url = url , json = {"a" : 1 }, is_reusable = True )
291+
292+ waits = []
293+ client .callback = waits .append
294+
295+ for _ in range (3 ):
296+ assert client .gp () == {"a" : 1 }
297+
298+ assert waits
299+
300+
301+ def test_additional_rate_limit (httpx2_mock , mock_auth , mock_gp_predicates ):
302+ url = api_url ("basicspacedata/query/class/gp" )
303+ httpx2_mock .add_response (method = "GET" , url = url , json = {"a" : 1 }, is_reusable = True )
304+
305+ with SpaceTrackClient (
306+ "identity" ,
307+ "password" ,
308+ additional_rate_limit = Quota (period = dt .timedelta (milliseconds = 50 ), count = 1 ),
309+ ) as client :
310+ waits = []
311+ client .callback = waits .append
312+
313+ assert client .gp () == {"a" : 1 }
314+ assert client .gp () == {"a" : 1 }
315+
316+ assert waits
317+
318+
319+ def test_modeldef_ratelimit_error (client , httpx2_mock , mock_auth ):
320+ # Change ratelimiter period to speed up test
321+ client ._per_minute_throttle .rate = Quota (
322+ period = dt .timedelta (milliseconds = 50 ), count = 30
323+ )
324+
325+ url = api_url ("basicspacedata/modeldef/class/gp" )
326+ httpx2_mock .add_response (
327+ method = "GET" , url = url , status_code = 500 , text = "violated your query rate limit"
328+ )
329+ httpx2_mock .add_response (method = "GET" , url = url , json = {"data" : []})
330+
331+ assert client .get_predicates ("gp" ) == []
332+ assert len (httpx2_mock .get_requests (method = "GET" , url = url )) == 2
333+
334+
255335def test_predicate_parse_modeldef (client ):
256336 predicates_data = [
257337 {
0 commit comments