11import unittest
2- from unittest .mock import Mock
2+ from unittest .mock import Mock , AsyncMock
33from tests import IntegrationTestCase
44from tests .holodeck import Request
55from twilio .base .exceptions import TwilioException
66from twilio .base .token_pagination import TokenPagination
77from 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+
442690if __name__ == "__main__" :
443691 unittest .main ()
0 commit comments