diff --git a/sdk/cosmos/.azure-pipelines/cosmos.test.nightly-emulator.yml b/sdk/cosmos/.azure-pipelines/cosmos.test.nightly-emulator.yml deleted file mode 100644 index 63a201b96888..000000000000 --- a/sdk/cosmos/.azure-pipelines/cosmos.test.nightly-emulator.yml +++ /dev/null @@ -1,50 +0,0 @@ -jobs: - - job: 'NightlyEmulator' - - timeoutInMinutes: 120 - continueOnError: false - strategy: - matrix: - Windows_Python34: - OSVmImage: 'vs2017-win2016' - PythonVersion: '3.4' - Windows_Python27: - OSVmImage: 'vs2017-win2016' - PythonVersion: '2.7' - - pool: - vmImage: $(OSVmImage) - - steps: - - task: UsePythonVersion@0 - displayName: 'Use Python $(PythonVersion)' - inputs: - versionSpec: $(PythonVersion) - - - script: | - python -m pip install --upgrade pip - python scripts/dev_setup.py --packageList azure-cosmos - displayName: 'Set up Environment' - - - task: azure-cosmosdb.emulator-internal-preview.run-cosmosdbemulatorcontainer.CosmosDbEmulator@2 - displayName: "Run Azure Cosmos DB Preview Emulator Container" - inputs: - username: '$(cosmos-cosmosdb-azurecr-io-username)' - password: '$(cosmos-cosmosdb-azurecr-io-password)' - defaultPartitionCount: 25 - - - task: PowerShell@1 - displayName: 'Run Python Tests' - inputs: - scriptType: inlineScript - inlineScript: | - $env:ACCOUNT_HOST="$(CosmosDBEmulator.Endpoint)" - cmd /c 'pytest --junitxml=sdk/cosmos/azure-cosmos/Test-junit-nightly.xml --verbose sdk/cosmos/azure-cosmos/test -k "not globaldb"' - - condition: succeededOrFailed() - - - task: PublishTestResults@2 - displayName: 'Publish Python Test Results' - inputs: - testResultsFiles: '**\TEST-*.xml' - testRunTitle: 'Cosmos $(OSName) Node $(PythonVersion) - Python' diff --git a/sdk/cosmos/.azure-pipelines/cosmos.test.yml b/sdk/cosmos/.azure-pipelines/cosmos.test.yml index db7bdc528d96..7eb61876023e 100644 --- a/sdk/cosmos/.azure-pipelines/cosmos.test.yml +++ b/sdk/cosmos/.azure-pipelines/cosmos.test.yml @@ -26,6 +26,51 @@ jobs: vmImage: $(OSVmImage) steps: + - task: PowerShell@1 + displayName: 'Download Public Cosmos Emulator' + inputs: + scriptType: inlineScript + inlineScript: | + Write-Host "Downloading Cosmos Emulator - $(EmulatorMsiUrl)" + wget "$(EmulatorMsiUrl)" -outfile "$env:temp\azure-cosmosdb-emulator.msi" + Write-Host "Finished Downloading Cosmos Emulator - $env:temp\azure-cosmosdb-emulator.msi" + dir "$env:temp" + + - task: CmdLine@2 + displayName: 'Cleanup already installed Azure Cosmos DB Emulator' + inputs: + script: | + echo "Deleting Azure Cosmos DB Emulator directory" + dir "%ProgramFiles%\" + rmdir /Q /S "%ProgramFiles%\Azure Cosmos DB Emulator" + echo "Directory after deleting" + dir "%ProgramFiles%\" + + - task: CmdLine@2 + displayName: 'Install Public Cosmos DB Emulator' + inputs: + script: | + choco install lessmsi + choco upgrade lessmsi + echo "Checking directory" + dir "%ProgramFiles%" + mkdir "%TEMP%\Azure Cosmos DB Emulator" + lessmsi x "%TEMP%\azure-cosmosdb-emulator.msi" "%TEMP%\Azure Cosmos DB Emulator\" + dir "%TEMP%" + dir "%Temp%\Azure Cosmos DB Emulator\" + + - task: PowerShell@1 + displayName: 'Run Public Cosmos DB Emulator' + inputs: + scriptType: inlineScript + inlineScript: | + dir "$env:Temp\" + dir "$env:Temp\Azure Cosmos DB Emulator" + dir "$env:Temp\Azure Cosmos DB Emulator\SourceDir\" + dir "$env:Temp\Azure Cosmos DB Emulator\SourceDir\Azure Cosmos DB Emulator" + Write-Host "Starting Comsos DB Emulator" + Start-Process "$env:Temp\Azure Cosmos DB Emulator\SourceDir\Azure Cosmos DB Emulator\CosmosDB.Emulator.exe" "/NoExplorer /NoUI" -Verb RunAs + - task: UsePythonVersion@0 displayName: 'Use Python $(PythonVersion)' inputs: @@ -36,17 +81,12 @@ jobs: python scripts/dev_setup.py --packageList azure-cosmos displayName: 'Set up Environment' - - task: azure-cosmosdb.emulator-public-preview.run-cosmosdbemulatorcontainer.CosmosDbEmulator@2 - displayName: "Run Azure Cosmos DB Emulator container" - inputs: - defaultPartitionCount: 25 - - task: PowerShell@1 displayName: 'Run Python Tests' inputs: scriptType: inlineScript inlineScript: | - $env:ACCOUNT_HOST="$(CosmosDBEmulator.Endpoint)" + $env:ACCOUNT_HOST="https://localhost:8081/" cmd /c 'pytest --junitxml=sdk/cosmos/azure-cosmos/Test-junit.xml --verbose sdk/cosmos/azure-cosmos/test -k "not globaldb"' condition: succeededOrFailed() diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py b/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py index a8f2617d8e86..728668b5036b 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py @@ -37,6 +37,7 @@ import azure.cosmos.routing.routing_map_provider as routing_map_provider import azure.cosmos.session as session import azure.cosmos.utils as utils +import os class CosmosClient(object): """Represents a document client. @@ -80,12 +81,15 @@ def __init__(self, :param documents.ConsistencyLevel consistency_level: The default consistency policy for client operations. + if url_connection and auth are not provided, + COSMOS_ENDPOINT and COSMOS_KEY environment variables will be used. """ - self.url_connection = url_connection + + self.url_connection = url_connection or os.environ.get('COSMOS_ENDPOINT') self.master_key = None self.resource_tokens = None - if auth != None: + if auth is not None: self.master_key = auth.get('masterKey') self.resource_tokens = auth.get('resourceTokens') @@ -95,6 +99,8 @@ def __init__(self, resource_parts = permission_feed['resource'].split('/') id = resource_parts[-1] self.resource_tokens[id] = permission_feed['_token'] + else: + self.master_key = os.environ.get('COSMOS_KEY') self.connection_policy = (connection_policy or documents.ConnectionPolicy()) diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/execution_context/document_producer.py b/sdk/cosmos/azure-cosmos/azure/cosmos/execution_context/document_producer.py index e0f79674fed4..c74a60e3bade 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/execution_context/document_producer.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/execution_context/document_producer.py @@ -108,6 +108,8 @@ def __lt__(self, other): return self._doc_producer_comp.compare(self, other) < 0 def _compare_helper(a, b): + if a is None and b is None: + return 0 return (a > b) - (a < b) class _PartitionKeyRangeDocumentProduerComparator(object): diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/http_constants.py b/sdk/cosmos/azure-cosmos/azure/cosmos/http_constants.py index 2131d3038f60..b9ceb6471349 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/http_constants.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/http_constants.py @@ -257,7 +257,7 @@ class Versions: """ CurrentVersion = '2018-09-17' SDKName = 'azure-cosmos' - SDKVersion = '3.0.3-SNAPSHOT' + SDKVersion = '3.1.0' class Delimiters: diff --git a/sdk/cosmos/azure-cosmos/doc/conf.py b/sdk/cosmos/azure-cosmos/doc/conf.py index 1b7b5dbd2bd7..c15c2f3b32ec 100644 --- a/sdk/cosmos/azure-cosmos/doc/conf.py +++ b/sdk/cosmos/azure-cosmos/doc/conf.py @@ -52,9 +52,9 @@ # built documents. # # The short X.Y version. -version = '3.0.3-SNAPSHOT' +version = '3.1.0' # The full version, including alpha/beta/rc tags. -release = '3.0.3-SNAPSHOT' +release = '3.1.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/sdk/cosmos/azure-cosmos/samples/CollectionManagement/Program.py b/sdk/cosmos/azure-cosmos/samples/CollectionManagement/Program.py index 2361d0096cb5..485de1f5b286 100644 --- a/sdk/cosmos/azure-cosmos/samples/CollectionManagement/Program.py +++ b/sdk/cosmos/azure-cosmos/samples/CollectionManagement/Program.py @@ -22,6 +22,8 @@ # 2.2 - Create collection with custom IndexPolicy # 2.3 - Create collection with offer throughput set # 2.4 - Create collection with unique key +# 2.5 - Create Collection with partition key +# 2.6 - Create Collection with partition key V2 # # 3. Manage Collection Offer Throughput # 3.1 - Get Collection performance tier @@ -165,12 +167,37 @@ def create_Container(client, id): collection = client.CreateContainer(database_link, coll) print('Collection with id \'{0}\' created'.format(collection['id'])) + print('Partition Key - \'{0}\''.format(collection['partitionKey'])) except errors.CosmosError as e: if e.status_code == 409: print('A collection with id \'{0}\' already exists'.format(collection['id'])) else: - raise errors.HTTPFailure(e.status_code) + raise errors.HTTPFailure(e.status_code) + + print("\n2.6 Create Collection - With Partition key V2") + + try: + coll = { + "id": "collection_partition_key_v2", + "partitionKey": { + "paths": [ + "/field1" + ], + "kind": "Hash", + "version": 2 + } + } + + collection = client.CreateContainer(database_link, coll) + print('Collection with id \'{0}\' created'.format(collection['id'])) + print('Partition Key - \'{0}\''.format(collection['partitionKey'])) + + except errors.CosmosError as e: + if e.status_code == 409: + print('A collection with id \'{0}\' already exists'.format(collection['id'])) + else: + raise errors.HTTPFailure(e.status_code) @staticmethod def manage_offer_throughput(client, id): diff --git a/sdk/cosmos/azure-cosmos/samples/IndexManagement/Program.py b/sdk/cosmos/azure-cosmos/samples/IndexManagement/Program.py index f794b6424157..e4c3f4a6b08b 100644 --- a/sdk/cosmos/azure-cosmos/samples/IndexManagement/Program.py +++ b/sdk/cosmos/azure-cosmos/samples/IndexManagement/Program.py @@ -565,6 +565,102 @@ def PerformIndexTransformations(client, database_id): else: raise +def PerformMultiOrderbyQuery(client, database_id): + try: + DeleteContainerIfExists(client, database_id, COLLECTION_ID) + database_link = GetDatabaseLink(database_id) + + # Create a collection with composite indexes + indexingPolicy = { + "compositeIndexes": [ + [ + { + "path": "/numberField", + "order": "ascending" + }, + { + "path": "/stringField", + "order": "descending" + } + ], + [ + { + "path": "/numberField", + "order": "descending" + }, + { + "path": "/stringField", + "order": "ascending" + }, + { + "path": "/numberField2", + "order": "descending" + }, + { + "path": "/stringField2", + "order": "ascending" + } + ] + ] + } + + container_definition = { + 'id': COLLECTION_ID, + 'indexingPolicy': indexingPolicy + } + + created_container = client.CreateContainer(database_link, container_definition) + + print(created_container) + + print("\n" + "-" * 25 + "\n8. Collection created with index policy") + print_dictionary_items(created_container["indexingPolicy"]) + + # Insert some documents + collection_link = GetContainerLink(database_id, COLLECTION_ID) + doc1 = client.CreateItem(collection_link, {"id": "doc1", "numberField": 1, "stringField": "1", "numberField2": 1, "stringField2": "1"}) + doc2 = client.CreateItem(collection_link, {"id": "doc2", "numberField": 1, "stringField": "1", "numberField2": 1, "stringField2": "2"}) + doc3 = client.CreateItem(collection_link, {"id": "doc3", "numberField": 1, "stringField": "1", "numberField2": 2, "stringField2": "1"}) + doc4 = client.CreateItem(collection_link, {"id": "doc4", "numberField": 1, "stringField": "1", "numberField2": 2, "stringField2": "2"}) + doc5 = client.CreateItem(collection_link, {"id": "doc5", "numberField": 1, "stringField": "2", "numberField2": 1, "stringField2": "1"}) + doc6 = client.CreateItem(collection_link, {"id": "doc6", "numberField": 1, "stringField": "2", "numberField2": 1, "stringField2": "2"}) + doc7 = client.CreateItem(collection_link, {"id": "doc7", "numberField": 1, "stringField": "2", "numberField2": 2, "stringField2": "1"}) + doc8 = client.CreateItem(collection_link, {"id": "doc8", "numberField": 1, "stringField": "2", "numberField2": 2, "stringField2": "2"}) + doc9 = client.CreateItem(collection_link, {"id": "doc9", "numberField": 2, "stringField": "1", "numberField2": 1, "stringField2": "1"}) + doc10 = client.CreateItem(collection_link, {"id": "doc10", "numberField": 2, "stringField": "1", "numberField2": 1, "stringField2": "2"}) + doc11 = client.CreateItem(collection_link, {"id": "doc11", "numberField": 2, "stringField": "1", "numberField2": 2, "stringField2": "1"}) + doc12 = client.CreateItem(collection_link, {"id": "doc12", "numberField": 2, "stringField": "1", "numberField2": 2, "stringField2": "2"}) + doc13 = client.CreateItem(collection_link, {"id": "doc13", "numberField": 2, "stringField": "2", "numberField2": 1, "stringField2": "1"}) + doc14 = client.CreateItem(collection_link, {"id": "doc14", "numberField": 2, "stringField": "2", "numberField2": 1, "stringField2": "2"}) + doc15 = client.CreateItem(collection_link, {"id": "doc15", "numberField": 2, "stringField": "2", "numberField2": 2, "stringField2": "1"}) + doc16 = client.CreateItem(collection_link, {"id": "doc16", "numberField": 2, "stringField": "2", "numberField2": 2, "stringField2": "2"}) + + print("Query documents and Order by 1st composite index: Ascending numberField and Descending stringField:") + + query = { + "query": "SELECT * FROM r ORDER BY r.numberField ASC, r.stringField DESC", + } + QueryDocumentsWithCustomQuery(client, collection_link, query) + + print("Query documents and Order by inverted 2nd composite index -") + print("Ascending numberField, Descending stringField, Ascending numberField2, Descending stringField2") + + query = { + "query": "SELECT * FROM r ORDER BY r.numberField ASC, r.stringField DESC, r.numberField2 ASC, r.stringField2 DESC", + } + QueryDocumentsWithCustomQuery(client, collection_link, query) + + # Cleanup + client.DeleteContainer(collection_link) + print("\n") + except errors.HTTPFailure as e: + if e.status_code == 409: + print("Entity already exists") + elif e.status_code == 404: + print("Entity doesn't exist") + else: + raise + def RunIndexDemo(): try: client = ObtainClient() @@ -592,6 +688,9 @@ def RunIndexDemo(): # 7. Perform an index transform PerformIndexTransformations(client, DATABASE_ID) + # 8. Perform Multi Orderby queries using composite indexes + PerformMultiOrderbyQuery(client, DATABASE_ID) + except errors.CosmosError as e: raise e diff --git a/sdk/cosmos/azure-cosmos/setup.py b/sdk/cosmos/azure-cosmos/setup.py index 995dc3ba68c8..aaa0880df181 100644 --- a/sdk/cosmos/azure-cosmos/setup.py +++ b/sdk/cosmos/azure-cosmos/setup.py @@ -4,7 +4,7 @@ import setuptools setup(name='azure-cosmos', - version='3.0.3-SNAPSHOT', + version='3.1.0', description='Azure Cosmos Python SDK', author="Microsoft", author_email="askdocdb@microsoft.com", diff --git a/sdk/cosmos/azure-cosmos/test/aggregate_tests.py b/sdk/cosmos/azure-cosmos/test/aggregate_tests.py index 60e3aecc6da5..7ba31202d8f8 100644 --- a/sdk/cosmos/azure-cosmos/test/aggregate_tests.py +++ b/sdk/cosmos/azure-cosmos/test/aggregate_tests.py @@ -33,6 +33,8 @@ import test_config from azure.cosmos.errors import HTTPFailure +pytestmark = pytest.mark.cosmosEmulator + class _config: host = test_config._test_config.host master_key = test_config._test_config.masterKey @@ -199,15 +201,18 @@ def _get_collection_link(database, document_collection, is_name_based=True): _all_tests = [] - _setup() - _generate_test_configs() - _run_all() - return type.__new__(mcs, name, bases, dict) @pytest.mark.usefixtures("teardown") class AggregationQueryTest(with_metaclass(AggregateQueryTestSequenceMeta, unittest.TestCase)): + + @classmethod + def setUpClass(cls): + cls._setup() + cls._generate_test_configs() + cls._run_all() + def _execute_query_and_validate_results(self, client, collection_link, query, expected): print('Running test with query: ' + query) diff --git a/sdk/cosmos/azure-cosmos/test/base_unit_tests.py b/sdk/cosmos/azure-cosmos/test/base_unit_tests.py index be418e03e854..2dd41a646231 100644 --- a/sdk/cosmos/azure-cosmos/test/base_unit_tests.py +++ b/sdk/cosmos/azure-cosmos/test/base_unit_tests.py @@ -2,6 +2,8 @@ import pytest import azure.cosmos.base as base +pytestmark = pytest.mark.cosmosEmulator + @pytest.mark.usefixtures("teardown") class BaseUnitTests(unittest.TestCase): def test_is_name_based(self): diff --git a/sdk/cosmos/azure-cosmos/test/crud_tests.py b/sdk/cosmos/azure-cosmos/test/crud_tests.py index 4e5598074d40..579064f42ba9 100644 --- a/sdk/cosmos/azure-cosmos/test/crud_tests.py +++ b/sdk/cosmos/azure-cosmos/test/crud_tests.py @@ -52,6 +52,7 @@ import test_partition_resolver import azure.cosmos.base as base +pytestmark = pytest.mark.cosmosEmulator #IMPORTANT NOTES: @@ -70,8 +71,6 @@ class CRUDTests(unittest.TestCase): host = configs.host masterKey = configs.masterKey connectionPolicy = configs.connectionPolicy - client = cosmos_client.CosmosClient(host, {'masterKey': masterKey}, connectionPolicy) - databseForTest = configs.create_database_if_not_exist(client) def __AssertHTTPFailureWithStatus(self, status_code, func, *args, **kwargs): """Assert HTTP failure with status. @@ -94,6 +93,9 @@ def setUpClass(cls): "You must specify your Azure Cosmos account values for " "'masterKey' and 'host' at the top of this class to run the " "tests.") + cls.client = cosmos_client.CosmosClient(cls.host, {'masterKey': cls.masterKey}, cls.connectionPolicy) + cls.databseForTest = cls.configs.create_database_if_not_exist(cls.client) + def setUp(self): self.client = cosmos_client.CosmosClient(self.host, {'masterKey': self.masterKey}, self.connectionPolicy) @@ -3116,6 +3118,75 @@ def _test_create_default_indexing_policy(self, is_name_based): self._check_default_indexing_policy_paths(collection['indexingPolicy']) self.client.DeleteContainer(collection['_self']) + def test_create_indexing_policy_with_composite_and_spatial_indexes_self_link(self): + self._test_create_indexing_policy_with_composite_and_spatial_indexes(False) + + def test_create_indexing_policy_with_composite_and_spatial_indexes_name_based(self): + self._test_create_indexing_policy_with_composite_and_spatial_indexes(True) + + def _test_create_indexing_policy_with_composite_and_spatial_indexes(self, is_name_based): + # create database + db = self.databseForTest + + indexing_policy = { + "spatialIndexes": [ + { + "path": "/path0/*", + "types": [ + "Point", + "LineString", + "Polygon" + ] + }, + { + "path": "/path1/*", + "types": [ + "LineString", + "Polygon", + "MultiPolygon" + ] + } + ], + "compositeIndexes": [ + [ + { + "path": "/path1", + "order": "ascending" + }, + { + "path": "/path2", + "order": "descending" + }, + { + "path": "/path3", + "order": "ascending" + } + ], + [ + { + "path": "/path4", + "order": "ascending" + }, + { + "path": "/path5", + "order": "descending" + }, + { + "path": "/path6", + "order": "ascending" + } + ] + ] + } + + container_id = 'composite_index_spatial_index' + str(uuid.uuid4()) + container_definition = {'id': container_id, 'indexingPolicy': indexing_policy} + created_container = self.client.CreateContainer(self.GetDatabaseLink(db, is_name_based), container_definition) + read_indexing_policy = created_container['indexingPolicy'] + self.assertListEqual(indexing_policy['spatialIndexes'], read_indexing_policy['spatialIndexes']) + self.assertListEqual(indexing_policy['compositeIndexes'], read_indexing_policy['compositeIndexes']) + self.client.DeleteContainer(created_container['_self']) + def _check_default_indexing_policy_paths(self, indexing_policy): def __get_first(array): if array: @@ -3135,8 +3206,8 @@ def __get_first(array): def test_client_request_timeout(self): connection_policy = documents.ConnectionPolicy() - # making timeout 1 ms to make sure it will throw - connection_policy.RequestTimeout = 1 + # making timeout 0 ms to make sure it will throw + connection_policy.RequestTimeout = 0 with self.assertRaises(Exception): # client does a getDatabaseAccount on initialization, which will time out cosmos_client.CosmosClient(CRUDTests.host, diff --git a/sdk/cosmos/azure-cosmos/test/encoding_tests.py b/sdk/cosmos/azure-cosmos/test/encoding_tests.py index 25592611bdbe..8bd61a267098 100644 --- a/sdk/cosmos/azure-cosmos/test/encoding_tests.py +++ b/sdk/cosmos/azure-cosmos/test/encoding_tests.py @@ -7,6 +7,8 @@ import azure.cosmos.documents as documents import test_config +pytestmark = pytest.mark.cosmosEmulator + @pytest.mark.usefixtures("teardown") class EncodingTest(unittest.TestCase): """Test to ensure escaping of non-ascii characters from partition key""" @@ -14,8 +16,19 @@ class EncodingTest(unittest.TestCase): host = test_config._test_config.host masterKey = test_config._test_config.masterKey connectionPolicy = test_config._test_config.connectionPolicy - client = cosmos_client.CosmosClient(host, {'masterKey': masterKey}, connectionPolicy) - created_collection = test_config._test_config.create_multi_partition_collection_with_custom_pk_if_not_exist(client) + + @classmethod + def setUpClass(cls): + if (cls.masterKey == '[YOUR_KEY_HERE]' or + cls.host == '[YOUR_ENDPOINT_HERE]'): + raise Exception( + "You must specify your Azure Cosmos account values for " + "'masterKey' and 'host' at the top of this class to run the " + "tests.") + + cls.client = cosmos_client.CosmosClient(cls.host, {'masterKey': cls.masterKey}, cls.connectionPolicy) + cls.created_collection = test_config._test_config.create_multi_partition_collection_with_custom_pk_if_not_exist(cls.client) + def test_unicode_characters_in_partition_key (self): test_string = u'€€ کلید پارتیشن विभाजन कुंजी 123' diff --git a/sdk/cosmos/azure-cosmos/test/env_test.py b/sdk/cosmos/azure-cosmos/test/env_test.py new file mode 100644 index 000000000000..3f9a0d957c20 --- /dev/null +++ b/sdk/cosmos/azure-cosmos/test/env_test.py @@ -0,0 +1,109 @@ +#The MIT License (MIT) +#Copyright (c) 2019 Microsoft Corporation + +#Permission is hereby granted, free of charge, to any person obtaining a copy +#of this software and associated documentation files (the "Software"), to deal +#in the Software without restriction, including without limitation the rights +#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +#copies of the Software, and to permit persons to whom the Software is +#furnished to do so, subject to the following conditions: + +#The above copyright notice and this permission notice shall be included in all +#copies or substantial portions of the Software. + +#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +#SOFTWARE. + +import unittest +import uuid +import pytest +import azure.cosmos.documents as documents +import azure.cosmos.cosmos_client as cosmos_client +import test_config +import os + +pytestmark = pytest.mark.cosmosEmulator + +#IMPORTANT NOTES: + +# Most test cases in this file create collections in your Azure Cosmos account. +# Collections are billing entities. By running these test cases, you may incur monetary costs on your account. + +# To Run the test, replace the two member fields (masterKey and host) with values +# associated with your Azure Cosmos account. + +@pytest.mark.usefixtures("teardown") +class EnvTest(unittest.TestCase): + """Env Tests. + """ + + host = test_config._test_config.host + masterKey = test_config._test_config.masterKey + connectionPolicy = test_config._test_config.connectionPolicy + + @classmethod + def setUpClass(cls): + # creates the database, collection, and insert all the documents + # we will gain some speed up in running the tests by creating the database, collection and inserting all the docs only once + + if (cls.masterKey == '[YOUR_KEY_HERE]' or + cls.host == '[YOUR_ENDPOINT_HERE]'): + raise Exception( + "You must specify your Azure Cosmos account values for " + "'masterKey' and 'host' at the top of this class to run the " + "tests.") + + os.environ["COSMOS_ENDPOINT"] = cls.host + os.environ["COSMOS_KEY"] = cls.masterKey + cls.client = cosmos_client.CosmosClient(None, None, cls.connectionPolicy) + cls.created_db = test_config._test_config.create_database_if_not_exist(cls.client) + cls.created_collection = test_config._test_config.create_single_partition_collection_if_not_exist(cls.client) + cls.collection_link = cls.GetDocumentCollectionLink(cls.created_db, cls.created_collection) + + @classmethod + def tearDownClass(cls): + del os.environ['COSMOS_ENDPOINT'] + del os.environ['COSMOS_KEY'] + + def test_insert(self): + # create a document using the document definition + d = {'id': '1', + 'name': 'sample document', + 'spam': 'eggs', + 'cnt': '1', + 'key': 'value', + 'spam2': 'eggs', + } + + self.client.CreateItem(self.collection_link, d) + + @classmethod + def GetDatabaseLink(cls, database, is_name_based=True): + if is_name_based: + return 'dbs/' + database['id'] + else: + return database['_self'] + + @classmethod + def GetDocumentCollectionLink(cls, database, document_collection, is_name_based=True): + if is_name_based: + return cls.GetDatabaseLink(database) + '/colls/' + document_collection['id'] + else: + return document_collection['_self'] + + @classmethod + def GetDocumentLink(cls, database, document_collection, document, is_name_based=True): + if is_name_based: + return cls.GetDocumentCollectionLink(database, document_collection) + '/docs/' + document['id'] + else: + return document['_self'] + + +if __name__ == "__main__": + #import sys;sys.argv = ['', 'Test.testName'] + unittest.main() \ No newline at end of file diff --git a/sdk/cosmos/azure-cosmos/test/globaldb_mock_tests.py b/sdk/cosmos/azure-cosmos/test/globaldb_mock_tests.py index 8f4647453b2b..1de31c8405c6 100644 --- a/sdk/cosmos/azure-cosmos/test/globaldb_mock_tests.py +++ b/sdk/cosmos/azure-cosmos/test/globaldb_mock_tests.py @@ -32,6 +32,8 @@ import azure.cosmos.retry_utility as retry_utility import test_config +pytestmark = pytest.mark.cosmosEmulator + location_changed = False class MockGlobalEndpointManager: diff --git a/sdk/cosmos/azure-cosmos/test/globaldb_tests.py b/sdk/cosmos/azure-cosmos/test/globaldb_tests.py index a0c43c959b2b..a0114516fd21 100644 --- a/sdk/cosmos/azure-cosmos/test/globaldb_tests.py +++ b/sdk/cosmos/azure-cosmos/test/globaldb_tests.py @@ -35,6 +35,8 @@ from azure.cosmos.http_constants import HttpHeaders, StatusCodes, SubStatusCodes import test_config +pytestmark = pytest.mark.cosmosEmulator + #IMPORTANT NOTES: # Most test cases in this file create collections in your Azure Cosmos account. diff --git a/sdk/cosmos/azure-cosmos/test/location_cache_tests.py b/sdk/cosmos/azure-cosmos/test/location_cache_tests.py index 72926671db0e..c341b63776f9 100644 --- a/sdk/cosmos/azure-cosmos/test/location_cache_tests.py +++ b/sdk/cosmos/azure-cosmos/test/location_cache_tests.py @@ -15,6 +15,8 @@ import azure.cosmos.retry_utility as retry_utility import six +pytestmark = pytest.mark.cosmosEmulator + class RefreshThread(threading.Thread): def __init__(self, group=None, target=None, name=None, args=(), kwargs=None, verbose=None): diff --git a/sdk/cosmos/azure-cosmos/test/multiOrderbyTests.py b/sdk/cosmos/azure-cosmos/test/multiOrderbyTests.py new file mode 100644 index 000000000000..778a26f8a4f4 --- /dev/null +++ b/sdk/cosmos/azure-cosmos/test/multiOrderbyTests.py @@ -0,0 +1,305 @@ +# The MIT License (MIT) +# Copyright (c) 2014 Microsoft Corporation + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import unittest +import uuid +import pytest +import random +import azure.cosmos.cosmos_client as cosmos_client +import test_config + +pytestmark = pytest.mark.cosmosEmulator + +# IMPORTANT NOTES: + +# Most test cases in this file create collections in your Azure Cosmos account. +# Collections are billing entities. By running these test cases, you may incur monetary costs on your account. + +# To Run the test, replace the two member fields (masterKey and host) with values +# associated with your Azure Cosmos account. + +@pytest.mark.usefixtures("teardown") +class MultiOrderbyTests(unittest.TestCase): + """Multi Orderby and Composite Indexes Tests. + """ + + NUMBER_FIELD = "numberField" + STRING_FIELD = "stringField" + NUMBER_FIELD_2 = "numberField2" + STRING_FIELD_2 = "stringField2" + BOOL_FIELD = "boolField" + NULL_FIELD = "nullField" + OBJECT_FIELD = "objectField" + ARRAY_FIELD = "arrayField" + SHORT_STRING_FIELD = "shortStringField" + MEDIUM_STRING_FIELD = "mediumStringField" + LONG_STRING_FIELD = "longStringField" + PARTITION_KEY = "pk" + documents = [] + host = test_config._test_config.host + masterKey = test_config._test_config.masterKey + connectionPolicy = test_config._test_config.connectionPolicy + + @classmethod + def setUpClass(cls): + if (cls.masterKey == '[YOUR_KEY_HERE]' or + cls.host == '[YOUR_ENDPOINT_HERE]'): + raise Exception( + "You must specify your Azure Cosmos account values for " + "'masterKey' and 'host' at the top of this class to run the " + "tests.") + + cls.client = cosmos_client.CosmosClient(cls.host, {'masterKey': cls.masterKey}, cls.connectionPolicy) + cls.database = test_config._test_config.create_database_if_not_exist(cls.client) + + + def generate_multi_orderby_document(self): + document = {} + document['id'] = str(uuid.uuid4()) + document[self.NUMBER_FIELD] = random.randint(0, 5) + document[self.NUMBER_FIELD_2] = random.randint(0, 5) + document[self.BOOL_FIELD] = random.randint(0, 2) % 2 == 0 + document[self.STRING_FIELD] = str(random.randint(0, 5)) + document[self.STRING_FIELD_2] = str(random.randint(0, 5)) + document[self.NULL_FIELD] = None + document[self.OBJECT_FIELD] = "" + document[self.ARRAY_FIELD] = [] + document[self.SHORT_STRING_FIELD] = "a" + str(random.randint(0, 100)) + document[self.MEDIUM_STRING_FIELD] = "a" + str(random.randint(0, 128) + 100) + document[self.LONG_STRING_FIELD] = "a" + str(random.randint(0, 255) + 128) + document[self.PARTITION_KEY] = random.randint(0, 5) + return document + + def create_random_documents(self, container, number_of_documents, number_of_duplicates): + for i in range(0, number_of_documents): + multi_orderby_document = self.generate_multi_orderby_document() + for j in range(0, number_of_duplicates): + # Add the document itself for exact duplicates + clone = multi_orderby_document.copy() + clone['id'] = str(uuid.uuid4()) + self.documents.append(clone) + + # Permute all the fields so that there are duplicates with tie breaks + number_clone = multi_orderby_document.copy() + number_clone[self.NUMBER_FIELD] = random.randint(0, 5) + number_clone['id'] = str(uuid.uuid4()) + self.documents.append(number_clone) + + string_clone = multi_orderby_document.copy() + string_clone[self.STRING_FIELD] = str(random.randint(0, 5)) + string_clone['id'] = str(uuid.uuid4()) + self.documents.append(string_clone) + + bool_clone = multi_orderby_document.copy() + bool_clone[self.BOOL_FIELD] = random.randint(0, 2) % 2 == 0 + bool_clone['id'] = str(uuid.uuid4()) + self.documents.append(bool_clone) + + # Also fuzz what partition it goes to + partition_clone = multi_orderby_document.copy() + partition_clone[self.PARTITION_KEY] = random.randint(0, 5) + partition_clone['id'] = str(uuid.uuid4()) + self.documents.append(partition_clone) + + for document in self.documents: + self.client.CreateItem(container['_self'], document) + + def test_multi_orderby_queries(self): + indexingPolicy = { + "indexingMode": "consistent", + "automatic": True, + "includedPaths": [ + { + "path": "/*", + "indexes": [] + } + ], + "excludedPaths": [ + { + "path": "/\"_etag\"/?" + } + ], + "compositeIndexes": [ + [ + { + "path": "/numberField", + "order": "ascending" + }, + { + "path": "/stringField", + "order": "descending" + } + ], + [ + { + "path": "/numberField", + "order": "descending" + }, + { + "path": "/stringField", + "order": "ascending" + }, + { + "path": "/numberField2", + "order": "descending" + }, + { + "path": "/stringField2", + "order": "ascending" + } + ], + [ + { + "path": "/numberField", + "order": "descending" + }, + { + "path": "/stringField", + "order": "ascending" + }, + { + "path": "/boolField", + "order": "descending" + }, + { + "path": "/nullField", + "order": "ascending" + } + ], + [ + { + "path": "/stringField", + "order": "ascending" + }, + { + "path": "/shortStringField", + "order": "ascending" + }, + { + "path": "/mediumStringField", + "order": "ascending" + }, + { + "path": "/longStringField", + "order": "ascending" + } + ] + ] + } + partitionKey = { + "paths": [ + "/pk" + ], + "kind": "Hash" + } + container_id = 'multi_orderby_container' + str(uuid.uuid4()) + container_definition = { + 'id': container_id, + 'indexingPolicy': indexingPolicy, + 'partitionKey': partitionKey + } + options = { 'offerThroughput': 25100 } + created_container = self.client.CreateContainer(self.database['_self'], container_definition, options) + + number_of_documents = 4 + number_of_duplicates = 5 + self.create_random_documents(created_container, number_of_documents, number_of_duplicates) + + feed_options = { 'enableCrossPartitionQuery': True } + bool_vals = [True, False] + composite_indexes = indexingPolicy['compositeIndexes'] + for composite_index in composite_indexes: + # for every order + for invert in bool_vals: + # for normal and inverted order + for has_top in bool_vals: + # with and without top + for has_filter in bool_vals: + # with and without filter + # Generate a multi order by from that index + orderby_items = [] + select_items = [] + for composite_path in composite_index: + is_desc = True if composite_path['order'] == "descending" else False + if invert: + is_desc = not is_desc + + is_desc_string = "DESC" if is_desc else "ASC" + composite_path_name = composite_path['path'].replace("/", "") + orderby_items_string = "root." + composite_path_name + " " + is_desc_string + select_items_string = "root." + composite_path_name + orderby_items.append(orderby_items_string) + select_items.append(select_items_string) + + top_count = 10 + select_item_builder = "" + for select_item in select_items: + select_item_builder += select_item + "," + select_item_builder = select_item_builder[:-1] + + orderby_item_builder = "" + for orderby_item in orderby_items: + orderby_item_builder += orderby_item + "," + orderby_item_builder = orderby_item_builder[:-1] + + top_string = "TOP " + str(top_count) if has_top else "" + where_string = "WHERE root." + self.NUMBER_FIELD + " % 2 = 0" if has_filter else "" + query = "SELECT " + top_string + " [" + select_item_builder + "] " + \ + "FROM root " + where_string + " " + \ + "ORDER BY " + orderby_item_builder + + expected_ordered_list = self.top(self.sort(self.filter(self.documents, has_filter), composite_index, invert), has_top, top_count) + + result_ordered_list = list(self.client.QueryItems(created_container['_self'], query, feed_options)) + + self.validate_results(expected_ordered_list, result_ordered_list, composite_index) + + def top(self, documents, has_top, top_count): + return documents[0:top_count] if has_top else documents + + def sort(self, documents, composite_index, invert): + current_docs = documents + for composite_path in reversed(composite_index): + order = composite_path['order'] + if invert: + order = "ascending" if order == "descending" else "descending" + path = composite_path['path'].replace("/", "") + if self.NULL_FIELD not in path: + current_docs = sorted(current_docs, key=lambda x: x[path], reverse=True if order == "descending" else False) + return current_docs + + def filter(self, documents, has_filter): + return [x for x in documents if x[self.NUMBER_FIELD] % 2 == 0] if has_filter else documents + + def validate_results(self, expected_ordered_list, result_ordered_list, composite_index): + self.assertEquals(len(expected_ordered_list), len(result_ordered_list)) + + for i in range(0, len(expected_ordered_list)): + result_values = result_ordered_list[i]['$1'] + self.assertEquals(len(result_values), len(composite_index)) + + for j in range(0, len(composite_index)): + path = composite_index[j]['path'].replace("/", "") + if self.NULL_FIELD in path: + self.assertIsNone(expected_ordered_list[i][path]) + self.assertIsNone(result_values[j]) + else: + self.assertEquals(expected_ordered_list[i][path], result_values[j]) diff --git a/sdk/cosmos/azure-cosmos/test/multimaster_tests.py b/sdk/cosmos/azure-cosmos/test/multimaster_tests.py index 7967a760c0bb..6ad4c5949be1 100644 --- a/sdk/cosmos/azure-cosmos/test/multimaster_tests.py +++ b/sdk/cosmos/azure-cosmos/test/multimaster_tests.py @@ -13,6 +13,8 @@ import azure.cosmos.retry_utility as retry_utility import test_config +pytestmark = pytest.mark.cosmosEmulator + @pytest.mark.usefixtures("teardown") class MultiMasterTests(unittest.TestCase): diff --git a/sdk/cosmos/azure-cosmos/test/orderby_tests.py b/sdk/cosmos/azure-cosmos/test/orderby_tests.py index ae71e0bebc92..9548220ff0da 100644 --- a/sdk/cosmos/azure-cosmos/test/orderby_tests.py +++ b/sdk/cosmos/azure-cosmos/test/orderby_tests.py @@ -29,6 +29,8 @@ from six.moves import xrange import test_config +pytestmark = pytest.mark.cosmosEmulator + #IMPORTANT NOTES: # Most test cases in this file create collections in your Azure Cosmos account. diff --git a/sdk/cosmos/azure-cosmos/test/proxy_tests.py b/sdk/cosmos/azure-cosmos/test/proxy_tests.py index 28b61fd56612..86c797373ccc 100644 --- a/sdk/cosmos/azure-cosmos/test/proxy_tests.py +++ b/sdk/cosmos/azure-cosmos/test/proxy_tests.py @@ -32,6 +32,8 @@ from threading import Thread from requests.exceptions import ProxyError +pytestmark = pytest.mark.cosmosEmulator + @pytest.mark.usefixtures("teardown") class CustomRequestHandler(BaseHTTPRequestHandler): database_name = None diff --git a/sdk/cosmos/azure-cosmos/test/query_execution_context_tests.py b/sdk/cosmos/azure-cosmos/test/query_execution_context_tests.py index b890f4613935..0f8096a785f3 100644 --- a/sdk/cosmos/azure-cosmos/test/query_execution_context_tests.py +++ b/sdk/cosmos/azure-cosmos/test/query_execution_context_tests.py @@ -29,6 +29,8 @@ import azure.cosmos.base as base import test_config +pytestmark = pytest.mark.cosmosEmulator + #IMPORTANT NOTES: # Most test cases in this file create collections in your Azure Cosmos account. diff --git a/sdk/cosmos/azure-cosmos/test/query_tests.py b/sdk/cosmos/azure-cosmos/test/query_tests.py index c51fe5410be5..f5b5935f68a6 100644 --- a/sdk/cosmos/azure-cosmos/test/query_tests.py +++ b/sdk/cosmos/azure-cosmos/test/query_tests.py @@ -5,6 +5,8 @@ import azure.cosmos.documents as documents import test_config +pytestmark = pytest.mark.cosmosEmulator + @pytest.mark.usefixtures("teardown") class QueryTest(unittest.TestCase): """Test to ensure escaping of non-ascii characters from partition key""" @@ -13,8 +15,18 @@ class QueryTest(unittest.TestCase): host = config.host masterKey = config.masterKey connectionPolicy = config.connectionPolicy - client = cosmos_client.CosmosClient(host, {'masterKey': masterKey}, connectionPolicy) - created_db = config.create_database_if_not_exist(client) + + @classmethod + def setUpClass(cls): + if (cls.masterKey == '[YOUR_KEY_HERE]' or + cls.host == '[YOUR_ENDPOINT_HERE]'): + raise Exception( + "You must specify your Azure Cosmos account values for " + "'masterKey' and 'host' at the top of this class to run the " + "tests.") + + cls.client = cosmos_client.CosmosClient(cls.host, {'masterKey': cls.masterKey}, cls.connectionPolicy) + cls.created_db = cls.config.create_database_if_not_exist(cls.client) def test_first_and_last_slashes_trimmed_for_query_string (self): created_collection = self.config.create_multi_partition_collection_with_custom_pk_if_not_exist(self.client) diff --git a/sdk/cosmos/azure-cosmos/test/retry_policy_tests.py b/sdk/cosmos/azure-cosmos/test/retry_policy_tests.py index ad12b9ac2414..3d466bcc449d 100644 --- a/sdk/cosmos/azure-cosmos/test/retry_policy_tests.py +++ b/sdk/cosmos/azure-cosmos/test/retry_policy_tests.py @@ -30,6 +30,7 @@ import azure.cosmos.retry_utility as retry_utility import test_config +pytestmark = pytest.mark.cosmosEmulator #IMPORTANT NOTES: diff --git a/sdk/cosmos/azure-cosmos/test/routing/collection_routing_map_test.py b/sdk/cosmos/azure-cosmos/test/routing/collection_routing_map_test.py index bc86c28be036..ae5fa2c011af 100644 --- a/sdk/cosmos/azure-cosmos/test/routing/collection_routing_map_test.py +++ b/sdk/cosmos/azure-cosmos/test/routing/collection_routing_map_test.py @@ -25,6 +25,8 @@ import azure.cosmos.routing.routing_range as routing_range from azure.cosmos.routing.routing_map_provider import _PartitionKeyRangeCache +pytestmark = pytest.mark.cosmosEmulator + @pytest.mark.usefixtures("teardown") class CollectionRoutingMapTests(unittest.TestCase): diff --git a/sdk/cosmos/azure-cosmos/test/routing/routing_map_provider_tests.py b/sdk/cosmos/azure-cosmos/test/routing/routing_map_provider_tests.py index 66ff18840473..50c88892ba3a 100644 --- a/sdk/cosmos/azure-cosmos/test/routing/routing_map_provider_tests.py +++ b/sdk/cosmos/azure-cosmos/test/routing/routing_map_provider_tests.py @@ -25,6 +25,8 @@ from azure.cosmos.routing.routing_map_provider import _CollectionRoutingMap from azure.cosmos.routing import routing_range as routing_range +pytestmark = pytest.mark.cosmosEmulator + @pytest.mark.usefixtures("teardown") class RoutingMapProviderTests(unittest.TestCase): diff --git a/sdk/cosmos/azure-cosmos/test/routing_map_tests.py b/sdk/cosmos/azure-cosmos/test/routing_map_tests.py index 3c1b9843164c..4271db674461 100644 --- a/sdk/cosmos/azure-cosmos/test/routing_map_tests.py +++ b/sdk/cosmos/azure-cosmos/test/routing_map_tests.py @@ -27,6 +27,8 @@ from azure.cosmos.routing import routing_range as routing_range import test_config +pytestmark = pytest.mark.cosmosEmulator + #IMPORTANT NOTES: # Most test cases in this file create collections in your Azure Cosmos account. @@ -43,8 +45,6 @@ class RoutingMapEndToEndTests(unittest.TestCase): host = test_config._test_config.host masterKey = test_config._test_config.masterKey connectionPolicy = test_config._test_config.connectionPolicy - client = cosmos_client.CosmosClient(host, {'masterKey': masterKey}, connectionPolicy) - collection_link = test_config._test_config.create_multi_partition_collection_with_custom_pk_if_not_exist(client)['_self'] @classmethod def setUpClass(cls): @@ -54,6 +54,9 @@ def setUpClass(cls): "You must specify your Azure Cosmos account values for " "'masterKey' and 'host' at the top of this class to run the " "tests.") + + cls.client = cosmos_client.CosmosClient(cls.host, {'masterKey': cls.masterKey}, cls.connectionPolicy) + cls.collection_link = test_config._test_config.create_multi_partition_collection_with_custom_pk_if_not_exist(cls.client)['_self'] def test_read_partition_key_ranges(self): partition_key_ranges = list(self.client._ReadPartitionKeyRanges(self.collection_link)) diff --git a/sdk/cosmos/azure-cosmos/test/ru_per_min_tests.py b/sdk/cosmos/azure-cosmos/test/ru_per_min_tests.py index d7a89398dd75..3c076a2c0b4c 100644 --- a/sdk/cosmos/azure-cosmos/test/ru_per_min_tests.py +++ b/sdk/cosmos/azure-cosmos/test/ru_per_min_tests.py @@ -28,6 +28,8 @@ import azure.cosmos.base as base import test_config +pytestmark = pytest.mark.cosmosEmulator + # IMPORTANT NOTES: # Most test cases in this file create collections in your Azure Cosmos @@ -47,8 +49,6 @@ class RuPerMinTests(unittest.TestCase): host = test_config._test_config.host masterKey = test_config._test_config.masterKey connectionPolicy = test_config._test_config.connectionPolicy - client = cosmos_client.CosmosClient(host, {'masterKey': masterKey}, connectionPolicy) - created_db = test_config._test_config.create_database_if_not_exist(client) @classmethod def setUpClass(cls): @@ -61,6 +61,9 @@ def setUpClass(cls): "'masterKey' and 'host' at the top of this class to run the " "tests.") + cls.client = cosmos_client.CosmosClient(cls.host, {'masterKey': cls.masterKey}, cls.connectionPolicy) + cls.created_db = test_config._test_config.create_database_if_not_exist(cls.client) + def _query_offers(self, collection_self_link): offers = list(self.client.ReadOffers()) for o in offers: diff --git a/sdk/cosmos/azure-cosmos/test/session_container_tests.py b/sdk/cosmos/azure-cosmos/test/session_container_tests.py index 743bb2f372c5..e109d193f205 100644 --- a/sdk/cosmos/azure-cosmos/test/session_container_tests.py +++ b/sdk/cosmos/azure-cosmos/test/session_container_tests.py @@ -32,6 +32,8 @@ import azure.cosmos.session as session import test_config +pytestmark = pytest.mark.cosmosEmulator + @pytest.mark.usefixtures("teardown") class Test_session_container(unittest.TestCase): # this test doesn't need real credentials, or connection to server diff --git a/sdk/cosmos/azure-cosmos/test/session_tests.py b/sdk/cosmos/azure-cosmos/test/session_tests.py index 72ee0379b55c..13f8eefa8adf 100644 --- a/sdk/cosmos/azure-cosmos/test/session_tests.py +++ b/sdk/cosmos/azure-cosmos/test/session_tests.py @@ -12,6 +12,8 @@ import azure.cosmos.synchronized_request as synchronized_request import azure.cosmos.retry_utility as retry_utility +pytestmark = pytest.mark.cosmosEmulator + @pytest.mark.usefixtures("teardown") class SessionTests(unittest.TestCase): """Test to ensure escaping of non-ascii characters from partition key""" @@ -19,8 +21,20 @@ class SessionTests(unittest.TestCase): host = test_config._test_config.host masterKey = test_config._test_config.masterKey connectionPolicy = test_config._test_config.connectionPolicy - client = cosmos_client.CosmosClient(host, {'masterKey': masterKey}, connectionPolicy) - created_collection = test_config._test_config.create_multi_partition_collection_with_custom_pk_if_not_exist(client) + + @classmethod + def setUpClass(cls): + # creates the database, collection, and insert all the documents + # we will gain some speed up in running the tests by creating the + # database, collection and inserting all the docs only once + + if (cls.masterKey == '[YOUR_KEY_HERE]' or cls.host == '[YOUR_ENDPOINT_HERE]'): + raise Exception("You must specify your Azure Cosmos account values for " + "'masterKey' and 'host' at the top of this class to run the " + "tests.") + + cls.client = cosmos_client.CosmosClient(cls.host, {'masterKey': cls.masterKey}, cls.connectionPolicy) + cls.created_collection = test_config._test_config.create_multi_partition_collection_with_custom_pk_if_not_exist(cls.client) def _MockRequest(self, global_endpoint_manager, request, connection_policy, requests_session, path, request_options, request_body): if HttpHeaders.SessionToken in request_options['headers']: diff --git a/sdk/cosmos/azure-cosmos/test/session_token_unit_tests.py b/sdk/cosmos/azure-cosmos/test/session_token_unit_tests.py index 003fc0f5644b..0af36a475dda 100644 --- a/sdk/cosmos/azure-cosmos/test/session_token_unit_tests.py +++ b/sdk/cosmos/azure-cosmos/test/session_token_unit_tests.py @@ -5,6 +5,8 @@ from azure.cosmos.vector_session_token import VectorSessionToken from azure.cosmos.errors import CosmosError +pytestmark = pytest.mark.cosmosEmulator + @pytest.mark.usefixtures("teardown") class SessionTokenUnitTest(unittest.TestCase): """Test to ensure escaping of non-ascii characters from partition key""" diff --git a/sdk/cosmos/azure-cosmos/test/streaming_failover_test.py b/sdk/cosmos/azure-cosmos/test/streaming_failover_test.py index 65b1c45fff86..15c330cd417f 100644 --- a/sdk/cosmos/azure-cosmos/test/streaming_failover_test.py +++ b/sdk/cosmos/azure-cosmos/test/streaming_failover_test.py @@ -11,6 +11,8 @@ import azure.cosmos.global_endpoint_manager as global_endpoint_manager import azure.cosmos.http_constants as http_constants +pytestmark = pytest.mark.cosmosEmulator + @pytest.mark.usefixtures("teardown") class TestStreamingFailover(unittest.TestCase): diff --git a/sdk/cosmos/azure-cosmos/test/ttl_tests.py b/sdk/cosmos/azure-cosmos/test/ttl_tests.py index c0d07ebcc3b8..0890395e5197 100644 --- a/sdk/cosmos/azure-cosmos/test/ttl_tests.py +++ b/sdk/cosmos/azure-cosmos/test/ttl_tests.py @@ -29,6 +29,7 @@ from azure.cosmos.http_constants import StatusCodes import test_config +pytestmark = pytest.mark.cosmosEmulator #IMPORTANT NOTES: @@ -46,8 +47,6 @@ class Test_ttl_tests(unittest.TestCase): host = test_config._test_config.host masterKey = test_config._test_config.masterKey connectionPolicy = test_config._test_config.connectionPolicy - client = cosmos_client.CosmosClient(host, {'masterKey': masterKey}, connectionPolicy) - created_db = test_config._test_config.create_database_if_not_exist(client) def __AssertHTTPFailureWithStatus(self, status_code, func, *args, **kwargs): """Assert HTTP failure with status. @@ -70,6 +69,8 @@ def setUpClass(cls): "You must specify your Azure Cosmos account values for " "'masterKey' and 'host' at the top of this class to run the " "tests.") + cls.client = cosmos_client.CosmosClient(cls.host, {'masterKey': cls.masterKey}, cls.connectionPolicy) + cls.created_db = test_config._test_config.create_database_if_not_exist(cls.client) def test_collection_and_document_ttl_values(self): collection_definition = {'id' : 'test_collection_and_document_ttl_values1' + str(uuid.uuid4()), diff --git a/sdk/cosmos/azure-cosmos/test/utils_tests.py b/sdk/cosmos/azure-cosmos/test/utils_tests.py index 7332ae5c696c..0dc7d7b8315a 100644 --- a/sdk/cosmos/azure-cosmos/test/utils_tests.py +++ b/sdk/cosmos/azure-cosmos/test/utils_tests.py @@ -25,6 +25,8 @@ import platform import azure.cosmos.http_constants as http_constants +pytestmark = pytest.mark.cosmosEmulator + @pytest.mark.usefixtures("teardown") class UtilsTests(unittest.TestCase): """Utils Tests