From bc332e9e85c7beebe24244331cd3baf414224ae9 Mon Sep 17 00:00:00 2001 From: Charles Lowell Date: Fri, 4 Oct 2019 10:36:47 -0700 Subject: [PATCH 01/11] move live test logic into a helper --- .../azure-identity/tests/test_live.py | 27 +++++++---------- .../azure-identity/tests/test_live_async.py | 29 +++++++------------ 2 files changed, 22 insertions(+), 34 deletions(-) diff --git a/sdk/identity/azure-identity/tests/test_live.py b/sdk/identity/azure-identity/tests/test_live.py index e5cf87c0c8a3..9686842b3791 100644 --- a/sdk/identity/azure-identity/tests/test_live.py +++ b/sdk/identity/azure-identity/tests/test_live.py @@ -8,16 +8,20 @@ ARM_SCOPE = "https://management.azure.com/.default" +def get_token(credential): + token = credential.get_token(ARM_SCOPE) + assert token + assert token.token + assert token.expires_on + + def test_certificate_credential(live_certificate_settings): credential = CertificateCredential( live_certificate_settings["tenant_id"], live_certificate_settings["client_id"], live_certificate_settings["cert_path"], ) - token = credential.get_token(ARM_SCOPE) - assert token - assert token.token - assert token.expires_on + get_token(credential) def test_client_secret_credential(live_identity_settings): @@ -26,18 +30,12 @@ def test_client_secret_credential(live_identity_settings): live_identity_settings["client_id"], live_identity_settings["client_secret"], ) - token = credential.get_token(ARM_SCOPE) - assert token - assert token.token - assert token.expires_on + get_token(credential) def test_default_credential(live_identity_settings): credential = DefaultAzureCredential() - token = credential.get_token(ARM_SCOPE) - assert token - assert token.token - assert token.expires_on + get_token(credential) def test_confidential_client_credential(live_identity_settings): @@ -47,7 +45,4 @@ def test_confidential_client_credential(live_identity_settings): authority=KnownAuthorities.AZURE_PUBLIC_CLOUD, tenant_id=live_identity_settings["tenant_id"], ) - token = credential.get_token(ARM_SCOPE) - assert token - assert token.token - assert token.expires_on + get_token(credential) diff --git a/sdk/identity/azure-identity/tests/test_live_async.py b/sdk/identity/azure-identity/tests/test_live_async.py index 9ede517c6a3f..2c358121ddc7 100644 --- a/sdk/identity/azure-identity/tests/test_live_async.py +++ b/sdk/identity/azure-identity/tests/test_live_async.py @@ -4,17 +4,19 @@ # ------------------------------------ import os -try: - from unittest import mock -except ImportError: # python < 3.3 - import mock # type: ignore - -from azure.identity.aio import DefaultAzureCredential, CertificateCredential, ClientSecretCredential import pytest +from azure.identity.aio import DefaultAzureCredential, CertificateCredential, ClientSecretCredential ARM_SCOPE = "https://management.azure.com/.default" +async def get_token(credential): + token = await credential.get_token(ARM_SCOPE) + assert token + assert token.token + assert token.expires_on + + @pytest.mark.asyncio async def test_certificate_credential(live_certificate_settings): credential = CertificateCredential( @@ -22,10 +24,7 @@ async def test_certificate_credential(live_certificate_settings): live_certificate_settings["client_id"], live_certificate_settings["cert_path"], ) - token = await credential.get_token(ARM_SCOPE) - assert token - assert token.token - assert token.expires_on + await get_token(credential) @pytest.mark.asyncio @@ -35,16 +34,10 @@ async def test_client_secret_credential(live_identity_settings): live_identity_settings["client_id"], live_identity_settings["client_secret"], ) - token = await credential.get_token(ARM_SCOPE) - assert token - assert token.token - assert token.expires_on + await get_token(credential) @pytest.mark.asyncio async def test_default_credential(live_identity_settings): credential = DefaultAzureCredential() - token = await credential.get_token(ARM_SCOPE) - assert token - assert token.token - assert token.expires_on + await get_token(credential) From 8bb23c722736cbf7663925105a6d8d6c7e34e8f6 Mon Sep 17 00:00:00 2001 From: Charles Lowell Date: Fri, 4 Oct 2019 10:55:02 -0700 Subject: [PATCH 02/11] test managed identity scenarios when possible --- sdk/identity/azure-identity/conftest.py | 11 +++--- .../azure-identity/tests/test_imds.py | 11 ------ .../azure-identity/tests/test_imds_async.py | 14 -------- .../azure-identity/tests/test_live.py | 35 ++++++++++++++++++- .../azure-identity/tests/test_live_async.py | 27 ++++++++++++++ 5 files changed, 66 insertions(+), 32 deletions(-) delete mode 100644 sdk/identity/azure-identity/tests/test_imds.py delete mode 100644 sdk/identity/azure-identity/tests/test_imds_async.py diff --git a/sdk/identity/azure-identity/conftest.py b/sdk/identity/azure-identity/conftest.py index d2e94c1e9cbd..2907a4bf412a 100644 --- a/sdk/identity/azure-identity/conftest.py +++ b/sdk/identity/azure-identity/conftest.py @@ -8,12 +8,9 @@ import pytest from azure.identity._constants import EnvironmentVariables -# IMDS tests must be run explicitly -collect_ignore_glob = ["*imds*"] # pylint:disable=invalid-name - -# Ignore collection of async tests on unsupported platforms +# Ignore async tests on unsupported platforms if sys.version_info < (3, 5): - collect_ignore_glob.append("*_async.py") + collect_ignore_glob = ["*_async.py"] @pytest.fixture() @@ -40,7 +37,9 @@ def live_identity_settings(): # pylint:disable=inconsistent-return-statements @pytest.fixture() -def live_certificate_settings(live_identity_settings): # pylint:disable=inconsistent-return-statements,redefined-outer-name +def live_certificate_settings( + live_identity_settings +): # pylint:disable=inconsistent-return-statements,redefined-outer-name """Fixture for live tests needing a certificate. Skips them when environment configuration is incomplete. """ diff --git a/sdk/identity/azure-identity/tests/test_imds.py b/sdk/identity/azure-identity/tests/test_imds.py deleted file mode 100644 index df1eb937713d..000000000000 --- a/sdk/identity/azure-identity/tests/test_imds.py +++ /dev/null @@ -1,11 +0,0 @@ -# ------------------------------------ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# ------------------------------------ -from azure.identity._internal import ImdsCredential - - -def test_imds_credential(): - credential = ImdsCredential() - token = credential.get_token("https://management.azure.com/.default") - assert token diff --git a/sdk/identity/azure-identity/tests/test_imds_async.py b/sdk/identity/azure-identity/tests/test_imds_async.py deleted file mode 100644 index 2f8466f64565..000000000000 --- a/sdk/identity/azure-identity/tests/test_imds_async.py +++ /dev/null @@ -1,14 +0,0 @@ -# ------------------------------------ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. -# ------------------------------------ -import pytest - -from azure.identity.aio._internal import ImdsCredential - - -@pytest.mark.asyncio -async def test_imds_credential_async(): - credential = ImdsCredential() - token = await credential.get_token("https://management.azure.com/.default") - assert token diff --git a/sdk/identity/azure-identity/tests/test_live.py b/sdk/identity/azure-identity/tests/test_live.py index 9686842b3791..820781d88a51 100644 --- a/sdk/identity/azure-identity/tests/test_live.py +++ b/sdk/identity/azure-identity/tests/test_live.py @@ -2,7 +2,19 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. # ------------------------------------ -from azure.identity import DefaultAzureCredential, CertificateCredential, ClientSecretCredential, KnownAuthorities +import os + +import pytest + +from azure.identity import ( + DefaultAzureCredential, + CertificateCredential, + ClientSecretCredential, + KnownAuthorities, + ManagedIdentityCredential, +) +from azure.identity._constants import EnvironmentVariables +from azure.identity._credentials.managed_identity import ImdsCredential, MsiCredential from azure.identity._internal import ConfidentialClientCredential ARM_SCOPE = "https://management.azure.com/.default" @@ -46,3 +58,24 @@ def test_confidential_client_credential(live_identity_settings): tenant_id=live_identity_settings["tenant_id"], ) get_token(credential) + + +@pytest.mark.skipif("TEST_IMDS" not in os.environ, reason="To test IMDS authentication, set $TEST_IMDS with any value") +def test_imds_credential(): + get_token(ImdsCredential()) + + +@pytest.mark.skipif( + EnvironmentVariables.MSI_ENDPOINT not in os.environ or EnvironmentVariables.MSI_SECRET in os.environ, + reason="Legacy MSI unavailable", +) +def test_msi_legacy(): + get_token(MsiCredential()) + + +@pytest.mark.skipif( + EnvironmentVariables.MSI_ENDPOINT not in os.environ or EnvironmentVariables.MSI_SECRET not in os.environ, + reason="App Service MSI unavailable", +) +def test_msi_app_service(): + get_token(MsiCredential()) diff --git a/sdk/identity/azure-identity/tests/test_live_async.py b/sdk/identity/azure-identity/tests/test_live_async.py index 2c358121ddc7..3be8e93c47e6 100644 --- a/sdk/identity/azure-identity/tests/test_live_async.py +++ b/sdk/identity/azure-identity/tests/test_live_async.py @@ -5,7 +5,10 @@ import os import pytest + +from azure.identity import EnvironmentVariables from azure.identity.aio import DefaultAzureCredential, CertificateCredential, ClientSecretCredential +from azure.identity.aio._credentials.managed_identity import ImdsCredential, MsiCredential ARM_SCOPE = "https://management.azure.com/.default" @@ -41,3 +44,27 @@ async def test_client_secret_credential(live_identity_settings): async def test_default_credential(live_identity_settings): credential = DefaultAzureCredential() await get_token(credential) + + +@pytest.mark.skipif("TEST_IMDS" not in os.environ, reason="To test IMDS authentication, set $TEST_IMDS with any value") +@pytest.mark.asyncio +async def test_imds_credential(): + await get_token(ImdsCredential()) + + +@pytest.mark.skipif( + EnvironmentVariables.MSI_ENDPOINT not in os.environ or EnvironmentVariables.MSI_SECRET in os.environ, + reason="Legacy MSI unavailable", +) +@pytest.mark.asyncio +async def test_msi_legacy(): + await get_token(MsiCredential()) + + +@pytest.mark.skipif( + EnvironmentVariables.MSI_ENDPOINT not in os.environ or EnvironmentVariables.MSI_SECRET not in os.environ, + reason="App Service MSI unavailable", +) +@pytest.mark.asyncio +async def test_msi_app_service(): + await get_token(MsiCredential()) From d7b02336cdb49f673ef9c7aedaa77b1a5f1f9a58 Mon Sep 17 00:00:00 2001 From: Charles Lowell Date: Fri, 4 Oct 2019 11:06:36 -0700 Subject: [PATCH 03/11] rename fixtures --- sdk/identity/azure-identity/conftest.py | 8 +++---- .../azure-identity/tests/test_live.py | 24 +++++++++---------- .../azure-identity/tests/test_live_async.py | 16 ++++++------- 3 files changed, 21 insertions(+), 27 deletions(-) diff --git a/sdk/identity/azure-identity/conftest.py b/sdk/identity/azure-identity/conftest.py index 2907a4bf412a..3f57268798ee 100644 --- a/sdk/identity/azure-identity/conftest.py +++ b/sdk/identity/azure-identity/conftest.py @@ -14,7 +14,7 @@ @pytest.fixture() -def live_identity_settings(): # pylint:disable=inconsistent-return-statements +def live_service_principal(): # pylint:disable=inconsistent-return-statements """Fixture for live Identity tests. Skips them when environment configuration is incomplete.""" missing_variables = [ @@ -37,9 +37,7 @@ def live_identity_settings(): # pylint:disable=inconsistent-return-statements @pytest.fixture() -def live_certificate_settings( - live_identity_settings -): # pylint:disable=inconsistent-return-statements,redefined-outer-name +def live_certificate(live_service_principal): # pylint:disable=inconsistent-return-statements,redefined-outer-name """Fixture for live tests needing a certificate. Skips them when environment configuration is incomplete. """ @@ -53,6 +51,6 @@ def live_certificate_settings( try: with open(pem_path, "w") as pem_file: pem_file.write(pem_content) - return dict(live_identity_settings, cert_path=pem_path) + return dict(live_service_principal, cert_path=pem_path) except IOError as ex: pytest.skip("Failed to write file '{}': {}".format(pem_path, ex)) diff --git a/sdk/identity/azure-identity/tests/test_live.py b/sdk/identity/azure-identity/tests/test_live.py index 820781d88a51..4b5993ca816c 100644 --- a/sdk/identity/azure-identity/tests/test_live.py +++ b/sdk/identity/azure-identity/tests/test_live.py @@ -27,35 +27,33 @@ def get_token(credential): assert token.expires_on -def test_certificate_credential(live_certificate_settings): +def test_certificate_credential(live_certificate): credential = CertificateCredential( - live_certificate_settings["tenant_id"], - live_certificate_settings["client_id"], - live_certificate_settings["cert_path"], + live_certificate["client_id"], live_certificate["tenant_id"], live_certificate["cert_path"] ) get_token(credential) -def test_client_secret_credential(live_identity_settings): +def test_client_secret_credential(live_service_principal): credential = ClientSecretCredential( - live_identity_settings["tenant_id"], - live_identity_settings["client_id"], - live_identity_settings["client_secret"], + live_service_principal["client_id"], + live_service_principal["client_secret"], + live_service_principal["tenant_id"], ) get_token(credential) -def test_default_credential(live_identity_settings): +def test_default_credential(live_service_principal): credential = DefaultAzureCredential() get_token(credential) -def test_confidential_client_credential(live_identity_settings): +def test_confidential_client_credential(live_service_principal): credential = ConfidentialClientCredential( - client_id=live_identity_settings["client_id"], - client_credential=live_identity_settings["client_secret"], + client_id=live_service_principal["client_id"], + client_credential=live_service_principal["client_secret"], authority=KnownAuthorities.AZURE_PUBLIC_CLOUD, - tenant_id=live_identity_settings["tenant_id"], + tenant=live_service_principal["tenant_id"], ) get_token(credential) diff --git a/sdk/identity/azure-identity/tests/test_live_async.py b/sdk/identity/azure-identity/tests/test_live_async.py index 3be8e93c47e6..a1ca43a7adc8 100644 --- a/sdk/identity/azure-identity/tests/test_live_async.py +++ b/sdk/identity/azure-identity/tests/test_live_async.py @@ -21,27 +21,25 @@ async def get_token(credential): @pytest.mark.asyncio -async def test_certificate_credential(live_certificate_settings): +async def test_certificate_credential(live_certificate): credential = CertificateCredential( - live_certificate_settings["tenant_id"], - live_certificate_settings["client_id"], - live_certificate_settings["cert_path"], + live_certificate["client_id"], live_certificate["tenant_id"], live_certificate["cert_path"] ) await get_token(credential) @pytest.mark.asyncio -async def test_client_secret_credential(live_identity_settings): +async def test_client_secret_credential(live_service_principal): credential = ClientSecretCredential( - live_identity_settings["tenant_id"], - live_identity_settings["client_id"], - live_identity_settings["client_secret"], + live_service_principal["client_id"], + live_service_principal["client_secret"], + live_service_principal["tenant_id"], ) await get_token(credential) @pytest.mark.asyncio -async def test_default_credential(live_identity_settings): +async def test_default_credential(live_service_principal): credential = DefaultAzureCredential() await get_token(credential) From c72e52e957b0c6e7a454f186c4bc6366b8119b57 Mon Sep 17 00:00:00 2001 From: Charles Lowell Date: Fri, 4 Oct 2019 13:13:09 -0700 Subject: [PATCH 04/11] live test for UsernamePasswordCredential --- sdk/identity/azure-identity/conftest.py | 16 +++++++++++++++- sdk/identity/azure-identity/tests/test_live.py | 11 +++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/sdk/identity/azure-identity/conftest.py b/sdk/identity/azure-identity/conftest.py index 3f57268798ee..1922d0a4a6dd 100644 --- a/sdk/identity/azure-identity/conftest.py +++ b/sdk/identity/azure-identity/conftest.py @@ -6,7 +6,7 @@ import sys import pytest -from azure.identity._constants import EnvironmentVariables +from azure.identity._constants import AZURE_CLI_CLIENT_ID, EnvironmentVariables # Ignore async tests on unsupported platforms if sys.version_info < (3, 5): @@ -54,3 +54,17 @@ def live_certificate(live_service_principal): # pylint:disable=inconsistent-ret return dict(live_service_principal, cert_path=pem_path) except IOError as ex: pytest.skip("Failed to write file '{}': {}".format(pem_path, ex)) + + +@pytest.fixture() +def live_user_details(): + user_details = { + "client_id": AZURE_CLI_CLIENT_ID, + "username": os.environ.get(EnvironmentVariables.AZURE_USERNAME), + "password": os.environ.get(EnvironmentVariables.AZURE_PASSWORD), + "tenant": os.environ.get("USER_TENANT"), + } + if None in user_details.values(): + pytest.skip("To test username/password authentication, set $AZURE_USERNAME, $AZURE_PASSWORD, $USER_TENANT") + else: + return user_details diff --git a/sdk/identity/azure-identity/tests/test_live.py b/sdk/identity/azure-identity/tests/test_live.py index 4b5993ca816c..310bb389bdd9 100644 --- a/sdk/identity/azure-identity/tests/test_live.py +++ b/sdk/identity/azure-identity/tests/test_live.py @@ -12,6 +12,7 @@ ClientSecretCredential, KnownAuthorities, ManagedIdentityCredential, + UsernamePasswordCredential, ) from azure.identity._constants import EnvironmentVariables from azure.identity._credentials.managed_identity import ImdsCredential, MsiCredential @@ -77,3 +78,13 @@ def test_msi_legacy(): ) def test_msi_app_service(): get_token(MsiCredential()) + + +def test_username_password_auth(live_user_details): + credential = UsernamePasswordCredential( + client_id=live_user_details["client_id"], + username=live_user_details["username"], + password=live_user_details["password"], + tenant=live_user_details["tenant"], + ) + get_token(credential) From 73987b002b4049a9a5c6f53e1b93af419bd85c09 Mon Sep 17 00:00:00 2001 From: Charles Lowell Date: Tue, 8 Oct 2019 13:48:25 -0700 Subject: [PATCH 05/11] manual tests of browser scenarios --- sdk/identity/azure-identity/conftest.py | 22 +++++++++++++++++++ .../azure-identity/tests/test_live.py | 22 ++++++++++++++++++- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/sdk/identity/azure-identity/conftest.py b/sdk/identity/azure-identity/conftest.py index 1922d0a4a6dd..a9e56d7d5eef 100644 --- a/sdk/identity/azure-identity/conftest.py +++ b/sdk/identity/azure-identity/conftest.py @@ -12,6 +12,22 @@ if sys.version_info < (3, 5): collect_ignore_glob = ["*_async.py"] +run_manual_tests = "manual" in sys.argv +stdout_captured = True + + +def pytest_configure(config): + config.addinivalue_line("markers", "manual: the test requires manual interaction, e.g. browser authentication") + global stdout_captured + stdout_captured = config.getoption("capture") != "no" + + +def pytest_runtest_setup(item): + # ensure manual tests only run when manual marker is selected with 'pytest -m manual' + if any(mark.name == "manual" for mark in item.own_markers): + if not run_manual_tests: + pytest.skip("To run manual tests, select 'manual' marker with '-m manual'") + @pytest.fixture() def live_service_principal(): # pylint:disable=inconsistent-return-statements @@ -68,3 +84,9 @@ def live_user_details(): pytest.skip("To test username/password authentication, set $AZURE_USERNAME, $AZURE_PASSWORD, $USER_TENANT") else: return user_details + + +@pytest.fixture() +def prints(): + if stdout_captured: + pytest.skip("This test prints to stdout. Run pytest with '-s' to ensure the output is visible.") diff --git a/sdk/identity/azure-identity/tests/test_live.py b/sdk/identity/azure-identity/tests/test_live.py index 310bb389bdd9..bcddc118dc89 100644 --- a/sdk/identity/azure-identity/tests/test_live.py +++ b/sdk/identity/azure-identity/tests/test_live.py @@ -10,11 +10,13 @@ DefaultAzureCredential, CertificateCredential, ClientSecretCredential, + DeviceCodeCredential, KnownAuthorities, + InteractiveBrowserCredential, ManagedIdentityCredential, UsernamePasswordCredential, ) -from azure.identity._constants import EnvironmentVariables +from azure.identity._constants import AZURE_CLI_CLIENT_ID, EnvironmentVariables from azure.identity._credentials.managed_identity import ImdsCredential, MsiCredential from azure.identity._internal import ConfidentialClientCredential @@ -88,3 +90,21 @@ def test_username_password_auth(live_user_details): tenant=live_user_details["tenant"], ) get_token(credential) + + +@pytest.mark.manual +def test_device_code(prints): + import webbrowser + + def prompt(url, user_code, _): + print("opening a browser to '{}', enter device code {}".format(url, user_code)) + webbrowser.open_new_tab(url) + + credential = DeviceCodeCredential(client_id=AZURE_CLI_CLIENT_ID, prompt_callback=prompt, timeout=25) + get_token(credential) + + +@pytest.mark.manual +def test_browser_auth(): + credential = InteractiveBrowserCredential(client_id=AZURE_CLI_CLIENT_ID, timeout=25) + get_token(credential) From 97cc7516d01bbd39717188fe7938684a61527d5e Mon Sep 17 00:00:00 2001 From: Charles Lowell Date: Tue, 8 Oct 2019 14:05:28 -0700 Subject: [PATCH 06/11] fix import --- sdk/identity/azure-identity/tests/test_live_async.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/identity/azure-identity/tests/test_live_async.py b/sdk/identity/azure-identity/tests/test_live_async.py index a1ca43a7adc8..0bc1b131af5a 100644 --- a/sdk/identity/azure-identity/tests/test_live_async.py +++ b/sdk/identity/azure-identity/tests/test_live_async.py @@ -6,7 +6,7 @@ import pytest -from azure.identity import EnvironmentVariables +from azure.identity._constants import EnvironmentVariables from azure.identity.aio import DefaultAzureCredential, CertificateCredential, ClientSecretCredential from azure.identity.aio._credentials.managed_identity import ImdsCredential, MsiCredential From d758b38544e434a7ac64b3bef351d2f62ddd0d89 Mon Sep 17 00:00:00 2001 From: Charles Lowell Date: Wed, 9 Oct 2019 13:11:03 -0700 Subject: [PATCH 07/11] test user-specified managed identity --- sdk/identity/azure-identity/conftest.py | 5 +++++ sdk/identity/azure-identity/tests/test_live.py | 12 +++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/sdk/identity/azure-identity/conftest.py b/sdk/identity/azure-identity/conftest.py index a9e56d7d5eef..d14955455a9f 100644 --- a/sdk/identity/azure-identity/conftest.py +++ b/sdk/identity/azure-identity/conftest.py @@ -90,3 +90,8 @@ def live_user_details(): def prints(): if stdout_captured: pytest.skip("This test prints to stdout. Run pytest with '-s' to ensure the output is visible.") + + +@pytest.fixture() +def managed_identity_id(): + return os.environ.get("MANAGED_IDENTITY_ID") diff --git a/sdk/identity/azure-identity/tests/test_live.py b/sdk/identity/azure-identity/tests/test_live.py index bcddc118dc89..952bae300b42 100644 --- a/sdk/identity/azure-identity/tests/test_live.py +++ b/sdk/identity/azure-identity/tests/test_live.py @@ -62,24 +62,30 @@ def test_confidential_client_credential(live_service_principal): @pytest.mark.skipif("TEST_IMDS" not in os.environ, reason="To test IMDS authentication, set $TEST_IMDS with any value") -def test_imds_credential(): +def test_imds_credential(managed_identity_id): get_token(ImdsCredential()) + if managed_identity_id: + get_token(ImdsCredential(client_id=managed_identity_id)) @pytest.mark.skipif( EnvironmentVariables.MSI_ENDPOINT not in os.environ or EnvironmentVariables.MSI_SECRET in os.environ, reason="Legacy MSI unavailable", ) -def test_msi_legacy(): +def test_msi_legacy(managed_identity_id): get_token(MsiCredential()) + if managed_identity_id: + get_token(ImdsCredential(client_id=managed_identity_id)) @pytest.mark.skipif( EnvironmentVariables.MSI_ENDPOINT not in os.environ or EnvironmentVariables.MSI_SECRET not in os.environ, reason="App Service MSI unavailable", ) -def test_msi_app_service(): +def test_msi_app_service(managed_identity_id): get_token(MsiCredential()) + if managed_identity_id: + get_token(ImdsCredential(client_id=managed_identity_id)) def test_username_password_auth(live_user_details): From 4d72cda7d78acc266011bba57fd02fa738d58213 Mon Sep 17 00:00:00 2001 From: Charles Lowell Date: Tue, 22 Oct 2019 08:54:00 -0700 Subject: [PATCH 08/11] fix positional arg order --- sdk/identity/azure-identity/tests/test_live.py | 8 ++++---- sdk/identity/azure-identity/tests/test_live_async.py | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/sdk/identity/azure-identity/tests/test_live.py b/sdk/identity/azure-identity/tests/test_live.py index 952bae300b42..d64b46f314b8 100644 --- a/sdk/identity/azure-identity/tests/test_live.py +++ b/sdk/identity/azure-identity/tests/test_live.py @@ -32,16 +32,16 @@ def get_token(credential): def test_certificate_credential(live_certificate): credential = CertificateCredential( - live_certificate["client_id"], live_certificate["tenant_id"], live_certificate["cert_path"] + live_certificate["tenant_id"], live_certificate["client_id"], live_certificate["cert_path"] ) get_token(credential) def test_client_secret_credential(live_service_principal): credential = ClientSecretCredential( + live_service_principal["tenant_id"], live_service_principal["client_id"], live_service_principal["client_secret"], - live_service_principal["tenant_id"], ) get_token(credential) @@ -56,7 +56,7 @@ def test_confidential_client_credential(live_service_principal): client_id=live_service_principal["client_id"], client_credential=live_service_principal["client_secret"], authority=KnownAuthorities.AZURE_PUBLIC_CLOUD, - tenant=live_service_principal["tenant_id"], + tenant_id=live_service_principal["tenant_id"], ) get_token(credential) @@ -93,7 +93,7 @@ def test_username_password_auth(live_user_details): client_id=live_user_details["client_id"], username=live_user_details["username"], password=live_user_details["password"], - tenant=live_user_details["tenant"], + tenant_id=live_user_details["tenant"], ) get_token(credential) diff --git a/sdk/identity/azure-identity/tests/test_live_async.py b/sdk/identity/azure-identity/tests/test_live_async.py index 0bc1b131af5a..13b26a721734 100644 --- a/sdk/identity/azure-identity/tests/test_live_async.py +++ b/sdk/identity/azure-identity/tests/test_live_async.py @@ -23,7 +23,7 @@ async def get_token(credential): @pytest.mark.asyncio async def test_certificate_credential(live_certificate): credential = CertificateCredential( - live_certificate["client_id"], live_certificate["tenant_id"], live_certificate["cert_path"] + live_certificate["tenant_id"], live_certificate["client_id"], live_certificate["cert_path"] ) await get_token(credential) @@ -31,9 +31,9 @@ async def test_certificate_credential(live_certificate): @pytest.mark.asyncio async def test_client_secret_credential(live_service_principal): credential = ClientSecretCredential( + live_service_principal["tenant_id"], live_service_principal["client_id"], live_service_principal["client_secret"], - live_service_principal["tenant_id"], ) await get_token(credential) From e8096e6c353082cfa27501a6fbf4fbd7e8929c30 Mon Sep 17 00:00:00 2001 From: Charles Lowell Date: Thu, 31 Oct 2019 14:21:00 -0700 Subject: [PATCH 09/11] increase timeouts --- sdk/identity/azure-identity/tests/test_live.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/identity/azure-identity/tests/test_live.py b/sdk/identity/azure-identity/tests/test_live.py index d64b46f314b8..322e5a04b2ca 100644 --- a/sdk/identity/azure-identity/tests/test_live.py +++ b/sdk/identity/azure-identity/tests/test_live.py @@ -106,11 +106,11 @@ def prompt(url, user_code, _): print("opening a browser to '{}', enter device code {}".format(url, user_code)) webbrowser.open_new_tab(url) - credential = DeviceCodeCredential(client_id=AZURE_CLI_CLIENT_ID, prompt_callback=prompt, timeout=25) + credential = DeviceCodeCredential(client_id=AZURE_CLI_CLIENT_ID, prompt_callback=prompt, timeout=40) get_token(credential) @pytest.mark.manual def test_browser_auth(): - credential = InteractiveBrowserCredential(client_id=AZURE_CLI_CLIENT_ID, timeout=25) + credential = InteractiveBrowserCredential(client_id=AZURE_CLI_CLIENT_ID, timeout=40) get_token(credential) From 71b3ad1df14e4a4a428975b02e054e9a1aa9a217 Mon Sep 17 00:00:00 2001 From: Charles Lowell Date: Mon, 4 Nov 2019 13:11:37 -0800 Subject: [PATCH 10/11] rework markers to decrease hackiness --- sdk/identity/azure-identity/conftest.py | 23 +++++++++---------- .../azure-identity/tests/test_live.py | 3 ++- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/sdk/identity/azure-identity/conftest.py b/sdk/identity/azure-identity/conftest.py index d14955455a9f..2d4420500dbf 100644 --- a/sdk/identity/azure-identity/conftest.py +++ b/sdk/identity/azure-identity/conftest.py @@ -12,21 +12,26 @@ if sys.version_info < (3, 5): collect_ignore_glob = ["*_async.py"] -run_manual_tests = "manual" in sys.argv +run_manual_tests = False stdout_captured = True def pytest_configure(config): - config.addinivalue_line("markers", "manual: the test requires manual interaction, e.g. browser authentication") - global stdout_captured + config.addinivalue_line( + "markers", "manual: the tested credential requires manual interaction, e.g. InteractiveBrowserCredential" + ) + config.addinivalue_line("markers", "prints: the tested credential prints to stdout, e.g. DeviceCodeCredential") + global run_manual_tests, stdout_captured + run_manual_tests = config.getoption("markexpr") == "manual" stdout_captured = config.getoption("capture") != "no" def pytest_runtest_setup(item): # ensure manual tests only run when manual marker is selected with 'pytest -m manual' - if any(mark.name == "manual" for mark in item.own_markers): - if not run_manual_tests: - pytest.skip("To run manual tests, select 'manual' marker with '-m manual'") + if item.get_closest_marker("manual") and not run_manual_tests: + pytest.skip("To run manual tests, select 'manual' marker with 'pytest -m manual'") + elif item.get_closest_marker("prints") and stdout_captured: + pytest.skip("This test prints to stdout. Run pytest with '-s' to ensure the output is visible.") @pytest.fixture() @@ -86,12 +91,6 @@ def live_user_details(): return user_details -@pytest.fixture() -def prints(): - if stdout_captured: - pytest.skip("This test prints to stdout. Run pytest with '-s' to ensure the output is visible.") - - @pytest.fixture() def managed_identity_id(): return os.environ.get("MANAGED_IDENTITY_ID") diff --git a/sdk/identity/azure-identity/tests/test_live.py b/sdk/identity/azure-identity/tests/test_live.py index 322e5a04b2ca..63548951ee5f 100644 --- a/sdk/identity/azure-identity/tests/test_live.py +++ b/sdk/identity/azure-identity/tests/test_live.py @@ -99,7 +99,8 @@ def test_username_password_auth(live_user_details): @pytest.mark.manual -def test_device_code(prints): +@pytest.mark.prints +def test_device_code(): import webbrowser def prompt(url, user_code, _): From 98aaaad90b1ff1b3029636c978f4f8458e4f8909 Mon Sep 17 00:00:00 2001 From: Charles Lowell Date: Wed, 6 Nov 2019 09:31:58 -0800 Subject: [PATCH 11/11] add --manual option to pytest --- sdk/identity/azure-identity/conftest.py | 34 ++++++++++++++----------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/sdk/identity/azure-identity/conftest.py b/sdk/identity/azure-identity/conftest.py index 2d4420500dbf..ed71382fbb00 100644 --- a/sdk/identity/azure-identity/conftest.py +++ b/sdk/identity/azure-identity/conftest.py @@ -12,26 +12,30 @@ if sys.version_info < (3, 5): collect_ignore_glob = ["*_async.py"] -run_manual_tests = False -stdout_captured = True + +def pytest_addoption(parser): + parser.addoption("--manual", action="store_true", default=False, help="run manual tests") def pytest_configure(config): - config.addinivalue_line( - "markers", "manual: the tested credential requires manual interaction, e.g. InteractiveBrowserCredential" - ) - config.addinivalue_line("markers", "prints: the tested credential prints to stdout, e.g. DeviceCodeCredential") - global run_manual_tests, stdout_captured - run_manual_tests = config.getoption("markexpr") == "manual" - stdout_captured = config.getoption("capture") != "no" + config.addinivalue_line("markers", "manual: mark test as requiring manual interaction") + config.addinivalue_line("markers", "prints: mark test as printing important information to stdout") + +def pytest_collection_modifyitems(config, items): + stdout_captured = config.getoption("capture") != "no" + run_manual_tests = config.getoption("--manual") + if not stdout_captured and run_manual_tests: + return -def pytest_runtest_setup(item): - # ensure manual tests only run when manual marker is selected with 'pytest -m manual' - if item.get_closest_marker("manual") and not run_manual_tests: - pytest.skip("To run manual tests, select 'manual' marker with 'pytest -m manual'") - elif item.get_closest_marker("prints") and stdout_captured: - pytest.skip("This test prints to stdout. Run pytest with '-s' to ensure the output is visible.") + # skip manual tests or tests which print to stdout, as appropriate + skip_manual = pytest.mark.skip(reason="run pytest with '--manual' to run manual tests") + skip_prints = pytest.mark.skip(reason="this test prints to stdout, run pytest with '-s' to make output visible") + for test in items: + if not run_manual_tests and "manual" in test.keywords: + test.add_marker(skip_manual) + elif stdout_captured and "prints" in test.keywords: + test.add_marker(skip_prints) @pytest.fixture()