Skip to content

Commit fa2d782

Browse files
committed
add integration tests
1 parent 74a5911 commit fa2d782

10 files changed

Lines changed: 781 additions & 288 deletions

File tree

python/lib/sift_client/_internal/low_level_wrappers/base.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ async def _handle_pagination(
2222
page_size: The number of results to return per page.
2323
page_token: The token to use for the next page.
2424
order_by: How to order the retrieved results.
25-
max_results: Maximum number of results to return. NOTE: Will be in increments of page_size or default page size defined by the call if no page_size is provided.
25+
max_results: Maximum number of results to return.
2626
2727
Returns:
2828
A list of all matching results.
@@ -31,6 +31,8 @@ async def _handle_pagination(
3131
kwargs = {}
3232

3333
results: list[Any] = []
34+
if max_results == 0:
35+
return results
3436
if page_token is None:
3537
page_token = ""
3638
while True:
@@ -45,4 +47,6 @@ async def _handle_pagination(
4547
results.extend(response)
4648
if page_token == "":
4749
break
50+
if max_results and len(results) > max_results:
51+
results = results[:max_results]
4852
return results

python/lib/sift_client/_tests/_internal/low_level_wrappers/__init__.py

Whitespace-only changes.
Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
"""Tests for LowLevelClientBase.
2+
3+
These tests validate the functionality of the LowLevelClientBase class including:
4+
- Pagination handling with various scenarios
5+
- Edge cases and error handling
6+
- Parameter validation and behavior
7+
"""
8+
9+
from unittest.mock import AsyncMock
10+
11+
import pytest
12+
13+
from sift_client._internal.low_level_wrappers.base import LowLevelClientBase
14+
15+
16+
class TestLowLevelClientBase:
17+
"""Test suite for LowLevelClientBase functionality."""
18+
19+
class TestHandlePagination:
20+
"""Tests for the _handle_pagination static method."""
21+
22+
@pytest.mark.asyncio
23+
async def test_basic_pagination_single_page(self):
24+
"""Test pagination with a single page of results."""
25+
# Mock function that returns results and empty page token (indicating no more pages)
26+
mock_func = AsyncMock(return_value=([1, 2, 3], ""))
27+
28+
results = await LowLevelClientBase._handle_pagination(mock_func)
29+
30+
assert results == [1, 2, 3]
31+
mock_func.assert_called_once_with(
32+
page_size=None,
33+
page_token="",
34+
order_by=None
35+
)
36+
37+
@pytest.mark.asyncio
38+
async def test_pagination_multiple_pages(self):
39+
"""Test pagination across multiple pages."""
40+
# Mock function that returns different results for different page tokens
41+
mock_func = AsyncMock()
42+
mock_func.side_effect = [
43+
([1, 2, 3], "token1"), # First page
44+
([4, 5, 6], "token2"), # Second page
45+
([7, 8, 9], ""), # Last page (empty token)
46+
]
47+
48+
results = await LowLevelClientBase._handle_pagination(mock_func)
49+
50+
assert results == [1, 2, 3, 4, 5, 6, 7, 8, 9]
51+
assert mock_func.call_count == 3
52+
53+
# Verify the calls were made with correct page tokens
54+
calls = mock_func.call_args_list
55+
assert calls[0][1]["page_token"] == ""
56+
assert calls[1][1]["page_token"] == "token1"
57+
assert calls[2][1]["page_token"] == "token2"
58+
59+
@pytest.mark.asyncio
60+
async def test_pagination_with_page_size(self):
61+
"""Test pagination with specified page size."""
62+
mock_func = AsyncMock(return_value=([1, 2], ""))
63+
64+
results = await LowLevelClientBase._handle_pagination(
65+
mock_func,
66+
page_size=2
67+
)
68+
69+
assert results == [1, 2]
70+
mock_func.assert_called_once_with(
71+
page_size=2,
72+
page_token="",
73+
order_by=None
74+
)
75+
76+
@pytest.mark.asyncio
77+
async def test_pagination_with_order_by(self):
78+
"""Test pagination with order_by parameter."""
79+
mock_func = AsyncMock(return_value=([1, 2, 3], ""))
80+
81+
results = await LowLevelClientBase._handle_pagination(
82+
mock_func,
83+
order_by="name asc"
84+
)
85+
86+
assert results == [1, 2, 3]
87+
mock_func.assert_called_once_with(
88+
page_size=None,
89+
page_token="",
90+
order_by="name asc"
91+
)
92+
93+
@pytest.mark.asyncio
94+
async def test_pagination_with_initial_page_token(self):
95+
"""Test pagination starting with a specific page token."""
96+
mock_func = AsyncMock(return_value=([4, 5, 6], ""))
97+
98+
results = await LowLevelClientBase._handle_pagination(
99+
mock_func,
100+
page_token="start_token"
101+
)
102+
103+
assert results == [4, 5, 6]
104+
mock_func.assert_called_once_with(
105+
page_size=None,
106+
page_token="start_token",
107+
order_by=None
108+
)
109+
110+
@pytest.mark.asyncio
111+
async def test_pagination_with_kwargs(self):
112+
"""Test pagination with additional keyword arguments."""
113+
mock_func = AsyncMock(return_value=([1, 2, 3], ""))
114+
kwargs = {"filter": "active", "include_archived": False}
115+
116+
results = await LowLevelClientBase._handle_pagination(
117+
mock_func,
118+
kwargs=kwargs
119+
)
120+
121+
assert results == [1, 2, 3]
122+
mock_func.assert_called_once_with(
123+
page_size=None,
124+
page_token="",
125+
order_by=None,
126+
filter="active",
127+
include_archived=False
128+
)
129+
130+
@pytest.mark.asyncio
131+
async def test_pagination_with_max_results_single_page(self):
132+
"""Test pagination with max_results that fits in a single page."""
133+
mock_func = AsyncMock(return_value=([1, 2, 3, 4, 5], ""))
134+
135+
results = await LowLevelClientBase._handle_pagination(
136+
mock_func,
137+
max_results=3
138+
)
139+
140+
# Should return only the max results
141+
assert results == [1, 2, 3]
142+
mock_func.assert_called_once()
143+
144+
@pytest.mark.asyncio
145+
async def test_pagination_with_max_results_multiple_pages(self):
146+
"""Test pagination with max_results across multiple pages."""
147+
mock_func = AsyncMock()
148+
mock_func.side_effect = [
149+
([1, 2, 3], "token1"), # First page (3 items)
150+
([4, 5, 6], "token2"), # Second page (6 total items, exceeds max_results=5)
151+
]
152+
153+
results = await LowLevelClientBase._handle_pagination(
154+
mock_func,
155+
max_results=5
156+
)
157+
158+
# Should include 2 pages and return the full first page but limited 2nd page
159+
assert results == [1, 2, 3, 4, 5]
160+
assert mock_func.call_count == 2
161+
162+
@pytest.mark.asyncio
163+
async def test_pagination_with_max_results_exact_match(self):
164+
"""Test pagination when results exactly match max_results."""
165+
mock_func = AsyncMock()
166+
mock_func.side_effect = [
167+
([1, 2, 3], "token1"), # First page
168+
([4, 5], ""), # Second page, total = 5
169+
]
170+
171+
results = await LowLevelClientBase._handle_pagination(
172+
mock_func,
173+
max_results=5
174+
)
175+
176+
assert results == [1, 2, 3, 4, 5]
177+
assert mock_func.call_count == 2
178+
179+
@pytest.mark.asyncio
180+
async def test_pagination_empty_results(self):
181+
"""Test pagination when function returns empty results."""
182+
mock_func = AsyncMock(return_value=([], ""))
183+
184+
results = await LowLevelClientBase._handle_pagination(mock_func)
185+
186+
assert results == []
187+
mock_func.assert_called_once()
188+
189+
190+
@pytest.mark.asyncio
191+
async def test_pagination_max_results_zero(self):
192+
"""Test pagination with max_results=0."""
193+
mock_func = AsyncMock(return_value=([1, 2, 3], ""))
194+
195+
results = await LowLevelClientBase._handle_pagination(
196+
mock_func,
197+
max_results=0
198+
)
199+
200+
# Should return empty list without calling the function
201+
assert results == []
202+
mock_func.assert_not_called()
203+

0 commit comments

Comments
 (0)