33
44import pytest
55
6- from datahub .cli .sso_cli import browser_sso_login
6+ from datahub .cli .sso_cli import _warn_about_existing_cli_tokens , browser_sso_login
77
88
99@pytest .fixture
@@ -64,8 +64,14 @@ def test_extracts_cookies_and_generates_token(self, mock_playwright: dict) -> No
6464 with patch ("datahub.cli.sso_cli.requests" ) as mock_requests :
6565 mock_session = MagicMock ()
6666 mock_requests .Session .return_value = mock_session
67- mock_response = MagicMock ()
68- mock_response .json .return_value = {
67+
68+ # First call: listAccessTokens (warning check), second: createAccessToken
69+ list_response = MagicMock ()
70+ list_response .json .return_value = {
71+ "data" : {"listAccessTokens" : {"total" : 0 , "tokens" : []}}
72+ }
73+ create_response = MagicMock ()
74+ create_response .json .return_value = {
6975 "data" : {
7076 "createAccessToken" : {
7177 "accessToken" : "generated-sso-token-xyz" ,
@@ -76,7 +82,7 @@ def test_extracts_cookies_and_generates_token(self, mock_playwright: dict) -> No
7682 }
7783 }
7884 }
79- mock_session .post .return_value = mock_response
85+ mock_session .post .side_effect = [ list_response , create_response ]
8086
8187 token_name , access_token = browser_sso_login (
8288 "http://localhost:9002" , "ONE_HOUR"
@@ -88,16 +94,16 @@ def test_extracts_cookies_and_generates_token(self, mock_playwright: dict) -> No
8894 # Verify cookies were set on the session
8995 assert mock_session .cookies .set .call_count == 2
9096
91- # Verify GraphQL call was made
92- mock_session .post .assert_called_once ()
93- call_args = mock_session .post .call_args
94- assert call_args [0 ][0 ] == "http://localhost:9002/api/v2/graphql"
95- assert "createAccessToken" in call_args [1 ]["json" ]["query" ]
97+ # Verify GraphQL calls were made (list + create)
98+ assert mock_session .post .call_count == 2
99+ create_call = mock_session .post .call_args_list [ 1 ]
100+ assert create_call [0 ][0 ] == "http://localhost:9002/api/v2/graphql"
101+ assert "createAccessToken" in create_call [1 ]["json" ]["query" ]
96102 assert (
97- call_args [1 ]["json" ]["variables" ]["input" ]["actorUrn" ]
103+ create_call [1 ]["json" ]["variables" ]["input" ]["actorUrn" ]
98104 == "urn:li:corpuser:john.doe"
99105 )
100- assert call_args [1 ]["json" ]["variables" ]["input" ]["duration" ] == "ONE_HOUR"
106+ assert create_call [1 ]["json" ]["variables" ]["input" ]["duration" ] == "ONE_HOUR"
101107
102108 def test_timeout_raises_error (self , mock_playwright : dict ) -> None :
103109 """Verify timeout if login never completes."""
@@ -150,3 +156,42 @@ def test_graphql_error_raises(self, mock_playwright: dict) -> None:
150156
151157 with pytest .raises (Exception , match = "Failed to generate access token" ):
152158 browser_sso_login ("http://localhost:9002" , "ONE_HOUR" )
159+
160+
161+ class TestWarnAboutExistingCliTokens :
162+ def test_warns_about_existing_cli_tokens (
163+ self , capsys : pytest .CaptureFixture
164+ ) -> None :
165+ session = MagicMock ()
166+ response = MagicMock ()
167+ response .json .return_value = {
168+ "data" : {
169+ "listAccessTokens" : {
170+ "total" : 4 ,
171+ "tokens" : [
172+ {"name" : "cli token 2026-03-01T10:00:00" },
173+ {"name" : "cli token 2026-03-02T10:00:00" },
174+ {"name" : "cli token 2026-03-03T10:00:00" },
175+ {"name" : "manually created token" },
176+ ],
177+ }
178+ }
179+ }
180+ session .post .return_value = response
181+
182+ _warn_about_existing_cli_tokens (
183+ session , "https://example.com" , "urn:li:corpuser:alice"
184+ )
185+
186+ captured = capsys .readouterr ()
187+ assert "3 existing CLI token(s)" in captured .out
188+ assert "https://example.com/settings/tokens" in captured .out
189+
190+ def test_warning_failure_does_not_block (self ) -> None :
191+ session = MagicMock ()
192+ session .post .side_effect = Exception ("network error" )
193+
194+ # Should not raise — failure is silently logged
195+ _warn_about_existing_cli_tokens (
196+ session , "https://example.com" , "urn:li:corpuser:alice"
197+ )
0 commit comments