@@ -181,6 +181,138 @@ def test_query_with_filter_and_pagination(self, explorer_service, mock_transport
181181 mock_transport .request .assert_has_calls (expected_calls )
182182 assert mock_transport .request .call_count == 4
183183
184+ def test_query_uses_pagination_meta_when_server_caps_page_size (
185+ self , explorer_service , mock_transport
186+ ):
187+ first = Mock ()
188+ first .json .return_value = {
189+ "data" : [_row_payload ("ws-1" ), _row_payload ("ws-2" )],
190+ "meta" : {
191+ "pagination" : {
192+ "current-page" : 1 ,
193+ "page-size" : 2 ,
194+ "next-page" : 2 ,
195+ "total-pages" : 2 ,
196+ }
197+ },
198+ }
199+ second = Mock ()
200+ second .json .return_value = {
201+ "data" : [_row_payload ("ws-3" )],
202+ "meta" : {
203+ "pagination" : {
204+ "current-page" : 2 ,
205+ "page-size" : 2 ,
206+ "next-page" : None ,
207+ "total-pages" : 2 ,
208+ }
209+ },
210+ }
211+ mock_transport .request .side_effect = [first , second ]
212+
213+ options = ExplorerQueryOptions (
214+ view_type = ExplorerViewType .WORKSPACES ,
215+ page_size = 50 ,
216+ )
217+
218+ rows = list (explorer_service .query (ORG , options ))
219+ assert [row .id for row in rows ] == ["ws-1" , "ws-2" , "ws-3" ]
220+
221+ expected_calls = [
222+ call (
223+ "GET" ,
224+ EXPLORER_PATH ,
225+ params = {"type" : "workspaces" , "page[size]" : 50 , "page[number]" : 1 },
226+ ),
227+ call (
228+ "GET" ,
229+ EXPLORER_PATH ,
230+ params = {"type" : "workspaces" , "page[size]" : 50 , "page[number]" : 2 },
231+ ),
232+ ]
233+ mock_transport .request .assert_has_calls (expected_calls )
234+ assert mock_transport .request .call_count == 2
235+
236+ def test_query_uses_current_and_total_pages_when_next_page_missing (
237+ self , explorer_service , mock_transport
238+ ):
239+ first = Mock ()
240+ first .json .return_value = {
241+ "data" : [_row_payload ("ws-1" )],
242+ "meta" : {"pagination" : {"current-page" : 1 , "total-pages" : 2 }},
243+ }
244+ second = Mock ()
245+ second .json .return_value = {
246+ "data" : [_row_payload ("ws-2" )],
247+ "meta" : {"pagination" : {"current-page" : 2 , "total-pages" : 2 }},
248+ }
249+ mock_transport .request .side_effect = [first , second ]
250+
251+ rows = list (
252+ explorer_service .query (
253+ ORG , ExplorerQueryOptions (view_type = ExplorerViewType .WORKSPACES )
254+ )
255+ )
256+ assert [row .id for row in rows ] == ["ws-1" , "ws-2" ]
257+
258+ expected_calls = [
259+ call (
260+ "GET" ,
261+ EXPLORER_PATH ,
262+ params = {"type" : "workspaces" , "page[number]" : 1 , "page[size]" : 100 },
263+ ),
264+ call (
265+ "GET" ,
266+ EXPLORER_PATH ,
267+ params = {"type" : "workspaces" , "page[number]" : 2 , "page[size]" : 100 },
268+ ),
269+ ]
270+ mock_transport .request .assert_has_calls (expected_calls )
271+ assert mock_transport .request .call_count == 2
272+
273+ def test_query_stops_when_pagination_meta_does_not_advance (
274+ self , explorer_service , mock_transport
275+ ):
276+ first = Mock ()
277+ first .json .return_value = {
278+ "data" : [_row_payload ("ws-1" )],
279+ "meta" : {"pagination" : {"current-page" : 1 , "total-pages" : 2 }},
280+ }
281+ second = Mock ()
282+ second .json .return_value = {
283+ "data" : [_row_payload ("ws-1" )],
284+ "meta" : {"pagination" : {"current-page" : 1 , "total-pages" : 2 }},
285+ }
286+ mock_transport .request .side_effect = [first , second ]
287+
288+ rows = list (
289+ explorer_service .query (
290+ ORG , ExplorerQueryOptions (view_type = ExplorerViewType .WORKSPACES )
291+ )
292+ )
293+ assert [row .id for row in rows ] == ["ws-1" , "ws-1" ]
294+ assert mock_transport .request .call_count == 2
295+
296+ def test_query_stops_on_empty_page_even_if_next_page_present (
297+ self , explorer_service , mock_transport
298+ ):
299+ first = Mock ()
300+ first .json .return_value = {
301+ "data" : [],
302+ "meta" : {
303+ "pagination" : {"current-page" : 1 , "next-page" : 2 , "total-pages" : 5 }
304+ },
305+ }
306+ mock_transport .request .return_value = first
307+
308+ rows = list (
309+ explorer_service .query (
310+ ORG , ExplorerQueryOptions (view_type = ExplorerViewType .WORKSPACES )
311+ )
312+ )
313+ assert rows == []
314+ assert mock_transport .request .call_count == 1
315+
184316 def test_query_invalid_org (self , explorer_service ):
185317 with pytest .raises (InvalidOrgError ):
186318 list (
@@ -379,50 +511,21 @@ def test_update_saved_view_invalid_data_shape_raises(
379511 explorer_service .update_saved_view (ORG , VIEW_ID , options )
380512
381513 def test_delete_saved_view (self , explorer_service , mock_transport ):
382- response = Mock ()
383- response .json .return_value = {"data" : _saved_view_payload ("sq-1" )}
384- response .text = '{"data":{"id":"sq-1"}}'
385- mock_transport .request .return_value = response
386-
387- view = explorer_service .delete_saved_view (ORG , VIEW_ID )
388- assert view .id == "sq-1"
514+ result = explorer_service .delete_saved_view (ORG , VIEW_ID )
515+ assert result is None
389516
390517 _assert_single_request_call (mock_transport , "DELETE" , f"{ VIEWS_PATH } /{ VIEW_ID } " )
391518
392- def test_delete_saved_view_empty_response (self , explorer_service , mock_transport ):
393- response = Mock ()
394- response .text = ""
395- response .json .side_effect = ValueError ("No JSON body" )
396- mock_transport .request .return_value = response
397-
398- view = explorer_service .delete_saved_view (ORG , VIEW_ID )
399- assert view .id == "sq-1"
400-
401- def test_delete_saved_view_non_json_body_returns_stub (
519+ def test_delete_saved_view_ignores_response_body (
402520 self , explorer_service , mock_transport
403521 ):
404522 response = Mock ()
405- response .text = "deleted"
523+ response .text = '{"data":{"id":"unexpected"}}'
406524 response .json .side_effect = ValueError ("No JSON body" )
407525 mock_transport .request .return_value = response
408526
409- view = explorer_service .delete_saved_view (ORG , VIEW_ID )
410- assert view .id == "sq-1"
411- assert view .name == ""
412- assert view .query_type == ExplorerViewType .WORKSPACES
413-
414- def test_delete_saved_view_invalid_data_shape_returns_stub (
415- self , explorer_service , mock_transport
416- ):
417- response = Mock ()
418- response .text = '{"data":[]}'
419- response .json .return_value = {"data" : []}
420- mock_transport .request .return_value = response
421-
422- view = explorer_service .delete_saved_view (ORG , VIEW_ID )
423- assert view .id == "sq-1"
424- assert view .name == ""
425- assert view .query_type == ExplorerViewType .WORKSPACES
527+ result = explorer_service .delete_saved_view (ORG , VIEW_ID )
528+ assert result is None
426529
427530 def test_saved_view_results (self , explorer_service , mock_transport ):
428531 first = Mock ()
0 commit comments