1+ from datetime import datetime , timedelta
12from unittest .mock import Mock
23
3- from databricks .sdk . credentials_provider import external_browser
4+ from databricks .sdk import credentials_provider , oauth , oidc
45
56
7+ # Tests for external_browser function
68def test_external_browser_refresh_success (mocker ):
79 """Tests successful refresh of existing credentials."""
810
@@ -21,7 +23,9 @@ def test_external_browser_refresh_success(mocker):
2123 mock_token_cache .load .return_value = mock_session_credentials
2224
2325 # Mock SessionCredentials.
24- want_credentials_provider = lambda c : "new_credentials"
26+ def want_credentials_provider (_ ):
27+ return "new_credentials"
28+
2529 mock_session_credentials .return_value = want_credentials_provider
2630
2731 # Inject the mock implementations.
@@ -30,7 +34,7 @@ def test_external_browser_refresh_success(mocker):
3034 return_value = mock_token_cache ,
3135 )
3236
33- got_credentials_provider = external_browser (mock_cfg )
37+ got_credentials_provider = credentials_provider . external_browser (mock_cfg )
3438
3539 mock_token_cache .load .assert_called_once ()
3640 mock_session_credentials .token .assert_called_once () # Verify token refresh was attempted
@@ -55,7 +59,9 @@ def test_external_browser_refresh_failure_new_oauth_flow(mocker):
5559 mock_token_cache .load .return_value = mock_session_credentials
5660
5761 # Mock SessionCredentials.
58- want_credentials_provider = lambda c : "new_credentials"
62+ def want_credentials_provider (_ ):
63+ return "new_credentials"
64+
5965 mock_session_credentials .return_value = want_credentials_provider
6066
6167 # Mock OAuthClient.
@@ -74,7 +80,7 @@ def test_external_browser_refresh_failure_new_oauth_flow(mocker):
7480 return_value = mock_oauth_client ,
7581 )
7682
77- got_credentials_provider = external_browser (mock_cfg )
83+ got_credentials_provider = credentials_provider . external_browser (mock_cfg )
7884
7985 mock_token_cache .load .assert_called_once ()
8086 mock_session_credentials .token .assert_called_once () # Refresh attempt
@@ -101,7 +107,10 @@ def test_external_browser_no_cached_credentials(mocker):
101107
102108 # Mock SessionCredentials.
103109 mock_session_credentials = Mock ()
104- want_credentials_provider = lambda c : "new_credentials"
110+
111+ def want_credentials_provider (_ ):
112+ return "new_credentials"
113+
105114 mock_session_credentials .return_value = want_credentials_provider
106115
107116 # Mock OAuthClient.
@@ -120,7 +129,7 @@ def test_external_browser_no_cached_credentials(mocker):
120129 return_value = mock_oauth_client ,
121130 )
122131
123- got_credentials_provider = external_browser (mock_cfg )
132+ got_credentials_provider = credentials_provider . external_browser (mock_cfg )
124133
125134 mock_token_cache .load .assert_called_once ()
126135 mock_oauth_client .initiate_consent .assert_called_once ()
@@ -158,8 +167,58 @@ def test_external_browser_consent_fails(mocker):
158167 return_value = mock_oauth_client ,
159168 )
160169
161- got_credentials_provider = external_browser (mock_cfg )
170+ got_credentials_provider = credentials_provider . external_browser (mock_cfg )
162171
163172 mock_token_cache .load .assert_called_once ()
164173 mock_oauth_client .initiate_consent .assert_called_once ()
165174 assert got_credentials_provider is None
175+
176+
177+ def test_oidc_credentials_provider_invalid_id_token_source ():
178+ # Use a mock config object to avoid initializing the auth initialization.
179+ mock_cfg = Mock ()
180+ mock_cfg .host = "https://test-workspace.cloud.databricks.com"
181+ mock_cfg .oidc_endpoints = Mock ()
182+ mock_cfg .oidc_endpoints .token_endpoint = "https://test-workspace.cloud.databricks.com/oidc/v1/token"
183+ mock_cfg .client_id = "test-client-id"
184+ mock_cfg .account_id = "test-account-id"
185+ mock_cfg .disable_async_token_refresh = True
186+
187+ # An IdTokenSource that raises an error when id_token() is called.
188+ id_token_source = Mock ()
189+ id_token_source .id_token .side_effect = ValueError ("Invalid ID token source" )
190+
191+ cp = credentials_provider ._oidc_credentials_provider (mock_cfg , id_token_source )
192+ assert cp is None
193+
194+
195+ def test_oidc_credentials_provider_valid_id_token_source (mocker ):
196+ # Use a mock config object to avoid initializing the auth initialization.
197+ mock_cfg = Mock ()
198+ mock_cfg .host = "https://test-workspace.cloud.databricks.com"
199+ mock_cfg .oidc_endpoints = Mock ()
200+ mock_cfg .oidc_endpoints .token_endpoint = "https://test-workspace.cloud.databricks.com/oidc/v1/token"
201+ mock_cfg .client_id = "test-client-id"
202+ mock_cfg .account_id = "test-account-id"
203+ mock_cfg .disable_async_token_refresh = True
204+
205+ # A valid IdTokenSource that never raises an error.
206+ id_token_source = Mock ()
207+ id_token_source .id_token .return_value = oidc .IdToken (jwt = "test-jwt-token" )
208+
209+ # Mock the _exchange_id_token method on DatabricksOidcTokenSource to return
210+ # a valid oauth.Token based on the IdToken.
211+ def mock_exchange_id_token (id_token : oidc .IdToken ):
212+ # Create a token based on the input ID token
213+ return oauth .Token (
214+ access_token = f"exchanged-{ id_token .jwt } " , token_type = "Bearer" , expiry = datetime .now () + timedelta (hours = 1 )
215+ )
216+
217+ mocker .patch .object (oidc .DatabricksOidcTokenSource , "_exchange_id_token" , side_effect = mock_exchange_id_token )
218+
219+ cp = credentials_provider ._oidc_credentials_provider (mock_cfg , id_token_source )
220+ assert cp is not None
221+
222+ # Test that the credentials provider returns the expected headers
223+ headers = cp ()
224+ assert headers == {"Authorization" : "Bearer exchanged-test-jwt-token" }
0 commit comments