@@ -1065,10 +1065,16 @@ def test_400_validation_error_no_content(self, mock_request):
10651065
10661066 @patch ("tango.client.httpx.Client.request" )
10671067 def test_429_rate_limit_error (self , mock_request ):
1068- """Test 429 Rate Limit raises TangoRateLimitError"""
1068+ """Test 429 Rate Limit raises TangoRateLimitError with parsed body """
10691069 mock_response = Mock ()
10701070 mock_response .is_success = False
10711071 mock_response .status_code = 429
1072+ mock_response .content = b'{"detail": "Rate limit exceeded for burst. Please try again in 45 seconds.", "wait_in_seconds": 45}'
1073+ mock_response .json .return_value = {
1074+ "detail" : "Rate limit exceeded for burst. Please try again in 45 seconds." ,
1075+ "wait_in_seconds" : 45 ,
1076+ }
1077+ mock_response .headers = {}
10721078 mock_request .return_value = mock_response
10731079
10741080 client = TangoClient (api_key = "test-key" )
@@ -1077,6 +1083,85 @@ def test_429_rate_limit_error(self, mock_request):
10771083 client .list_agencies ()
10781084
10791085 assert exc_info .value .status_code == 429
1086+ assert exc_info .value .wait_in_seconds == 45
1087+ assert "burst" in exc_info .value .detail
1088+ assert exc_info .value .limit_type == "burst"
1089+
1090+ @patch ("tango.client.httpx.Client.request" )
1091+ def test_429_daily_limit_error (self , mock_request ):
1092+ """Test 429 for daily limit includes correct limit_type"""
1093+ mock_response = Mock ()
1094+ mock_response .is_success = False
1095+ mock_response .status_code = 429
1096+ mock_response .content = b'{"detail": "Rate limit exceeded for daily. Please try again in 3600 seconds.", "wait_in_seconds": 3600}'
1097+ mock_response .json .return_value = {
1098+ "detail" : "Rate limit exceeded for daily. Please try again in 3600 seconds." ,
1099+ "wait_in_seconds" : 3600 ,
1100+ }
1101+ mock_response .headers = {}
1102+ mock_request .return_value = mock_response
1103+
1104+ client = TangoClient (api_key = "test-key" )
1105+
1106+ with pytest .raises (TangoRateLimitError ) as exc_info :
1107+ client .list_agencies ()
1108+
1109+ assert exc_info .value .limit_type == "daily"
1110+ assert exc_info .value .wait_in_seconds == 3600
1111+
1112+ @patch ("tango.client.httpx.Client.request" )
1113+ def test_429_empty_body (self , mock_request ):
1114+ """Test 429 with no content body still works"""
1115+ mock_response = Mock ()
1116+ mock_response .is_success = False
1117+ mock_response .status_code = 429
1118+ mock_response .content = None
1119+ mock_response .headers = {}
1120+ mock_request .return_value = mock_response
1121+
1122+ client = TangoClient (api_key = "test-key" )
1123+
1124+ with pytest .raises (TangoRateLimitError ) as exc_info :
1125+ client .list_agencies ()
1126+
1127+ assert exc_info .value .status_code == 429
1128+ assert exc_info .value .wait_in_seconds is None
1129+ assert exc_info .value .limit_type is None
1130+
1131+ @patch ("tango.client.httpx.Client.request" )
1132+ def test_rate_limit_headers_parsed (self , mock_request ):
1133+ """Test rate limit headers are parsed from successful responses"""
1134+ mock_response = Mock ()
1135+ mock_response .is_success = True
1136+ mock_response .status_code = 200
1137+ mock_response .content = b'{"results": []}'
1138+ mock_response .json .return_value = {"results" : []}
1139+ mock_response .headers = {
1140+ "X-RateLimit-Limit" : "100" ,
1141+ "X-RateLimit-Remaining" : "95" ,
1142+ "X-RateLimit-Reset" : "45" ,
1143+ "X-RateLimit-Daily-Limit" : "2400" ,
1144+ "X-RateLimit-Daily-Remaining" : "2350" ,
1145+ "X-RateLimit-Daily-Reset" : "86400" ,
1146+ "X-RateLimit-Burst-Limit" : "100" ,
1147+ "X-RateLimit-Burst-Remaining" : "95" ,
1148+ "X-RateLimit-Burst-Reset" : "45" ,
1149+ }
1150+ mock_request .return_value = mock_response
1151+
1152+ client = TangoClient (api_key = "test-key" )
1153+ assert client .rate_limit_info is None
1154+
1155+ client ._request ("GET" , "/api/agencies/" )
1156+
1157+ info = client .rate_limit_info
1158+ assert info is not None
1159+ assert info .limit == 100
1160+ assert info .remaining == 95
1161+ assert info .reset == 45
1162+ assert info .daily_limit == 2400
1163+ assert info .daily_remaining == 2350
1164+ assert info .burst_remaining == 95
10801165
10811166 @patch ("tango.client.httpx.Client.request" )
10821167 def test_500_server_error (self , mock_request ):
0 commit comments