From d4083fc3751d072eec3194531a966728bd03a3ec Mon Sep 17 00:00:00 2001 From: Ian McCowan Date: Mon, 12 Jun 2017 09:20:25 -0700 Subject: [PATCH] Add support for testing with azure-devtools --- .travis.yml | 5 +- appveyor.yml | 3 +- ..._mgmt_containerregistry.test_registry.yaml | 0 ...stry_2017_06_01_preview.test_registry.yaml | 0 ...istry_2017_06_01_preview.test_webhook.yaml | 0 .../tests/test_mgmt_containerregistry.py | 68 ++--- ...mt_containerregistry_2017_06_01_preview.py | 62 +++-- azure-mgmt-media/tests/__init__.py | 0 .../test_mgmt_media.test_media.yaml | 0 .../tests/test_mgmt_media.py | 76 +++--- azure-mgmt-storage/tests/__init__.py | 0 ...st_mgmt_storage.test_storage_accounts.yaml | 0 .../test_mgmt_storage.test_storage_usage.yaml | 0 .../tests/test_mgmt_storage.py | 29 ++- azure-mgmt/tests/mgmt_testcase.py | 9 +- .../devtools_testutils/__init__.py | 9 + .../devtools_testutils/config.py | 1 + .../devtools_testutils/mgmt_settings_fake.py | 36 +++ .../devtools_testutils/mgmt_testcase.py | 232 ++++++++++++++++++ .../devtools_testutils/resource_testcase.py | 58 +++++ .../devtools_testutils/setup.py | 0 .../devtools_testutils/storage_testcase.py | 75 ++++++ .../devtools_testutils/testsettings_local.cfg | 1 + azure-sdk-testutils/setup.py | 1 + requirements.txt | 1 + setup.cfg | 1 - 26 files changed, 527 insertions(+), 140 deletions(-) rename {azure-mgmt => azure-mgmt-containerregistry}/tests/recordings/test_mgmt_containerregistry.test_registry.yaml (100%) rename {azure-mgmt => azure-mgmt-containerregistry}/tests/recordings/test_mgmt_containerregistry_2017_06_01_preview.test_registry.yaml (100%) rename {azure-mgmt => azure-mgmt-containerregistry}/tests/recordings/test_mgmt_containerregistry_2017_06_01_preview.test_webhook.yaml (100%) rename {azure-mgmt => azure-mgmt-containerregistry}/tests/test_mgmt_containerregistry.py (55%) rename {azure-mgmt => azure-mgmt-containerregistry}/tests/test_mgmt_containerregistry_2017_06_01_preview.py (72%) create mode 100644 azure-mgmt-media/tests/__init__.py rename {azure-mgmt => azure-mgmt-media}/tests/recordings/test_mgmt_media.test_media.yaml (100%) rename {azure-mgmt => azure-mgmt-media}/tests/test_mgmt_media.py (52%) create mode 100644 azure-mgmt-storage/tests/__init__.py rename {azure-mgmt => azure-mgmt-storage}/tests/recordings/test_mgmt_storage.test_storage_accounts.yaml (100%) rename {azure-mgmt => azure-mgmt-storage}/tests/recordings/test_mgmt_storage.test_storage_usage.yaml (100%) rename {azure-mgmt => azure-mgmt-storage}/tests/test_mgmt_storage.py (87%) create mode 100644 azure-sdk-testutils/devtools_testutils/__init__.py create mode 100644 azure-sdk-testutils/devtools_testutils/config.py create mode 100644 azure-sdk-testutils/devtools_testutils/mgmt_settings_fake.py create mode 100644 azure-sdk-testutils/devtools_testutils/mgmt_testcase.py create mode 100644 azure-sdk-testutils/devtools_testutils/resource_testcase.py create mode 100644 azure-sdk-testutils/devtools_testutils/setup.py create mode 100644 azure-sdk-testutils/devtools_testutils/storage_testcase.py create mode 100644 azure-sdk-testutils/devtools_testutils/testsettings_local.cfg create mode 100644 azure-sdk-testutils/setup.py diff --git a/.travis.yml b/.travis.yml index 264a692056fe..cb19bc2dc8b6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -64,7 +64,10 @@ install: - pip install nose - pip uninstall -y azure-common # Use azure-common from this repo, not the PyPI version azure-storage pulls in. script: - - python ./azure_nosetests.py + - python ./azure_nosetests.py */tests + # If you get "argument list too long" you have to switch to this: + # find azure-* -name tests | xargs python ./azure_nosetests.py + # coverage combine after_success: - bash <(curl -s https://codecov.io/bash) before_deploy: diff --git a/appveyor.yml b/appveyor.yml index 053a82d5e8ef..e5ee7e5160fc 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -17,4 +17,5 @@ install: build: off -test_script: "%PYTHON%\\python.exe ./azure_nosetests.py" +test_script: + - ps: & ${env:PYTHON}/python.exe ./azure_nosetests.py azure-mgmt/tests azure-mgmt-media/tests diff --git a/azure-mgmt/tests/recordings/test_mgmt_containerregistry.test_registry.yaml b/azure-mgmt-containerregistry/tests/recordings/test_mgmt_containerregistry.test_registry.yaml similarity index 100% rename from azure-mgmt/tests/recordings/test_mgmt_containerregistry.test_registry.yaml rename to azure-mgmt-containerregistry/tests/recordings/test_mgmt_containerregistry.test_registry.yaml diff --git a/azure-mgmt/tests/recordings/test_mgmt_containerregistry_2017_06_01_preview.test_registry.yaml b/azure-mgmt-containerregistry/tests/recordings/test_mgmt_containerregistry_2017_06_01_preview.test_registry.yaml similarity index 100% rename from azure-mgmt/tests/recordings/test_mgmt_containerregistry_2017_06_01_preview.test_registry.yaml rename to azure-mgmt-containerregistry/tests/recordings/test_mgmt_containerregistry_2017_06_01_preview.test_registry.yaml diff --git a/azure-mgmt/tests/recordings/test_mgmt_containerregistry_2017_06_01_preview.test_webhook.yaml b/azure-mgmt-containerregistry/tests/recordings/test_mgmt_containerregistry_2017_06_01_preview.test_webhook.yaml similarity index 100% rename from azure-mgmt/tests/recordings/test_mgmt_containerregistry_2017_06_01_preview.test_webhook.yaml rename to azure-mgmt-containerregistry/tests/recordings/test_mgmt_containerregistry_2017_06_01_preview.test_webhook.yaml diff --git a/azure-mgmt/tests/test_mgmt_containerregistry.py b/azure-mgmt-containerregistry/tests/test_mgmt_containerregistry.py similarity index 55% rename from azure-mgmt/tests/test_mgmt_containerregistry.py rename to azure-mgmt-containerregistry/tests/test_mgmt_containerregistry.py index 3954119a9b29..b38d3799ba4a 100644 --- a/azure-mgmt/tests/test_mgmt_containerregistry.py +++ b/azure-mgmt-containerregistry/tests/test_mgmt_containerregistry.py @@ -9,74 +9,58 @@ import azure.mgmt.containerregistry import azure.mgmt.storage -from testutils.common_recordingtestcase import record -from tests.mgmt_testcase import HttpStatusCode, AzureMgmtTestCase + +from devtools_testutils import ( + AzureMgmtTestCase, FakeStorageAccount, + ResourceGroupPreparer, StorageAccountPreparer +) + + +FAKE_STORAGE = FakeStorageAccount( + name='pyacrstorage', + id='' +) class MgmtACRTest(AzureMgmtTestCase): def setUp(self): super(MgmtACRTest, self).setUp() - self.region = 'westus' self.client = self.create_mgmt_client( azure.mgmt.containerregistry.ContainerRegistryManagementClient ) - self.storage_name = self.get_resource_name('pyacrstorage') - self.storage_key = '' - if not self.is_playback(): - self.create_resource_group() - - self.storage_client = self.create_mgmt_client( - azure.mgmt.storage.StorageManagementClient - ) - - async_storage_creation = self.storage_client.storage_accounts.create( - self.group_name, - self.storage_name, - { - 'location': self.region, - 'sku': { - 'name': 'Standard_LRS' - }, - 'kind': 'Storage' - } - ) - self.storage_account = async_storage_creation.result() - storage_keys = self.storage_client.storage_accounts.list_keys( - self.group_name, self.storage_name) - storage_keys = {v.key_name: v.value for v in storage_keys.keys} - self.storage_key = storage_keys['key1'] - - @record - def test_registry(self): + + @ResourceGroupPreparer() + @StorageAccountPreparer(name_prefix='pyacrstorage', playback_fake_resource=FAKE_STORAGE) + def test_registry(self, resource_group, location, storage_account, storage_account_key): registry_name = self.get_resource_name('pyacr') name_status = self.client.registries.check_name_availability(registry_name) self.assertTrue(name_status.name_available) async_registry_creation = self.client.registries.create( - self.group_name, + resource_group.name, registry_name, { - 'location': self.region, + 'location': location, 'sku': { 'name': 'Basic' }, 'storage_account': { - 'name': self.storage_name, - 'access_key': self.storage_key + 'name': storage_account.name, + 'access_key': storage_account_key, } } ) registry = async_registry_creation.result() self.assertEqual(registry.name, registry_name) - self.assertEqual(registry.location, self.region) + self.assertEqual(registry.location, location) self.assertEqual(registry.sku.name, 'Basic') self.assertEqual(registry.sku.tier, 'Basic') self.assertEqual(registry.admin_user_enabled, False) registry = self.client.registries.update( - self.group_name, + resource_group.name, registry_name, { 'admin_user_enabled': True @@ -85,21 +69,21 @@ def test_registry(self): self.assertEqual(registry.name, registry_name) self.assertEqual(registry.admin_user_enabled, True) - registry = self.client.registries.get(self.group_name, registry_name) + registry = self.client.registries.get(resource_group.name, registry_name) self.assertEqual(registry.name, registry_name) self.assertEqual(registry.admin_user_enabled, True) - registries = list(self.client.registries.list_by_resource_group(self.group_name)) + registries = list(self.client.registries.list_by_resource_group(resource_group.name)) self.assertEqual(len(registries), 1) - credentials = self.client.registries.list_credentials(self.group_name, registry_name) + credentials = self.client.registries.list_credentials(resource_group.name, registry_name) self.assertEqual(len(credentials.passwords), 2) credentials = self.client.registries.regenerate_credential( - self.group_name, registry_name, 'password') + resource_group.name, registry_name, 'password') self.assertEqual(len(credentials.passwords), 2) - self.client.registries.delete(self.group_name, registry_name) + self.client.registries.delete(resource_group.name, registry_name) #------------------------------------------------------------------------------ diff --git a/azure-mgmt/tests/test_mgmt_containerregistry_2017_06_01_preview.py b/azure-mgmt-containerregistry/tests/test_mgmt_containerregistry_2017_06_01_preview.py similarity index 72% rename from azure-mgmt/tests/test_mgmt_containerregistry_2017_06_01_preview.py rename to azure-mgmt-containerregistry/tests/test_mgmt_containerregistry_2017_06_01_preview.py index 3fa307ca66c1..5d98def75b69 100644 --- a/azure-mgmt/tests/test_mgmt_containerregistry_2017_06_01_preview.py +++ b/azure-mgmt-containerregistry/tests/test_mgmt_containerregistry_2017_06_01_preview.py @@ -8,35 +8,31 @@ import unittest import azure.mgmt.containerregistry -import azure.mgmt.storage -from testutils.common_recordingtestcase import record -from tests.mgmt_testcase import HttpStatusCode, AzureMgmtTestCase + +from devtools_testutils import AzureMgmtTestCase, ResourceGroupPreparer class MgmtACRTest20170601Preview(AzureMgmtTestCase): def setUp(self): super(MgmtACRTest20170601Preview, self).setUp() - self.region = 'westcentralus' self.client = self.create_mgmt_client( azure.mgmt.containerregistry.ContainerRegistryManagementClient, api_version='2017-06-01-preview' ) - if not self.is_playback(): - self.create_resource_group() - @record - def test_registry(self): + @ResourceGroupPreparer(location='westcentralus') + def test_registry(self, resource_group, location): registry_name = self.get_resource_name('pyacr') name_status = self.client.registries.check_name_availability(registry_name) self.assertTrue(name_status.name_available) async_registry_creation = self.client.registries.create( - self.group_name, + resource_group.name, registry_name, { - 'location': self.region, + 'location': location, 'sku': { 'name': 'Managed_Standard' } @@ -44,14 +40,14 @@ def test_registry(self): ) registry = async_registry_creation.result() self.assertEqual(registry.name, registry_name) - self.assertEqual(registry.location, self.region) + self.assertEqual(registry.location, location) self.assertEqual(registry.sku.name, 'Managed_Standard') self.assertEqual(registry.sku.tier, 'Managed') self.assertEqual(registry.provisioning_state, 'Succeeded') self.assertEqual(registry.admin_user_enabled, False) registry = self.client.registries.update( - self.group_name, + resource_group.name, registry_name, { 'admin_user_enabled': True @@ -60,32 +56,32 @@ def test_registry(self): self.assertEqual(registry.name, registry_name) self.assertEqual(registry.admin_user_enabled, True) - registry = self.client.registries.get(self.group_name, registry_name) + registry = self.client.registries.get(resource_group.name, registry_name) self.assertEqual(registry.name, registry_name) self.assertEqual(registry.admin_user_enabled, True) - registries = list(self.client.registries.list_by_resource_group(self.group_name)) + registries = list(self.client.registries.list_by_resource_group(resource_group.name)) self.assertEqual(len(registries), 1) - credentials = self.client.registries.list_credentials(self.group_name, registry_name) + credentials = self.client.registries.list_credentials(resource_group.name, registry_name) self.assertEqual(len(credentials.passwords), 2) credentials = self.client.registries.regenerate_credential( - self.group_name, registry_name, 'password') + resource_group.name, registry_name, 'password') self.assertEqual(len(credentials.passwords), 2) - self.client.registries.delete(self.group_name, registry_name) + self.client.registries.delete(resource_group.name, registry_name) - @record - def test_webhook(self): + @ResourceGroupPreparer(location='westcentralus') + def test_webhook(self, resource_group, location): registry_name = self.get_resource_name('pyacr') webhook_name = self.get_resource_name('pyacrwebhook') async_registry_creation = self.client.registries.create( - self.group_name, + resource_group.name, registry_name, { - 'location': self.region, + 'location': location, 'sku': { 'name': 'Managed_Standard' } @@ -93,30 +89,30 @@ def test_webhook(self): ) registry = async_registry_creation.result() self.assertEqual(registry.name, registry_name) - self.assertEqual(registry.location, self.region) + self.assertEqual(registry.location, location) self.assertEqual(registry.sku.name, 'Managed_Standard') self.assertEqual(registry.sku.tier, 'Managed') self.assertEqual(registry.provisioning_state, 'Succeeded') self.assertEqual(registry.admin_user_enabled, False) async_webhook_creation = self.client.webhooks.create( - self.group_name, + resource_group.name, registry_name, webhook_name, { - 'location': self.region, + 'location': location, 'service_uri': 'http://www.microsoft.com', 'actions': ['push'] } ) webhook = async_webhook_creation.result() self.assertEqual(webhook.name, webhook_name) - self.assertEqual(webhook.location, self.region) + self.assertEqual(webhook.location, location) self.assertEqual(webhook.actions, ['push']) self.assertEqual(webhook.status, 'enabled') async_webhook_update = self.client.webhooks.update( - self.group_name, + resource_group.name, registry_name, webhook_name, { @@ -130,12 +126,12 @@ def test_webhook(self): self.assertEqual(webhook.name, webhook_name) self.assertEqual(webhook.scope, 'hello-world') - webhook = self.client.webhooks.get(self.group_name, registry_name, webhook_name) + webhook = self.client.webhooks.get(resource_group.name, registry_name, webhook_name) self.assertEqual(webhook.name, webhook_name) self.assertEqual(webhook.scope, 'hello-world') webhook_config = self.client.webhooks.get_callback_config( - self.group_name, + resource_group.name, registry_name, webhook_name ) @@ -145,14 +141,14 @@ def test_webhook(self): 'key': 'value' }) - webhooks = list(self.client.webhooks.list(self.group_name, registry_name)) + webhooks = list(self.client.webhooks.list(resource_group.name, registry_name)) self.assertEqual(len(webhooks), 1) - self.client.webhooks.ping(self.group_name, registry_name, webhook_name) - self.client.webhooks.list_events(self.group_name, registry_name, webhook_name) + self.client.webhooks.ping(resource_group.name, registry_name, webhook_name) + self.client.webhooks.list_events(resource_group.name, registry_name, webhook_name) - self.client.webhooks.delete(self.group_name, registry_name, webhook_name) - self.client.registries.delete(self.group_name, registry_name) + self.client.webhooks.delete(resource_group.name, registry_name, webhook_name) + self.client.registries.delete(resource_group.name, registry_name) #------------------------------------------------------------------------------ diff --git a/azure-mgmt-media/tests/__init__.py b/azure-mgmt-media/tests/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/azure-mgmt/tests/recordings/test_mgmt_media.test_media.yaml b/azure-mgmt-media/tests/recordings/test_mgmt_media.test_media.yaml similarity index 100% rename from azure-mgmt/tests/recordings/test_mgmt_media.test_media.yaml rename to azure-mgmt-media/tests/recordings/test_mgmt_media.test_media.yaml diff --git a/azure-mgmt/tests/test_mgmt_media.py b/azure-mgmt-media/tests/test_mgmt_media.py similarity index 52% rename from azure-mgmt/tests/test_mgmt_media.py rename to azure-mgmt-media/tests/test_mgmt_media.py index b42d26cc7610..ee05ce00761d 100644 --- a/azure-mgmt/tests/test_mgmt_media.py +++ b/azure-mgmt-media/tests/test_mgmt_media.py @@ -9,8 +9,23 @@ import azure.mgmt.media import azure.mgmt.storage -from testutils.common_recordingtestcase import record -from tests.mgmt_testcase import HttpStatusCode, AzureMgmtTestCase + +from devtools_testutils import ( + AzureMgmtTestCase, ResourceGroupPreparer, + StorageAccountPreparer, FakeStorageAccount, +) + + +PLAYBACK_STORAGE_ID = ( + '/subscriptions/00000000-0000-0000-0000-000000000000/' + 'resourceGroups/test_mgmt_media_test_media8fdd0a81/' + 'providers/Microsoft.Storage/storageAccounts/msmediapttest' +) + +FAKE_STORAGE = FakeStorageAccount( + name='msmediapttest', + id=PLAYBACK_STORAGE_ID, +) class MgmtMediaTest(AzureMgmtTestCase): @@ -20,33 +35,10 @@ def setUp(self): self.client = self.create_mgmt_client( azure.mgmt.media.MediaServicesManagementClient ) - if not self.is_playback(): - self.create_resource_group() - - self.storage_client = self.create_mgmt_client( - azure.mgmt.storage.StorageManagementClient - ) - - params_create = azure.mgmt.storage.models.StorageAccountCreateParameters( - sku=azure.mgmt.storage.models.Sku(azure.mgmt.storage.models.SkuName.standard_lrs), - kind=azure.mgmt.storage.models.Kind.storage, - location=self.region - ) - result_create = self.storage_client.storage_accounts.create( - self.group_name, - 'msmediapttest', - params_create, - ) - self.storage_account = result_create.result() - self.storage_id = self.storage_account.id - else: - self.storage_id = ('/subscriptions/00000000-0000-0000-0000-000000000000/' - 'resourceGroups/test_mgmt_media_test_media8fdd0a81/' - 'providers/Microsoft.Storage/storageAccounts/msmediapttest') - - - @record - def test_media(self): + + @ResourceGroupPreparer() + @StorageAccountPreparer(name_prefix='msmediapttest', playback_fake_resource=FAKE_STORAGE) + def test_media(self, resource_group, location, storage_account): media_name = self.get_resource_name('pymedia') available = self.client.media_service.check_name_availability( @@ -55,47 +47,47 @@ def test_media(self): self.assertTrue(available.name_available) media_obj = self.client.media_service.create( - self.group_name, + resource_group.name, media_name, { - 'location': self.region, + 'location': location, 'storage_accounts': [{ - 'id': self.storage_id, - 'is_primary': True + 'id': storage_account.id, + 'is_primary': True, }] } ) media_obj = self.client.media_service.get( - self.group_name, + resource_group.name, media_name ) self.assertEqual(media_obj.name, media_name) - - - medias = list(self.client.media_service.list_by_resource_group(self.group_name)) + + medias = list(self.client.media_service.list_by_resource_group(resource_group.name)) self.assertEqual(len(medias), 1) self.assertEqual(medias[0].name, media_name) + # why is keys assigned to here? keys = self.client.media_service.list_keys( - self.group_name, + resource_group.name, media_name ) keys = self.client.media_service.regenerate_key( - self.group_name, + resource_group.name, media_name, "Primary" ) self.client.media_service.sync_storage_keys( - self.group_name, + resource_group.name, media_name, - self.storage_id + storage_account.id ) media_obj = self.client.media_service.delete( - self.group_name, + resource_group.name, media_name ) diff --git a/azure-mgmt-storage/tests/__init__.py b/azure-mgmt-storage/tests/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/azure-mgmt/tests/recordings/test_mgmt_storage.test_storage_accounts.yaml b/azure-mgmt-storage/tests/recordings/test_mgmt_storage.test_storage_accounts.yaml similarity index 100% rename from azure-mgmt/tests/recordings/test_mgmt_storage.test_storage_accounts.yaml rename to azure-mgmt-storage/tests/recordings/test_mgmt_storage.test_storage_accounts.yaml diff --git a/azure-mgmt/tests/recordings/test_mgmt_storage.test_storage_usage.yaml b/azure-mgmt-storage/tests/recordings/test_mgmt_storage.test_storage_usage.yaml similarity index 100% rename from azure-mgmt/tests/recordings/test_mgmt_storage.test_storage_usage.yaml rename to azure-mgmt-storage/tests/recordings/test_mgmt_storage.test_storage_usage.yaml diff --git a/azure-mgmt/tests/test_mgmt_storage.py b/azure-mgmt-storage/tests/test_mgmt_storage.py similarity index 87% rename from azure-mgmt/tests/test_mgmt_storage.py rename to azure-mgmt-storage/tests/test_mgmt_storage.py index dfcf73cd6510..cab229510344 100644 --- a/azure-mgmt/tests/test_mgmt_storage.py +++ b/azure-mgmt-storage/tests/test_mgmt_storage.py @@ -8,8 +8,8 @@ import unittest import azure.mgmt.storage.models -from testutils.common_recordingtestcase import record -from tests.mgmt_testcase import HttpStatusCode, AzureMgmtTestCase + +from devtools_testutils import AzureMgmtTestCase, ResourceGroupPreparer class MgmtStorageTest(AzureMgmtTestCase): @@ -19,16 +19,13 @@ def setUp(self): self.storage_client = self.create_mgmt_client( azure.mgmt.storage.StorageManagementClient ) - if not self.is_playback(): - self.create_resource_group() - @record def test_storage_usage(self): usages = list(self.storage_client.usage.list()) self.assertGreater(len(usages), 0) - @record - def test_storage_accounts(self): + @ResourceGroupPreparer() + def test_storage_accounts(self, resource_group, location): account_name = self.get_resource_name('pyarmstorage') result_check = self.storage_client.storage_accounts.check_name_availability( @@ -41,10 +38,10 @@ def test_storage_accounts(self): params_create = azure.mgmt.storage.models.StorageAccountCreateParameters( sku=azure.mgmt.storage.models.Sku(azure.mgmt.storage.models.SkuName.standard_lrs), kind=azure.mgmt.storage.models.Kind.storage, - location=self.region + location=location, ) result_create = self.storage_client.storage_accounts.create( - self.group_name, + resource_group.name, account_name, params_create, ) @@ -52,13 +49,13 @@ def test_storage_accounts(self): self.assertEqual(storage_account.name, account_name) storage_account = self.storage_client.storage_accounts.get_properties( - self.group_name, + resource_group.name, account_name, ) self.assertEqual(storage_account.name, account_name) result_list_keys = self.storage_client.storage_accounts.list_keys( - self.group_name, + resource_group.name, account_name, ) keys = {v.key_name: (v.value, v.permissions) for v in result_list_keys.keys} @@ -67,7 +64,7 @@ def test_storage_accounts(self): self.assertGreater(len(keys['key1'][0]), 0) result_regen_keys = self.storage_client.storage_accounts.regenerate_key( - self.group_name, + resource_group.name, account_name, "key1" ) @@ -83,7 +80,7 @@ def test_storage_accounts(self): ) result_list = self.storage_client.storage_accounts.list_by_resource_group( - self.group_name, + resource_group.name, ) result_list = list(result_list) self.assertGreater(len(result_list), 0) @@ -93,15 +90,17 @@ def test_storage_accounts(self): self.assertGreater(len(result_list), 0) storage_account = self.storage_client.storage_accounts.update( - self.group_name, + resource_group.name, account_name, azure.mgmt.storage.models.StorageAccountUpdateParameters( sku=azure.mgmt.storage.models.Sku(azure.mgmt.storage.models.SkuName.standard_grs) ) ) + # should there be a test of the update operation? + self.storage_client.storage_accounts.delete( - self.group_name, + resource_group.name, account_name, ) diff --git a/azure-mgmt/tests/mgmt_testcase.py b/azure-mgmt/tests/mgmt_testcase.py index 07da336389eb..ade61187bc81 100644 --- a/azure-mgmt/tests/mgmt_testcase.py +++ b/azure-mgmt/tests/mgmt_testcase.py @@ -128,11 +128,10 @@ def create_mgmt_client(self, client_class, **kwargs): def _scrub(self, val): val = super(AzureMgmtTestCase, self)._scrub(val) - - constants_to_scrub = ['SUBSCRIPTION_ID', 'AD_DOMAIN', 'TENANT_ID', 'CLIENT_OID'] - - real_to_fake_dict = {getattr(self.settings, key): getattr(self.fake_settings, key) for key in constants_to_scrub if - hasattr(self.settings, key) and hasattr(self.fake_settings, key)} + real_to_fake_dict = { + self.settings.SUBSCRIPTION_ID: self.fake_settings.SUBSCRIPTION_ID, + self.settings.AD_DOMAIN: self.fake_settings.AD_DOMAIN + } val = self._scrub_using_dict(val, real_to_fake_dict) return val diff --git a/azure-sdk-testutils/devtools_testutils/__init__.py b/azure-sdk-testutils/devtools_testutils/__init__.py new file mode 100644 index 000000000000..e1322487b5d8 --- /dev/null +++ b/azure-sdk-testutils/devtools_testutils/__init__.py @@ -0,0 +1,9 @@ +from .mgmt_testcase import (AzureMgmtTestCase, AzureMgmtPreparer) +from .resource_testcase import (FakeResource, ResourceGroupPreparer) +from .storage_testcase import (FakeStorageAccount, StorageAccountPreparer) + +__all__ = [ + 'AzureMgmtTestCase', 'AzureMgmtPreparer', + 'FakeResource', 'ResourceGroupPreparer', + 'FakeStorageAccount', 'StorageAccountPreparer', +] \ No newline at end of file diff --git a/azure-sdk-testutils/devtools_testutils/config.py b/azure-sdk-testutils/devtools_testutils/config.py new file mode 100644 index 000000000000..60da66a9a490 --- /dev/null +++ b/azure-sdk-testutils/devtools_testutils/config.py @@ -0,0 +1 @@ +TEST_SETTING_FILENAME = 'testsettings_local.cfg' \ No newline at end of file diff --git a/azure-sdk-testutils/devtools_testutils/mgmt_settings_fake.py b/azure-sdk-testutils/devtools_testutils/mgmt_settings_fake.py new file mode 100644 index 000000000000..5731494735c4 --- /dev/null +++ b/azure-sdk-testutils/devtools_testutils/mgmt_settings_fake.py @@ -0,0 +1,36 @@ +#------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +#-------------------------------------------------------------------------- + +from azure.common.credentials import ( + BasicTokenAuthentication, + UserPassCredentials +) + +SUBSCRIPTION_ID = "00000000-0000-0000-0000-000000000000" + +# this is used explicitly for ADLA job id replacement in recordings. +ADLA_JOB_ID = "00000000-0000-0000-0000-000000000000" +# GraphRBAC tests +AD_DOMAIN = "myaddomain.onmicrosoft.com" +# Keyvault tests +TENANT_ID = '00000000-0000-0000-0000-000000000000' +CLIENT_OID = '00000000-0000-0000-0000-000000000000' + +# Read for details of this file: +# https://github.com/Azure/azure-sdk-for-python/wiki/Contributing-to-the-tests + +def get_credentials(): + # Put your credentials here in the "real" file + #return UserPassCredentials( + # 'user@myaddomain.onmicrosoft.com', + # 'Password' + #) + # Needed to play recorded tests + return BasicTokenAuthentication( + token = { + 'access_token':'faked_token' + } + ) \ No newline at end of file diff --git a/azure-sdk-testutils/devtools_testutils/mgmt_testcase.py b/azure-sdk-testutils/devtools_testutils/mgmt_testcase.py new file mode 100644 index 000000000000..d5f49648d0b6 --- /dev/null +++ b/azure-sdk-testutils/devtools_testutils/mgmt_testcase.py @@ -0,0 +1,232 @@ +#------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +#-------------------------------------------------------------------------- +from collections import namedtuple +import inspect +import os.path +import zlib + +from azure_devtools.scenario_tests import ( + ReplayableTest, AzureTestError, + AbstractPreparer, GeneralNameReplacer, + OAuthRequestResponsesFilter, DeploymentNameReplacer, +) +from .config import TEST_SETTING_FILENAME +from . import mgmt_settings_fake as fake_settings + + +should_log = os.getenv('SDK_TESTS_LOG', '0') +if should_log.lower() == 'true' or should_log == '1': + import logging + logger = logging.getLogger('msrest') + logger.setLevel(logging.DEBUG) + logger.addHandler(logging.StreamHandler()) + + +class HttpStatusCode(object): + OK = 200 + Created = 201 + Accepted = 202 + NoContent = 204 + NotFound = 404 + + +def get_resource_name(name_prefix, identifier): + # Append a suffix to the name, based on the fully qualified test name + # We use a checksum of the test name so that each test gets different + # resource names, but each test will get the same name on repeat runs, + # which is needed for playback. + # Most resource names have a length limit, so we use a crc32 + checksum = zlib.adler32(identifier) & 0xffffffff + name = '{}{}'.format(name_prefix, hex(checksum)[2:]).rstrip('L') + if name.endswith('L'): + name = name[:-1] + return name + + +def get_qualified_method_name(obj, method_name): + # example of qualified test name: + # test_mgmt_network.test_public_ip_addresses + _, filename = os.path.split(inspect.getsourcefile(type(obj))) + module_name, _ = os.path.splitext(filename) + return '{0}.{1}'.format(module_name, method_name) + + +class AzureMgmtTestCase(ReplayableTest): + def __init__(self, method_name, config_file=None, + recording_dir=None, recording_name=None, + recording_processors=None, replay_processors=None, + recording_patches=None, replay_patches=None): + self.working_folder = os.path.dirname(__file__) + self.qualified_test_name = get_qualified_method_name(self, method_name) + self._fake_settings, self._real_settings = self._load_settings() + self.region = 'westus' + self.scrubber = GeneralNameReplacer() + super(AzureMgmtTestCase, self).__init__( + method_name, + config_file=config_file or os.path.join(self.working_folder, TEST_SETTING_FILENAME), + recording_dir=recording_dir, + recording_name=recording_name or self.qualified_test_name, + recording_processors=recording_processors or self._get_recording_processors(), + replay_processors=replay_processors, + recording_patches=recording_patches, + replay_patches=replay_patches, + ) + + @property + def settings(self): + if self.is_live: + if self._real_settings: + return self._real_settings + else: + raise AzureTestError('Need a mgmt_settings_real.py file to run tests live.') + else: + return self._fake_settings + + def _load_settings(self): + try: + from . import mgmt_settings_real as real_settings + return fake_settings, real_settings + except ImportError: + return fake_settings, None + + def _get_recording_processors(self): + return [ + self.scrubber, + OAuthRequestResponsesFilter(), + DeploymentNameReplacer(), + ] + + def is_playback(self): + return not self.is_live + + def _setup_scrubber(self): + constants_to_scrub = ['SUBSCRIPTION_ID', 'AD_DOMAIN', 'TENANT_ID', 'CLIENT_OID'] + for key in constants_to_scrub: + if hasattr(self.settings, key) and hasattr(self._fake_settings, key): + self.scrubber.register_name_pair(getattr(self.settings, key), + getattr(self._fake_settings, key)) + + def setUp(self): + # Every test uses a different resource group name calculated from its + # qualified test name. + # + # When running all tests serially, this allows us to delete + # the resource group in teardown without waiting for the delete to + # complete. The next test in line will use a different resource group, + # so it won't have any trouble creating its resource group even if the + # previous test resource group hasn't finished deleting. + # + # When running tests individually, if you try to run the same test + # multiple times in a row, it's possible that the delete in the previous + # teardown hasn't completed yet (because we don't wait), and that + # would make resource group creation fail. + # To avoid that, we also delete the resource group in the + # setup, and we wait for that delete to complete. + self._setup_scrubber() + super(AzureMgmtTestCase, self).setUp() + + def tearDown(self): + return super(AzureMgmtTestCase, self).tearDown() + + def create_basic_client(self, client_class, **kwargs): + # Whatever the client, if credentials is None, fail + with self.assertRaises(ValueError): + client = client_class( + credentials=None, + **kwargs + ) + # Whatever the client, if accept_language is not str, fail + with self.assertRaises(TypeError): + client = client_class( + credentials=self.settings.get_credentials(), + accept_language=42, + **kwargs + ) + + # Real client creation + client = client_class( + credentials=self.settings.get_credentials(), + **kwargs + ) + if self.is_playback(): + client.config.long_running_operation_timeout = 0 + return client + + def create_mgmt_client(self, client_class, **kwargs): + # Whatever the client, if subscription_id is None, fail + with self.assertRaises(ValueError): + self.create_basic_client( + client_class, + subscription_id=None, + **kwargs + ) + # Whatever the client, if subscription_id is not a string, fail + with self.assertRaises(TypeError): + self.create_basic_client( + client_class, + subscription_id=42, + **kwargs + ) + + return self.create_basic_client( + client_class, + subscription_id=self.settings.SUBSCRIPTION_ID, + **kwargs + ) + + def create_random_name(self, name): + return get_resource_name(name, self.qualified_test_name.encode()) + + def get_resource_name(self, name): + """Alias to create_random_name for back compatibility.""" + return self.create_random_name(name) + + def get_preparer_resource_name(self, prefix): + """Random name generation for use by preparers. + + If prefix is a blank string, use the fully qualified test name instead. + This is what legacy tests do for resource groups.""" + return self.get_resource_name(prefix or self.qualified_test_name.replace('.', '_')) + + +class AzureMgmtPreparer(AbstractPreparer): + def __init__(self, name_prefix, random_name_length, + disable_recording=True, + playback_fake_resource=None): + super(AzureMgmtPreparer, self).__init__(name_prefix, random_name_length, + disable_recording=disable_recording) + self.client = None + self.resource = playback_fake_resource + + @property + def is_live(self): + return self.test_class_instance.is_live + + def create_random_name(self): + return self.test_class_instance.get_preparer_resource_name(self.name_prefix) + + @property + def moniker(self): + """Override moniker generation for backwards compatibility. + + azure-devtools preparers, by default, generate "monikers" which replace + resource names in request URIs by tacking on a resource count to + name_prefix. By contrast, SDK tests used the fully qualified (module + method) + test name and the hashing process in get_resource_name. + + This property override implements the SDK test name generation so that + the URIs don't change and tests don't need to be re-recorded. + """ + if not self.resource_moniker: + self.resource_moniker = self.random_name + return self.resource_moniker + + def create_mgmt_client(self, client_class, **kwargs): + return client_class( + credentials=self.test_class_instance.settings.get_credentials(), + subscription_id=self.test_class_instance.settings.SUBSCRIPTION_ID, + **kwargs + ) diff --git a/azure-sdk-testutils/devtools_testutils/resource_testcase.py b/azure-sdk-testutils/devtools_testutils/resource_testcase.py new file mode 100644 index 000000000000..2186bd66a9e7 --- /dev/null +++ b/azure-sdk-testutils/devtools_testutils/resource_testcase.py @@ -0,0 +1,58 @@ +from collections import namedtuple + +from azure_devtools.scenario_tests import AzureTestError + +from azure.common.exceptions import CloudError +from azure.mgmt.resource import ResourceManagementClient + +from . import AzureMgmtPreparer + + +RESOURCE_GROUP_PARAM = 'resource_group' + + +FakeResource = namedtuple( + 'FakeResource', + ['name', 'id'] +) + + +class ResourceGroupPreparer(AzureMgmtPreparer): + def __init__(self, name_prefix='', + random_name_length=75, + parameter_name=RESOURCE_GROUP_PARAM, + parameter_name_for_location='location', location='westus', + disable_recording=True, playback_fake_resource=None): + super(ResourceGroupPreparer, self).__init__(name_prefix, random_name_length, + disable_recording=disable_recording, + playback_fake_resource=playback_fake_resource) + self.location = location + self.parameter_name = parameter_name + self.parameter_name_for_location = parameter_name_for_location + + def create_resource(self, name, **kwargs): + if self.is_live: + self.client = self.create_mgmt_client(ResourceManagementClient) + self.resource = self.client.resource_groups.create_or_update( + name, {'location': self.location} + ) + else: + self.resource = self.resource or FakeResource(name=name, id='') + return { + self.parameter_name: self.resource, + self.parameter_name_for_location: self.location, + } + + def remove_resource(self, name, **kwargs): + if self.is_live: + try: + if 'wait_timeout' in kwargs: + azure_poller = self.client.resource_groups.delete(name) + azure_poller.wait(kwargs.get('wait_timeout')) + if azure_poller.done(): + return + raise AzureTestError('Timed out waiting for resource group to be deleted.') + else: + self.client.resource_groups.delete(name, raw=True) + except CloudError: + pass diff --git a/azure-sdk-testutils/devtools_testutils/setup.py b/azure-sdk-testutils/devtools_testutils/setup.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/azure-sdk-testutils/devtools_testutils/storage_testcase.py b/azure-sdk-testutils/devtools_testutils/storage_testcase.py new file mode 100644 index 000000000000..cdfd7c5f57b6 --- /dev/null +++ b/azure-sdk-testutils/devtools_testutils/storage_testcase.py @@ -0,0 +1,75 @@ +from collections import namedtuple +import os + +from azure.mgmt.storage import StorageManagementClient + +from azure_devtools.scenario_tests.preparers import ( + AbstractPreparer, + SingleValueReplacer, +) +from azure_devtools.scenario_tests.exceptions import AzureTestError + +from . import AzureMgmtPreparer, ResourceGroupPreparer, FakeResource +from .resource_testcase import RESOURCE_GROUP_PARAM + + +FakeStorageAccount = FakeResource + + +# Storage Account Preparer and its shorthand decorator + +class StorageAccountPreparer(AzureMgmtPreparer): + def __init__(self, + name_prefix='', + sku='Standard_LRS', location='westus', kind='storage', + parameter_name='storage_account', + resource_group_parameter_name=RESOURCE_GROUP_PARAM, + disable_recording=True, playback_fake_resource=None): + super(StorageAccountPreparer, self).__init__(name_prefix, 24, + disable_recording=disable_recording, + playback_fake_resource=playback_fake_resource) + self.location = location + self.sku = sku + self.kind = kind + self.resource_group_parameter_name = resource_group_parameter_name + self.parameter_name = parameter_name + self.storage_key = '' + + def create_resource(self, name, **kwargs): + if self.is_live: + self.client = self.create_mgmt_client(StorageManagementClient) + group = self._get_resource_group(**kwargs) + storage_async_operation = self.client.storage_accounts.create( + group.name, + name, + { + 'sku': {'name': self.sku}, + 'location': self.location, + 'kind': self.kind, + } + ) + self.resource = storage_async_operation.result() + storage_keys = { + v.key_name: v.value + for v in self.client.storage_accounts.list_keys(group.name, name).keys + } + self.storage_key = storage_keys['key1'] + + return { + self.parameter_name: self.resource, + '{}_key'.format(self.parameter_name): self.storage_key, + } + + def remove_resource(self, name, **kwargs): + if self.is_live: + group = self._get_resource_group(**kwargs) + self.client.storage_accounts.delete(group.name, name) + + def _get_resource_group(self, **kwargs): + try: + return kwargs.get(self.resource_group_parameter_name) + except KeyError: + template = 'To create a storage account a resource group is required. Please add ' \ + 'decorator @{} in front of this storage account preparer.' + raise AzureTestError(template.format(ResourceGroupPreparer.__name__)) + diff --git a/azure-sdk-testutils/devtools_testutils/testsettings_local.cfg b/azure-sdk-testutils/devtools_testutils/testsettings_local.cfg new file mode 100644 index 000000000000..e29ef84d102a --- /dev/null +++ b/azure-sdk-testutils/devtools_testutils/testsettings_local.cfg @@ -0,0 +1 @@ +live-mode: false diff --git a/azure-sdk-testutils/setup.py b/azure-sdk-testutils/setup.py new file mode 100644 index 000000000000..09bde61f89cc --- /dev/null +++ b/azure-sdk-testutils/setup.py @@ -0,0 +1 @@ +# Blank setup.py so this directory will be added to PYTHONPATH by azure_nosetests.py \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index b6beec40f9bd..ca9e59bffdab 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,6 +2,7 @@ futures python-dateutil requests vcrpy +azure-devtools>=0.4.1 coverage pyopenssl msrest>=0.4.0,<0.5.0 diff --git a/setup.cfg b/setup.cfg index 74d64ce33f3f..2030169a7745 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,4 +1,3 @@ [nosetests] -tests=azure-mgmt,azure-servicemanagement-legacy,azure-servicebus with-coverage=1 cover-erase=1 \ No newline at end of file