Skip to content

Commit

Permalink
- Fix pagination bug (#305)
Browse files Browse the repository at this point in the history
- Add unit test for pagination
  • Loading branch information
nwithan8 authored Oct 30, 2023
1 parent 370601f commit 6faef91
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 1 deletion.
2 changes: 1 addition & 1 deletion easypost/services/base_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ def _get_next_page_resources(
response = Requestor(self._client).request(method=RequestMethod.GET, url=url, params=params)

response_array: List[Any] = response.get(url[1:]) # type: ignore
if response is None or len(response_array) == 0 or not response.get("has_more"):
if response is None or len(response_array) == 0:
raise EndOfPaginationError(NO_MORE_PAGES_ERROR)

return convert_to_easypost_object(response=response)
69 changes: 69 additions & 0 deletions tests/test_base_service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
from unittest.mock import patch

import pytest
from easypost.constant import NO_MORE_PAGES_ERROR
from easypost.errors import EndOfPaginationError


@pytest.mark.vcr() # Cassette not needed due to mocking, but used to avoid making real bogus API calls
def test_get_next_page_collects_all_pages(test_client):
page_size = 1 # Doesn't matter what this is, we're mocking the response
all_results = []

# Using scanforms as an example, but this should work for any service since it's a base class method
first_page_response = {
"scan_forms": [
{
"id": "sf_123",
}
],
"has_more": True,
}

# Mock the initial "get all scanforms" call
with patch("easypost.requestor.Requestor.request", return_value=first_page_response):
first_page = test_client.scan_form.all(page_size=page_size)
all_results += first_page["scan_forms"]
previous_page = first_page

second_page_response = {
"scan_forms": [
{
"id": "sf_456",
}
],
"has_more": True,
}

# Mock the first "get next page" call with more to collect after
# (current page "has_more" = True, next page "has_more" = True)
with patch("easypost.requestor.Requestor.request", return_value=second_page_response):
next_page = test_client.scan_form.get_next_page(scan_forms=previous_page, page_size=page_size) # type: ignore
all_results += next_page["scan_forms"]
previous_page = next_page

third_page_response = {
"scan_forms": [
{
"id": "sf_789",
}
],
"has_more": False,
}

# Mock the second "get next page" call with no more to collect
# (current page "has_more" = True, next page "has_more" = False)
with patch("easypost.requestor.Requestor.request", return_value=third_page_response):
next_page = test_client.scan_form.get_next_page(scan_forms=previous_page, page_size=page_size) # type: ignore
all_results += next_page["scan_forms"]
previous_page = next_page

# Verify we have all scan_forms (from the initial "get all scanforms" and two subsequent "get next page" calls)
# Ensures that no guard clauses inside the "get next page" method are preventing us from collecting all scanforms
assert len(all_results) == 3

# Now that the previous page has "has_more" = False, it should throw an error before even making the API call
with pytest.raises(EndOfPaginationError) as error:
_ = test_client.scan_form.get_next_page(scan_forms=previous_page, page_size=page_size) # type: ignore

assert error.value.message == NO_MORE_PAGES_ERROR

0 comments on commit 6faef91

Please sign in to comment.