2020
2121from tests .utils .object_assertions import assert_match_object , assert_object_lists_match
2222from typesense import exceptions
23- from typesense .api_call import ApiCall
23+ from typesense .api_call import ApiCall , RequestHandler
2424from typesense .configuration import Configuration , Node
2525from typesense .logger import logger
2626
@@ -76,8 +76,8 @@ def test_initialization(
7676) -> None :
7777 """Test the initialization of the ApiCall object."""
7878 assert api_call .config == config
79- assert_object_lists_match (api_call .nodes , config .nodes )
80- assert api_call .node_index == 0
79+ assert_object_lists_match (api_call .node_manager . nodes , config .nodes )
80+ assert api_call .node_manager . node_index == 0
8181
8282
8383def test_node_due_for_health_check (
@@ -86,14 +86,14 @@ def test_node_due_for_health_check(
8686 """Test that it correctly identifies if a node is due for health check."""
8787 node = Node (host = "localhost" , port = 8108 , protocol = "http" , path = " " )
8888 node .last_access_ts = time .time () - 61
89- assert api_call .node_due_for_health_check (node ) is True
89+ assert api_call .node_manager . _is_due_for_health_check (node ) is True
9090
9191
9292def test_get_node_nearest_healthy (
9393 api_call : ApiCall ,
9494) -> None :
9595 """Test that it correctly selects the nearest node if it is healthy."""
96- node = api_call .get_node ()
96+ node = api_call .node_manager . get_node ()
9797 assert_match_object (node , api_call .config .nearest_node )
9898
9999
@@ -102,8 +102,8 @@ def test_get_node_nearest_not_healthy(
102102) -> None :
103103 """Test that it selects the next available node if the nearest node is not healthy."""
104104 api_call .config .nearest_node .healthy = False
105- node = api_call .get_node ()
106- assert_match_object (node , api_call .nodes [0 ])
105+ node = api_call .node_manager . get_node ()
106+ assert_match_object (node , api_call .node_manager . nodes [0 ])
107107
108108
109109def test_get_node_round_robin_selection (
@@ -114,34 +114,34 @@ def test_get_node_round_robin_selection(
114114 api_call .config .nearest_node = None
115115 mocker .patch ("time.time" , return_value = 100 )
116116
117- node1 = api_call .get_node ()
117+ node1 = api_call .node_manager . get_node ()
118118 assert_match_object (node1 , api_call .config .nodes [0 ])
119119
120- node2 = api_call .get_node ()
120+ node2 = api_call .node_manager . get_node ()
121121 assert_match_object (node2 , api_call .config .nodes [1 ])
122122
123- node3 = api_call .get_node ()
123+ node3 = api_call .node_manager . get_node ()
124124 assert_match_object (node3 , api_call .config .nodes [2 ])
125125
126126
127127def test_get_exception () -> None :
128128 """Test that it correctly returns the exception class for a given status code."""
129- assert ApiCall . get_exception (0 ) == exceptions .HTTPStatus0Error
130- assert ApiCall . get_exception (400 ) == exceptions .RequestMalformed
131- assert ApiCall . get_exception (401 ) == exceptions .RequestUnauthorized
132- assert ApiCall . get_exception (403 ) == exceptions .RequestForbidden
133- assert ApiCall . get_exception (404 ) == exceptions .ObjectNotFound
134- assert ApiCall . get_exception (409 ) == exceptions .ObjectAlreadyExists
135- assert ApiCall . get_exception (422 ) == exceptions .ObjectUnprocessable
136- assert ApiCall . get_exception (500 ) == exceptions .ServerError
137- assert ApiCall . get_exception (503 ) == exceptions .ServiceUnavailable
138- assert ApiCall . get_exception (999 ) == exceptions .TypesenseClientError
129+ assert RequestHandler . _get_exception (0 ) == exceptions .HTTPStatus0Error
130+ assert RequestHandler . _get_exception (400 ) == exceptions .RequestMalformed
131+ assert RequestHandler . _get_exception (401 ) == exceptions .RequestUnauthorized
132+ assert RequestHandler . _get_exception (403 ) == exceptions .RequestForbidden
133+ assert RequestHandler . _get_exception (404 ) == exceptions .ObjectNotFound
134+ assert RequestHandler . _get_exception (409 ) == exceptions .ObjectAlreadyExists
135+ assert RequestHandler . _get_exception (422 ) == exceptions .ObjectUnprocessable
136+ assert RequestHandler . _get_exception (500 ) == exceptions .ServerError
137+ assert RequestHandler . _get_exception (503 ) == exceptions .ServiceUnavailable
138+ assert RequestHandler . _get_exception (999 ) == exceptions .TypesenseClientError
139139
140140
141141def test_normalize_params_with_booleans () -> None :
142142 """Test that it correctly normalizes boolean values to strings."""
143143 parameter_dict : typing .Dict [str , str | bool ] = {"key1" : True , "key2" : False }
144- ApiCall .normalize_params (parameter_dict )
144+ RequestHandler .normalize_params (parameter_dict )
145145
146146 assert parameter_dict == {"key1" : "true" , "key2" : "false" }
147147
@@ -151,13 +151,13 @@ def test_normalize_params_with_non_dict() -> None:
151151 parameter_non_dict = "string"
152152
153153 with pytest .raises (ValueError ):
154- ApiCall .normalize_params (parameter_non_dict )
154+ RequestHandler .normalize_params (parameter_non_dict )
155155
156156
157157def test_normalize_params_with_mixed_types () -> None :
158158 """Test that it correctly normalizes boolean values to strings."""
159159 parameter_dict = {"key1" : True , "key2" : False , "key3" : "value" , "key4" : 123 }
160- ApiCall .normalize_params (parameter_dict )
160+ RequestHandler .normalize_params (parameter_dict )
161161 assert parameter_dict == {
162162 "key1" : "true" ,
163163 "key2" : "false" ,
@@ -169,14 +169,14 @@ def test_normalize_params_with_mixed_types() -> None:
169169def test_normalize_params_with_empty_dict () -> None :
170170 """Test that it correctly normalizes an empty dictionary."""
171171 parameter_dict : typing .Dict [str , str ] = {}
172- ApiCall .normalize_params (parameter_dict )
172+ RequestHandler .normalize_params (parameter_dict )
173173 assert not parameter_dict
174174
175175
176176def test_normalize_params_with_no_booleans () -> None :
177177 """Test that it correctly normalizes a dictionary with no boolean values."""
178178 parameter_dict = {"key1" : "value" , "key2" : 123 }
179- ApiCall .normalize_params (parameter_dict )
179+ RequestHandler .normalize_params (parameter_dict )
180180 assert parameter_dict == {"key1" : "value" , "key2" : 123 }
181181
182182
@@ -191,7 +191,7 @@ def test_make_request_as_json(api_call: ApiCall) -> None:
191191 status_code = 200 ,
192192 )
193193
194- response = api_call .make_request (
194+ response = api_call ._execute_request (
195195 session .get ,
196196 "/test" ,
197197 as_json = True ,
@@ -211,7 +211,7 @@ def test_make_request_as_text(api_call: ApiCall) -> None:
211211 status_code = 200 ,
212212 )
213213
214- response = api_call .make_request (
214+ response = api_call ._execute_request (
215215 session .get ,
216216 "/test" ,
217217 as_json = False ,
@@ -387,7 +387,7 @@ def test_raise_custom_exception_with_header(
387387 )
388388
389389 with pytest .raises (exceptions .RequestMalformed ) as exception :
390- api_call .make_request (
390+ api_call ._execute_request (
391391 requests .get ,
392392 "/test" ,
393393 as_json = True ,
@@ -408,7 +408,7 @@ def test_raise_custom_exception_without_header(
408408 )
409409
410410 with pytest .raises (exceptions .RequestMalformed ) as exception :
411- api_call .make_request (
411+ api_call ._execute_request (
412412 requests .get ,
413413 "/test" ,
414414 as_json = True ,
@@ -456,24 +456,30 @@ def test_get_node_no_healthy_nodes(
456456 caplog : pytest .LogCaptureFixture ,
457457) -> None :
458458 """Test that it logs a message if no healthy nodes are found."""
459- for api_node in api_call .nodes :
459+ for api_node in api_call .node_manager . nodes :
460460 api_node .healthy = False
461461
462462 api_call .config .nearest_node .healthy = False
463463
464- mocker .patch .object (api_call , "node_due_for_health_check" , return_value = False )
464+ mocker .patch .object (
465+ api_call .node_manager ,
466+ "_is_due_for_health_check" ,
467+ return_value = False ,
468+ )
465469
466470 # Need to set the logger level to DEBUG to capture the message
467471 logger .setLevel (logging .DEBUG )
468472
469- selected_node = api_call .get_node ()
473+ selected_node = api_call .node_manager . get_node ()
470474
471475 with caplog .at_level (logging .DEBUG ):
472476 assert "No healthy nodes were found. Returning the next node." in caplog .text
473477
474- assert selected_node == api_call .nodes [api_call .node_index ]
478+ assert (
479+ selected_node == api_call .node_manager .nodes [api_call .node_manager .node_index ]
480+ )
475481
476- assert api_call .node_index == 0
482+ assert api_call .node_manager . node_index == 0
477483
478484
479485def test_raises_if_no_nodes_are_healthy_with_the_last_exception (
@@ -579,3 +585,19 @@ def test_uses_nearest_node_if_present_and_healthy( # noqa: WPS213
579585 assert request_mocker .request_history [11 ].url == "http://nearest:8108/"
580586 assert request_mocker .request_history [12 ].url == "http://nearest:8108/"
581587 assert request_mocker .request_history [13 ].url == "http://nearest:8108/"
588+
589+
590+ def test_max_retries_no_last_exception (api_call : ApiCall ) -> None :
591+ """Test that it raises if the maximum number of retries is reached."""
592+ with pytest .raises (
593+ exceptions .TypesenseClientError ,
594+ match = "All nodes are unhealthy" ,
595+ ):
596+ api_call ._execute_request (
597+ requests .get ,
598+ "/" ,
599+ as_json = True ,
600+ entity_type = typing .Dict [str , str ],
601+ num_retries = 10 ,
602+ last_exception = None ,
603+ )
0 commit comments