Skip to content

Commit eae41ef

Browse files
committed
chore: add token pagination
1 parent 2528b34 commit eae41ef

File tree

2 files changed

+86
-188
lines changed

2 files changed

+86
-188
lines changed

tests/unit/base/test_token_pagination.py

Lines changed: 70 additions & 168 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import unittest
2-
from unittest.mock import Mock, AsyncMock
2+
from unittest.mock import Mock
33
from tests import IntegrationTestCase
44
from tests.holodeck import Request
55
from twilio.base.exceptions import TwilioException
@@ -48,9 +48,14 @@ def setUp(self):
4848

4949
self.solution = {
5050
"account_sid": "ACxxxx",
51-
"uri": "/Accounts/ACxxxx/Resources.json",
51+
"api_uri": "/Accounts/ACxxxx/Resources.json",
5252
}
53-
self.page = TestTokenPaginationPage(self.version, self.response, self.solution)
53+
self.page = TestTokenPaginationPage(
54+
self.version,
55+
self.response,
56+
"/Accounts/ACxxxx/Resources.json",
57+
self.solution,
58+
)
5459

5560
def test_key_property(self):
5661
"""Test that key property returns the correct value"""
@@ -74,7 +79,9 @@ def test_properties_without_meta(self):
7479
response.text = '{"items": []}'
7580
response.status_code = 200
7681

77-
page = TestTokenPaginationPage(self.version, response, self.solution)
82+
page = TestTokenPaginationPage(
83+
self.version, response, "/Accounts/ACxxxx/Resources.json", self.solution
84+
)
7885

7986
self.assertIsNone(page.key)
8087
self.assertIsNone(page.page_size)
@@ -87,7 +94,9 @@ def test_properties_with_partial_meta(self):
8794
response.text = '{"meta": {"key": "items"}, "items": []}'
8895
response.status_code = 200
8996

90-
page = TestTokenPaginationPage(self.version, response, self.solution)
97+
page = TestTokenPaginationPage(
98+
self.version, response, "/Accounts/ACxxxx/Resources.json", self.solution
99+
)
91100

92101
self.assertEqual(page.key, "items")
93102
self.assertIsNone(page.page_size)
@@ -117,7 +126,9 @@ def setUp(self):
117126
}
118127
""",
119128
),
120-
Request(url="https://api.twilio.com/Accounts/ACaaaa/Resources.json"),
129+
Request(
130+
url="https://api.twilio.com/2010-04-01/Accounts/ACaaaa/Resources.json"
131+
),
121132
)
122133

123134
# Mock second page response (next page)
@@ -130,14 +141,14 @@ def setUp(self):
130141
"key": "items",
131142
"pageSize": 2,
132143
"nextToken": "token_page3",
133-
"previousToken": "token_page2"
144+
"previousToken": "token_prev1"
134145
},
135146
"items": [{"id": 3, "name": "Item 3"}, {"id": 4, "name": "Item 4"}]
136147
}
137148
""",
138149
),
139150
Request(
140-
url="https://api.twilio.com/Accounts/ACaaaa/Resources.json?pageToken=token_page2"
151+
url="https://api.twilio.com/2010-04-01/Accounts/ACaaaa/Resources.json?pageToken=token_page2"
141152
),
142153
)
143154

@@ -151,18 +162,18 @@ def setUp(self):
151162
"key": "items",
152163
"pageSize": 2,
153164
"nextToken": null,
154-
"previousToken": "token_page3"
165+
"previousToken": "token_prev2"
155166
},
156167
"items": [{"id": 5, "name": "Item 5"}]
157168
}
158169
""",
159170
),
160171
Request(
161-
url="https://api.twilio.com/Accounts/ACaaaa/Resources.json?pageToken=token_page3"
172+
url="https://api.twilio.com/2010-04-01/Accounts/ACaaaa/Resources.json?pageToken=token_page3"
162173
),
163174
)
164175

165-
# Mock previous page response
176+
# Mock previous page response (going back to page 1)
166177
self.holodeck.mock(
167178
Response(
168179
200,
@@ -179,7 +190,28 @@ def setUp(self):
179190
""",
180191
),
181192
Request(
182-
url="https://api.twilio.com/Accounts/ACaaaa/Resources.json?pageToken=token_page2"
193+
url="https://api.twilio.com/2010-04-01/Accounts/ACaaaa/Resources.json?pageToken=token_prev1"
194+
),
195+
)
196+
197+
# Mock going back from page 3 to page 2
198+
self.holodeck.mock(
199+
Response(
200+
200,
201+
"""
202+
{
203+
"meta": {
204+
"key": "items",
205+
"pageSize": 2,
206+
"nextToken": "token_page3",
207+
"previousToken": "token_prev1"
208+
},
209+
"items": [{"id": 3, "name": "Item 3"}, {"id": 4, "name": "Item 4"}]
210+
}
211+
""",
212+
),
213+
Request(
214+
url="https://api.twilio.com/2010-04-01/Accounts/ACaaaa/Resources.json?pageToken=token_prev2"
183215
),
184216
)
185217

@@ -190,10 +222,15 @@ def setUp(self):
190222

191223
self.solution = {
192224
"account_sid": "ACaaaa",
193-
"uri": "/Accounts/ACaaaa/Resources.json",
225+
"api_uri": "/2010-04-01/Accounts/ACaaaa/Resources.json",
194226
}
195227

196-
self.page = TestTokenPaginationPage(self.version, self.response, self.solution)
228+
self.page = TestTokenPaginationPage(
229+
self.version,
230+
self.response,
231+
"/2010-04-01/Accounts/ACaaaa/Resources.json",
232+
self.solution,
233+
)
197234

198235
def test_next_page(self):
199236
"""Test that next_page() navigates to the next page using token"""
@@ -206,7 +243,7 @@ def test_next_page(self):
206243
self.assertIsInstance(next_page, TestTokenPaginationPage)
207244
# Verify we got the next page's data
208245
self.assertEqual(next_page.next_token, "token_page3")
209-
self.assertEqual(next_page.previous_token, "token_page2")
246+
self.assertEqual(next_page.previous_token, "token_prev1")
210247

211248
def test_next_page_none_when_no_token(self):
212249
"""Test that next_page() returns None when there's no next token"""
@@ -226,7 +263,7 @@ def test_previous_page(self):
226263
# Navigate to second page first
227264
next_page = self.page.next_page()
228265
self.assertIsNotNone(next_page.previous_token)
229-
self.assertEqual(next_page.previous_token, "token_page2")
266+
self.assertEqual(next_page.previous_token, "token_prev1")
230267

231268
# Go back to previous page
232269
prev_page = next_page.previous_page()
@@ -250,12 +287,12 @@ def test_navigation_chain(self):
250287
"""Test navigating through multiple pages forward and backward"""
251288
# Page 1 -> Page 2
252289
page2 = self.page.next_page()
253-
self.assertEqual(page2.previous_token, "token_page2")
290+
self.assertEqual(page2.previous_token, "token_prev1")
254291

255292
# Page 2 -> Page 3
256293
page3 = page2.next_page()
257294
self.assertIsNone(page3.next_token)
258-
self.assertEqual(page3.previous_token, "token_page3")
295+
self.assertEqual(page3.previous_token, "token_prev2")
259296

260297
# Page 3 -> Page 2 (backward)
261298
back_to_page2 = page3.previous_page()
@@ -266,7 +303,7 @@ class TokenPaginationErrorTest(unittest.TestCase):
266303
"""Test TokenPagination error handling"""
267304

268305
def test_next_page_without_uri_in_solution(self):
269-
"""Test that next_page() raises error when URI is missing from solution"""
306+
"""Test that next_page() raises error when URI is missing"""
270307
version = Mock()
271308
response = Mock()
272309
response.text = """
@@ -284,139 +321,14 @@ def test_next_page_without_uri_in_solution(self):
284321
# Solution without URI
285322
solution = {"account_sid": "ACxxxx"}
286323

287-
page = TestTokenPaginationPage(version, response, solution)
324+
# Pass empty string as URI to test the error case
325+
page = TestTokenPaginationPage(version, response, "", solution)
288326

289327
with self.assertRaises(TwilioException) as context:
290328
page.next_page()
291329

292330
self.assertIn("URI must be provided", str(context.exception))
293331

294-
def test_previous_page_without_uri_in_solution(self):
295-
"""Test that previous_page() raises error when URI is missing from solution"""
296-
version = Mock()
297-
response = Mock()
298-
response.text = """
299-
{
300-
"meta": {
301-
"key": "items",
302-
"pageSize": 50,
303-
"previousToken": "xyz789"
304-
},
305-
"items": []
306-
}
307-
"""
308-
response.status_code = 200
309-
310-
# Solution without URI
311-
solution = {"account_sid": "ACxxxx"}
312-
313-
page = TestTokenPaginationPage(version, response, solution)
314-
315-
with self.assertRaises(TwilioException) as context:
316-
page.previous_page()
317-
318-
self.assertIn("URI must be provided", str(context.exception))
319-
320-
321-
class TokenPaginationAsyncTest(unittest.TestCase):
322-
"""Test TokenPagination async methods"""
323-
324-
async def test_next_page_async(self):
325-
"""Test that next_page_async() works correctly"""
326-
version = Mock()
327-
version.domain = Mock()
328-
version.domain.absolute_url = Mock(
329-
return_value="https://api.twilio.com/Accounts/ACxxxx/Resources.json"
330-
)
331-
version.domain.twilio = Mock()
332-
333-
# Mock async request
334-
future_response = Mock()
335-
future_response.text = """
336-
{
337-
"meta": {
338-
"key": "items",
339-
"pageSize": 50,
340-
"nextToken": "next_token_2",
341-
"previousToken": "prev_token_1"
342-
},
343-
"items": [{"id": 3}]
344-
}
345-
"""
346-
future_response.status_code = 200
347-
348-
version.domain.twilio.request_async = AsyncMock(return_value=future_response)
349-
350-
response = Mock()
351-
response.text = """
352-
{
353-
"meta": {
354-
"key": "items",
355-
"pageSize": 50,
356-
"nextToken": "token_next",
357-
"previousToken": null
358-
},
359-
"items": [{"id": 1}]
360-
}
361-
"""
362-
response.status_code = 200
363-
364-
solution = {"account_sid": "ACxxxx", "uri": "/Accounts/ACxxxx/Resources.json"}
365-
page = TestTokenPaginationPage(version, response, solution)
366-
367-
next_page = await page.next_page_async()
368-
369-
self.assertIsNotNone(next_page)
370-
version.domain.twilio.request_async.assert_called_once()
371-
372-
async def test_previous_page_async(self):
373-
"""Test that previous_page_async() works correctly"""
374-
version = Mock()
375-
version.domain = Mock()
376-
version.domain.absolute_url = Mock(
377-
return_value="https://api.twilio.com/Accounts/ACxxxx/Resources.json"
378-
)
379-
version.domain.twilio = Mock()
380-
381-
# Mock async request
382-
future_response = Mock()
383-
future_response.text = """
384-
{
385-
"meta": {
386-
"key": "items",
387-
"pageSize": 50,
388-
"nextToken": null,
389-
"previousToken": null
390-
},
391-
"items": [{"id": 1}]
392-
}
393-
"""
394-
future_response.status_code = 200
395-
396-
version.domain.twilio.request_async = AsyncMock(return_value=future_response)
397-
398-
response = Mock()
399-
response.text = """
400-
{
401-
"meta": {
402-
"key": "items",
403-
"pageSize": 50,
404-
"nextToken": "token_next",
405-
"previousToken": "token_prev"
406-
},
407-
"items": [{"id": 2}]
408-
}
409-
"""
410-
response.status_code = 200
411-
412-
solution = {"account_sid": "ACxxxx", "uri": "/Accounts/ACxxxx/Resources.json"}
413-
page = TestTokenPaginationPage(version, response, solution)
414-
415-
prev_page = await page.previous_page_async()
416-
417-
self.assertIsNotNone(prev_page)
418-
version.domain.twilio.request_async.assert_called_once()
419-
420332

421333
class TokenPaginationStreamTest(IntegrationTestCase):
422334
"""Test streaming with TokenPagination"""
@@ -439,7 +351,9 @@ def setUp(self):
439351
}
440352
""",
441353
),
442-
Request(url="https://api.twilio.com/Accounts/ACaaaa/Records.json"),
354+
Request(
355+
url="https://api.twilio.com/2010-04-01/Accounts/ACaaaa/Records.json"
356+
),
443357
)
444358

445359
# Mock page 2
@@ -459,7 +373,7 @@ def setUp(self):
459373
""",
460374
),
461375
Request(
462-
url="https://api.twilio.com/Accounts/ACaaaa/Records.json?pageToken=token_2"
376+
url="https://api.twilio.com/2010-04-01/Accounts/ACaaaa/Records.json?pageToken=token_2"
463377
),
464378
)
465379

@@ -480,7 +394,7 @@ def setUp(self):
480394
""",
481395
),
482396
Request(
483-
url="https://api.twilio.com/Accounts/ACaaaa/Records.json?pageToken=token_3"
397+
url="https://api.twilio.com/2010-04-01/Accounts/ACaaaa/Records.json?pageToken=token_3"
484398
),
485399
)
486400

@@ -491,10 +405,15 @@ def setUp(self):
491405

492406
self.solution = {
493407
"account_sid": "ACaaaa",
494-
"uri": "/Accounts/ACaaaa/Records.json",
408+
"api_uri": "/2010-04-01/Accounts/ACaaaa/Records.json",
495409
}
496410

497-
self.page = TestTokenPaginationPage(self.version, self.response, self.solution)
411+
self.page = TestTokenPaginationPage(
412+
self.version,
413+
self.response,
414+
"/2010-04-01/Accounts/ACaaaa/Records.json",
415+
self.solution,
416+
)
498417

499418
def test_stream_all_records(self):
500419
"""Test streaming through all pages"""
@@ -520,22 +439,5 @@ def test_stream_with_page_limit(self):
520439
self.assertEqual(len(records), 2)
521440

522441

523-
class TokenPaginationReprTest(unittest.TestCase):
524-
"""Test TokenPagination string representation"""
525-
526-
def test_repr(self):
527-
"""Test __repr__ method"""
528-
version = Mock()
529-
response = Mock()
530-
response.text = '{"meta": {"key": "items"}, "items": []}'
531-
response.status_code = 200
532-
533-
solution = {"account_sid": "ACxxxx"}
534-
page = TestTokenPaginationPage(version, response, solution)
535-
536-
repr_str = repr(page)
537-
self.assertEqual(repr_str, "<TokenPagination>")
538-
539-
540442
if __name__ == "__main__":
541443
unittest.main()

0 commit comments

Comments
 (0)