async def test_cannot_execute_shell(): """The credential should raise CredentialUnavailableError when the subprocess doesn't start""" with mock.patch(SUBPROCESS_EXEC, mock.Mock(side_effect=OSError())): with pytest.raises(CredentialUnavailableError): credential = AzureCliCredential() await credential.get_token("scope")
async def test_get_token(): """The credential should parse the CLI's output to an AccessToken""" access_token = "access token" valid_seconds = 42 successful_output = json.dumps({ # expiresOn is a naive datetime representing valid_seconds from the epoch "expiresOn": datetime.fromtimestamp(valid_seconds).strftime("%Y-%m-%d %H:%M:%S.%f"), "accessToken": access_token, "subscription": "some-guid", "tenant": "some-guid", "tokenType": "Bearer", }) with mock.patch(SUBPROCESS_EXEC, mock_exec(successful_output)): credential = AzureCliCredential() token = await credential.get_token("scope") assert token.token == access_token assert type(token.expires_on) == int assert token.expires_on == valid_seconds
async def test_not_logged_in(): """When the CLI isn't logged in, the credential should raise an error containing the CLI's output""" output = "ERROR: Please run 'az login' to setup account." with mock.patch(SUBPROCESS_EXEC, mock_exec(output, return_code=1)): with pytest.raises(ClientAuthenticationError, match=output): credential = AzureCliCredential() await credential.get_token("scope")
async def test_cli_not_installed_windows(): """The credential should raise CredentialUnavailableError when the CLI isn't installed""" output = "'az' is not recognized as an internal or external command, operable program or batch file." with mock.patch(SUBPROCESS_EXEC, mock_exec(output, return_code=1)): with pytest.raises(CredentialUnavailableError, match=CLI_NOT_FOUND): credential = AzureCliCredential() await credential.get_token("scope")
async def test_cli_not_installed_linux(): """The credential should raise CredentialUnavailableError when the CLI isn't installed""" output = "/bin/sh: 1: az: not found" with mock.patch(SUBPROCESS_EXEC, mock_exec(output, return_code=127)): with pytest.raises(CredentialUnavailableError, match=CLI_NOT_FOUND): credential = AzureCliCredential() await credential.get_token("scope")
async def test_subprocess_error_does_not_expose_token(output): """Errors from the subprocess shouldn't expose access tokens in CLI output""" with mock.patch(SUBPROCESS_EXEC, mock_exec(output, return_code=1)): with pytest.raises(ClientAuthenticationError) as ex: credential = AzureCliCredential() await credential.get_token("scope") assert "secret value" not in str(ex.value) assert "secret value" not in repr(ex.value)
async def test_parsing_error_does_not_expose_token(output): """Errors during CLI output parsing shouldn't expose access tokens in that output""" with mock.patch(SUBPROCESS_EXEC, mock_exec(output)): with pytest.raises(ClientAuthenticationError) as ex: credential = AzureCliCredential() await credential.get_token("scope") assert "secret value" not in str(ex.value) assert "secret value" not in repr(ex.value)
async def test_windows_fallback(): """The credential should fall back to the sync implementation when not using ProactorEventLoop on Windows""" sync_get_token = mock.Mock() with mock.patch( "azure.identity.aio._credentials.azure_cli._SyncAzureCliCredential" ) as fallback: fallback.return_value = mock.Mock(get_token=sync_get_token) with mock.patch(AzureCliCredential.__module__ + ".asyncio.get_event_loop"): # asyncio.get_event_loop now returns Mock, i.e. never ProactorEventLoop credential = AzureCliCredential() await credential.get_token("scope") assert sync_get_token.call_count == 1
async def test_context_manager(): """The credential must be a context manager, although it does nothing as one because it has no transport""" async with AzureCliCredential(): pass