Skip to content

Commit ef0435c

Browse files
committed
chore: add token pagination test
1 parent eae41ef commit ef0435c

File tree

1 file changed

+259
-11
lines changed

1 file changed

+259
-11
lines changed

tests/unit/base/test_token_pagination.py

Lines changed: 259 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import unittest
2-
from unittest.mock import Mock
2+
from unittest.mock import Mock, AsyncMock
33
from tests import IntegrationTestCase
44
from tests.holodeck import Request
55
from twilio.base.exceptions import TwilioException
66
from twilio.base.token_pagination import TokenPagination
77
from twilio.http.response import Response
88

99

10-
class TestTokenPaginationPage(TokenPagination):
11-
"""Test implementation of TokenPagination"""
10+
class MockTokenPaginationPage(TokenPagination):
11+
"""Mock implementation of TokenPagination for testing"""
1212

1313
def get_instance(self, payload):
1414
return payload
@@ -50,7 +50,7 @@ def setUp(self):
5050
"account_sid": "ACxxxx",
5151
"api_uri": "/Accounts/ACxxxx/Resources.json",
5252
}
53-
self.page = TestTokenPaginationPage(
53+
self.page = MockTokenPaginationPage(
5454
self.version,
5555
self.response,
5656
"/Accounts/ACxxxx/Resources.json",
@@ -79,7 +79,7 @@ def test_properties_without_meta(self):
7979
response.text = '{"items": []}'
8080
response.status_code = 200
8181

82-
page = TestTokenPaginationPage(
82+
page = MockTokenPaginationPage(
8383
self.version, response, "/Accounts/ACxxxx/Resources.json", self.solution
8484
)
8585

@@ -94,7 +94,7 @@ def test_properties_with_partial_meta(self):
9494
response.text = '{"meta": {"key": "items"}, "items": []}'
9595
response.status_code = 200
9696

97-
page = TestTokenPaginationPage(
97+
page = MockTokenPaginationPage(
9898
self.version, response, "/Accounts/ACxxxx/Resources.json", self.solution
9999
)
100100

@@ -225,7 +225,7 @@ def setUp(self):
225225
"api_uri": "/2010-04-01/Accounts/ACaaaa/Resources.json",
226226
}
227227

228-
self.page = TestTokenPaginationPage(
228+
self.page = MockTokenPaginationPage(
229229
self.version,
230230
self.response,
231231
"/2010-04-01/Accounts/ACaaaa/Resources.json",
@@ -240,7 +240,7 @@ def test_next_page(self):
240240
next_page = self.page.next_page()
241241

242242
self.assertIsNotNone(next_page)
243-
self.assertIsInstance(next_page, TestTokenPaginationPage)
243+
self.assertIsInstance(next_page, MockTokenPaginationPage)
244244
# Verify we got the next page's data
245245
self.assertEqual(next_page.next_token, "token_page3")
246246
self.assertEqual(next_page.previous_token, "token_prev1")
@@ -269,7 +269,7 @@ def test_previous_page(self):
269269
prev_page = next_page.previous_page()
270270

271271
self.assertIsNotNone(prev_page)
272-
self.assertIsInstance(prev_page, TestTokenPaginationPage)
272+
self.assertIsInstance(prev_page, MockTokenPaginationPage)
273273
# Verify we got the first page's data
274274
self.assertIsNone(prev_page.previous_token)
275275
self.assertEqual(prev_page.next_token, "token_page2")
@@ -322,7 +322,7 @@ def test_next_page_without_uri_in_solution(self):
322322
solution = {"account_sid": "ACxxxx"}
323323

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

327327
with self.assertRaises(TwilioException) as context:
328328
page.next_page()
@@ -408,7 +408,7 @@ def setUp(self):
408408
"api_uri": "/2010-04-01/Accounts/ACaaaa/Records.json",
409409
}
410410

411-
self.page = TestTokenPaginationPage(
411+
self.page = MockTokenPaginationPage(
412412
self.version,
413413
self.response,
414414
"/2010-04-01/Accounts/ACaaaa/Records.json",
@@ -439,5 +439,253 @@ def test_stream_with_page_limit(self):
439439
self.assertEqual(len(records), 2)
440440

441441

442+
class TokenPaginationInternalMethodTest(unittest.TestCase):
443+
"""Test TokenPagination internal methods"""
444+
445+
def setUp(self):
446+
self.version = Mock()
447+
self.version.domain = Mock()
448+
self.version.domain.twilio = Mock()
449+
self.version.domain.absolute_url = Mock(
450+
side_effect=lambda uri: f"https://api.twilio.com{uri}"
451+
)
452+
453+
# Mock first page response
454+
self.first_response = Mock()
455+
self.first_response.text = """
456+
{
457+
"meta": {
458+
"key": "items",
459+
"pageSize": 2,
460+
"nextToken": "token_page2",
461+
"previousToken": null
462+
},
463+
"items": [{"id": 1}, {"id": 2}]
464+
}
465+
"""
466+
self.first_response.status_code = 200
467+
468+
# Mock next page response
469+
self.next_response = Mock()
470+
self.next_response.text = """
471+
{
472+
"meta": {
473+
"key": "items",
474+
"pageSize": 2,
475+
"nextToken": null,
476+
"previousToken": "token_prev"
477+
},
478+
"items": [{"id": 3}, {"id": 4}]
479+
}
480+
"""
481+
self.next_response.status_code = 200
482+
483+
self.solution = {"account_sid": "ACxxxx"}
484+
self.page = MockTokenPaginationPage(
485+
self.version,
486+
self.first_response,
487+
"/2010-04-01/Accounts/ACxxxx/Resources.json",
488+
self.solution,
489+
)
490+
491+
def test_get_page_with_valid_token(self):
492+
"""Test _get_page() with a valid token"""
493+
self.version.domain.twilio.request = Mock(return_value=self.next_response)
494+
495+
result = self.page._get_page("token_page2")
496+
497+
self.assertIsNotNone(result)
498+
self.assertIsInstance(result, MockTokenPaginationPage)
499+
self.version.domain.twilio.request.assert_called_once_with(
500+
"GET",
501+
"https://api.twilio.com/2010-04-01/Accounts/ACxxxx/Resources.json?pageToken=token_page2",
502+
)
503+
504+
def test_get_page_with_none_token(self):
505+
"""Test _get_page() with None token returns None"""
506+
result = self.page._get_page(None)
507+
self.assertIsNone(result)
508+
509+
def test_get_page_without_uri(self):
510+
"""Test _get_page() raises error when URI is missing"""
511+
page = MockTokenPaginationPage(
512+
self.version, self.first_response, "", self.solution
513+
)
514+
515+
with self.assertRaises(TwilioException) as context:
516+
page._get_page("some_token")
517+
518+
self.assertIn("URI must be provided", str(context.exception))
519+
520+
def test_repr(self):
521+
"""Test __repr__ method returns correct string"""
522+
self.assertEqual(repr(self.page), "<TokenPagination>")
523+
524+
525+
class TokenPaginationAsyncTest(unittest.IsolatedAsyncioTestCase):
526+
"""Test TokenPagination async methods"""
527+
528+
def setUp(self):
529+
self.version = Mock()
530+
self.version.domain = Mock()
531+
self.version.domain.twilio = Mock()
532+
self.version.domain.absolute_url = Mock(
533+
side_effect=lambda uri: f"https://api.twilio.com{uri}"
534+
)
535+
536+
# Mock first page response
537+
self.first_response = Mock()
538+
self.first_response.text = """
539+
{
540+
"meta": {
541+
"key": "items",
542+
"pageSize": 2,
543+
"nextToken": "token_page2",
544+
"previousToken": null
545+
},
546+
"items": [{"id": 1}, {"id": 2}]
547+
}
548+
"""
549+
self.first_response.status_code = 200
550+
551+
# Mock next page response
552+
self.next_response = Mock()
553+
self.next_response.text = """
554+
{
555+
"meta": {
556+
"key": "items",
557+
"pageSize": 2,
558+
"nextToken": "token_page3",
559+
"previousToken": "token_prev"
560+
},
561+
"items": [{"id": 3}, {"id": 4}]
562+
}
563+
"""
564+
self.next_response.status_code = 200
565+
566+
# Mock previous page response
567+
self.prev_response = Mock()
568+
self.prev_response.text = """
569+
{
570+
"meta": {
571+
"key": "items",
572+
"pageSize": 2,
573+
"nextToken": "token_page2",
574+
"previousToken": null
575+
},
576+
"items": [{"id": 1}, {"id": 2}]
577+
}
578+
"""
579+
self.prev_response.status_code = 200
580+
581+
self.solution = {"account_sid": "ACxxxx"}
582+
583+
# Page with next token
584+
self.page = MockTokenPaginationPage(
585+
self.version,
586+
self.first_response,
587+
"/2010-04-01/Accounts/ACxxxx/Resources.json",
588+
self.solution,
589+
)
590+
591+
# Page with previous token (page 2)
592+
self.page_with_prev = MockTokenPaginationPage(
593+
self.version,
594+
self.next_response,
595+
"/2010-04-01/Accounts/ACxxxx/Resources.json",
596+
self.solution,
597+
)
598+
599+
async def test_get_page_async_with_valid_token(self):
600+
"""Test _get_page_async() with a valid token"""
601+
self.version.domain.twilio.request_async = AsyncMock(
602+
return_value=self.next_response
603+
)
604+
605+
result = await self.page._get_page_async("token_page2")
606+
607+
self.assertIsNotNone(result)
608+
self.assertIsInstance(result, MockTokenPaginationPage)
609+
self.version.domain.twilio.request_async.assert_called_once_with(
610+
"GET",
611+
"https://api.twilio.com/2010-04-01/Accounts/ACxxxx/Resources.json?pageToken=token_page2",
612+
)
613+
614+
async def test_get_page_async_with_none_token(self):
615+
"""Test _get_page_async() with None token returns None"""
616+
result = await self.page._get_page_async(None)
617+
self.assertIsNone(result)
618+
619+
async def test_get_page_async_without_uri(self):
620+
"""Test _get_page_async() raises error when URI is missing"""
621+
page = MockTokenPaginationPage(
622+
self.version, self.first_response, "", self.solution
623+
)
624+
625+
with self.assertRaises(TwilioException) as context:
626+
await page._get_page_async("some_token")
627+
628+
self.assertIn("URI must be provided", str(context.exception))
629+
630+
async def test_next_page_async(self):
631+
"""Test next_page_async() navigates to next page"""
632+
self.version.domain.twilio.request_async = AsyncMock(
633+
return_value=self.next_response
634+
)
635+
636+
next_page = await self.page.next_page_async()
637+
638+
self.assertIsNotNone(next_page)
639+
self.assertIsInstance(next_page, MockTokenPaginationPage)
640+
self.assertEqual(next_page.next_token, "token_page3")
641+
self.assertEqual(next_page.previous_token, "token_prev")
642+
643+
async def test_next_page_async_none_when_no_token(self):
644+
"""Test next_page_async() returns None when there's no next token"""
645+
# Create page with no next token
646+
no_next_response = Mock()
647+
no_next_response.text = """
648+
{
649+
"meta": {
650+
"key": "items",
651+
"pageSize": 2,
652+
"nextToken": null,
653+
"previousToken": "token_prev"
654+
},
655+
"items": [{"id": 5}]
656+
}
657+
"""
658+
no_next_response.status_code = 200
659+
660+
page = MockTokenPaginationPage(
661+
self.version,
662+
no_next_response,
663+
"/2010-04-01/Accounts/ACxxxx/Resources.json",
664+
self.solution,
665+
)
666+
667+
result = await page.next_page_async()
668+
self.assertIsNone(result)
669+
670+
async def test_previous_page_async(self):
671+
"""Test previous_page_async() navigates to previous page"""
672+
self.version.domain.twilio.request_async = AsyncMock(
673+
return_value=self.prev_response
674+
)
675+
676+
prev_page = await self.page_with_prev.previous_page_async()
677+
678+
self.assertIsNotNone(prev_page)
679+
self.assertIsInstance(prev_page, MockTokenPaginationPage)
680+
self.assertIsNone(prev_page.previous_token)
681+
self.assertEqual(prev_page.next_token, "token_page2")
682+
683+
async def test_previous_page_async_none_when_no_token(self):
684+
"""Test previous_page_async() returns None when there's no previous token"""
685+
# First page has no previous token
686+
result = await self.page.previous_page_async()
687+
self.assertIsNone(result)
688+
689+
442690
if __name__ == "__main__":
443691
unittest.main()

0 commit comments

Comments
 (0)