1919APP_SECRET = '0x5EC07'
2020TOKEN_CREATION_TIMESTAMP = 1613745000
2121MOCK_NOW = 1613745082
22- REDIRECT_URL = 'https://redirect.url.com'
22+ CALLBACK_URL = 'https://redirect.url.com'
2323
2424
2525class ClientFromLoginFlowTest (unittest .TestCase ):
@@ -562,7 +562,7 @@ def test_no_token_file(
562562
563563 self .assertEqual ('returned client' ,
564564 auth .client_from_manual_flow (
565- API_KEY , APP_SECRET , REDIRECT_URL , self .token_path ))
565+ API_KEY , APP_SECRET , CALLBACK_URL , self .token_path ))
566566
567567 with open (self .token_path , 'r' ) as f :
568568 self .assertEqual ({
@@ -594,7 +594,7 @@ def dummy_token_write_func(token):
594594
595595 self .assertEqual ('returned client' ,
596596 auth .client_from_manual_flow (
597- API_KEY , APP_SECRET , REDIRECT_URL ,
597+ API_KEY , APP_SECRET , CALLBACK_URL ,
598598 self .token_path ,
599599 token_write_func = dummy_token_write_func ))
600600
@@ -657,7 +657,7 @@ def test_enforce_enums_disabled(
657657
658658 self .assertEqual ('returned client' ,
659659 auth .client_from_manual_flow (
660- API_KEY , APP_SECRET , REDIRECT_URL , self .token_path ,
660+ API_KEY , APP_SECRET , CALLBACK_URL , self .token_path ,
661661 enforce_enums = False ))
662662
663663 client .assert_called_once_with (API_KEY , _ , token_metadata = _ ,
@@ -682,7 +682,7 @@ def test_enforce_enums_enabled(
682682
683683 self .assertEqual ('returned client' ,
684684 auth .client_from_manual_flow (
685- API_KEY , APP_SECRET , REDIRECT_URL , self .token_path ))
685+ API_KEY , APP_SECRET , CALLBACK_URL , self .token_path ))
686686
687687 client .assert_called_once_with (API_KEY , _ , token_metadata = _ ,
688688 enforce_enums = True )
@@ -733,3 +733,161 @@ def test_token_age(self):
733733 token , unwrapped_token_write_func = None )
734734 self .assertEqual (metadata .token_age (),
735735 MOCK_NOW - TOKEN_CREATION_TIMESTAMP )
736+
737+
738+ class EasyClientTest (unittest .TestCase ):
739+
740+ def setUp (self ):
741+ self .tmp_dir = tempfile .TemporaryDirectory ()
742+ self .token_path = os .path .join (self .tmp_dir .name , 'token.json' )
743+ self .raw_token = {'token' : 'yes' }
744+
745+ def put_token (self ):
746+ with open (self .token_path , 'w' ) as f :
747+ f .write (json .dumps (self .raw_token ))
748+
749+
750+ @no_duplicates
751+ @patch ('schwab.auth.client_from_token_file' )
752+ @patch ('schwab.auth.client_from_login_flow' , new_callable = MockOAuthClient )
753+ @patch ('time.time' , MagicMock (return_value = MOCK_NOW ))
754+ def test_no_token (
755+ self , client_from_login_flow , client_from_token_file ):
756+ mock_client = MagicMock ()
757+ client_from_login_flow .return_value = mock_client
758+
759+ c = auth .easy_client (API_KEY , APP_SECRET , CALLBACK_URL , self .token_path )
760+
761+ assert c is mock_client
762+
763+
764+ @no_duplicates
765+ @patch ('schwab.auth.client_from_token_file' )
766+ @patch ('schwab.auth.client_from_login_flow' , new_callable = MockOAuthClient )
767+ @patch ('time.time' , MagicMock (return_value = MOCK_NOW ))
768+ def test_no_token_passing_parameters (
769+ self , client_from_login_flow , client_from_token_file ):
770+ mock_client = MagicMock ()
771+ client_from_login_flow .return_value = mock_client
772+
773+ c = auth .easy_client (
774+ API_KEY , APP_SECRET , CALLBACK_URL , self .token_path ,
775+ asyncio = 'asyncio' , enforce_enums = 'enforce_enums' ,
776+ callback_timeout = 'callback_timeout' , interactive = 'interactive' ,
777+ requested_browser = 'requested_browser' )
778+
779+ assert c is mock_client
780+
781+ client_from_login_flow .assert_called_once_with (
782+ API_KEY , APP_SECRET , CALLBACK_URL , self .token_path ,
783+ asyncio = 'asyncio' , enforce_enums = 'enforce_enums' ,
784+ callback_timeout = 'callback_timeout' , interactive = 'interactive' ,
785+ requested_browser = 'requested_browser' )
786+
787+
788+ @no_duplicates
789+ @patch ('schwab.auth.client_from_token_file' )
790+ @patch ('schwab.auth.client_from_login_flow' , new_callable = MockOAuthClient )
791+ @patch ('time.time' , MagicMock (return_value = MOCK_NOW ))
792+ def test_existing_token (
793+ self , client_from_login_flow , client_from_token_file ):
794+ self .put_token ()
795+
796+ mock_client = MagicMock ()
797+ client_from_token_file .return_value = mock_client
798+ mock_client .token_age .return_value = 1
799+
800+ c = auth .easy_client (API_KEY , APP_SECRET , CALLBACK_URL , self .token_path )
801+
802+ assert c is mock_client
803+
804+
805+ @no_duplicates
806+ @patch ('schwab.auth.client_from_token_file' )
807+ @patch ('schwab.auth.client_from_login_flow' , new_callable = MockOAuthClient )
808+ @patch ('time.time' , MagicMock (return_value = MOCK_NOW ))
809+ def test_existing_token_passing_parameters (
810+ self , client_from_login_flow , client_from_token_file ):
811+ self .put_token ()
812+
813+ mock_client = MagicMock ()
814+ client_from_token_file .return_value = mock_client
815+ mock_client .token_age .return_value = 1
816+
817+ c = auth .easy_client (API_KEY , APP_SECRET , CALLBACK_URL , self .token_path ,
818+ asyncio = 'asyncio' , enforce_enums = 'enforce_enums' )
819+
820+ assert c is mock_client
821+
822+ client_from_token_file .assert_called_once_with (
823+ self .token_path , API_KEY , APP_SECRET ,
824+ asyncio = 'asyncio' , enforce_enums = 'enforce_enums' )
825+
826+
827+ @no_duplicates
828+ @patch ('schwab.auth.client_from_token_file' )
829+ @patch ('schwab.auth.client_from_login_flow' , new_callable = MockOAuthClient )
830+ @patch ('time.time' , MagicMock (return_value = MOCK_NOW ))
831+ def test_token_too_old (
832+ self , client_from_login_flow , client_from_token_file ):
833+ self .put_token ()
834+
835+ mock_file_client = MagicMock ()
836+ client_from_token_file .return_value = mock_file_client
837+ mock_file_client .token_age .return_value = 9999999999
838+
839+ mock_browser_client = MagicMock ()
840+ client_from_login_flow .return_value = mock_browser_client
841+ mock_browser_client .token_age .return_value = 1
842+
843+ c = auth .easy_client (API_KEY , APP_SECRET , CALLBACK_URL , self .token_path )
844+
845+ assert c is mock_browser_client
846+
847+
848+ @no_duplicates
849+ @patch ('schwab.auth.client_from_token_file' )
850+ @patch ('schwab.auth.client_from_login_flow' , new_callable = MockOAuthClient )
851+ @patch ('time.time' , MagicMock (return_value = MOCK_NOW ))
852+ def test_negative_max_token_age (
853+ self , client_from_login_flow , client_from_token_file ):
854+ with self .assertRaisesRegex (
855+ ValueError , 'max_token_age must be positive, zero, or None' ):
856+ c = auth .easy_client (API_KEY , APP_SECRET , CALLBACK_URL ,
857+ self .token_path , max_token_age = - 1 )
858+
859+
860+ @no_duplicates
861+ @patch ('schwab.auth.client_from_token_file' )
862+ @patch ('schwab.auth.client_from_login_flow' , new_callable = MockOAuthClient )
863+ @patch ('time.time' , MagicMock (return_value = MOCK_NOW ))
864+ def test_none_max_token_age (
865+ self , client_from_login_flow , client_from_token_file ):
866+ self .put_token ()
867+
868+ mock_client = MagicMock ()
869+ client_from_token_file .return_value = mock_client
870+ mock_client .token_age .return_value = 9999999999
871+
872+ c = auth .easy_client (API_KEY , APP_SECRET , CALLBACK_URL , self .token_path ,
873+ max_token_age = None )
874+
875+ assert c is mock_client
876+
877+
878+ @no_duplicates
879+ @patch ('schwab.auth.client_from_token_file' )
880+ @patch ('schwab.auth.client_from_login_flow' , new_callable = MockOAuthClient )
881+ @patch ('time.time' , MagicMock (return_value = MOCK_NOW ))
882+ def test_zero_max_token_age (
883+ self , client_from_login_flow , client_from_token_file ):
884+ self .put_token ()
885+
886+ mock_client = MagicMock ()
887+ client_from_token_file .return_value = mock_client
888+ mock_client .token_age .return_value = 9999999999
889+
890+ c = auth .easy_client (API_KEY , APP_SECRET , CALLBACK_URL , self .token_path ,
891+ max_token_age = 0 )
892+
893+ assert c is mock_client
0 commit comments