def test_create_client_with_api_version(self): url = self.account_url(self.tables_storage_account_name, "table") client = TableServiceClient(url, credential=self.credential) assert client._client._config.version == "2019-02-02" table = client.get_table_client('tablename') assert table._client._config.version == "2019-02-02" client = TableServiceClient(url, credential=self.credential, api_version="2019-07-07") assert client._client._config.version == "2019-07-07" table = client.get_table_client('tablename') assert table._client._config.version == "2019-07-07" with pytest.raises(ValueError): TableServiceClient(url, credential=self.credential, api_version="foo")
def test_set_table_acl_with_signed_identifiers(self, tables_storage_account_name, tables_primary_storage_account_key): # Arrange account_url = self.account_url(tables_storage_account_name, "table") ts = TableServiceClient(credential=tables_primary_storage_account_key, endpoint=account_url) table = self._create_table(ts) client = ts.get_table_client(table_name=table.table_name) # Act start = datetime(2021, 6, 8, 2, 10, 9) - timedelta(minutes=5) expiry = datetime(2021, 6, 8, 2, 10, 9) + timedelta(hours=1) identifiers = {'testid': TableAccessPolicy(start=start, expiry=expiry, permission='r')} try: client.set_table_access_policy(signed_identifiers=identifiers) # Assert acl = client.get_table_access_policy() assert acl is not None assert len(acl) == 1 assert acl.get('testid') self._assert_policy_datetime(start, acl['testid'].start) self._assert_policy_datetime(expiry, acl['testid'].expiry) assert acl['testid'].permission == 'r' finally: ts.delete_table(table.table_name)
def test_create_service_with_socket_timeout(self): # Arrange for service_type in SERVICES.items(): # Act default_service = service_type[0]( self.account_url(self.tables_storage_account_name, "table"), credential=self.tables_primary_storage_account_key, table_name='foo') service = service_type[0]( self.account_url(self.tables_storage_account_name, "table"), credential=self.tables_primary_storage_account_key, table_name='foo', connection_timeout=22) # Assert self.validate_standard_account_endpoints( service, self.tables_storage_account_name, self.tables_primary_storage_account_key) assert service._client._client._pipeline._transport.connection_config.timeout == 22 assert default_service._client._client._pipeline._transport.connection_config.timeout == 300 # Assert Parent transport is shared with child client service = TableServiceClient( self.account_url(self.tables_storage_account_name, "table"), credential=self.tables_primary_storage_account_key, connection_timeout=22) assert service._client._client._pipeline._transport.connection_config.timeout == 22 table = service.get_table_client('tablename') assert table._client._client._pipeline._transport._transport.connection_config.timeout == 22
def test_set_table_acl_with_signed_identifiers(self, resource_group, location, storage_account, storage_account_key): # Arrange url = self.account_url(storage_account, "table") if 'cosmos' in url: pytest.skip("Cosmos endpoint does not support this") ts = TableServiceClient(url, storage_account_key) table = self._create_table(ts) client = ts.get_table_client(table_name=table.table_name) # Act identifiers = dict() identifiers['testid'] = AccessPolicy( start=datetime.utcnow() - timedelta(minutes=5), expiry=datetime.utcnow() + timedelta(hours=1), permission='r') try: client.set_table_access_policy(signed_identifiers=identifiers) # Assert acl = client.get_table_access_policy() self.assertIsNotNone(acl) self.assertEqual(len(acl), 1) self.assertTrue('testid' in acl) finally: # self._delete_table(table) ts.delete_table(table.table_name)
def test_set_table_acl_with_signed_identifiers(self, resource_group, location, cosmos_account, cosmos_account_key): # Arrange url = self.account_url(cosmos_account, "cosmos") if 'cosmos' in url: pytest.skip("Cosmos endpoint does not support this") ts = TableServiceClient(url, cosmos_account_key) table = self._create_table(ts) client = ts.get_table_client(table_name=table.table_name) # Act identifiers = dict() identifiers['testid'] = AccessPolicy(start=datetime.utcnow() - timedelta(minutes=5), expiry=datetime.utcnow() + timedelta(hours=1), permission='r') try: client.set_table_access_policy(signed_identifiers=identifiers) # Assert acl = client.get_table_access_policy() assert acl is not None assert len(acl) == 1 assert 'testid' in acl finally: ts.delete_table(table.table_name) if self.is_live: sleep(SLEEP_DELAY)
def test_set_table_acl_with_signed_identifiers( self, tables_cosmos_account_name, tables_primary_cosmos_account_key): # Arrange url = self.account_url(tables_cosmos_account_name, "cosmos") ts = TableServiceClient(url, tables_primary_cosmos_account_key) table = self._create_table(ts) client = ts.get_table_client(table_name=table.table_name) # Act identifiers = dict() identifiers['testid'] = AccessPolicy( start=datetime.utcnow() - timedelta(minutes=5), expiry=datetime.utcnow() + timedelta(hours=1), permission='r') try: client.set_table_access_policy(signed_identifiers=identifiers) # Assert acl = client.get_table_access_policy() assert acl is not None assert len(acl) == 1 assert 'testid' in acl finally: ts.delete_table(table.table_name) if self.is_live: sleep(SLEEP_DELAY)
def test_batch_sas_auth(self, tables_cosmos_account_name, tables_primary_cosmos_account_key): # this can be reverted to set_bodiless_matcher() after tests are re-recorded and don't contain these headers set_custom_default_matcher( compare_bodies=False, excluded_headers= "Authorization,Content-Length,x-ms-client-request-id,x-ms-request-id" ) # Arrange self._set_up(tables_cosmos_account_name, tables_primary_cosmos_account_key, url="cosmos") try: token = self.generate_sas( generate_table_sas, tables_primary_cosmos_account_key, self.table_name, permission=TableSasPermissions(add=True, read=True, update=True, delete=True), expiry=datetime.utcnow() + timedelta(hours=1), start=datetime.utcnow() - timedelta(minutes=1), ) token = AzureSasCredential(token) # Act service = TableServiceClient( self.account_url(tables_cosmos_account_name, "cosmos"), credential=token, ) table = service.get_table_client(self.table_name) entity = TableEntity() entity['PartitionKey'] = 'batch_inserts' entity['test'] = EntityProperty(True, EdmType.BOOLEAN) entity['test2'] = 'value' entity['test3'] = 3 entity['test4'] = EntityProperty(1234567890, EdmType.INT32) batch = [] transaction_count = 0 for i in range(10): entity['RowKey'] = str(i) batch.append(('create', entity.copy())) transaction_count += 1 transaction_result = table.submit_transaction(batch) assert transaction_result total_entities = 0 for e in table.list_entities(): total_entities += 1 assert total_entities == transaction_count finally: self._tear_down()
def test_batch_sas_auth(self, tables_storage_account_name, tables_primary_storage_account_key): set_bodiless_matcher() # Arrange self._set_up(tables_storage_account_name, tables_primary_storage_account_key) try: token = self.generate_sas( generate_table_sas, tables_primary_storage_account_key, self.table_name, permission=TableSasPermissions(add=True, read=True, update=True, delete=True), expiry=datetime.utcnow() + timedelta(hours=1), start=datetime.utcnow() - timedelta(minutes=1), ) token = AzureSasCredential(token) # Act service = TableServiceClient( self.account_url(tables_storage_account_name, "table"), credential=token, ) table = service.get_table_client(self.table_name) entity = TableEntity() entity['PartitionKey'] = 'batch_inserts' entity['test'] = EntityProperty(True, EdmType.BOOLEAN) entity['test2'] = 'value' entity['test3'] = 3 entity['test4'] = EntityProperty(1234567890, EdmType.INT32) batch = [] transaction_count = 0 for i in range(10): entity['RowKey'] = str(i) batch.append(('create', entity.copy())) transaction_count += 1 transaction_result = table.submit_transaction(batch) assert transaction_result total_entities = 0 for e in table.list_entities(): total_entities += 1 assert total_entities == transaction_count finally: self._tear_down()
def test_batch_sas_auth(self, tables_storage_account_name, tables_primary_storage_account_key): # Arrange self._set_up(tables_storage_account_name, tables_primary_storage_account_key) try: token = generate_table_sas( tables_storage_account_name, tables_primary_storage_account_key, self.table_name, permission=TableSasPermissions(add=True, read=True, update=True, delete=True), expiry=datetime.utcnow() + timedelta(hours=1), start=datetime.utcnow() - timedelta(minutes=1), ) token = AzureSasCredential(token) # Act service = TableServiceClient( self.account_url(tables_storage_account_name, "table"), credential=token, ) table = service.get_table_client(self.table_name) entity = TableEntity() entity.PartitionKey = 'batch_inserts' entity.test = EntityProperty(True) entity.test2 = 'value' entity.test3 = 3 entity.test4 = EntityProperty(1234567890) batch = table.create_batch() transaction_count = 0 for i in range(10): entity.RowKey = str(i) batch.create_entity(entity) transaction_count += 1 transaction_result = table.send_batch(batch) assert transaction_result is not None total_entities = 0 for e in table.list_entities(): total_entities += 1 assert total_entities == transaction_count finally: self._tear_down()
def test_create_table(self, tables_storage_account_name, tables_primary_storage_account_key): # # Arrange account_url = self.account_url(tables_storage_account_name, "table") ts = TableServiceClient(credential=tables_primary_storage_account_key, endpoint=account_url) table_name = self._get_table_reference() # Act table = ts.get_table_client(table_name) created = table.create_table() # Assert assert created.name == table_name ts.delete_table(table_name)
def test_create_table(self, tables_cosmos_account_name, tables_primary_cosmos_account_key): # Arrange ts = TableServiceClient(self.account_url(tables_cosmos_account_name, "cosmos"), credential=tables_primary_cosmos_account_key) table_name = self._get_table_reference() # Act table = ts.get_table_client(table_name) created = table.create_table() # Assert assert created.name == table_name ts.delete_table(table_name)
def test_account_sas(self, tables_storage_account_name, tables_primary_storage_account_key): account_url = self.account_url(tables_storage_account_name, "table") tsc = TableServiceClient(credential=tables_primary_storage_account_key, endpoint=account_url) table = self._create_table(tsc) try: entity = { 'PartitionKey': u'test', 'RowKey': u'test1', 'text': u'hello', } table.upsert_entity(mode=UpdateMode.MERGE, entity=entity) entity['RowKey'] = u'test2' table.upsert_entity(mode=UpdateMode.MERGE, entity=entity) token = AzureSasCredential( self.generate_sas( generate_account_sas, tables_primary_storage_account_key, resource_types=ResourceTypes(object=True), permission=AccountSasPermissions(read=True), expiry=datetime.utcnow() + timedelta(hours=1), start=datetime.utcnow() - timedelta(minutes=1), )) account_url = self.account_url(tables_storage_account_name, "table") service = TableServiceClient(credential=token, endpoint=account_url) # Act sas_table = service.get_table_client(table.table_name) entities = list(sas_table.list_entities()) # Assert assert len(entities) == 2 assert entities[0]['text'] == u'hello' assert entities[1]['text'] == u'hello' finally: tsc.delete_table(table.table_name)
def test_account_sas(self, resource_group, location, storage_account, storage_account_key): # SAS URL is calculated from storage key, so this test runs live only # Arrange url = self.account_url(storage_account, "table") if 'cosmos' in url: pytest.skip("Cosmos Tables does not yet support sas") tsc = TableServiceClient(url, storage_account_key) table = self._create_table(tsc) try: entity = { 'PartitionKey': 'test', 'RowKey': 'test1', 'text': 'hello', } table.upsert_entity(mode=UpdateMode.MERGE, entity=entity) entity['RowKey'] = 'test2' table.upsert_entity(mode=UpdateMode.MERGE, entity=entity) token = generate_account_sas( storage_account.name, storage_account_key, resource_types=ResourceTypes(object=True), permission=AccountSasPermissions(read=True), expiry=datetime.utcnow() + timedelta(hours=1), start=datetime.utcnow() - timedelta(minutes=1), ) # Act service = TableServiceClient( self.account_url(storage_account, "table"), credential=token, ) sas_table = service.get_table_client(table.table_name) entities = list(sas_table.list_entities()) # Assert self.assertEqual(len(entities), 2) self.assertEqual(entities[0].text, 'hello') self.assertEqual(entities[1].text, 'hello') finally: self._delete_table(table=table, ts=tsc)
def test_account_sas(self, tables_cosmos_account_name, tables_primary_cosmos_account_key): # SAS URL is calculated from storage key, so this test runs live only # Arrange url = self.account_url(tables_cosmos_account_name, "cosmos") tsc = TableServiceClient(url, tables_primary_cosmos_account_key) table = self._create_table(tsc) try: entity = { 'PartitionKey': 'test', 'RowKey': 'test1', 'text': 'hello', } table.upsert_entity(mode=UpdateMode.MERGE, entity=entity) entity['RowKey'] = 'test2' table.upsert_entity(mode=UpdateMode.MERGE, entity=entity) token = generate_account_sas( tables_cosmos_account_name, tables_primary_cosmos_account_key, resource_types=ResourceTypes(object=True), permission=AccountSasPermissions(read=True), expiry=datetime.utcnow() + timedelta(hours=1), start=datetime.utcnow() - timedelta(minutes=1), ) # Act service = TableServiceClient( self.account_url(tables_cosmos_account_name, "cosmos"), credential=token, ) sas_table = service.get_table_client(table.table_name) entities = list(sas_table.list_entities()) # Assert assert len(entities) == 2 assert entities[0].text == 'hello' assert entities[1].text == 'hello' finally: self._delete_table(table=table, ts=tsc)
class StorageRetryTest(AzureTestCase, TableTestCase): def _set_up(self, tables_storage_account_name, tables_primary_storage_account_key, url='table', default_table=True, **kwargs): self.table_name = self.get_resource_name('uttable') self.ts = TableServiceClient( self.account_url(tables_storage_account_name, url), credential=tables_primary_storage_account_key, **kwargs) self.table = self.ts.get_table_client(self.table_name) if self.is_live and default_table: try: self.ts.create_table(self.table_name) except ResourceExistsError: pass self.query_tables = [] def _tear_down(self, **kwargs): if self.is_live: try: self.ts.delete_table(self.table_name, **kwargs) except: pass try: for table_name in self.query_tables: try: self.ts.delete_table(table_name, **kwargs) except: pass except AttributeError: pass # --Test Cases -------------------------------------------- @TablesPreparer() def test_retry_on_server_error(self, tables_storage_account_name, tables_primary_storage_account_key): self._set_up(tables_storage_account_name, tables_primary_storage_account_key, default_table=False) try: callback = ResponseCallback(status=201, new_status=500).override_status new_table_name = self.get_resource_name('uttable') # The initial create will return 201, but we overwrite it with 500 and retry. # The retry will then get a 409 conflict. with pytest.raises(ResourceExistsError): self.ts.create_table(new_table_name, raw_response_hook=callback) finally: self.ts.delete_table(new_table_name) self._tear_down() @TablesPreparer() def test_retry_on_timeout(self, tables_storage_account_name, tables_primary_storage_account_key): retry = ExponentialRetry(initial_backoff=1, increment_base=2) self._set_up(tables_storage_account_name, tables_primary_storage_account_key, retry_policy=retry, default_table=False) new_table_name = self.get_resource_name('uttable') callback = ResponseCallback(status=201, new_status=408).override_status try: # The initial create will return 201, but we overwrite it with 408 and retry. # The retry will then get a 409 conflict. with pytest.raises(ResourceExistsError): self.ts.create_table(new_table_name, raw_response_hook=callback) finally: self.ts.delete_table(new_table_name) self._tear_down() @TablesPreparer() def test_retry_callback_and_retry_context( self, tables_storage_account_name, tables_primary_storage_account_key): retry = LinearRetry(backoff=1) self._set_up(tables_storage_account_name, tables_primary_storage_account_key, retry_policy=retry, default_table=False) new_table_name = self.get_resource_name('uttable') callback = ResponseCallback(status=201, new_status=408).override_status def assert_exception_is_present_on_retry_context(**kwargs): self.assertIsNotNone(kwargs.get('response')) self.assertEqual(kwargs['response'].status_code, 408) try: # The initial create will return 201, but we overwrite it with 408 and retry. # The retry will then get a 409 conflict. with pytest.raises(ResourceExistsError): self.ts.create_table( new_table_name, raw_response_hook=callback, retry_hook=assert_exception_is_present_on_retry_context) finally: self.ts.delete_table(new_table_name) self._tear_down() @pytest.mark.live_test_only @TablesPreparer() def test_retry_on_socket_timeout(self, tables_storage_account_name, tables_primary_storage_account_key): retry = LinearRetry(backoff=1) retry_transport = RetryRequestTransport(connection_timeout=11, read_timeout=0.000000000001) self._set_up(tables_storage_account_name, tables_primary_storage_account_key, retry_policy=retry, transport=retry_transport, default_table=False) new_table_name = self.get_resource_name('uttable') try: with pytest.raises(AzureError) as error: self.ts.create_table(new_table_name) # 3 retries + 1 original == 4 assert retry_transport.count == 4 # This call should succeed on the server side, but fail on the client side due to socket timeout self.assertTrue( 'read timeout' in str(error.value), 'Expected socket timeout but got different exception.') finally: # we must make the timeout normal again to let the delete operation succeed self.ts.delete_table(new_table_name, connection_timeout=(11, 11)) self._tear_down(connection_timeout=(11, 11))
class StorageTableBatchTest(AzureTestCase, TableTestCase): def _set_up(self, tables_storage_account_name, tables_primary_storage_account_key): self.ts = TableServiceClient( self.account_url(tables_storage_account_name, "table"), tables_primary_storage_account_key) self.table_name = self.get_resource_name('uttable') self.table = self.ts.get_table_client(self.table_name) if self.is_live: try: self.ts.create_table(self.table_name) except ResourceExistsError: pass self.test_tables = [] def _tear_down(self): if self.is_live: try: self.ts.delete_table(self.table_name) except: pass for table_name in self.test_tables: try: self.ts.delete_table(table_name) except: pass #--Helpers----------------------------------------------------------------- def _get_table_reference(self, prefix=TEST_TABLE_PREFIX): table_name = self.get_resource_name(prefix) self.test_tables.append(table_name) return self.ts.get_table_client(table_name) def _create_pk_rk(self, pk, rk): try: pk = pk if pk is not None else self.get_resource_name('pk').decode( 'utf-8') rk = rk if rk is not None else self.get_resource_name('rk').decode( 'utf-8') except AttributeError: pk = pk if pk is not None else self.get_resource_name('pk') rk = rk if rk is not None else self.get_resource_name('rk') return pk, rk def _create_random_entity_dict(self, pk=None, rk=None): """ Creates a dictionary-based entity with fixed values, using all of the supported data types. """ # partition = pk if pk is not None else self.get_resource_name('pk').decode('utf-8') # row = rk if rk is not None else self.get_resource_name('rk').decode('utf-8') partition, row = self._create_pk_rk(pk, rk) properties = { 'PartitionKey': partition, 'RowKey': row, 'age': 39, 'sex': u'male', 'married': True, 'deceased': False, 'optional': None, 'ratio': 3.1, 'evenratio': 3.0, 'large': 933311100, 'Birthday': datetime(1973, 10, 4, tzinfo=tzutc()), 'birthday': datetime(1970, 10, 4, tzinfo=tzutc()), 'binary': b'binary', 'other': EntityProperty(value=20, type=EdmType.INT32), 'clsid': uuid.UUID('c9da6455-213d-42c9-9a79-3e9149a57833') } return TableEntity(**properties) def _create_updated_entity_dict(self, partition, row): ''' Creates a dictionary-based entity with fixed values, with a different set of values than the default entity. It adds fields, changes field values, changes field types, and removes fields when compared to the default entity. ''' return { 'PartitionKey': partition, 'RowKey': row, 'age': u'abc', 'sex': u'female', 'sign': u'aquarius', 'birthday': datetime(1991, 10, 4, tzinfo=tzutc()) } def _assert_default_entity(self, entity): ''' Asserts that the entity passed in matches the default entity. ''' assert entity['age'] == 39 assert entity['sex'] == 'male' assert entity['married'] == True assert entity['deceased'] == False assert not "optional" in entity assert entity['ratio'] == 3.1 assert entity['evenratio'] == 3.0 assert entity['large'] == 933311100 assert entity['Birthday'] == datetime(1973, 10, 4, tzinfo=tzutc()) assert entity['birthday'] == datetime(1970, 10, 4, tzinfo=tzutc()) assert entity['binary'].value == b'binary' assert entity['other'] == 20 assert entity['clsid'] == uuid.UUID( 'c9da6455-213d-42c9-9a79-3e9149a57833') assert '_metadata' in entity def _assert_updated_entity(self, entity): ''' Asserts that the entity passed in matches the updated entity. ''' assert entity.age == 'abc' assert entity.sex == 'female' assert not hasattr(entity, "married") assert not hasattr(entity, "deceased") assert entity.sign == 'aquarius' assert not hasattr(entity, "optional") assert not hasattr(entity, "ratio") assert not hasattr(entity, "evenratio") assert not hasattr(entity, "large") assert not hasattr(entity, "Birthday") assert entity.birthday, datetime(1991, 10, 4, tzinfo=tzutc()) assert not hasattr(entity, "other") assert not hasattr(entity, "clsid") assert entity['_metadata']['etag'] is not None def _assert_valid_batch_transaction(self, transaction, length): assert length == len(transaction) @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @TablesPreparer() def test_batch_single_insert(self, tables_storage_account_name, tables_primary_storage_account_key): # Arrange self._set_up(tables_storage_account_name, tables_primary_storage_account_key) try: # Act entity = TableEntity() entity.PartitionKey = '001' entity.RowKey = 'batch_insert' entity.test = EntityProperty(True) entity.test2 = 'value' entity.test3 = 3 entity.test4 = EntityProperty(1234567890) entity.test5 = datetime.utcnow() batch = [('create', entity)] transaction_result = self.table.submit_transaction(batch) # Assert self._assert_valid_batch_transaction(transaction_result, 1) assert 'etag' in transaction_result[0] e = self.table.get_entity(row_key=entity.RowKey, partition_key=entity.PartitionKey) assert e.test == entity.test.value assert e.test2 == entity.test2 assert e.test3 == entity.test3 assert e.test4 == entity.test4.value finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @TablesPreparer() def test_batch_single_update(self, tables_storage_account_name, tables_primary_storage_account_key): # Arrange self._set_up(tables_storage_account_name, tables_primary_storage_account_key) try: # Act entity = TableEntity() entity.PartitionKey = '001' entity.RowKey = 'batch_insert' entity.test = EntityProperty(True) entity.test2 = 'value' entity.test3 = 3 entity.test4 = EntityProperty(1234567890) entity.test5 = datetime.utcnow() resp = self.table.create_entity(entity) assert resp is not None entity.test3 = 5 entity.test5 = datetime.utcnow() batch = [('update', entity, {'mode': UpdateMode.MERGE})] transaction_result = self.table.submit_transaction(batch) # Assert self._assert_valid_batch_transaction(transaction_result, 1) assert 'etag' in transaction_result[0] result = self.table.get_entity(row_key=entity.RowKey, partition_key=entity.PartitionKey) assert result.PartitionKey == u'001' assert result.RowKey == u'batch_insert' assert result.test3 == 5 finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @TablesPreparer() def test_batch_update(self, tables_storage_account_name, tables_primary_storage_account_key): # Arrange self._set_up(tables_storage_account_name, tables_primary_storage_account_key) try: # Act entity = TableEntity() entity.PartitionKey = u'001' entity.RowKey = u'batch_update' entity.test = EntityProperty(True) entity.test2 = u'value' entity.test3 = 3 entity.test4 = EntityProperty(1234567890) entity.test5 = datetime.utcnow() self.table.create_entity(entity) entity = self.table.get_entity(u'001', u'batch_update') assert 3 == entity.test3 entity.test2 = u'value1' batch = [('update', entity)] transaction_result = self.table.submit_transaction(batch) # Assert self._assert_valid_batch_transaction(transaction_result, 1) assert 'etag' in transaction_result[0] result = self.table.get_entity('001', 'batch_update') assert 'value1' == result.test2 assert entity.PartitionKey == u'001' assert entity.RowKey == u'batch_update' finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @TablesPreparer() def test_batch_merge(self, tables_storage_account_name, tables_primary_storage_account_key): # Arrange self._set_up(tables_storage_account_name, tables_primary_storage_account_key) try: # Act entity = TableEntity() entity.PartitionKey = u'001' entity.RowKey = u'batch_merge' entity.test = EntityProperty(True) entity.test2 = u'value' entity.test3 = 3 entity.test4 = EntityProperty(1234567890) entity.test5 = datetime.utcnow() self.table.create_entity(entity) resp_entity = self.table.get_entity(partition_key=u'001', row_key=u'batch_merge') assert 3 == entity.test3 entity = TableEntity() entity.PartitionKey = u'001' entity.RowKey = u'batch_merge' entity.test2 = u'value1' batch = [('update', entity, {'mode': UpdateMode.MERGE})] transaction_result = self.table.submit_transaction(batch) # Assert self._assert_valid_batch_transaction(transaction_result, 1) assert 'etag' in transaction_result[0] resp_entity = self.table.get_entity(partition_key=u'001', row_key=u'batch_merge') assert entity.test2 == resp_entity.test2 assert 1234567890 == resp_entity.test4 assert entity.PartitionKey == resp_entity.PartitionKey assert entity.RowKey == resp_entity.RowKey finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @TablesPreparer() def test_batch_update_if_match(self, tables_storage_account_name, tables_primary_storage_account_key): # Arrange self._set_up(tables_storage_account_name, tables_primary_storage_account_key) try: entity = self._create_random_entity_dict() resp = self.table.create_entity(entity=entity) etag = resp['etag'] # Act sent_entity = self._create_updated_entity_dict( entity['PartitionKey'], entity['RowKey']) batch = [('update', sent_entity, { 'etag': etag, 'match_condition': MatchConditions.IfNotModified, 'mode': UpdateMode.REPLACE })] transaction_result = self.table.submit_transaction(batch) # Assert self._assert_valid_batch_transaction(transaction_result, 1) assert 'etag' in transaction_result[0] entity = self.table.get_entity( partition_key=entity['PartitionKey'], row_key=entity['RowKey']) self._assert_updated_entity(entity) finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @TablesPreparer() def test_batch_update_if_doesnt_match(self, tables_storage_account_name, tables_primary_storage_account_key): # Arrange self._set_up(tables_storage_account_name, tables_primary_storage_account_key) try: entity = self._create_random_entity_dict() self.table.create_entity(entity) # Act sent_entity1 = self._create_updated_entity_dict( entity['PartitionKey'], entity['RowKey']) batch = [('update', sent_entity1, { 'etag': u'W/"datetime\'2012-06-15T22%3A51%3A44.9662825Z\'"', 'match_condition': MatchConditions.IfNotModified })] with pytest.raises(TableTransactionError): self.table.submit_transaction(batch) # Assert received_entity = self.table.get_entity(entity['PartitionKey'], entity['RowKey']) self._assert_default_entity(received_entity) finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @TablesPreparer() def test_batch_single_op_if_doesnt_match( self, tables_storage_account_name, tables_primary_storage_account_key): # Arrange self._set_up(tables_storage_account_name, tables_primary_storage_account_key) try: # Act entity = TableEntity() entity.PartitionKey = 'batch_inserts' entity.test = EntityProperty(True) entity.test2 = 'value' entity.test3 = 3 entity.test4 = EntityProperty(1234567890) batch = [] transaction_count = 0 for i in range(10): entity.RowKey = str(i) batch.append(('create', entity.copy())) transaction_count += 1 entity = self._create_random_entity_dict() self.table.create_entity(entity) # Act sent_entity1 = self._create_updated_entity_dict( entity['PartitionKey'], entity['RowKey']) batch = [('update', sent_entity1, { 'etag': u'W/"datetime\'2012-06-15T22%3A51%3A44.9662825Z\'"', 'match_condition': MatchConditions.IfNotModified })] with pytest.raises(TableTransactionError): self.table.submit_transaction(batch) # Assert received_entity = self.table.get_entity(entity['PartitionKey'], entity['RowKey']) self._assert_default_entity(received_entity) finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @TablesPreparer() def test_batch_insert_replace(self, tables_storage_account_name, tables_primary_storage_account_key): # Arrange self._set_up(tables_storage_account_name, tables_primary_storage_account_key) try: # Act entity = TableEntity() entity.PartitionKey = '001' entity.RowKey = 'batch_insert_replace' entity.test = True entity.test2 = 'value' entity.test3 = 3 entity.test4 = EntityProperty(1234567890) entity.test5 = datetime.utcnow() batch = [('upsert', entity, {'mode': UpdateMode.REPLACE})] transaction_result = self.table.submit_transaction(batch) # Assert self._assert_valid_batch_transaction(transaction_result, 1) assert 'etag' in transaction_result[0] entity = self.table.get_entity('001', 'batch_insert_replace') assert entity is not None assert 'value' == entity.test2 assert 1234567890 == entity.test4 finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @TablesPreparer() def test_batch_insert_merge(self, tables_storage_account_name, tables_primary_storage_account_key): # Arrange self._set_up(tables_storage_account_name, tables_primary_storage_account_key) try: # Act entity = TableEntity() entity.PartitionKey = '001' entity.RowKey = 'batch_insert_merge' entity.test = True entity.test2 = 'value' entity.test3 = 3 entity.test4 = EntityProperty(1234567890) entity.test5 = datetime.utcnow() batch = [('upsert', entity, {'mode': UpdateMode.MERGE})] transaction_result = self.table.submit_transaction(batch) # Assert self._assert_valid_batch_transaction(transaction_result, 1) assert 'etag' in transaction_result[0] entity = self.table.get_entity('001', 'batch_insert_merge') assert entity is not None assert 'value' == entity.test2 assert 1234567890 == entity.test4 finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @TablesPreparer() def test_batch_delete(self, tables_storage_account_name, tables_primary_storage_account_key): # Arrange self._set_up(tables_storage_account_name, tables_primary_storage_account_key) try: # Act entity = TableEntity() entity.PartitionKey = u'001' entity.RowKey = u'batch_delete' entity.test = EntityProperty(True) entity.test2 = u'value' entity.test3 = 3 entity.test4 = EntityProperty(1234567890) entity.test5 = datetime.utcnow() self.table.create_entity(entity) entity = self.table.get_entity(partition_key=u'001', row_key=u'batch_delete') assert 3 == entity.test3 batch = [('delete', entity)] transaction_result = self.table.submit_transaction(batch) # Assert self._assert_valid_batch_transaction(transaction_result, 1) assert 'etag' not in transaction_result[0] with pytest.raises(ResourceNotFoundError): entity = self.table.get_entity( partition_key=entity.PartitionKey, row_key=entity.RowKey) finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @TablesPreparer() def test_batch_inserts(self, tables_storage_account_name, tables_primary_storage_account_key): # Arrange self._set_up(tables_storage_account_name, tables_primary_storage_account_key) try: # Act entity = TableEntity() entity.PartitionKey = 'batch_inserts' entity.test = EntityProperty(True) entity.test2 = 'value' entity.test3 = 3 entity.test4 = EntityProperty(1234567890) transaction_count = 0 batch = [] for i in range(100): entity.RowKey = str(i) batch.append(('create', entity.copy())) transaction_count += 1 transaction_result = self.table.submit_transaction(batch) # Assert self._assert_valid_batch_transaction(transaction_result, transaction_count) assert 'etag' in transaction_result[0] entities = list( self.table.query_entities("PartitionKey eq 'batch_inserts'")) # Assert assert entities is not None assert transaction_count == len(entities) e = self.table.get_entity('batch_inserts', '1') finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @TablesPreparer() def test_batch_all_operations_together(self, tables_storage_account_name, tables_primary_storage_account_key): # Arrange self._set_up(tables_storage_account_name, tables_primary_storage_account_key) try: # Act entity = TableEntity() entity.PartitionKey = '003' entity.RowKey = 'batch_all_operations_together-1' entity.test = EntityProperty(True) entity.test2 = 'value' entity.test3 = 3 entity.test4 = EntityProperty(1234567890) entity.test5 = datetime.utcnow() self.table.create_entity(entity) entity.RowKey = 'batch_all_operations_together-2' self.table.create_entity(entity) entity.RowKey = 'batch_all_operations_together-3' self.table.create_entity(entity) entity.RowKey = 'batch_all_operations_together-4' self.table.create_entity(entity) transaction_count = 0 batch = [] entity.RowKey = 'batch_all_operations_together' batch.append((TransactionOperation.CREATE, entity.copy())) transaction_count += 1 entity.RowKey = 'batch_all_operations_together-1' batch.append((TransactionOperation.DELETE, entity.copy())) transaction_count += 1 entity.RowKey = 'batch_all_operations_together-2' entity.test3 = 10 batch.append((TransactionOperation.UPDATE, entity.copy())) transaction_count += 1 entity.RowKey = 'batch_all_operations_together-3' entity.test3 = 100 batch.append((TransactionOperation.UPDATE, entity.copy(), { 'mode': UpdateMode.REPLACE })) transaction_count += 1 entity.RowKey = 'batch_all_operations_together-4' entity.test3 = 10 batch.append((TransactionOperation.UPSERT, entity.copy())) transaction_count += 1 entity.RowKey = 'batch_all_operations_together-5' batch.append((TransactionOperation.UPSERT, entity.copy(), { 'mode': UpdateMode.REPLACE })) transaction_count += 1 transaction_result = self.table.submit_transaction(batch) # Assert self._assert_valid_batch_transaction(transaction_result, transaction_count) assert 'etag' in transaction_result[0] assert 'etag' not in transaction_result[1] assert 'etag' in transaction_result[2] assert 'etag' in transaction_result[3] assert 'etag' in transaction_result[4] assert 'etag' in transaction_result[5] # Assert entities = list(self.table.query_entities("PartitionKey eq '003'")) assert 5 == len(entities) finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @TablesPreparer() def test_batch_reuse(self, tables_storage_account_name, tables_primary_storage_account_key): # Arrange self._set_up(tables_storage_account_name, tables_primary_storage_account_key) try: table2 = self._get_table_reference('table2') table2.create_table() # Act entity = TableEntity() entity.PartitionKey = '003' entity.RowKey = 'batch_all_operations_together-1' entity.test = EntityProperty(True) entity.test2 = 'value' entity.test3 = 3 entity.test4 = EntityProperty(1234567890) entity.test5 = datetime.utcnow() batch = [] batch.append(('upsert', entity.copy())) entity.RowKey = 'batch_all_operations_together-2' batch.append(('upsert', entity.copy())) entity.RowKey = 'batch_all_operations_together-3' batch.append(('upsert', entity.copy())) entity.RowKey = 'batch_all_operations_together-4' batch.append(('upsert', entity.copy())) resp1 = self.table.submit_transaction(batch) resp2 = table2.submit_transaction(batch) entities = list(self.table.query_entities("PartitionKey eq '003'")) assert 4 == len(entities) table2 = list(table2.query_entities("PartitionKey eq '003'")) assert 4 == len(entities) finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @TablesPreparer() def test_batch_same_row_operations_fail( self, tables_storage_account_name, tables_primary_storage_account_key): # Arrange self._set_up(tables_storage_account_name, tables_primary_storage_account_key) try: entity = self._create_random_entity_dict('001', 'batch_negative_1') self.table.create_entity(entity) # Act batch = [] entity = self._create_updated_entity_dict('001', 'batch_negative_1') batch.append(('update', entity.copy())) entity = self._create_random_entity_dict('001', 'batch_negative_1') batch.append(('update', entity.copy(), { 'mode': UpdateMode.REPLACE })) # Assert with pytest.raises(TableTransactionError): self.table.submit_transaction(batch) finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @TablesPreparer() def test_batch_different_partition_operations_fail( self, tables_storage_account_name, tables_primary_storage_account_key): # Arrange self._set_up(tables_storage_account_name, tables_primary_storage_account_key) try: entity = self._create_random_entity_dict('001', 'batch_negative_1') self.table.create_entity(entity) # Act batch = [] entity = self._create_updated_entity_dict('001', 'batch_negative_1') batch.append(('update', entity.copy())) entity = self._create_random_entity_dict('002', 'batch_negative_1') batch.append(('update', entity.copy())) with pytest.raises(ValueError): self.table.submit_transaction(batch) finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @TablesPreparer() def test_batch_too_many_ops(self, tables_storage_account_name, tables_primary_storage_account_key): # Arrange self._set_up(tables_storage_account_name, tables_primary_storage_account_key) try: entity = self._create_random_entity_dict('001', 'batch_negative_1') self.table.create_entity(entity) # Act with pytest.raises(TableTransactionError): batch = [] for i in range(0, 101): entity = TableEntity() entity.PartitionKey = 'large' entity.RowKey = 'item{0}'.format(i) batch.append(('create', entity.copy())) self.table.submit_transaction(batch) # Assert finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @TablesPreparer() def test_batch_different_partition_keys( self, tables_storage_account_name, tables_primary_storage_account_key): # Arrange self._set_up(tables_storage_account_name, tables_primary_storage_account_key) try: entity = self._create_random_entity_dict('001', 'batch_negative_1') entity2 = self._create_random_entity_dict('002', 'batch_negative_1') batch = [('create', entity), ('create', entity2)] with pytest.raises(ValueError): self.table.submit_transaction(batch) # Assert finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @TablesPreparer() def test_new_non_existent_table(self, tables_storage_account_name, tables_primary_storage_account_key): # Arrange self._set_up(tables_storage_account_name, tables_primary_storage_account_key) try: entity = self._create_random_entity_dict('001', 'batch_negative_1') tc = self.ts.get_table_client("doesntexist") batch = [('create', entity)] with pytest.raises(TableTransactionError): resp = tc.submit_transaction(batch) # Assert finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @TablesPreparer() def test_new_invalid_key(self, tables_storage_account_name, tables_primary_storage_account_key): # Arrange invalid_key = tables_primary_storage_account_key[ 0:-6] + "==" # cut off a bit from the end to invalidate self.ts = TableServiceClient( self.account_url(tables_storage_account_name, "table"), invalid_key) self.table_name = self.get_resource_name('uttable') self.table = self.ts.get_table_client(self.table_name) entity = self._create_random_entity_dict('001', 'batch_negative_1') batch = [('create', entity)] with pytest.raises(ClientAuthenticationError): resp = self.table.submit_transaction(batch) @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @TablesPreparer() def test_new_delete_nonexistent_entity(self, tables_storage_account_name, tables_primary_storage_account_key): # Arrange self._set_up(tables_storage_account_name, tables_primary_storage_account_key) try: entity = self._create_random_entity_dict('001', 'batch_negative_1') batch = [('delete', entity)] with pytest.raises(TableTransactionError): resp = self.table.submit_transaction(batch) finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @pytest.mark.live_test_only @TablesPreparer() def test_batch_sas_auth(self, tables_storage_account_name, tables_primary_storage_account_key): # Arrange self._set_up(tables_storage_account_name, tables_primary_storage_account_key) try: token = generate_table_sas( tables_storage_account_name, tables_primary_storage_account_key, self.table_name, permission=TableSasPermissions(add=True, read=True, update=True, delete=True), expiry=datetime.utcnow() + timedelta(hours=1), start=datetime.utcnow() - timedelta(minutes=1), ) token = AzureSasCredential(token) # Act service = TableServiceClient( self.account_url(tables_storage_account_name, "table"), credential=token, ) table = service.get_table_client(self.table_name) entity = TableEntity() entity.PartitionKey = 'batch_inserts' entity.test = EntityProperty(True) entity.test2 = 'value' entity.test3 = 3 entity.test4 = EntityProperty(1234567890) batch = [] transaction_count = 0 for i in range(10): entity.RowKey = str(i) batch.append(('create', entity.copy())) transaction_count += 1 transaction_result = table.submit_transaction(batch) assert transaction_result total_entities = 0 for e in table.list_entities(): total_entities += 1 assert total_entities == transaction_count finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @pytest.mark.live_test_only # Request bodies are very large @TablesPreparer() def test_batch_request_too_large(self, tables_storage_account_name, tables_primary_storage_account_key): # Arrange self._set_up(tables_storage_account_name, tables_primary_storage_account_key) try: batch = [] entity = { 'PartitionKey': 'pk001', 'Foo': os.urandom(1024 * 64), 'Bar': os.urandom(1024 * 64), 'Baz': os.urandom(1024 * 64) } for i in range(50): entity['RowKey'] = str(i) batch.append(('create', entity.copy())) with pytest.raises(RequestTooLargeError): self.table.submit_transaction(batch) finally: self._tear_down()
class StorageTableClientTest(TableTestCase): def _set_up(self, tables_cosmos_account_name, tables_primary_cosmos_account_key): self.ts = TableServiceClient(self.account_url(tables_cosmos_account_name, "cosmos"), tables_primary_cosmos_account_key) self.table_name = self.get_resource_name('uttable') self.table = self.ts.get_table_client(self.table_name) if self.is_live: try: self.ts.create_table(self.table_name) except ResourceExistsError: pass self.test_tables = [] def _tear_down(self): if self.is_live: try: self.ts.delete_table(self.table_name) except: pass for table_name in self.test_tables: try: self.ts.delete_table(table_name) except: pass sleep(SLEEP_DELAY) #--Helpers----------------------------------------------------------------- def _get_table_reference(self, prefix=TEST_TABLE_PREFIX): table_name = self.get_resource_name(prefix) self.test_tables.append(table_name) return self.ts.get_table_client(table_name) def _create_random_entity_dict(self, pk=None, rk=None): ''' Creates a dictionary-based entity with fixed values, using all of the supported data types. ''' partition = pk if pk is not None else self.get_resource_name('pk') row = rk if rk is not None else self.get_resource_name('rk') properties = { 'PartitionKey': partition, 'RowKey': row, 'age': 39, 'sex': 'male', 'married': True, 'deceased': False, 'optional': None, 'ratio': 3.1, 'evenratio': 3.0, 'large': 933311100, 'Birthday': datetime(1973, 10, 4, tzinfo=tzutc()), 'birthday': datetime(1970, 10, 4, tzinfo=tzutc()), 'binary': b'binary', 'other': EntityProperty(20, EdmType.INT32), 'clsid': uuid.UUID('c9da6455-213d-42c9-9a79-3e9149a57833') } return TableEntity(**properties) def _create_updated_entity_dict(self, partition, row): ''' Creates a dictionary-based entity with fixed values, with a different set of values than the default entity. It adds fields, changes field values, changes field types, and removes fields when compared to the default entity. ''' return { 'PartitionKey': partition, 'RowKey': row, 'age': 'abc', 'sex': 'female', 'sign': 'aquarius', 'birthday': datetime(1991, 10, 4, tzinfo=tzutc()) } def _assert_default_entity(self, entity): ''' Asserts that the entity passed in matches the default entity. ''' assert entity['age'] == 39 assert entity['sex'] == 'male' assert entity['married'] == True assert entity['deceased'] == False assert not "optional" in entity assert entity['ratio'] == 3.1 assert entity['evenratio'] == 3.0 assert entity['large'] == 933311100 assert entity['Birthday'] == datetime(1973, 10, 4, tzinfo=tzutc()) assert entity['birthday'] == datetime(1970, 10, 4, tzinfo=tzutc()) assert entity['binary'].value == b'binary' assert entity['other'] == 20 assert entity['clsid'] == uuid.UUID('c9da6455-213d-42c9-9a79-3e9149a57833') assert '_metadata' in entity def _assert_updated_entity(self, entity): ''' Asserts that the entity passed in matches the updated entity. ''' assert entity.age == 'abc' assert entity.sex == 'female' assert not hasattr(entity, "married") assert not hasattr(entity, "deceased") assert entity.sign == 'aquarius' assert not hasattr(entity, "optional") assert not hasattr(entity, "ratio") assert not hasattr(entity, "evenratio") assert not hasattr(entity, "large") assert not hasattr(entity, "Birthday") assert entity.birthday, datetime(1991, 10, 4, tzinfo=tzutc()) assert not hasattr(entity, "other") assert not hasattr(entity, "clsid") assert entity['_metadata']['etag'] is not None #--Test cases for batch --------------------------------------------- def test_inferred_types(self): # Arrange # Act entity = TableEntity() entity.PartitionKey = '003' entity.RowKey = 'batch_all_operations_together-1' entity.test = EntityProperty(True) entity.test2 = EntityProperty(b'abcdef') entity.test3 = EntityProperty(u'c9da6455-213d-42c9-9a79-3e9149a57833') entity.test4 = EntityProperty(datetime(1973, 10, 4, tzinfo=tzutc())) entity.test5 = EntityProperty(u"stringystring") entity.test6 = EntityProperty(3.14159) entity.test7 = EntityProperty(100) entity.test8 = EntityProperty(10, EdmType.INT64) # Assert assert entity.test.type == EdmType.BOOLEAN assert entity.test2.type == EdmType.BINARY assert entity.test3.type == EdmType.GUID assert entity.test4.type == EdmType.DATETIME assert entity.test5.type == EdmType.STRING assert entity.test6.type == EdmType.DOUBLE assert entity.test7.type == EdmType.INT32 assert entity.test8.type == EdmType.INT64 def _assert_valid_batch_transaction(self, transaction, length): assert isinstance(transaction, BatchTransactionResult) assert length == len(transaction.entities) assert length == len(transaction.results) assert length == len(transaction.requests) @pytest.mark.skip("pending") @CosmosPreparer() def test_batch_insert(self, tables_cosmos_account_name, tables_primary_cosmos_account_key): # Arrange self._set_up(tables_cosmos_account_name, tables_primary_cosmos_account_key) try: # Act entity = Entity() entity.PartitionKey = '001' entity.RowKey = 'batch_insert' entity.test = EntityProperty(True) entity.test2 = 'value' entity.test3 = 3 entity.test4 = EntityProperty(1234567890) entity.test5 = datetime.utcnow() batch = self.table.create_batch() batch.create_item(entity) resp = self.table.commit_batch(batch) # Assert assert resp is not None result, headers = self.table.read_item('001', 'batch_insert', response_hook=lambda e, h: (e, h)) assert list(resp)[0].headers['Etag'] == headers['etag'] finally: self._tear_down() @pytest.mark.skip("merge operations fail in cosmos: https://github.com/Azure/azure-sdk-for-python/issues/13844") @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @CosmosPreparer() def test_batch_update(self, tables_cosmos_account_name, tables_primary_cosmos_account_key): # Arrange self._set_up(tables_cosmos_account_name, tables_primary_cosmos_account_key) try: # Act entity = TableEntity() entity.PartitionKey = u'001' entity.RowKey = u'batch_update' entity.test = EntityProperty(True) entity.test2 = u'value' entity.test3 = 3 entity.test4 = EntityProperty(1234567890) entity.test5 = datetime.utcnow() self.table.create_entity(entity) entity = self.table.get_entity(u'001', u'batch_update') assert 3 == entity.test3.value entity.test2 = u'value1' batch = self.table.create_batch() batch.update_entity(entity) transaction_result = self.table.send_batch(batch) # Assert self._assert_valid_batch_transaction(transaction_result, 1) assert transaction_result.get_entity(entity.RowKey) is not None result = self.table.get_entity('001', 'batch_update') assert 'value1' == result.test2.value assert entity.PartitionKey == u'001' assert entity.RowKey == u'batch_update' finally: self._tear_down() @pytest.mark.skip("merge operations fail in cosmos: https://github.com/Azure/azure-sdk-for-python/issues/13844") @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @CosmosPreparer() def test_batch_merge(self, tables_cosmos_account_name, tables_primary_cosmos_account_key): # Arrange self._set_up(tables_cosmos_account_name, tables_primary_cosmos_account_key) try: # Act entity = TableEntity() entity.PartitionKey = '001' entity.RowKey = 'batch_merge' entity.test = EntityProperty(True) entity.test2 = 'value' entity.test3 = 3 entity.test4 = EntityProperty(1234567890) entity.test5 = datetime.utcnow() self.table.create_entity(entity) entity = self.table.get_entity('001', 'batch_merge') assert 3 == entity.test3 entity = TableEntity() entity.PartitionKey = '001' entity.RowKey = 'batch_merge' entity.test2 = 'value1' batch = self.table.create_batch() batch.update_entity(entity, mode='MERGE') resp = self.table.send_batch(batch) # Assert assert resp is not None entity, headers = self.table.get_entity('001', 'batch_merge', response_hook=lambda e, h: (e, h)) assert 'value1' == entity.test2 assert 1234567890 == entity.test4 assert list(resp)[0].headers['Etag'] == headers['etag'] finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @CosmosPreparer() def test_batch_update_if_match(self, tables_cosmos_account_name, tables_primary_cosmos_account_key): # Arrange self._set_up(tables_cosmos_account_name, tables_primary_cosmos_account_key) try: entity = self._create_random_entity_dict() resp = self.table.create_entity(entity=entity) etag = resp['etag'] # Act sent_entity = self._create_updated_entity_dict(entity['PartitionKey'], entity['RowKey']) batch = self.table.create_batch() batch.update_entity( sent_entity, etag=etag, match_condition=MatchConditions.IfNotModified, mode=UpdateMode.REPLACE ) transaction_result = self.table.send_batch(batch) # Assert self._assert_valid_batch_transaction(transaction_result, 1) assert transaction_result.get_entity(sent_entity['RowKey']) is not None entity = self.table.get_entity(partition_key=entity['PartitionKey'], row_key=entity['RowKey']) self._assert_updated_entity(entity) finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @CosmosPreparer() def test_batch_update_if_doesnt_match(self, tables_cosmos_account_name, tables_primary_cosmos_account_key): # Arrange self._set_up(tables_cosmos_account_name, tables_primary_cosmos_account_key) try: entity = self._create_random_entity_dict() self.table.create_entity(entity) # Act sent_entity1 = self._create_updated_entity_dict(entity['PartitionKey'], entity['RowKey']) batch = self.table.create_batch() batch.update_entity( sent_entity1, etag=u'W/"datetime\'2012-06-15T22%3A51%3A44.9662825Z\'"', match_condition=MatchConditions.IfNotModified ) with pytest.raises(HttpResponseError): self.table.send_batch(batch) # Assert received_entity = self.table.get_entity(entity['PartitionKey'], entity['RowKey']) self._assert_default_entity(received_entity) finally: self._tear_down() @pytest.mark.skip("merge operations fail in cosmos: https://github.com/Azure/azure-sdk-for-python/issues/13844") @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @CosmosPreparer() def test_batch_insert_replace(self, tables_cosmos_account_name, tables_primary_cosmos_account_key): # Arrange self._set_up(tables_cosmos_account_name, tables_primary_cosmos_account_key) try: # Act entity = TableEntity() entity.PartitionKey = '001' entity.RowKey = 'batch_insert_replace' entity.test = True entity.test2 = 'value' entity.test3 = 3 entity.test4 = EntityProperty(1234567890) entity.test5 = datetime.utcnow() batch = self.table.create_batch() batch.upsert_item(entity) resp = self.table.send_batch(batch) # Assert assert resp is not None entity, headers = self.table.get_entity('001', 'batch_insert_replace', response_hook=lambda e, h: (e, h)) assert entity is not None assert 'value' == entity.test2 assert 1234567890 == entity.test4 assert list(resp)[0].headers['Etag'] == headers['etag'] finally: self._tear_down() @pytest.mark.skip("merge operations fail in cosmos: https://github.com/Azure/azure-sdk-for-python/issues/13844") @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @CosmosPreparer() def test_batch_insert_merge(self, tables_cosmos_account_name, tables_primary_cosmos_account_key): # Arrange self._set_up(tables_cosmos_account_name, tables_primary_cosmos_account_key) try: # Act entity = TableEntity() entity.PartitionKey = '001' entity.RowKey = 'batch_insert_merge' entity.test = True entity.test2 = 'value' entity.test3 = 3 entity.test4 = EntityProperty(1234567890) entity.test5 = datetime.utcnow() batch = self.table.create_batch() batch.upsert_item(entity, mode='MERGE') resp = self.table.send_batch(batch) # Assert assert resp is not None entity, headers = self.table.get_entity('001', 'batch_insert_merge', response_hook=lambda e, h: (e, h)) assert entity is not None assert 'value' == entity.test2 assert 1234567890 == entity.test4 assert list(resp)[0].headers['Etag'] == headers['etag'] finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @CosmosPreparer() def test_batch_delete(self, tables_cosmos_account_name, tables_primary_cosmos_account_key): # Arrange self._set_up(tables_cosmos_account_name, tables_primary_cosmos_account_key) try: # Act entity = TableEntity() entity.PartitionKey = u'001' entity.RowKey = u'batch_delete' entity.test = EntityProperty(True) entity.test2 = u'value' entity.test3 = 3 entity.test4 = EntityProperty(1234567890) entity.test5 = datetime.utcnow() self.table.create_entity(entity) entity = self.table.get_entity(partition_key=u'001', row_key=u'batch_delete') assert 3 == entity.test3 batch = self.table.create_batch() batch.delete_entity(partition_key=entity.PartitionKey, row_key=entity.RowKey) transaction_result = self.table.send_batch(batch) # Assert self._assert_valid_batch_transaction(transaction_result, 1) assert transaction_result.get_entity(entity.RowKey) is not None with pytest.raises(ResourceNotFoundError): entity = self.table.get_entity(partition_key=entity.PartitionKey, row_key=entity.RowKey) finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @CosmosPreparer() def test_batch_inserts(self, tables_cosmos_account_name, tables_primary_cosmos_account_key): # Arrange self._set_up(tables_cosmos_account_name, tables_primary_cosmos_account_key) try: # Act entity = TableEntity() entity.PartitionKey = 'batch_inserts' entity.test = EntityProperty(True) entity.test2 = 'value' entity.test3 = 3 entity.test4 = EntityProperty(1234567890) batch = self.table.create_batch() transaction_count = 0 for i in range(20): entity.RowKey = str(i) batch.create_entity(entity) transaction_count += 1 transaction_result = self.table.send_batch(batch) # Assert self._assert_valid_batch_transaction(transaction_result, transaction_count) assert transaction_result.get_entity(entity.RowKey) is not None entities = list(self.table.query_entities("PartitionKey eq 'batch_inserts'")) # Assert assert entities is not None assert transaction_count == len(entities) e = self.table.get_entity('batch_inserts', '1') finally: self._tear_down() @pytest.mark.skip("merge operations fail in cosmos: https://github.com/Azure/azure-sdk-for-python/issues/13844") @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @CosmosPreparer() def test_batch_all_operations_together(self, tables_cosmos_account_name, tables_primary_cosmos_account_key): # Arrange self._set_up(tables_cosmos_account_name, tables_primary_cosmos_account_key) try: # Act entity = TableEntity() entity.PartitionKey = '003' entity.RowKey = 'batch_all_operations_together-1' entity.test = EntityProperty(True) entity.test2 = 'value' entity.test3 = 3 entity.test4 = EntityProperty(1234567890) entity.test5 = datetime.utcnow() self.table.create_entity(entity) entity.RowKey = 'batch_all_operations_together-2' self.table.create_entity(entity) entity.RowKey = 'batch_all_operations_together-3' self.table.create_entity(entity) entity.RowKey = 'batch_all_operations_together-4' self.table.create_entity(entity) batch = self.table.create_batch() entity.RowKey = 'batch_all_operations_together' batch.create_entity(entity) entity.RowKey = 'batch_all_operations_together-1' batch.delete_item(entity.PartitionKey, entity.RowKey) entity.RowKey = 'batch_all_operations_together-2' entity.test3 = 10 batch.update_entity(entity) entity.RowKey = 'batch_all_operations_together-3' entity.test3 = 100 batch.update_entity(entity, mode='MERGE') entity.RowKey = 'batch_all_operations_together-4' entity.test3 = 10 batch.upsert_item(entity) entity.RowKey = 'batch_all_operations_together-5' batch.upsert_item(entity, mode='MERGE') resp = self.table.send_batch(batch) # Assert assert 6 == len(list(resp)) entities = list(self.table.query_items("PartitionKey eq '003'")) assert 5 == len(entities) finally: self._tear_down() @pytest.mark.skip("merge operations fail in cosmos: https://github.com/Azure/azure-sdk-for-python/issues/13844") @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @CosmosPreparer() def test_batch_all_operations_together_context_manager(self, tables_cosmos_account_name, tables_primary_cosmos_account_key): # Arrange self._set_up(tables_cosmos_account_name, tables_primary_cosmos_account_key) try: # Act entity = TableEntity() entity.PartitionKey = '003' entity.RowKey = 'batch_all_operations_together-1' entity.test = EntityProperty(True) entity.test2 = 'value' entity.test3 = 3 entity.test4 = EntityProperty(1234567890) entity.test5 = datetime.utcnow() self.table.create_entity(entity) entity.RowKey = 'batch_all_operations_together-2' self.table.create_entity(entity) entity.RowKey = 'batch_all_operations_together-3' self.table.create_entity(entity) entity.RowKey = 'batch_all_operations_together-4' self.table.create_entity(entity) with self.table.create_batch() as batch: entity.RowKey = 'batch_all_operations_together' batch.create_entity(entity) entity.RowKey = 'batch_all_operations_together-1' batch.delete_item(entity.PartitionKey, entity.RowKey) entity.RowKey = 'batch_all_operations_together-2' entity.test3 = 10 batch.update_entity(entity) entity.RowKey = 'batch_all_operations_together-3' entity.test3 = 100 batch.update_entity(entity, mode='MERGE') entity.RowKey = 'batch_all_operations_together-4' entity.test3 = 10 batch.upsert_item(entity) entity.RowKey = 'batch_all_operations_together-5' batch.upsert_item(entity, mode='MERGE') # Assert entities = list(self.table.query_items("PartitionKey eq '003'")) assert 5 == len(entities) finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @CosmosPreparer() def test_batch_same_row_operations_fail(self, tables_cosmos_account_name, tables_primary_cosmos_account_key): # Arrange self._set_up(tables_cosmos_account_name, tables_primary_cosmos_account_key) try: entity = self._create_random_entity_dict('001', 'batch_negative_1') self.table.create_entity(entity) # Act batch = self.table.create_batch() entity = self._create_updated_entity_dict( '001', 'batch_negative_1') batch.update_entity(entity) entity = self._create_random_entity_dict( '001', 'batch_negative_1') batch.update_entity(entity, mode=UpdateMode.MERGE) # Assert with pytest.raises(BatchErrorException): self.table.send_batch(batch) finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @CosmosPreparer() def test_batch_different_partition_operations_fail(self, tables_cosmos_account_name, tables_primary_cosmos_account_key): # Arrange self._set_up(tables_cosmos_account_name, tables_primary_cosmos_account_key) try: entity = self._create_random_entity_dict('001', 'batch_negative_1') self.table.create_entity(entity) # Act batch = self.table.create_batch() entity = self._create_updated_entity_dict( '001', 'batch_negative_1') batch.update_entity(entity) entity = self._create_random_entity_dict( '002', 'batch_negative_1') # Assert with pytest.raises(ValueError): batch.create_entity(entity) finally: self._tear_down() @pytest.mark.skip("On Cosmos, the limit is not specified.") @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @CosmosPreparer() def test_batch_too_many_ops(self, tables_cosmos_account_name, tables_primary_cosmos_account_key): # Arrange self._set_up(tables_cosmos_account_name, tables_primary_cosmos_account_key) try: entity = self._create_random_entity_dict('001', 'batch_negative_1') self.table.create_entity(entity) # Act with pytest.raises(BatchErrorException): batch = self.table.create_batch() for i in range(0, 101): entity = TableEntity() entity.PartitionKey = 'large' entity.RowKey = 'item{0}'.format(i) batch.create_entity(entity) # Assert finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @CosmosPreparer() def test_new_non_existent_table(self, tables_cosmos_account_name, tables_primary_cosmos_account_key): # Arrange self._set_up(tables_cosmos_account_name, tables_primary_cosmos_account_key) try: entity = self._create_random_entity_dict('001', 'batch_negative_1') tc = self.ts.get_table_client("doesntexist") batch = tc.create_batch() batch.create_entity(entity) with pytest.raises(ResourceNotFoundError): resp = tc.send_batch(batch) # Assert finally: self._tear_down() @pytest.mark.skip("Cannot fake cosmos credential") @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @CosmosPreparer() def test_new_invalid_key(self, tables_cosmos_account_name, tables_primary_cosmos_account_key): # Arrange invalid_key = tables_primary_cosmos_account_key[0:-6] + "==" # cut off a bit from the end to invalidate key_list = list(tables_primary_cosmos_account_key) key_list[-6:] = list("0000==") invalid_key = ''.join(key_list) self.ts = TableServiceClient(self.account_url(tables_cosmos_account_name, "table"), invalid_key) self.table_name = self.get_resource_name('uttable') self.table = self.ts.get_table_client(self.table_name) entity = self._create_random_entity_dict('001', 'batch_negative_1') batch = self.table.create_batch() batch.create_entity(entity) with pytest.raises(ClientAuthenticationError): resp = self.table.send_batch(batch) @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @CosmosPreparer() def test_new_delete_nonexistent_entity(self, tables_cosmos_account_name, tables_primary_cosmos_account_key): # Arrange self._set_up(tables_cosmos_account_name, tables_primary_cosmos_account_key) try: entity = self._create_random_entity_dict('001', 'batch_negative_1') batch = self.table.create_batch() batch.delete_entity(entity['PartitionKey'], entity['RowKey']) with pytest.raises(ResourceNotFoundError): resp = self.table.send_batch(batch) finally: self._tear_down()
class StorageTableBatchTest(AzureTestCase, TableTestCase): @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @tables_decorator def test_batch_single_insert(self, tables_storage_account_name, tables_primary_storage_account_key): # Arrange self._set_up(tables_storage_account_name, tables_primary_storage_account_key) try: # Act entity = TableEntity() entity['PartitionKey'] = '001' entity['RowKey'] = 'batch_insert' entity['test'] = EntityProperty(True, EdmType.BOOLEAN) entity['test2'] = 'value' entity['test3'] = 3 entity['test4'] = EntityProperty(1234567890, EdmType.INT32) entity['test5'] = datetime.utcnow() batch = [('create', entity)] transaction_result = self.table.submit_transaction(batch) # Assert self._assert_valid_batch_transaction(transaction_result, 1) assert 'etag' in transaction_result[0] e = self.table.get_entity(row_key=entity['RowKey'], partition_key=entity['PartitionKey']) assert e['test'] == entity['test'].value assert e['test2'] == entity['test2'] assert e['test3'] == entity['test3'] assert e['test4'] == entity['test4'].value finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @tables_decorator def test_batch_single_update(self, tables_storage_account_name, tables_primary_storage_account_key): # Arrange self._set_up(tables_storage_account_name, tables_primary_storage_account_key) try: # Act entity = TableEntity() entity['PartitionKey'] = '001' entity['RowKey'] = 'batch_insert' entity['test'] = EntityProperty(True, EdmType.BOOLEAN) entity['test2'] = 'value' entity['test3'] = 3 entity['test4'] = EntityProperty(1234567890, EdmType.INT32) entity['test5'] = datetime.utcnow() resp = self.table.create_entity(entity) assert resp is not None entity['test3'] = 5 entity['test5'] = datetime.utcnow() batch = [('update', entity, {'mode': UpdateMode.MERGE})] transaction_result = self.table.submit_transaction(batch) # Assert self._assert_valid_batch_transaction(transaction_result, 1) assert 'etag' in transaction_result[0] result = self.table.get_entity( row_key=entity['RowKey'], partition_key=entity['PartitionKey']) assert result['PartitionKey'] == u'001' assert result['RowKey'] == u'batch_insert' assert result['test3'] == 5 finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @tables_decorator def test_batch_update(self, tables_storage_account_name, tables_primary_storage_account_key): # Arrange self._set_up(tables_storage_account_name, tables_primary_storage_account_key) try: # Act entity = TableEntity() entity['PartitionKey'] = u'001' entity['RowKey'] = u'batch_update' entity['test'] = EntityProperty(True, EdmType.BOOLEAN) entity['test2'] = u'value' entity['test3'] = 3 entity['test4'] = EntityProperty(1234567890, EdmType.INT32) entity['test5'] = datetime.utcnow() entity['test6'] = (2**40, "Edm.Int64") self.table.create_entity(entity) entity = self.table.get_entity(u'001', u'batch_update') assert 3 == entity['test3'] entity['test2'] = u'value1' batch = [('update', entity)] transaction_result = self.table.submit_transaction(batch) # Assert self._assert_valid_batch_transaction(transaction_result, 1) assert 'etag' in transaction_result[0] result = self.table.get_entity('001', 'batch_update') assert 'value1' == result['test2'] assert entity['PartitionKey'] == u'001' assert entity['RowKey'] == u'batch_update' finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @tables_decorator def test_batch_merge(self, tables_storage_account_name, tables_primary_storage_account_key): # Arrange self._set_up(tables_storage_account_name, tables_primary_storage_account_key) try: # Act entity = TableEntity() entity['PartitionKey'] = u'001' entity['RowKey'] = u'batch_merge' entity['test'] = EntityProperty(True, EdmType.BOOLEAN) entity['test2'] = u'value' entity['test3'] = 3 entity['test4'] = EntityProperty(1234567890, EdmType.INT32) entity['test5'] = datetime.utcnow() self.table.create_entity(entity) resp_entity = self.table.get_entity(partition_key=u'001', row_key=u'batch_merge') assert 3 == entity['test3'] entity = TableEntity() entity['PartitionKey'] = u'001' entity['RowKey'] = u'batch_merge' entity['test2'] = u'value1' batch = [('update', entity, {'mode': UpdateMode.MERGE})] transaction_result = self.table.submit_transaction(batch) # Assert self._assert_valid_batch_transaction(transaction_result, 1) assert 'etag' in transaction_result[0] resp_entity = self.table.get_entity(partition_key=u'001', row_key=u'batch_merge') assert entity['test2'] == resp_entity['test2'] assert 1234567890 == resp_entity['test4'] assert entity['PartitionKey'] == resp_entity['PartitionKey'] assert entity['RowKey'] == resp_entity['RowKey'] finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @tables_decorator def test_batch_update_if_match(self, tables_storage_account_name, tables_primary_storage_account_key): # Arrange self._set_up(tables_storage_account_name, tables_primary_storage_account_key) try: entity = self._create_random_entity_dict() resp = self.table.create_entity(entity=entity) etag = resp['etag'] # Act sent_entity = self._create_updated_entity_dict( entity['PartitionKey'], entity['RowKey']) batch = [('update', sent_entity, { 'etag': etag, 'match_condition': MatchConditions.IfNotModified, 'mode': UpdateMode.REPLACE })] transaction_result = self.table.submit_transaction(batch) # Assert self._assert_valid_batch_transaction(transaction_result, 1) assert 'etag' in transaction_result[0] entity = self.table.get_entity( partition_key=entity['PartitionKey'], row_key=entity['RowKey']) self._assert_updated_entity(entity) finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @tables_decorator def test_batch_update_if_doesnt_match(self, tables_storage_account_name, tables_primary_storage_account_key): # Arrange self._set_up(tables_storage_account_name, tables_primary_storage_account_key) try: entity = self._create_random_entity_dict() self.table.create_entity(entity) # Act sent_entity1 = self._create_updated_entity_dict( entity['PartitionKey'], entity['RowKey']) batch = [('update', sent_entity1, { 'etag': u'W/"datetime\'2012-06-15T22%3A51%3A44.9662825Z\'"', 'match_condition': MatchConditions.IfNotModified })] with pytest.raises(TableTransactionError) as error: self.table.submit_transaction(batch) assert error.value.status_code == 412 assert error.value.error_code == TableErrorCode.update_condition_not_satisfied # Assert received_entity = self.table.get_entity(entity['PartitionKey'], entity['RowKey']) self._assert_default_entity(received_entity) finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @tables_decorator def test_batch_single_op_if_doesnt_match( self, tables_storage_account_name, tables_primary_storage_account_key): # Arrange self._set_up(tables_storage_account_name, tables_primary_storage_account_key) try: # Act entity = TableEntity() entity['PartitionKey'] = 'batch_inserts' entity['test'] = EntityProperty(True, EdmType.BOOLEAN) entity['test2'] = 'value' entity['test3'] = 3 entity['test4'] = EntityProperty(1234567890, EdmType.INT32) batch = [] transaction_count = 0 for i in range(10): entity['RowKey'] = str(i) batch.append(('create', entity.copy())) transaction_count += 1 entity = self._create_random_entity_dict() self.table.create_entity(entity) # Act sent_entity1 = self._create_updated_entity_dict( entity['PartitionKey'], entity['RowKey']) batch = [('update', sent_entity1, { 'etag': u'W/"datetime\'2012-06-15T22%3A51%3A44.9662825Z\'"', 'match_condition': MatchConditions.IfNotModified })] with pytest.raises(TableTransactionError) as error: self.table.submit_transaction(batch) assert error.value.status_code == 412 assert error.value.error_code == TableErrorCode.update_condition_not_satisfied # Assert received_entity = self.table.get_entity(entity['PartitionKey'], entity['RowKey']) self._assert_default_entity(received_entity) finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @tables_decorator def test_batch_insert_replace(self, tables_storage_account_name, tables_primary_storage_account_key): # Arrange self._set_up(tables_storage_account_name, tables_primary_storage_account_key) try: # Act entity = TableEntity() entity['PartitionKey'] = '001' entity['RowKey'] = 'batch_insert_replace' entity['test'] = True entity['test2'] = 'value' entity['test3'] = 3 entity['test4'] = EntityProperty(1234567890, EdmType.INT32) entity['test5'] = datetime.utcnow() batch = [('upsert', entity, {'mode': UpdateMode.REPLACE})] transaction_result = self.table.submit_transaction(batch) # Assert self._assert_valid_batch_transaction(transaction_result, 1) assert 'etag' in transaction_result[0] entity = self.table.get_entity('001', 'batch_insert_replace') assert entity is not None assert 'value' == entity['test2'] assert 1234567890 == entity['test4'] finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @tables_decorator def test_batch_insert_merge(self, tables_storage_account_name, tables_primary_storage_account_key): # Arrange self._set_up(tables_storage_account_name, tables_primary_storage_account_key) try: # Act entity = TableEntity() entity['PartitionKey'] = '001' entity['RowKey'] = 'batch_insert_merge' entity['test'] = True entity['test2'] = 'value' entity['test3'] = 3 entity['test4'] = EntityProperty(1234567890, EdmType.INT32) entity['test5'] = datetime.utcnow() batch = [('upsert', entity, {'mode': UpdateMode.MERGE})] transaction_result = self.table.submit_transaction(batch) # Assert self._assert_valid_batch_transaction(transaction_result, 1) assert 'etag' in transaction_result[0] entity = self.table.get_entity('001', 'batch_insert_merge') assert entity is not None assert 'value' == entity['test2'] assert 1234567890 == entity['test4'] finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @tables_decorator def test_batch_delete(self, tables_storage_account_name, tables_primary_storage_account_key): # Arrange self._set_up(tables_storage_account_name, tables_primary_storage_account_key) try: # Act entity = TableEntity() entity['PartitionKey'] = u'001' entity['RowKey'] = u'batch_delete' entity['test'] = EntityProperty(True, EdmType.BOOLEAN) entity['test2'] = u'value' entity['test3'] = 3 entity['test4'] = EntityProperty(1234567890, EdmType.INT32) entity['test5'] = datetime.utcnow() self.table.create_entity(entity) entity = self.table.get_entity(partition_key=u'001', row_key=u'batch_delete') assert 3 == entity['test3'] batch = [('delete', entity)] transaction_result = self.table.submit_transaction(batch) # Assert self._assert_valid_batch_transaction(transaction_result, 1) assert 'etag' not in transaction_result[0] with pytest.raises(ResourceNotFoundError): entity = self.table.get_entity( partition_key=entity['PartitionKey'], row_key=entity['RowKey']) finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @tables_decorator def test_batch_inserts(self, tables_storage_account_name, tables_primary_storage_account_key): # Arrange self._set_up(tables_storage_account_name, tables_primary_storage_account_key) try: # Act entity = TableEntity() entity['PartitionKey'] = 'batch_inserts' entity['test'] = EntityProperty(True, EdmType.BOOLEAN) entity['test2'] = 'value' entity['test3'] = 3 entity['test4'] = EntityProperty(1234567890, EdmType.INT32) transaction_count = 0 batch = [] for i in range(100): entity['RowKey'] = str(i) batch.append(('create', entity.copy())) transaction_count += 1 transaction_result = self.table.submit_transaction(batch) # Assert self._assert_valid_batch_transaction(transaction_result, transaction_count) assert 'etag' in transaction_result[0] entities = list( self.table.query_entities("PartitionKey eq 'batch_inserts'")) # Assert assert entities is not None assert transaction_count == len(entities) e = self.table.get_entity('batch_inserts', '1') finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @tables_decorator def test_batch_all_operations_together(self, tables_storage_account_name, tables_primary_storage_account_key): # Arrange self._set_up(tables_storage_account_name, tables_primary_storage_account_key) try: # Act entity = TableEntity() entity['PartitionKey'] = '003' entity['RowKey'] = 'batch_all_operations_together-1' entity['test'] = EntityProperty(True, EdmType.BOOLEAN) entity['test2'] = 'value' entity['test3'] = 3 entity['test4'] = EntityProperty(1234567890, EdmType.INT32) entity['test5'] = datetime.utcnow() self.table.create_entity(entity) entity['RowKey'] = 'batch_all_operations_together-2' self.table.create_entity(entity) entity['RowKey'] = 'batch_all_operations_together-3' self.table.create_entity(entity) entity['RowKey'] = 'batch_all_operations_together-4' self.table.create_entity(entity) transaction_count = 0 batch = [] entity['RowKey'] = 'batch_all_operations_together' batch.append((TransactionOperation.CREATE, entity.copy())) transaction_count += 1 entity['RowKey'] = 'batch_all_operations_together-1' batch.append((TransactionOperation.DELETE, entity.copy())) transaction_count += 1 entity['RowKey'] = 'batch_all_operations_together-2' entity['test3'] = 10 batch.append((TransactionOperation.UPDATE, entity.copy())) transaction_count += 1 entity['RowKey'] = 'batch_all_operations_together-3' entity['test3'] = 100 batch.append((TransactionOperation.UPDATE, entity.copy(), { 'mode': UpdateMode.REPLACE })) transaction_count += 1 entity['RowKey'] = 'batch_all_operations_together-4' entity['test3'] = 10 batch.append((TransactionOperation.UPSERT, entity.copy())) transaction_count += 1 entity['RowKey'] = 'batch_all_operations_together-5' batch.append((TransactionOperation.UPSERT, entity.copy(), { 'mode': UpdateMode.REPLACE })) transaction_count += 1 transaction_result = self.table.submit_transaction(batch) # Assert self._assert_valid_batch_transaction(transaction_result, transaction_count) assert 'etag' in transaction_result[0] assert 'etag' not in transaction_result[1] assert 'etag' in transaction_result[2] assert 'etag' in transaction_result[3] assert 'etag' in transaction_result[4] assert 'etag' in transaction_result[5] # Assert entities = list(self.table.query_entities("PartitionKey eq '003'")) assert 5 == len(entities) finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @tables_decorator def test_batch_reuse(self, tables_storage_account_name, tables_primary_storage_account_key): # Arrange self._set_up(tables_storage_account_name, tables_primary_storage_account_key) try: table2_name = self._get_table_reference('table2') table2 = self.ts.get_table_client(table2_name) table2.create_table() # Act entity = TableEntity() entity['PartitionKey'] = '003' entity['RowKey'] = 'batch_all_operations_together-1' entity['test'] = EntityProperty(True, EdmType.BOOLEAN) entity['test2'] = 'value' entity['test3'] = 3 entity['test4'] = EntityProperty(1234567890, EdmType.INT32) entity['test5'] = datetime.utcnow() batch = [] batch.append(('upsert', entity.copy())) entity['RowKey'] = 'batch_all_operations_together-2' batch.append(('upsert', entity.copy())) entity['RowKey'] = 'batch_all_operations_together-3' batch.append(('upsert', entity.copy())) entity['RowKey'] = 'batch_all_operations_together-4' batch.append(('upsert', entity.copy())) resp1 = self.table.submit_transaction(batch) resp2 = table2.submit_transaction(batch) entities = list(self.table.query_entities("PartitionKey eq '003'")) assert 4 == len(entities) table2 = list(table2.query_entities("PartitionKey eq '003'")) assert 4 == len(entities) finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @tables_decorator def test_batch_same_row_operations_fail( self, tables_storage_account_name, tables_primary_storage_account_key): # Arrange self._set_up(tables_storage_account_name, tables_primary_storage_account_key) try: entity = self._create_random_entity_dict('001', 'batch_negative_1') self.table.create_entity(entity) # Act batch = [] entity = self._create_updated_entity_dict('001', 'batch_negative_1') batch.append(('update', entity.copy())) entity = self._create_random_entity_dict('001', 'batch_negative_1') batch.append(('update', entity.copy(), { 'mode': UpdateMode.REPLACE })) # Assert with pytest.raises(TableTransactionError): self.table.submit_transaction(batch) finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @tables_decorator def test_batch_different_partition_operations_fail( self, tables_storage_account_name, tables_primary_storage_account_key): # Arrange self._set_up(tables_storage_account_name, tables_primary_storage_account_key) try: entity = self._create_random_entity_dict('001', 'batch_negative_1') self.table.create_entity(entity) # Act batch = [] entity = self._create_updated_entity_dict('001', 'batch_negative_1') batch.append(('update', entity.copy())) entity = self._create_random_entity_dict('002', 'batch_negative_1') batch.append(('update', entity.copy())) with pytest.raises(ValueError): self.table.submit_transaction(batch) finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @tables_decorator def test_batch_too_many_ops(self, tables_storage_account_name, tables_primary_storage_account_key): # Arrange self._set_up(tables_storage_account_name, tables_primary_storage_account_key) try: entity = self._create_random_entity_dict('001', 'batch_negative_1') self.table.create_entity(entity) # Act with pytest.raises(TableTransactionError): batch = [] for i in range(0, 101): entity = TableEntity() entity['PartitionKey'] = 'large' entity['RowKey'] = 'item{0}'.format(i) batch.append(('create', entity.copy())) self.table.submit_transaction(batch) # Assert finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @tables_decorator def test_batch_different_partition_keys( self, tables_storage_account_name, tables_primary_storage_account_key): # Arrange self._set_up(tables_storage_account_name, tables_primary_storage_account_key) try: entity = self._create_random_entity_dict('001', 'batch_negative_1') entity2 = self._create_random_entity_dict('002', 'batch_negative_1') batch = [('create', entity), ('create', entity2)] with pytest.raises(ValueError): self.table.submit_transaction(batch) # Assert finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @tables_decorator def test_new_non_existent_table(self, tables_storage_account_name, tables_primary_storage_account_key): # Arrange self._set_up(tables_storage_account_name, tables_primary_storage_account_key) try: entity = self._create_random_entity_dict('001', 'batch_negative_1') tc = self.ts.get_table_client("doesntexist") batch = [('create', entity)] with pytest.raises(TableTransactionError): resp = tc.submit_transaction(batch) # Assert finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @tables_decorator def test_new_invalid_key(self, tables_storage_account_name, tables_primary_storage_account_key): invalid_key = tables_primary_storage_account_key.named_key.key[ 0:-6] + "==" # cut off a bit from the end to invalidate tables_primary_storage_account_key = AzureNamedKeyCredential( tables_storage_account_name, invalid_key) credential = AzureNamedKeyCredential( name=tables_storage_account_name, key=tables_primary_storage_account_key.named_key.key) self.ts = TableServiceClient(self.account_url( tables_storage_account_name, "table"), credential=credential) self.table_name = self.get_resource_name('uttable') self.table = self.ts.get_table_client(self.table_name) entity = self._create_random_entity_dict('001', 'batch_negative_1') batch = [('create', entity)] with pytest.raises(ClientAuthenticationError): resp = self.table.submit_transaction(batch) @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @tables_decorator def test_new_delete_nonexistent_entity(self, tables_storage_account_name, tables_primary_storage_account_key): # Arrange self._set_up(tables_storage_account_name, tables_primary_storage_account_key) try: entity = self._create_random_entity_dict('001', 'batch_negative_1') batch = [('delete', entity)] with pytest.raises(TableTransactionError): resp = self.table.submit_transaction(batch) finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @tables_decorator def test_delete_batch_with_bad_kwarg(self, tables_storage_account_name, tables_primary_storage_account_key): # Arrange self._set_up(tables_storage_account_name, tables_primary_storage_account_key) try: entity = self._create_random_entity_dict('001', 'batch_negative_1') self.table.create_entity(entity) received = self.table.get_entity(entity["PartitionKey"], entity["RowKey"]) good_etag = received.metadata["etag"] received.metadata[ "etag"] = u'W/"datetime\'2012-06-15T22%3A51%3A44.9662825Z\'"' batch = [('delete', received, { "match_condition": MatchConditions.IfNotModified })] with pytest.raises(TableTransactionError) as error: self.table.submit_transaction(batch) assert error.value.status_code == 412 assert error.value.error_code == TableErrorCode.update_condition_not_satisfied received.metadata["etag"] = good_etag batch = [('delete', received, { "match_condition": MatchConditions.IfNotModified })] resp = self.table.submit_transaction(batch) assert resp is not None finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @pytest.mark.live_test_only @tables_decorator def test_batch_sas_auth(self, tables_storage_account_name, tables_primary_storage_account_key): # Arrange self._set_up(tables_storage_account_name, tables_primary_storage_account_key) try: token = self.generate_sas( generate_table_sas, tables_primary_storage_account_key, self.table_name, permission=TableSasPermissions(add=True, read=True, update=True, delete=True), expiry=datetime.utcnow() + timedelta(hours=1), start=datetime.utcnow() - timedelta(minutes=1), ) token = AzureSasCredential(token) # Act service = TableServiceClient( self.account_url(tables_storage_account_name, "table"), credential=token, ) table = service.get_table_client(self.table_name) entity = TableEntity() entity['PartitionKey'] = 'batch_inserts' entity['test'] = EntityProperty(True, EdmType.BOOLEAN) entity['test2'] = 'value' entity['test3'] = 3 entity['test4'] = EntityProperty(1234567890, EdmType.INT32) batch = [] transaction_count = 0 for i in range(10): entity['RowKey'] = str(i) batch.append(('create', entity.copy())) transaction_count += 1 transaction_result = table.submit_transaction(batch) assert transaction_result total_entities = 0 for e in table.list_entities(): total_entities += 1 assert total_entities == transaction_count finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @pytest.mark.live_test_only # Request bodies are very large @tables_decorator def test_batch_request_too_large(self, tables_storage_account_name, tables_primary_storage_account_key): # Arrange self._set_up(tables_storage_account_name, tables_primary_storage_account_key) try: batch = [] entity = { 'PartitionKey': 'pk001', 'Foo': os.urandom(1024 * 64), 'Bar': os.urandom(1024 * 64), 'Baz': os.urandom(1024 * 64) } for i in range(50): entity['RowKey'] = str(i) batch.append(('create', entity.copy())) with pytest.raises(RequestTooLargeError): self.table.submit_transaction(batch) finally: self._tear_down()
class StorageTableClientTest(AzureTestCase, TableTestCase): def _set_up(self, tables_cosmos_account_name, tables_primary_cosmos_account_key): self.ts = TableServiceClient( self.account_url(tables_cosmos_account_name, "cosmos"), tables_primary_cosmos_account_key) self.table_name = self.get_resource_name('uttable') self.table = self.ts.get_table_client(self.table_name) if self.is_live: try: self.ts.create_table(self.table_name) except ResourceExistsError: pass self.test_tables = [] def _tear_down(self): if self.is_live: try: self.ts.delete_table(self.table_name) except: pass for table_name in self.test_tables: try: self.ts.delete_table(table_name) except: pass sleep(SLEEP_DELAY) #--Helpers----------------------------------------------------------------- def _get_table_reference(self, prefix=TEST_TABLE_PREFIX): table_name = self.get_resource_name(prefix) self.test_tables.append(table_name) return self.ts.get_table_client(table_name) def _create_pk_rk(self, pk, rk): try: pk = pk if pk is not None else self.get_resource_name('pk').decode( 'utf-8') rk = rk if rk is not None else self.get_resource_name('rk').decode( 'utf-8') except AttributeError: pk = pk if pk is not None else self.get_resource_name('pk') rk = rk if rk is not None else self.get_resource_name('rk') return pk, rk def _create_random_entity_dict(self, pk=None, rk=None): ''' Creates a dictionary-based entity with fixed values, using all of the supported data types. ''' partition, row = self._create_pk_rk(pk, rk) properties = { 'PartitionKey': partition, 'RowKey': row, 'age': 39, 'sex': u'male', 'married': True, 'deceased': False, 'optional': None, 'ratio': 3.1, 'evenratio': 3.0, 'large': 933311100, 'Birthday': datetime(1973, 10, 4, tzinfo=tzutc()), 'birthday': datetime(1970, 10, 4, tzinfo=tzutc()), 'binary': b'binary', 'other': EntityProperty(value=20, type=EdmType.INT32), 'clsid': uuid.UUID('c9da6455-213d-42c9-9a79-3e9149a57833') } return TableEntity(**properties) def _create_updated_entity_dict(self, partition, row): ''' Creates a dictionary-based entity with fixed values, with a different set of values than the default entity. It adds fields, changes field values, changes field types, and removes fields when compared to the default entity. ''' return { 'PartitionKey': partition, 'RowKey': row, 'age': u'abc', 'sex': u'female', 'sign': u'aquarius', 'birthday': datetime(1991, 10, 4, tzinfo=tzutc()) } def _assert_default_entity(self, entity): ''' Asserts that the entity passed in matches the default entity. ''' assert entity['age'] == 39 assert entity['sex'] == 'male' assert entity['married'] == True assert entity['deceased'] == False assert not "optional" in entity assert entity['ratio'] == 3.1 assert entity['evenratio'] == 3.0 assert entity['large'] == 933311100 assert entity['Birthday'] == datetime(1973, 10, 4, tzinfo=tzutc()) assert entity['birthday'] == datetime(1970, 10, 4, tzinfo=tzutc()) assert entity['binary'].value == b'binary' assert entity['other'] == 20 assert entity['clsid'] == uuid.UUID( 'c9da6455-213d-42c9-9a79-3e9149a57833') assert '_metadata' in entity def _assert_updated_entity(self, entity): ''' Asserts that the entity passed in matches the updated entity. ''' assert entity.age == 'abc' assert entity.sex == 'female' assert not hasattr(entity, "married") assert not hasattr(entity, "deceased") assert entity.sign == 'aquarius' assert not hasattr(entity, "optional") assert not hasattr(entity, "ratio") assert not hasattr(entity, "evenratio") assert not hasattr(entity, "large") assert not hasattr(entity, "Birthday") assert entity.birthday, datetime(1991, 10, 4, tzinfo=tzutc()) assert not hasattr(entity, "other") assert not hasattr(entity, "clsid") assert entity['_metadata']['etag'] is not None #--Test cases for batch --------------------------------------------- def _assert_valid_batch_transaction(self, transaction, length): assert isinstance(transaction, BatchTransactionResult) assert length == len(transaction.entities) assert length == len(transaction.results) assert length == len(transaction.requests) @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @CosmosPreparer() def test_batch_insert(self, tables_cosmos_account_name, tables_primary_cosmos_account_key): # Arrange self._set_up(tables_cosmos_account_name, tables_primary_cosmos_account_key) try: # Act entity = TableEntity() entity.PartitionKey = '001' entity.RowKey = 'batch_insert_replace' entity.test = True entity.test2 = 'value' entity.test3 = 3 entity.test4 = EntityProperty(1234567890) entity.test5 = datetime.utcnow() batch = self.table.create_batch() batch.upsert_entity(entity) transaction_result = self.table.send_batch(batch) # Assert self._assert_valid_batch_transaction(transaction_result, 1) assert transaction_result.get_entity(entity.RowKey) is not None entity = self.table.get_entity('001', 'batch_insert_replace') assert entity is not None assert 'value' == entity.test2 assert 1234567890 == entity.test4 finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @CosmosPreparer() def test_batch_update(self, tables_cosmos_account_name, tables_primary_cosmos_account_key): # Arrange self._set_up(tables_cosmos_account_name, tables_primary_cosmos_account_key) try: # Act entity = TableEntity() entity.PartitionKey = u'001' entity.RowKey = u'batch_update' entity.test = EntityProperty(True) entity.test2 = u'value' entity.test3 = 3 entity.test4 = EntityProperty(1234567890) entity.test5 = datetime.utcnow() self.table.create_entity(entity) entity = self.table.get_entity(u'001', u'batch_update') assert 3 == entity.test3 entity.test2 = u'value1' batch = self.table.create_batch() batch.update_entity(entity) transaction_result = self.table.send_batch(batch) # Assert self._assert_valid_batch_transaction(transaction_result, 1) assert transaction_result.get_entity(entity.RowKey) is not None result = self.table.get_entity('001', 'batch_update') assert 'value1' == result.test2 assert entity.PartitionKey == u'001' assert entity.RowKey == u'batch_update' finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @CosmosPreparer() def test_batch_merge(self, tables_cosmos_account_name, tables_primary_cosmos_account_key): # Arrange self._set_up(tables_cosmos_account_name, tables_primary_cosmos_account_key) try: # Act entity = TableEntity() entity.PartitionKey = u'001' entity.RowKey = u'batch_merge' entity.test = EntityProperty(True) entity.test2 = u'value' entity.test3 = 3 entity.test4 = EntityProperty(1234567890) entity.test5 = datetime.utcnow() self.table.create_entity(entity) resp_entity = self.table.get_entity(partition_key=u'001', row_key=u'batch_merge') assert 3 == entity.test3 entity = TableEntity() entity.PartitionKey = u'001' entity.RowKey = u'batch_merge' entity.test2 = u'value1' batch = self.table.create_batch() batch.update_entity(entity, mode=UpdateMode.MERGE) transaction_result = self.table.send_batch(batch) # Assert self._assert_valid_batch_transaction(transaction_result, 1) assert transaction_result.get_entity(entity.RowKey) is not None resp_entity = self.table.get_entity(partition_key=u'001', row_key=u'batch_merge') assert entity.test2 == resp_entity.test2 assert 1234567890 == resp_entity.test4 assert entity.PartitionKey == resp_entity.PartitionKey assert entity.RowKey == resp_entity.RowKey finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @CosmosPreparer() def test_batch_update_if_match(self, tables_cosmos_account_name, tables_primary_cosmos_account_key): # Arrange self._set_up(tables_cosmos_account_name, tables_primary_cosmos_account_key) try: entity = self._create_random_entity_dict() resp = self.table.create_entity(entity=entity) etag = resp['etag'] # Act sent_entity = self._create_updated_entity_dict( entity['PartitionKey'], entity['RowKey']) batch = self.table.create_batch() batch.update_entity(sent_entity, etag=etag, match_condition=MatchConditions.IfNotModified, mode=UpdateMode.REPLACE) transaction_result = self.table.send_batch(batch) # Assert self._assert_valid_batch_transaction(transaction_result, 1) assert transaction_result.get_entity( sent_entity['RowKey']) is not None entity = self.table.get_entity( partition_key=entity['PartitionKey'], row_key=entity['RowKey']) self._assert_updated_entity(entity) finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @CosmosPreparer() def test_batch_update_if_doesnt_match(self, tables_cosmos_account_name, tables_primary_cosmos_account_key): # Arrange self._set_up(tables_cosmos_account_name, tables_primary_cosmos_account_key) try: entity = self._create_random_entity_dict() self.table.create_entity(entity) # Act sent_entity1 = self._create_updated_entity_dict( entity['PartitionKey'], entity['RowKey']) batch = self.table.create_batch() batch.update_entity( sent_entity1, etag=u'W/"datetime\'2012-06-15T22%3A51%3A44.9662825Z\'"', match_condition=MatchConditions.IfNotModified) with pytest.raises(BatchErrorException): self.table.send_batch(batch) # Assert received_entity = self.table.get_entity(entity['PartitionKey'], entity['RowKey']) self._assert_default_entity(received_entity) finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @CosmosPreparer() def test_batch_insert_replace(self, tables_cosmos_account_name, tables_primary_cosmos_account_key): # Arrange self._set_up(tables_cosmos_account_name, tables_primary_cosmos_account_key) try: # Act entity = TableEntity() entity.PartitionKey = '001' entity.RowKey = 'batch_insert_replace' entity.test = True entity.test2 = 'value' entity.test3 = 3 entity.test4 = EntityProperty(1234567890) entity.test5 = datetime.utcnow() batch = self.table.create_batch() batch.upsert_entity(entity) transaction_result = self.table.send_batch(batch) # Assert self._assert_valid_batch_transaction(transaction_result, 1) assert transaction_result.get_entity(entity.RowKey) is not None entity = self.table.get_entity('001', 'batch_insert_replace') assert entity is not None assert 'value' == entity.test2 assert 1234567890 == entity.test4 finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @CosmosPreparer() def test_batch_insert_merge(self, tables_cosmos_account_name, tables_primary_cosmos_account_key): # Arrange self._set_up(tables_cosmos_account_name, tables_primary_cosmos_account_key) try: # Act entity = TableEntity() entity.PartitionKey = '001' entity.RowKey = 'batch_insert_merge' entity.test = True entity.test2 = 'value' entity.test3 = 3 entity.test4 = EntityProperty(1234567890) entity.test5 = datetime.utcnow() batch = self.table.create_batch() batch.upsert_entity(entity, mode=UpdateMode.MERGE) transaction_result = self.table.send_batch(batch) # Assert self._assert_valid_batch_transaction(transaction_result, 1) assert transaction_result.get_entity(entity.RowKey) is not None entity = self.table.get_entity('001', 'batch_insert_merge') assert entity is not None assert 'value' == entity.test2 assert 1234567890 == entity.test4 finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @CosmosPreparer() def test_batch_delete(self, tables_cosmos_account_name, tables_primary_cosmos_account_key): # Arrange self._set_up(tables_cosmos_account_name, tables_primary_cosmos_account_key) try: # Act entity = TableEntity() entity.PartitionKey = u'001' entity.RowKey = u'batch_delete' entity.test = EntityProperty(True) entity.test2 = u'value' entity.test3 = 3 entity.test4 = EntityProperty(1234567890) entity.test5 = datetime.utcnow() self.table.create_entity(entity) entity = self.table.get_entity(partition_key=u'001', row_key=u'batch_delete') assert 3 == entity.test3 batch = self.table.create_batch() batch.delete_entity(partition_key=entity.PartitionKey, row_key=entity.RowKey) transaction_result = self.table.send_batch(batch) # Assert self._assert_valid_batch_transaction(transaction_result, 1) assert transaction_result.get_entity(entity.RowKey) is not None with pytest.raises(ResourceNotFoundError): entity = self.table.get_entity( partition_key=entity.PartitionKey, row_key=entity.RowKey) finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @CosmosPreparer() def test_batch_inserts(self, tables_cosmos_account_name, tables_primary_cosmos_account_key): # Arrange self._set_up(tables_cosmos_account_name, tables_primary_cosmos_account_key) try: # Act entity = TableEntity() entity.PartitionKey = 'batch_inserts' entity.test = EntityProperty(True) entity.test2 = 'value' entity.test3 = 3 entity.test4 = EntityProperty(1234567890) batch = self.table.create_batch() transaction_count = 0 for i in range(20): entity.RowKey = str(i) batch.create_entity(entity) transaction_count += 1 transaction_result = self.table.send_batch(batch) # Assert self._assert_valid_batch_transaction(transaction_result, transaction_count) assert transaction_result.get_entity(entity.RowKey) is not None entities = list( self.table.query_entities("PartitionKey eq 'batch_inserts'")) # Assert assert entities is not None assert transaction_count == len(entities) e = self.table.get_entity('batch_inserts', '1') finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @CosmosPreparer() def test_batch_all_operations_together(self, tables_cosmos_account_name, tables_primary_cosmos_account_key): # Arrange self._set_up(tables_cosmos_account_name, tables_primary_cosmos_account_key) try: # Act entity = TableEntity() entity.PartitionKey = '003' entity.RowKey = 'batch_all_operations_together-1' entity.test = EntityProperty(True) entity.test2 = 'value' entity.test3 = 3 entity.test4 = EntityProperty(1234567890) entity.test5 = datetime.utcnow() self.table.create_entity(entity) entity.RowKey = 'batch_all_operations_together-2' self.table.create_entity(entity) entity.RowKey = 'batch_all_operations_together-3' self.table.create_entity(entity) entity.RowKey = 'batch_all_operations_together-4' self.table.create_entity(entity) transaction_count = 0 batch = self.table.create_batch() entity.RowKey = 'batch_all_operations_together' batch.create_entity(entity) transaction_count += 1 entity.RowKey = 'batch_all_operations_together-1' batch.delete_entity(entity.PartitionKey, entity.RowKey) transaction_count += 1 entity.RowKey = 'batch_all_operations_together-2' entity.test3 = 10 batch.update_entity(entity) transaction_count += 1 entity.RowKey = 'batch_all_operations_together-3' entity.test3 = 100 batch.update_entity(entity, mode=UpdateMode.MERGE) transaction_count += 1 entity.RowKey = 'batch_all_operations_together-4' entity.test3 = 10 batch.upsert_entity(entity) transaction_count += 1 entity.RowKey = 'batch_all_operations_together-5' batch.upsert_entity(entity, mode=UpdateMode.MERGE) transaction_count += 1 transaction_result = self.table.send_batch(batch) # Assert self._assert_valid_batch_transaction(transaction_result, transaction_count) assert transaction_result.get_entity( 'batch_all_operations_together') is not None assert transaction_result.get_entity( 'batch_all_operations_together-1') is not None assert transaction_result.get_entity( 'batch_all_operations_together-2') is not None assert transaction_result.get_entity( 'batch_all_operations_together-3') is not None assert transaction_result.get_entity( 'batch_all_operations_together-4') is not None assert transaction_result.get_entity( 'batch_all_operations_together-5') is not None # Assert entities = list(self.table.query_entities("PartitionKey eq '003'")) assert 5 == len(entities) finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @CosmosPreparer() def test_batch_all_operations_together_context_manager( self, tables_cosmos_account_name, tables_primary_cosmos_account_key): # Arrange self._set_up(tables_cosmos_account_name, tables_primary_cosmos_account_key) try: # Act entity = TableEntity() entity.PartitionKey = '003' entity.RowKey = 'batch_all_operations_together-1' entity.test = EntityProperty(True) entity.test2 = 'value' entity.test3 = 3 entity.test4 = EntityProperty(1234567890) entity.test5 = datetime.utcnow() self.table.create_entity(entity) entity.RowKey = 'batch_all_operations_together-2' self.table.create_entity(entity) entity.RowKey = 'batch_all_operations_together-3' self.table.create_entity(entity) entity.RowKey = 'batch_all_operations_together-4' self.table.create_entity(entity) with self.table.create_batch() as batch: entity.RowKey = 'batch_all_operations_together' batch.create_entity(entity) entity.RowKey = 'batch_all_operations_together-1' batch.delete_entity(entity.PartitionKey, entity.RowKey) entity.RowKey = 'batch_all_operations_together-2' entity.test3 = 10 batch.update_entity(entity) entity.RowKey = 'batch_all_operations_together-3' entity.test3 = 100 batch.update_entity(entity, mode=UpdateMode.MERGE) entity.RowKey = 'batch_all_operations_together-4' entity.test3 = 10 batch.upsert_entity(entity) entity.RowKey = 'batch_all_operations_together-5' batch.upsert_entity(entity, mode=UpdateMode.MERGE) # Assert entities = list(self.table.query_entities("PartitionKey eq '003'")) assert 4 == len(entities) finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @CosmosPreparer() def test_batch_different_partition_operations_fail( self, tables_cosmos_account_name, tables_primary_cosmos_account_key): # Arrange self._set_up(tables_cosmos_account_name, tables_primary_cosmos_account_key) try: entity = self._create_random_entity_dict('001', 'batch_negative_1') self.table.create_entity(entity) # Act batch = self.table.create_batch() entity = self._create_updated_entity_dict('001', 'batch_negative_1') batch.update_entity(entity) entity = self._create_random_entity_dict('002', 'batch_negative_1') # Assert with pytest.raises(ValueError): batch.create_entity(entity) finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @CosmosPreparer() def test_new_non_existent_table(self, tables_cosmos_account_name, tables_primary_cosmos_account_key): # Arrange self._set_up(tables_cosmos_account_name, tables_primary_cosmos_account_key) try: entity = self._create_random_entity_dict('001', 'batch_negative_1') tc = self.ts.get_table_client("doesntexist") batch = tc.create_batch() batch.create_entity(entity) with pytest.raises(ResourceNotFoundError): resp = tc.send_batch(batch) # Assert finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @CosmosPreparer() def test_new_delete_nonexistent_entity(self, tables_cosmos_account_name, tables_primary_cosmos_account_key): # Arrange self._set_up(tables_cosmos_account_name, tables_primary_cosmos_account_key) try: entity = self._create_random_entity_dict('001', 'batch_negative_1') batch = self.table.create_batch() batch.delete_entity(entity['PartitionKey'], entity['RowKey']) with pytest.raises(ResourceNotFoundError): resp = self.table.send_batch(batch) finally: self._tear_down()
class TableTestCase(object): def connection_string(self, account, key): return ("DefaultEndpointsProtocol=https;AccountName=" + account + ";AccountKey=" + str(key) + ";EndpointSuffix=core.windows.net") def account_url(self, account, endpoint_type): """Return an url of storage account. :param str storage_account: Storage account name :param str storage_type: The Storage type part of the URL. Should be "table", or "cosmos", etc. """ try: if endpoint_type == "table": return account.primary_endpoints.table.rstrip("/") if endpoint_type == "cosmos": return "https://{}.table.cosmos.azure.com".format(account.name) except AttributeError: # Didn't find "primary_endpoints" if endpoint_type == "table": return "https://{}.{}.core.windows.net".format( account, endpoint_type) if endpoint_type == "cosmos": return "https://{}.table.cosmos.azure.com".format(account) def generate_sas_token(self): fake_key = "a" * 30 + "b" * 30 return "?" + generate_account_sas( credential=AzureNamedKeyCredential(name="fakename", key=fake_key), resource_types=ResourceTypes(object=True), permission=AccountSasPermissions(read=True, list=True), start=datetime.now() - timedelta(hours=24), expiry=datetime.now() + timedelta(days=8), ) def get_token_credential(self): if is_live(): return DefaultAzureCredential() return self.generate_fake_token() def generate_fake_token(self): return FakeTokenCredential() def _get_table_reference(self, prefix=TEST_TABLE_PREFIX): table_name = self.get_resource_name(prefix) return table_name def _create_table(self, ts, prefix=TEST_TABLE_PREFIX, table_list=None): table_name = self._get_table_reference(prefix) try: table = ts.create_table(table_name) if table_list is not None: table_list.append(table) except ResourceExistsError: table = ts.get_table_client(table_name) return table def _delete_all_tables(self, ts): if self.is_live: for table in ts.list_tables(): ts.delete_table(table.name) self.sleep(10) def _create_pk_rk(self, pk, rk): try: pk = pk if pk is not None else self.get_resource_name("pk").decode( "utf-8") rk = rk if rk is not None else self.get_resource_name("rk").decode( "utf-8") except AttributeError: pk = pk if pk is not None else self.get_resource_name("pk") rk = rk if rk is not None else self.get_resource_name("rk") return pk, rk def _create_random_base_entity_dict(self): """ Creates a dict-based entity with only pk and rk. """ partition, row = self._create_pk_rk(None, None) return { "PartitionKey": partition, "RowKey": row, } def _create_random_entity_dict(self, pk=None, rk=None): """ Creates a dictionary-based entity with fixed values, using all of the supported data types. """ partition, row = self._create_pk_rk(pk, rk) properties = { "PartitionKey": partition, "RowKey": row, "age": 39, "sex": u"male", "married": True, "deceased": False, "optional": None, "ratio": 3.1, "evenratio": 3.0, "double": (5, EdmType.DOUBLE), "large": 933311100, "Birthday": datetime(1973, 10, 4, tzinfo=tzutc()), "birthday": datetime(1970, 10, 4, tzinfo=tzutc()), "binary": b"binary", "other": EntityProperty(20, EdmType.INT32), "clsid": uuid.UUID("c9da6455-213d-42c9-9a79-3e9149a57833"), } return TableEntity(**properties) def _create_updated_entity_dict(self, partition, row): """ Creates a dictionary-based entity with fixed values, with a different set of values than the default entity. It adds fields, changes field values, changes field types, and removes fields when compared to the default entity. """ return { "PartitionKey": partition, "RowKey": row, "age": u"abc", "sex": u"female", "sign": u"aquarius", "birthday": datetime(1991, 10, 4, tzinfo=tzutc()), } def _assert_default_entity(self, entity): """ Asserts that the entity passed in matches the default entity. """ assert entity["age"] == 39 assert entity["sex"] == "male" assert entity["married"] == True assert entity["deceased"] == False assert not "optional" in entity assert entity["ratio"] == 3.1 assert entity["evenratio"] == 3.0 assert entity["double"] == 5.0 assert entity["large"] == 933311100 assert entity["Birthday"] == datetime(1973, 10, 4, tzinfo=tzutc()) assert entity["birthday"] == datetime(1970, 10, 4, tzinfo=tzutc()) assert entity["binary"] == b"binary" assert entity["other"] == 20 assert entity["clsid"] == uuid.UUID( "c9da6455-213d-42c9-9a79-3e9149a57833") assert entity.metadata.pop("etag", None) assert isinstance(entity.metadata.pop("timestamp", None), datetime) assert not entity.metadata, "Found metadata: {}".format( entity.metadata) def _assert_default_entity_json_full_metadata(self, entity, headers=None): """ Asserts that the entity passed in matches the default entity. """ assert entity["age"] == 39 assert entity["sex"] == "male" assert entity["married"] == True assert entity["deceased"] == False assert not "optional" in entity assert not "aquarius" in entity assert entity["ratio"] == 3.1 assert entity["evenratio"] == 3.0 assert entity["double"] == 5.0 assert entity["large"] == 933311100 assert entity["Birthday"] == datetime(1973, 10, 4, tzinfo=tzutc()) assert entity["birthday"] == datetime(1970, 10, 4, tzinfo=tzutc()) assert entity["binary"] == b"binary" assert entity["other"] == 20 assert entity["clsid"] == uuid.UUID( "c9da6455-213d-42c9-9a79-3e9149a57833") assert entity.metadata.pop("etag", None) assert isinstance(entity.metadata.pop("timestamp", None), datetime) assert sorted(list(entity.metadata.keys())) == [ 'editLink', 'id', 'type' ], "Found metadata: {}".format(entity.metadata) def _assert_default_entity_json_no_metadata(self, entity, headers=None): """ Asserts that the entity passed in matches the default entity. """ assert entity["age"] == 39 assert entity["sex"] == "male" assert entity["married"] == True assert entity["deceased"] == False assert not "optional" in entity assert not "aquarius" in entity assert entity["ratio"] == 3.1 assert entity["evenratio"] == 3.0 assert entity["double"] == 5.0 assert entity["large"] == 933311100 assert entity["Birthday"].startswith("1973-10-04T00:00:00") assert entity["birthday"].startswith("1970-10-04T00:00:00") assert entity["Birthday"].endswith("00Z") assert entity["birthday"].endswith("00Z") assert entity["binary"] == b64encode(b"binary").decode("utf-8") assert entity["other"] == 20 assert entity["clsid"] == "c9da6455-213d-42c9-9a79-3e9149a57833" assert entity.metadata.pop("etag", None) assert isinstance(entity.metadata.pop("timestamp", None), datetime) assert not entity.metadata def _assert_updated_entity(self, entity): """ Asserts that the entity passed in matches the updated entity. """ assert entity["age"] == "abc" assert entity["sex"] == "female" assert not "married" in entity assert not "deceased" in entity assert entity["sign"] == "aquarius" assert not "optional" in entity assert not "ratio" in entity assert not "evenratio" in entity assert not "double" in entity assert not "large" in entity assert not "Birthday" in entity assert entity["birthday"] == datetime(1991, 10, 4, tzinfo=tzutc()) assert not "other" in entity assert not "clsid" in entity assert entity.metadata["etag"] assert entity.metadata["timestamp"] def _assert_merged_entity(self, entity): """ Asserts that the entity passed in matches the default entity merged with the updated entity. """ assert entity["age"] == "abc" assert entity["sex"] == "female" assert entity["sign"] == "aquarius" assert entity["married"] == True assert entity["deceased"] == False assert entity["ratio"] == 3.1 assert entity["evenratio"] == 3.0 assert entity["double"] == 5.0 assert entity["large"] == 933311100 assert entity["Birthday"] == datetime(1973, 10, 4, tzinfo=tzutc()) assert entity["birthday"] == datetime(1991, 10, 4, tzinfo=tzutc()) assert entity["other"] == 20 assert isinstance(entity["clsid"], uuid.UUID) assert str(entity["clsid"]) == "c9da6455-213d-42c9-9a79-3e9149a57833" assert entity.metadata["etag"] assert entity.metadata["timestamp"] def _assert_valid_metadata(self, metadata): keys = metadata.keys() assert "version" in keys assert "date" in keys assert "etag" in keys assert len(keys) == 3 def _assert_valid_batch_transaction(self, transaction, length): assert length == len(transaction) def _assert_properties_default(self, prop): assert prop is not None self._assert_logging_equal(prop["analytics_logging"], TableAnalyticsLogging()) self._assert_metrics_equal(prop["hour_metrics"], TableMetrics()) self._assert_metrics_equal(prop["minute_metrics"], TableMetrics()) self._assert_cors_equal(prop["cors"], list()) def _assert_policy_datetime(self, val1, val2): assert isinstance(val2, datetime) assert val1.year == val2.year assert val1.month == val2.month assert val1.day == val2.day assert val1.hour == val2.hour assert val1.minute == val2.minute assert val1.second == val2.second def _assert_logging_equal(self, log1, log2): if log1 is None or log2 is None: assert log1 == log2 return assert log1.version == log2.version assert log1.read == log2.read assert log1.write == log2.write assert log1.delete == log2.delete self._assert_retention_equal(log1.retention_policy, log2.retention_policy) def _assert_delete_retention_policy_equal(self, policy1, policy2): if policy1 is None or policy2 is None: assert policy1 == policy2 return assert policy1.enabled == policy2.enabled assert policy1.days == policy2.days def _assert_static_website_equal(self, prop1, prop2): if prop1 is None or prop2 is None: assert prop1 == prop2 return assert prop1.enabled == prop2.enabled assert prop1.index_document == prop2.index_document assert prop1.error_document404_path == prop2.error_document404_path def _assert_delete_retention_policy_not_equal(self, policy1, policy2): if policy1 is None or policy2 is None: assert policy1 != policy2 return assert policy1.enabled == policy2.enabled and policy1.days == policy2.days def _assert_metrics_equal(self, metrics1, metrics2): if metrics1 is None or metrics2 is None: assert metrics1 == metrics2 return assert metrics1.version == metrics2.version assert metrics1.enabled == metrics2.enabled assert metrics1.include_apis == metrics2.include_apis self._assert_retention_equal(metrics1.retention_policy, metrics2.retention_policy) def _assert_cors_equal(self, cors1, cors2): if cors1 is None or cors2 is None: assert cors1 == cors2 return assert len(cors1) == len(cors2) for i in range(0, len(cors1)): rule1 = cors1[i] rule2 = cors2[i] assert sorted(rule1.allowed_origins) == sorted( rule2.allowed_origins) assert sorted(rule1.allowed_methods) == sorted( rule2.allowed_methods) assert rule1.max_age_in_seconds == rule2.max_age_in_seconds assert sorted(rule1.exposed_headers) == sorted( rule2.exposed_headers) assert sorted(rule1.allowed_headers) == sorted( rule2.allowed_headers) def _assert_retention_equal(self, ret1, ret2): assert ret1.enabled == ret2.enabled assert ret1.days == ret2.days def _tear_down(self): if is_live(): self._delete_all_tables(self.ts) self.test_tables = [] self.ts.close() def _create_query_table(self, entity_count): """ Creates a table with the specified name and adds entities with the default set of values. PartitionKey is set to 'MyPartition' and RowKey is set to a unique counter value starting at 1 (as a string). """ table_name = self.get_resource_name("querytable") table = self.ts.create_table(table_name) self.query_tables.append(table_name) client = self.ts.get_table_client(table_name) entity = self._create_random_entity_dict() for i in range(1, entity_count + 1): entity["RowKey"] = entity["RowKey"] + str(i) client.create_entity(entity) return client def _insert_two_opposite_entities(self, pk=None, rk=None): entity1 = self._create_random_entity_dict() resp = self.table.create_entity(entity1) partition, row = self._create_pk_rk(pk, rk) properties = { "PartitionKey": partition + u"1", "RowKey": row + u"1", "age": 49, "sex": u"female", "married": False, "deceased": True, "optional": None, "ratio": 5.2, "evenratio": 6.0, "large": 39999011, "Birthday": datetime(1993, 4, 1, tzinfo=tzutc()), "birthday": datetime(1990, 4, 1, tzinfo=tzutc()), "binary": b"binary-binary", "other": EntityProperty(40, EdmType.INT32), "clsid": uuid.UUID("c8da6455-213e-42d9-9b79-3f9149a57833"), } self.table.create_entity(properties) return entity1, resp def _insert_random_entity(self, pk=None, rk=None): entity = self._create_random_entity_dict(pk, rk) metadata = self.table.create_entity(entity) return entity, metadata["etag"] def _set_up(self, account_name, credential, url="table"): self.table_name = self.get_resource_name("uttable") self.ts = TableServiceClient(self.account_url(account_name, url), credential=credential, table_name=self.table_name) self.table = self.ts.get_table_client(self.table_name) if self.is_live: try: self.ts.create_table(self.table_name) except ResourceExistsError: pass self.query_tables = [] def _assert_stats_default(self, stats): assert stats is not None assert stats["geo_replication"] is not None assert stats["geo_replication"]["status"] == "live" assert stats["geo_replication"]["last_sync_time"] is not None def _assert_stats_unavailable(self, stats): assert stats is not None assert stats["geo_replication"] is not None assert stats["geo_replication"]["status"] == "unavailable" assert stats["geo_replication"]["last_sync_time"] is None @staticmethod def override_response_body_with_unavailable_status(response): response.http_response.text = lambda _: SERVICE_UNAVAILABLE_RESP_BODY @staticmethod def override_response_body_with_live_status(response): response.http_response.text = lambda _: SERVICE_LIVE_RESP_BODY
class AsyncTableTestCase(TableTestCase): def generate_fake_token(self): return AsyncFakeTokenCredential() def _get_table_reference(self, prefix=TEST_TABLE_PREFIX): table_name = self.get_resource_name(prefix) return table_name async def _create_table(self, ts, prefix=TEST_TABLE_PREFIX, table_list=None): table_name = self._get_table_reference(prefix) try: table = await ts.create_table(table_name) if table_list is not None: table_list.append(table) except ResourceExistsError: table = ts.get_table_client(table_name) return table async def _delete_all_tables(self, account_name, key): client = TableServiceClient(self.account_url(account_name, "cosmos"), key) async for table in client.list_tables(): await client.delete_table(table.name) if self.is_live: self.sleep(10) async def _tear_down(self): if is_live(): async for table in self.ts.list_tables(): await self.ts.delete_table(table.name) if self.ts._cosmos_endpoint: self.sleep(SLEEP_DELAY) self.test_tables = [] await self.ts.close() async def _create_query_table(self, entity_count): """ Creates a table with the specified name and adds entities with the default set of values. PartitionKey is set to 'MyPartition' and RowKey is set to a unique counter value starting at 1 (as a string). """ table_name = self.get_resource_name("querytable") table = await self.ts.create_table(table_name) self.query_tables.append(table_name) client = self.ts.get_table_client(table_name) entity = self._create_random_entity_dict() for i in range(1, entity_count + 1): entity["RowKey"] = entity["RowKey"] + str(i) await client.create_entity(entity=entity) return client async def _insert_two_opposite_entities(self, pk=None, rk=None): entity1 = self._create_random_entity_dict() resp = await self.table.create_entity(entity1) partition, row = self._create_pk_rk(pk, rk) properties = { "PartitionKey": partition + u"1", "RowKey": row + u"1", "age": 49, "sex": u"female", "married": False, "deceased": True, "optional": None, "ratio": 5.2, "evenratio": 6.0, "large": 39999011, "Birthday": datetime(1993, 4, 1, tzinfo=tzutc()), "birthday": datetime(1990, 4, 1, tzinfo=tzutc()), "binary": b"binary-binary", "other": EntityProperty(40, EdmType.INT32), "clsid": uuid.UUID("c8da6455-213e-42d9-9b79-3f9149a57833"), } await self.table.create_entity(properties) return entity1, resp async def _insert_random_entity(self, pk=None, rk=None): entity = self._create_random_entity_dict(pk, rk) metadata = await self.table.create_entity(entity=entity) return entity, metadata["etag"] async def _set_up(self, account_name, account_key, url="table"): account_url = self.account_url(account_name, url) self.ts = TableServiceClient(account_url, account_key) self.table_name = self.get_resource_name("uttable") self.table = self.ts.get_table_client(self.table_name) if self.is_live: try: await self.ts.create_table(table_name=self.table_name) except ResourceExistsError: pass self.query_tables = []
class StorageTableBatchTest(TableTestCase): def _set_up(self, storage_account, storage_account_key): self.ts = TableServiceClient( self.account_url(storage_account, "table"), storage_account_key) self.table_name = self.get_resource_name('uttable') self.table = self.ts.get_table_client(self.table_name) if self.is_live: try: self.ts.create_table(self.table_name) except ResourceExistsError: pass self.test_tables = [] def _tear_down(self): if self.is_live: try: self.ts.delete_table(self.table_name) except: pass for table_name in self.test_tables: try: self.ts.delete_table(table_name) except: pass #--Helpers----------------------------------------------------------------- def _get_table_reference(self, prefix=TEST_TABLE_PREFIX): table_name = self.get_resource_name(prefix) self.test_tables.append(table_name) return self.ts.get_table_client(table_name) def _create_pk_rk(self, pk, rk): try: pk = pk if pk is not None else self.get_resource_name('pk').decode( 'utf-8') rk = rk if rk is not None else self.get_resource_name('rk').decode( 'utf-8') except AttributeError: pk = pk if pk is not None else self.get_resource_name('pk') rk = rk if rk is not None else self.get_resource_name('rk') return pk, rk def _create_random_entity_dict(self, pk=None, rk=None): """ Creates a dictionary-based entity with fixed values, using all of the supported data types. """ # partition = pk if pk is not None else self.get_resource_name('pk').decode('utf-8') # row = rk if rk is not None else self.get_resource_name('rk').decode('utf-8') partition, row = self._create_pk_rk(pk, rk) properties = { 'PartitionKey': partition, 'RowKey': row, 'age': 39, 'sex': u'male', 'married': True, 'deceased': False, 'optional': None, 'ratio': 3.1, 'evenratio': 3.0, 'large': 933311100, 'Birthday': datetime(1973, 10, 4, tzinfo=tzutc()), 'birthday': datetime(1970, 10, 4, tzinfo=tzutc()), 'binary': b'binary', 'other': EntityProperty(value=20, type=EdmType.INT32), 'clsid': uuid.UUID('c9da6455-213d-42c9-9a79-3e9149a57833') } return TableEntity(**properties) def _create_updated_entity_dict(self, partition, row): ''' Creates a dictionary-based entity with fixed values, with a different set of values than the default entity. It adds fields, changes field values, changes field types, and removes fields when compared to the default entity. ''' return { 'PartitionKey': partition, 'RowKey': row, 'age': u'abc', 'sex': u'female', 'sign': u'aquarius', 'birthday': datetime(1991, 10, 4, tzinfo=tzutc()) } def _assert_default_entity(self, entity): ''' Asserts that the entity passed in matches the default entity. ''' self.assertEqual(entity['age'].value, 39) self.assertEqual(entity['sex'].value, u'male') self.assertEqual(entity['married'], True) self.assertEqual(entity['deceased'], False) self.assertFalse("optional" in entity) self.assertEqual(entity['ratio'], 3.1) self.assertEqual(entity['evenratio'], 3.0) self.assertEqual(entity['large'].value, 933311100) self.assertEqual(entity['Birthday'], datetime(1973, 10, 4, tzinfo=tzutc())) self.assertEqual(entity['birthday'], datetime(1970, 10, 4, tzinfo=tzutc())) self.assertEqual(entity['binary'].value, b'binary') self.assertIsInstance(entity['other'], EntityProperty) self.assertEqual(entity['other'].type, EdmType.INT32) self.assertEqual(entity['other'].value, 20) self.assertEqual(entity['clsid'], uuid.UUID('c9da6455-213d-42c9-9a79-3e9149a57833')) self.assertTrue('_metadata' in entity) def _assert_updated_entity(self, entity): ''' Asserts that the entity passed in matches the updated entity. ''' self.assertEqual(entity.age.value, 'abc') self.assertEqual(entity.sex.value, 'female') self.assertFalse(hasattr(entity, "married")) self.assertFalse(hasattr(entity, "deceased")) self.assertEqual(entity.sign.value, 'aquarius') self.assertFalse(hasattr(entity, "optional")) self.assertFalse(hasattr(entity, "ratio")) self.assertFalse(hasattr(entity, "evenratio")) self.assertFalse(hasattr(entity, "large")) self.assertFalse(hasattr(entity, "Birthday")) self.assertEqual(entity.birthday, datetime(1991, 10, 4, tzinfo=tzutc())) self.assertFalse(hasattr(entity, "other")) self.assertFalse(hasattr(entity, "clsid")) self.assertIsNotNone(entity['_metadata']['etag']) def _assert_valid_batch_transaction(self, transaction, length): self.assertIsInstance(transaction, BatchTransactionResult) self.assertEqual(length, len(transaction.entities)) self.assertEqual(length, len(transaction.results)) self.assertEqual(length, len(transaction.requests)) #--Test cases for batch --------------------------------------------- def test_inferred_types(self): # Arrange # Act entity = TableEntity() entity.PartitionKey = '003' entity.RowKey = 'batch_all_operations_together-1' entity.test = EntityProperty(True) entity.test2 = EntityProperty(b'abcdef') entity.test3 = EntityProperty(u'c9da6455-213d-42c9-9a79-3e9149a57833') entity.test4 = EntityProperty(datetime(1973, 10, 4, tzinfo=tzutc())) entity.test5 = EntityProperty(u"stringystring") entity.test6 = EntityProperty(3.14159) entity.test7 = EntityProperty(100) entity.test8 = EntityProperty(2**33, EdmType.INT64) # Assert self.assertEqual(entity.test.type, EdmType.BOOLEAN) self.assertEqual(entity.test2.type, EdmType.BINARY) self.assertEqual(entity.test3.type, EdmType.GUID) self.assertEqual(entity.test4.type, EdmType.DATETIME) self.assertEqual(entity.test5.type, EdmType.STRING) self.assertEqual(entity.test6.type, EdmType.DOUBLE) self.assertEqual(entity.test7.type, EdmType.INT32) self.assertEqual(entity.test8.type, EdmType.INT64) @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @CachedResourceGroupPreparer(name_prefix="tablestest") @CachedStorageAccountPreparer(name_prefix="tablestest") def test_batch_single_insert(self, resource_group, location, storage_account, storage_account_key): # Arrange self._set_up(storage_account, storage_account_key) try: # Act entity = TableEntity() entity.PartitionKey = '001' entity.RowKey = 'batch_insert' entity.test = EntityProperty(True) entity.test2 = 'value' entity.test3 = 3 entity.test4 = EntityProperty(1234567890) entity.test5 = datetime.utcnow() batch = self.table.create_batch() batch.create_entity(entity) transaction_result = self.table.send_batch(batch) # Assert self._assert_valid_batch_transaction(transaction_result, 1) sent_entity = transaction_result.get_entity(entity.RowKey) self.assertIsNotNone(sent_entity) e = self.table.get_entity(row_key=entity.RowKey, partition_key=entity.PartitionKey) self.assertEqual(e.test, entity.test.value) self.assertEqual(e.test2.value, entity.test2) self.assertEqual(e.test3.value, entity.test3) self.assertEqual(e.test4.value, entity.test4.value) self.assertEqual(sent_entity['test'], entity.test.value) self.assertEqual(sent_entity['test2'], entity.test2) self.assertEqual(sent_entity['test3'], entity.test3) self.assertEqual(sent_entity['test4'], entity.test4.value) finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @CachedResourceGroupPreparer(name_prefix="tablestest") @CachedStorageAccountPreparer(name_prefix="tablestest") def test_batch_single_update(self, resource_group, location, storage_account, storage_account_key): # Arrange self._set_up(storage_account, storage_account_key) try: # Act entity = TableEntity() entity.PartitionKey = '001' entity.RowKey = 'batch_insert' entity.test = EntityProperty(True) entity.test2 = 'value' entity.test3 = 3 entity.test4 = EntityProperty(1234567890) entity.test5 = datetime.utcnow() resp = self.table.create_entity(entity) self.assertIsNotNone(resp) entity.test3 = 5 entity.test5 = datetime.utcnow() batch = self.table.create_batch() batch.update_entity(entity, mode=UpdateMode.MERGE) transaction_result = self.table.send_batch(batch) # Assert self._assert_valid_batch_transaction(transaction_result, 1) self.assertIsNotNone(transaction_result.get_entity(entity.RowKey)) result = self.table.get_entity(row_key=entity.RowKey, partition_key=entity.PartitionKey) self.assertEqual(result.PartitionKey, u'001') self.assertEqual(result.RowKey, u'batch_insert') self.assertEqual(result.test3.value, 5) finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @CachedResourceGroupPreparer(name_prefix="tablestest") @CachedStorageAccountPreparer(name_prefix="tablestest") def test_batch_update(self, resource_group, location, storage_account, storage_account_key): # Arrange self._set_up(storage_account, storage_account_key) try: # Act entity = TableEntity() entity.PartitionKey = u'001' entity.RowKey = u'batch_update' entity.test = EntityProperty(True) entity.test2 = u'value' entity.test3 = 3 entity.test4 = EntityProperty(1234567890) entity.test5 = datetime.utcnow() self.table.create_entity(entity) entity = self.table.get_entity(u'001', u'batch_update') self.assertEqual(3, entity.test3.value) entity.test2 = u'value1' batch = self.table.create_batch() batch.update_entity(entity) transaction_result = self.table.send_batch(batch) # Assert self._assert_valid_batch_transaction(transaction_result, 1) self.assertIsNotNone(transaction_result.get_entity(entity.RowKey)) result = self.table.get_entity('001', 'batch_update') self.assertEqual('value1', result.test2.value) self.assertEqual(entity.PartitionKey, u'001') self.assertEqual(entity.RowKey, u'batch_update') finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @CachedResourceGroupPreparer(name_prefix="tablestest") @CachedStorageAccountPreparer(name_prefix="tablestest") def test_batch_merge(self, resource_group, location, storage_account, storage_account_key): # Arrange self._set_up(storage_account, storage_account_key) try: # Act entity = TableEntity() entity.PartitionKey = u'001' entity.RowKey = u'batch_merge' entity.test = EntityProperty(True) entity.test2 = u'value' entity.test3 = 3 entity.test4 = EntityProperty(1234567890) entity.test5 = datetime.utcnow() self.table.create_entity(entity) resp_entity = self.table.get_entity(partition_key=u'001', row_key=u'batch_merge') self.assertEqual(3, entity.test3) entity = TableEntity() entity.PartitionKey = u'001' entity.RowKey = u'batch_merge' entity.test2 = u'value1' batch = self.table.create_batch() batch.update_entity(entity, mode=UpdateMode.MERGE) transaction_result = self.table.send_batch(batch) # Assert self._assert_valid_batch_transaction(transaction_result, 1) self.assertIsNotNone(transaction_result.get_entity(entity.RowKey)) resp_entity = self.table.get_entity(partition_key=u'001', row_key=u'batch_merge') self.assertEqual(entity.test2, resp_entity.test2.value) self.assertEqual(1234567890, resp_entity.test4.value) self.assertEqual(entity.PartitionKey, resp_entity.PartitionKey) self.assertEqual(entity.RowKey, resp_entity.RowKey) finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @CachedResourceGroupPreparer(name_prefix="tablestest") @CachedStorageAccountPreparer(name_prefix="tablestest") def test_batch_update_if_match(self, resource_group, location, storage_account, storage_account_key): # Arrange self._set_up(storage_account, storage_account_key) try: entity = self._create_random_entity_dict() resp = self.table.create_entity(entity=entity) etag = resp['etag'] # Act sent_entity = self._create_updated_entity_dict( entity['PartitionKey'], entity['RowKey']) batch = self.table.create_batch() batch.update_entity(sent_entity, etag=etag, match_condition=MatchConditions.IfNotModified, mode=UpdateMode.REPLACE) transaction_result = self.table.send_batch(batch) # Assert self._assert_valid_batch_transaction(transaction_result, 1) self.assertIsNotNone( transaction_result.get_entity(sent_entity['RowKey'])) entity = self.table.get_entity( partition_key=entity['PartitionKey'], row_key=entity['RowKey']) self._assert_updated_entity(entity) finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @CachedResourceGroupPreparer(name_prefix="tablestest") @CachedStorageAccountPreparer(name_prefix="tablestest") def test_batch_update_if_doesnt_match(self, resource_group, location, storage_account, storage_account_key): # Arrange self._set_up(storage_account, storage_account_key) try: entity = self._create_random_entity_dict() self.table.create_entity(entity) # Act sent_entity1 = self._create_updated_entity_dict( entity['PartitionKey'], entity['RowKey']) batch = self.table.create_batch() batch.update_entity( sent_entity1, etag=u'W/"datetime\'2012-06-15T22%3A51%3A44.9662825Z\'"', match_condition=MatchConditions.IfNotModified) with self.assertRaises(HttpResponseError): self.table.send_batch(batch) # Assert received_entity = self.table.get_entity(entity['PartitionKey'], entity['RowKey']) self._assert_default_entity(received_entity) finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @CachedResourceGroupPreparer(name_prefix="tablestest") @CachedStorageAccountPreparer(name_prefix="tablestest") def test_batch_single_op_if_doesnt_match(self, resource_group, location, storage_account, storage_account_key): # Arrange self._set_up(storage_account, storage_account_key) try: # Act entity = TableEntity() entity.PartitionKey = 'batch_inserts' entity.test = EntityProperty(True) entity.test2 = 'value' entity.test3 = 3 entity.test4 = EntityProperty(1234567890) batch = self.table.create_batch() transaction_count = 0 for i in range(10): entity.RowKey = str(i) batch.create_entity(entity) transaction_count += 1 entity = self._create_random_entity_dict() self.table.create_entity(entity) # Act sent_entity1 = self._create_updated_entity_dict( entity['PartitionKey'], entity['RowKey']) batch = self.table.create_batch() batch.update_entity( sent_entity1, etag=u'W/"datetime\'2012-06-15T22%3A51%3A44.9662825Z\'"', match_condition=MatchConditions.IfNotModified) with self.assertRaises(HttpResponseError): self.table.send_batch(batch) # Assert received_entity = self.table.get_entity(entity['PartitionKey'], entity['RowKey']) self._assert_default_entity(received_entity) finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @CachedResourceGroupPreparer(name_prefix="tablestest") @CachedStorageAccountPreparer(name_prefix="tablestest") def test_batch_insert_replace(self, resource_group, location, storage_account, storage_account_key): # Arrange self._set_up(storage_account, storage_account_key) try: # Act entity = TableEntity() entity.PartitionKey = '001' entity.RowKey = 'batch_insert_replace' entity.test = True entity.test2 = 'value' entity.test3 = 3 entity.test4 = EntityProperty(1234567890) entity.test5 = datetime.utcnow() batch = self.table.create_batch() batch.upsert_entity(entity) transaction_result = self.table.send_batch(batch) # Assert self._assert_valid_batch_transaction(transaction_result, 1) self.assertIsNotNone(transaction_result.get_entity(entity.RowKey)) entity = self.table.get_entity('001', 'batch_insert_replace') self.assertIsNotNone(entity) self.assertEqual('value', entity.test2.value) self.assertEqual(1234567890, entity.test4.value) finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @CachedResourceGroupPreparer(name_prefix="tablestest") @CachedStorageAccountPreparer(name_prefix="tablestest") def test_batch_insert_merge(self, resource_group, location, storage_account, storage_account_key): # Arrange self._set_up(storage_account, storage_account_key) try: # Act entity = TableEntity() entity.PartitionKey = '001' entity.RowKey = 'batch_insert_merge' entity.test = True entity.test2 = 'value' entity.test3 = 3 entity.test4 = EntityProperty(1234567890) entity.test5 = datetime.utcnow() batch = self.table.create_batch() batch.upsert_entity(entity, mode=UpdateMode.MERGE) transaction_result = self.table.send_batch(batch) # Assert self._assert_valid_batch_transaction(transaction_result, 1) self.assertIsNotNone(transaction_result.get_entity(entity.RowKey)) entity = self.table.get_entity('001', 'batch_insert_merge') self.assertIsNotNone(entity) self.assertEqual('value', entity.test2.value) self.assertEqual(1234567890, entity.test4.value) finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @CachedResourceGroupPreparer(name_prefix="tablestest") @CachedStorageAccountPreparer(name_prefix="tablestest") def test_batch_delete(self, resource_group, location, storage_account, storage_account_key): # Arrange self._set_up(storage_account, storage_account_key) try: # Act entity = TableEntity() entity.PartitionKey = u'001' entity.RowKey = u'batch_delete' entity.test = EntityProperty(True) entity.test2 = u'value' entity.test3 = 3 entity.test4 = EntityProperty(1234567890) entity.test5 = datetime.utcnow() self.table.create_entity(entity) entity = self.table.get_entity(partition_key=u'001', row_key=u'batch_delete') self.assertEqual(3, entity.test3.value) batch = self.table.create_batch() batch.delete_entity(partition_key=entity.PartitionKey, row_key=entity.RowKey) transaction_result = self.table.send_batch(batch) # Assert self._assert_valid_batch_transaction(transaction_result, 1) self.assertIsNotNone(transaction_result.get_entity(entity.RowKey)) with self.assertRaises(ResourceNotFoundError): entity = self.table.get_entity( partition_key=entity.PartitionKey, row_key=entity.RowKey) finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @CachedResourceGroupPreparer(name_prefix="tablestest") @CachedStorageAccountPreparer(name_prefix="tablestest") def test_batch_inserts(self, resource_group, location, storage_account, storage_account_key): # Arrange self._set_up(storage_account, storage_account_key) try: # Act entity = TableEntity() entity.PartitionKey = 'batch_inserts' entity.test = EntityProperty(True) entity.test2 = 'value' entity.test3 = 3 entity.test4 = EntityProperty(1234567890) batch = self.table.create_batch() transaction_count = 0 for i in range(100): entity.RowKey = str(i) batch.create_entity(entity) transaction_count += 1 transaction_result = self.table.send_batch(batch) # Assert self._assert_valid_batch_transaction(transaction_result, transaction_count) self.assertIsNotNone(transaction_result.get_entity(entity.RowKey)) entities = list( self.table.query_entities("PartitionKey eq 'batch_inserts'")) # Assert self.assertIsNotNone(entities) self.assertEqual(100, len(entities)) e = self.table.get_entity('batch_inserts', '1') finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @CachedResourceGroupPreparer(name_prefix="tablestest") @CachedStorageAccountPreparer(name_prefix="tablestest") def test_batch_all_operations_together(self, resource_group, location, storage_account, storage_account_key): # Arrange self._set_up(storage_account, storage_account_key) try: # Act entity = TableEntity() entity.PartitionKey = '003' entity.RowKey = 'batch_all_operations_together-1' entity.test = EntityProperty(True) entity.test2 = 'value' entity.test3 = 3 entity.test4 = EntityProperty(1234567890) entity.test5 = datetime.utcnow() self.table.create_entity(entity) entity.RowKey = 'batch_all_operations_together-2' self.table.create_entity(entity) entity.RowKey = 'batch_all_operations_together-3' self.table.create_entity(entity) entity.RowKey = 'batch_all_operations_together-4' self.table.create_entity(entity) transaction_count = 0 batch = self.table.create_batch() entity.RowKey = 'batch_all_operations_together' batch.create_entity(entity) transaction_count += 1 entity.RowKey = 'batch_all_operations_together-1' batch.delete_entity(entity.PartitionKey, entity.RowKey) transaction_count += 1 entity.RowKey = 'batch_all_operations_together-2' entity.test3 = 10 batch.update_entity(entity) transaction_count += 1 entity.RowKey = 'batch_all_operations_together-3' entity.test3 = 100 batch.update_entity(entity, mode=UpdateMode.MERGE) transaction_count += 1 entity.RowKey = 'batch_all_operations_together-4' entity.test3 = 10 batch.upsert_entity(entity) transaction_count += 1 entity.RowKey = 'batch_all_operations_together-5' batch.upsert_entity(entity, mode=UpdateMode.MERGE) transaction_count += 1 transaction_result = self.table.send_batch(batch) # Assert self._assert_valid_batch_transaction(transaction_result, transaction_count) self.assertIsNotNone( transaction_result.get_entity('batch_all_operations_together')) self.assertIsNotNone( transaction_result.get_entity( 'batch_all_operations_together-1')) self.assertIsNotNone( transaction_result.get_entity( 'batch_all_operations_together-2')) self.assertIsNotNone( transaction_result.get_entity( 'batch_all_operations_together-3')) self.assertIsNotNone( transaction_result.get_entity( 'batch_all_operations_together-4')) self.assertIsNotNone( transaction_result.get_entity( 'batch_all_operations_together-5')) # Assert entities = list(self.table.query_entities("PartitionKey eq '003'")) self.assertEqual(5, len(entities)) finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @CachedResourceGroupPreparer(name_prefix="tablestest") @CachedStorageAccountPreparer(name_prefix="tablestest") def test_batch_all_operations_together_context_manager( self, resource_group, location, storage_account, storage_account_key): # Arrange self._set_up(storage_account, storage_account_key) try: # Act entity = TableEntity() entity.PartitionKey = '003' entity.RowKey = 'batch_all_operations_together-1' entity.test = EntityProperty(True) entity.test2 = 'value' entity.test3 = 3 entity.test4 = EntityProperty(1234567890) entity.test5 = datetime.utcnow() self.table.create_entity(entity) entity.RowKey = 'batch_all_operations_together-2' self.table.create_entity(entity) entity.RowKey = 'batch_all_operations_together-3' self.table.create_entity(entity) entity.RowKey = 'batch_all_operations_together-4' self.table.create_entity(entity) with self.table.create_batch() as batch: entity.RowKey = 'batch_all_operations_together' batch.create_entity(entity) entity.RowKey = 'batch_all_operations_together-1' batch.delete_entity(entity.PartitionKey, entity.RowKey) entity.RowKey = 'batch_all_operations_together-2' entity.test3 = 10 batch.update_entity(entity) entity.RowKey = 'batch_all_operations_together-3' entity.test3 = 100 batch.update_entity(entity, mode=UpdateMode.MERGE) entity.RowKey = 'batch_all_operations_together-4' entity.test3 = 10 batch.upsert_entity(entity) entity.RowKey = 'batch_all_operations_together-5' batch.upsert_entity(entity, mode=UpdateMode.MERGE) # Assert entities = list(self.table.query_entities("PartitionKey eq '003'")) self.assertEqual(4, len(entities)) finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @CachedResourceGroupPreparer(name_prefix="tablestest") @CachedStorageAccountPreparer(name_prefix="tablestest") def test_batch_reuse(self, resource_group, location, storage_account, storage_account_key): # Arrange self._set_up(storage_account, storage_account_key) try: table2 = self._get_table_reference('table2') table2.create_table() # Act entity = TableEntity() entity.PartitionKey = '003' entity.RowKey = 'batch_all_operations_together-1' entity.test = EntityProperty(True) entity.test2 = 'value' entity.test3 = 3 entity.test4 = EntityProperty(1234567890) entity.test5 = datetime.utcnow() batch = self.table.create_batch() batch.create_entity(entity) entity.RowKey = 'batch_all_operations_together-2' batch.create_entity(entity) entity.RowKey = 'batch_all_operations_together-3' batch.create_entity(entity) entity.RowKey = 'batch_all_operations_together-4' batch.create_entity(entity) self.table.send_batch(batch) with self.assertRaises(HttpResponseError): resp = table2.send_batch(batch) entities = list(self.table.query_entities("PartitionKey eq '003'")) self.assertEqual(4, len(entities)) finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @CachedResourceGroupPreparer(name_prefix="tablestest") @CachedStorageAccountPreparer(name_prefix="tablestest") def test_batch_same_row_operations_fail(self, resource_group, location, storage_account, storage_account_key): # Arrange self._set_up(storage_account, storage_account_key) try: entity = self._create_random_entity_dict('001', 'batch_negative_1') self.table.create_entity(entity) # Act batch = self.table.create_batch() entity = self._create_updated_entity_dict('001', 'batch_negative_1') batch.update_entity(entity) entity = self._create_random_entity_dict('001', 'batch_negative_1') batch.update_entity(entity, mode=UpdateMode.MERGE) # Assert with self.assertRaises(HttpResponseError): self.table.send_batch(batch) finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @CachedResourceGroupPreparer(name_prefix="tablestest") @CachedStorageAccountPreparer(name_prefix="tablestest") def test_batch_different_partition_operations_fail(self, resource_group, location, storage_account, storage_account_key): # Arrange self._set_up(storage_account, storage_account_key) try: entity = self._create_random_entity_dict('001', 'batch_negative_1') self.table.create_entity(entity) # Act batch = self.table.create_batch() entity = self._create_updated_entity_dict('001', 'batch_negative_1') batch.update_entity(entity) entity = self._create_random_entity_dict('002', 'batch_negative_1') with self.assertRaises(ValueError): batch.create_entity(entity) finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @CachedResourceGroupPreparer(name_prefix="tablestest") @CachedStorageAccountPreparer(name_prefix="tablestest") def test_batch_too_many_ops(self, resource_group, location, storage_account, storage_account_key): # Arrange self._set_up(storage_account, storage_account_key) try: entity = self._create_random_entity_dict('001', 'batch_negative_1') self.table.create_entity(entity) # Act with self.assertRaises(HttpResponseError): batch = self.table.create_batch() for i in range(0, 101): entity = TableEntity() entity.PartitionKey = 'large' entity.RowKey = 'item{0}'.format(i) batch.create_entity(entity) self.table.send_batch(batch) # Assert finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @CachedResourceGroupPreparer(name_prefix="tablestest") @CachedStorageAccountPreparer(name_prefix="tablestest") def test_batch_different_partition_keys(self, resource_group, location, storage_account, storage_account_key): # Arrange self._set_up(storage_account, storage_account_key) try: entity = self._create_random_entity_dict('001', 'batch_negative_1') entity2 = self._create_random_entity_dict('002', 'batch_negative_1') batch = self.table.create_batch() batch.create_entity(entity) with self.assertRaises(ValueError): batch.create_entity(entity2) # Assert finally: self._tear_down()
class StorageTableClientTest(AzureTestCase, TableTestCase): def _set_up(self, tables_cosmos_account_name, tables_primary_cosmos_account_key): self.ts = TableServiceClient( self.account_url(tables_cosmos_account_name, "cosmos"), tables_primary_cosmos_account_key) self.table_name = self.get_resource_name('uttable') self.table = self.ts.get_table_client(self.table_name) if self.is_live: try: self.ts.create_table(self.table_name) except ResourceExistsError: pass self.test_tables = [] def _tear_down(self): if self.is_live: try: self.ts.delete_table(self.table_name) except: pass for table_name in self.test_tables: try: self.ts.delete_table(table_name) except: pass self.sleep(SLEEP_DELAY) #--Helpers----------------------------------------------------------------- def _get_table_reference(self, prefix=TEST_TABLE_PREFIX): table_name = self.get_resource_name(prefix) self.test_tables.append(table_name) return self.ts.get_table_client(table_name) def _create_pk_rk(self, pk, rk): try: pk = pk if pk is not None else self.get_resource_name('pk').decode( 'utf-8') rk = rk if rk is not None else self.get_resource_name('rk').decode( 'utf-8') except AttributeError: pk = pk if pk is not None else self.get_resource_name('pk') rk = rk if rk is not None else self.get_resource_name('rk') return pk, rk def _create_random_entity_dict(self, pk=None, rk=None): ''' Creates a dictionary-based entity with fixed values, using all of the supported data types. ''' partition, row = self._create_pk_rk(pk, rk) properties = { 'PartitionKey': partition, 'RowKey': row, 'age': 39, 'sex': u'male', 'married': True, 'deceased': False, 'optional': None, 'ratio': 3.1, 'evenratio': 3.0, 'large': 933311100, 'Birthday': datetime(1973, 10, 4, tzinfo=tzutc()), 'birthday': datetime(1970, 10, 4, tzinfo=tzutc()), 'binary': b'binary', 'other': EntityProperty(20, EdmType.INT32), 'clsid': uuid.UUID('c9da6455-213d-42c9-9a79-3e9149a57833') } return TableEntity(**properties) def _create_updated_entity_dict(self, partition, row): ''' Creates a dictionary-based entity with fixed values, with a different set of values than the default entity. It adds fields, changes field values, changes field types, and removes fields when compared to the default entity. ''' return { 'PartitionKey': partition, 'RowKey': row, 'age': u'abc', 'sex': u'female', 'sign': u'aquarius', 'birthday': datetime(1991, 10, 4, tzinfo=tzutc()) } def _assert_default_entity(self, entity): ''' Asserts that the entity passed in matches the default entity. ''' assert entity['age'] == 39 assert entity['sex'] == 'male' assert entity['married'] == True assert entity['deceased'] == False assert not "optional" in entity assert entity['ratio'] == 3.1 assert entity['evenratio'] == 3.0 assert entity['large'] == 933311100 assert entity['Birthday'] == datetime(1973, 10, 4, tzinfo=tzutc()) assert entity['birthday'] == datetime(1970, 10, 4, tzinfo=tzutc()) assert entity['binary'].value == b'binary' assert entity['other'] == 20 assert entity['clsid'] == uuid.UUID( 'c9da6455-213d-42c9-9a79-3e9149a57833') assert entity.metadata['etag'] assert entity.metadata['timestamp'] def _assert_updated_entity(self, entity): ''' Asserts that the entity passed in matches the updated entity. ''' assert entity['age'] == 'abc' assert entity['sex'] == 'female' assert not "married" in entity assert not "deceased" in entity assert entity['sign'] == 'aquarius' assert not "optional" in entity assert not "ratio" in entity assert not "evenratio" in entity assert not "large" in entity assert not "Birthday" in entity assert entity['birthday'] == datetime(1991, 10, 4, tzinfo=tzutc()) assert not "other" in entity assert not "clsid" in entity assert entity.metadata['etag'] assert entity.metadata['timestamp'] #--Test cases for batch --------------------------------------------- def _assert_valid_batch_transaction(self, transaction, length): assert length == len(transaction) @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @cosmos_decorator def test_batch_insert(self, tables_cosmos_account_name, tables_primary_cosmos_account_key): # Arrange self._set_up(tables_cosmos_account_name, tables_primary_cosmos_account_key) try: # Act entity = TableEntity() entity['PartitionKey'] = '001' entity['RowKey'] = 'batch_insert_replace' entity['test'] = True entity['test2'] = 'value' entity['test3'] = 3 entity['test4'] = EntityProperty(1234567890, EdmType.INT32) entity['test5'] = datetime.utcnow() batch = [('upsert', entity)] transaction_result = self.table.submit_transaction(batch) # Assert self._assert_valid_batch_transaction(transaction_result, 1) assert 'etag' in transaction_result[0] entity = self.table.get_entity('001', 'batch_insert_replace') assert entity is not None assert 'value' == entity['test2'] assert 1234567890 == entity['test4'] finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @cosmos_decorator def test_batch_update(self, tables_cosmos_account_name, tables_primary_cosmos_account_key): # Arrange self._set_up(tables_cosmos_account_name, tables_primary_cosmos_account_key) try: # Act entity = TableEntity() entity['PartitionKey'] = u'001' entity['RowKey'] = u'batch_update' entity['test'] = EntityProperty(True, EdmType.BOOLEAN) entity['test2'] = u'value' entity['test3'] = 3 entity['test4'] = EntityProperty(1234567890, EdmType.INT32) entity['test5'] = datetime.utcnow() self.table.create_entity(entity) entity = self.table.get_entity(u'001', u'batch_update') assert 3 == entity['test3'] entity['test2'] = u'value1' batch = [('update', entity)] transaction_result = self.table.submit_transaction(batch) # Assert self._assert_valid_batch_transaction(transaction_result, 1) assert 'etag' in transaction_result[0] result = self.table.get_entity('001', 'batch_update') assert 'value1' == result['test2'] assert entity['PartitionKey'] == u'001' assert entity['RowKey'] == u'batch_update' finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @cosmos_decorator def test_batch_merge(self, tables_cosmos_account_name, tables_primary_cosmos_account_key): # Arrange self._set_up(tables_cosmos_account_name, tables_primary_cosmos_account_key) try: # Act entity = TableEntity() entity['PartitionKey'] = u'001' entity['RowKey'] = u'batch_merge' entity['test'] = EntityProperty(True, EdmType.BOOLEAN) entity['test2'] = u'value' entity['test3'] = 3 entity['test4'] = EntityProperty(1234567890, EdmType.INT32) entity['test5'] = datetime.utcnow() self.table.create_entity(entity) resp_entity = self.table.get_entity(partition_key=u'001', row_key=u'batch_merge') assert 3 == entity['test3'] entity = TableEntity() entity['PartitionKey'] = u'001' entity['RowKey'] = u'batch_merge' entity['test2'] = u'value1' batch = [('update', entity, {'mode': UpdateMode.MERGE})] transaction_result = self.table.submit_transaction(batch) # Assert self._assert_valid_batch_transaction(transaction_result, 1) assert 'etag' in transaction_result[0] resp_entity = self.table.get_entity(partition_key=u'001', row_key=u'batch_merge') assert entity['test2'] == resp_entity['test2'] assert 1234567890 == resp_entity['test4'] assert entity['PartitionKey'] == resp_entity['PartitionKey'] assert entity['RowKey'] == resp_entity['RowKey'] finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @cosmos_decorator def test_batch_update_if_match(self, tables_cosmos_account_name, tables_primary_cosmos_account_key): # Arrange self._set_up(tables_cosmos_account_name, tables_primary_cosmos_account_key) try: entity = self._create_random_entity_dict() resp = self.table.create_entity(entity=entity) etag = resp['etag'] # Act sent_entity = self._create_updated_entity_dict( entity['PartitionKey'], entity['RowKey']) batch = [('update', sent_entity, { 'mode': UpdateMode.REPLACE, 'etag': etag, 'match_condition': MatchConditions.IfNotModified, 'mode': UpdateMode.REPLACE })] transaction_result = self.table.submit_transaction(batch) # Assert self._assert_valid_batch_transaction(transaction_result, 1) assert 'etag' in transaction_result[0] entity = self.table.get_entity( partition_key=entity['PartitionKey'], row_key=entity['RowKey']) self._assert_updated_entity(entity) finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @cosmos_decorator def test_batch_update_if_doesnt_match(self, tables_cosmos_account_name, tables_primary_cosmos_account_key): # Arrange self._set_up(tables_cosmos_account_name, tables_primary_cosmos_account_key) try: entity = self._create_random_entity_dict() self.table.create_entity(entity) # Act sent_entity1 = self._create_updated_entity_dict( entity['PartitionKey'], entity['RowKey']) batch = [('update', sent_entity1, { 'etag': u'W/"datetime\'2012-06-15T22%3A51%3A44.9662825Z\'"', 'match_condition': MatchConditions.IfNotModified })] with pytest.raises(TableTransactionError): self.table.submit_transaction(batch) # Assert received_entity = self.table.get_entity(entity['PartitionKey'], entity['RowKey']) self._assert_default_entity(received_entity) finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @cosmos_decorator def test_batch_insert_replace(self, tables_cosmos_account_name, tables_primary_cosmos_account_key): # Arrange self._set_up(tables_cosmos_account_name, tables_primary_cosmos_account_key) try: # Act entity = TableEntity() entity['PartitionKey'] = '001' entity['RowKey'] = 'batch_insert_replace' entity['test'] = True entity['test2'] = 'value' entity['test3'] = 3 entity['test4'] = EntityProperty(1234567890, EdmType.INT32) entity['test5'] = datetime.utcnow() batch = [('upsert', entity, {'mode': UpdateMode.REPLACE})] transaction_result = self.table.submit_transaction(batch) # Assert self._assert_valid_batch_transaction(transaction_result, 1) assert 'etag' in transaction_result[0] entity = self.table.get_entity('001', 'batch_insert_replace') assert entity is not None assert 'value' == entity['test2'] assert 1234567890 == entity['test4'] finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @cosmos_decorator def test_batch_insert_merge(self, tables_cosmos_account_name, tables_primary_cosmos_account_key): # Arrange self._set_up(tables_cosmos_account_name, tables_primary_cosmos_account_key) try: # Act entity = TableEntity() entity['PartitionKey'] = '001' entity['RowKey'] = 'batch_insert_merge' entity['test'] = True entity['test2'] = 'value' entity['test3'] = 3 entity['test4'] = EntityProperty(1234567890, EdmType.INT32) entity['test5'] = datetime.utcnow() batch = [('upsert', entity, {'mode': UpdateMode.MERGE})] transaction_result = self.table.submit_transaction(batch) # Assert self._assert_valid_batch_transaction(transaction_result, 1) assert 'etag' in transaction_result[0] entity = self.table.get_entity('001', 'batch_insert_merge') assert entity is not None assert 'value' == entity['test2'] assert 1234567890 == entity['test4'] finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @cosmos_decorator def test_batch_delete(self, tables_cosmos_account_name, tables_primary_cosmos_account_key): # Arrange self._set_up(tables_cosmos_account_name, tables_primary_cosmos_account_key) try: # Act entity = TableEntity() entity['PartitionKey'] = u'001' entity['RowKey'] = u'batch_delete' entity['test'] = EntityProperty(True, EdmType.BOOLEAN) entity['test2'] = u'value' entity['test3'] = 3 entity['test4'] = EntityProperty(1234567890, EdmType.INT32) entity['test5'] = datetime.utcnow() self.table.create_entity(entity) entity = self.table.get_entity(partition_key=u'001', row_key=u'batch_delete') assert 3 == entity['test3'] batch = [('delete', entity)] transaction_result = self.table.submit_transaction(batch) # Assert self._assert_valid_batch_transaction(transaction_result, 1) assert 'etag' not in transaction_result[0] with pytest.raises(ResourceNotFoundError): entity = self.table.get_entity( partition_key=entity['PartitionKey'], row_key=entity['RowKey']) finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @cosmos_decorator def test_batch_inserts(self, tables_cosmos_account_name, tables_primary_cosmos_account_key): # Arrange self._set_up(tables_cosmos_account_name, tables_primary_cosmos_account_key) try: # Act entity = TableEntity() entity['PartitionKey'] = 'batch_inserts' entity['test'] = EntityProperty(True, EdmType.BOOLEAN) entity['test2'] = 'value' entity['test3'] = 3 entity['test4'] = EntityProperty(1234567890, EdmType.INT32) transaction_count = 0 batch = [] for i in range(100): entity['RowKey'] = str(i) batch.append(('create', entity.copy())) transaction_count += 1 transaction_result = self.table.submit_transaction(batch) # Assert self._assert_valid_batch_transaction(transaction_result, transaction_count) assert 'etag' in transaction_result[0] entities = list( self.table.query_entities("PartitionKey eq 'batch_inserts'")) # Assert assert entities is not None assert transaction_count == len(entities) e = self.table.get_entity('batch_inserts', '1') finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @cosmos_decorator def test_batch_all_operations_together(self, tables_cosmos_account_name, tables_primary_cosmos_account_key): # Arrange self._set_up(tables_cosmos_account_name, tables_primary_cosmos_account_key) try: # Act entity = TableEntity() entity['PartitionKey'] = '003' entity['RowKey'] = 'batch_all_operations_together-1' entity['test'] = EntityProperty(True, EdmType.BOOLEAN) entity['test2'] = 'value' entity['test3'] = 3 entity['test4'] = EntityProperty(1234567890, EdmType.INT32) entity['test5'] = datetime.utcnow() self.table.create_entity(entity) entity['RowKey'] = 'batch_all_operations_together-2' self.table.create_entity(entity) entity['RowKey'] = 'batch_all_operations_together-3' self.table.create_entity(entity) entity['RowKey'] = 'batch_all_operations_together-4' self.table.create_entity(entity) transaction_count = 0 batch = [] entity['RowKey'] = 'batch_all_operations_together' batch.append((TransactionOperation.CREATE, entity.copy())) transaction_count += 1 entity['RowKey'] = 'batch_all_operations_together-1' batch.append((TransactionOperation.DELETE, entity.copy())) transaction_count += 1 entity['RowKey'] = 'batch_all_operations_together-2' entity['test3'] = 10 batch.append((TransactionOperation.UPDATE, entity.copy())) transaction_count += 1 entity['RowKey'] = 'batch_all_operations_together-3' entity['test3'] = 100 batch.append((TransactionOperation.UPDATE, entity.copy(), { 'mode': UpdateMode.REPLACE })) transaction_count += 1 entity['RowKey'] = 'batch_all_operations_together-4' entity['test3'] = 10 batch.append((TransactionOperation.UPSERT, entity.copy())) transaction_count += 1 entity['RowKey'] = 'batch_all_operations_together-5' batch.append((TransactionOperation.UPSERT, entity.copy(), { 'mode': UpdateMode.REPLACE })) transaction_count += 1 transaction_result = self.table.submit_transaction(batch) # Assert self._assert_valid_batch_transaction(transaction_result, transaction_count) assert 'etag' in transaction_result[0] assert 'etag' not in transaction_result[1] assert 'etag' in transaction_result[2] assert 'etag' in transaction_result[3] assert 'etag' in transaction_result[4] assert 'etag' in transaction_result[5] # Assert entities = list(self.table.query_entities("PartitionKey eq '003'")) assert 5 == len(entities) finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @cosmos_decorator def test_batch_different_partition_operations_fail( self, tables_cosmos_account_name, tables_primary_cosmos_account_key): # Arrange self._set_up(tables_cosmos_account_name, tables_primary_cosmos_account_key) try: entity = self._create_random_entity_dict('001', 'batch_negative_1') self.table.create_entity(entity) # Act batch = [] entity = self._create_updated_entity_dict('001', 'batch_negative_1') batch.append(('update', entity.copy())) entity = self._create_random_entity_dict('002', 'batch_negative_1') batch.append(('update', entity.copy())) with pytest.raises(ValueError): self.table.submit_transaction(batch) finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @cosmos_decorator def test_new_non_existent_table(self, tables_cosmos_account_name, tables_primary_cosmos_account_key): # Arrange self._set_up(tables_cosmos_account_name, tables_primary_cosmos_account_key) try: entity = self._create_random_entity_dict('001', 'batch_negative_1') tc = self.ts.get_table_client("doesntexist") batch = [('create', entity)] with pytest.raises(TableTransactionError): resp = tc.submit_transaction(batch) # Assert finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @cosmos_decorator def test_new_delete_nonexistent_entity(self, tables_cosmos_account_name, tables_primary_cosmos_account_key): # Arrange self._set_up(tables_cosmos_account_name, tables_primary_cosmos_account_key) try: entity = self._create_random_entity_dict('001', 'batch_negative_1') batch = [('delete', entity)] with pytest.raises(TableTransactionError): resp = self.table.submit_transaction(batch) finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @cosmos_decorator def test_delete_batch_with_bad_kwarg(self, tables_cosmos_account_name, tables_primary_cosmos_account_key): # Arrange self._set_up(tables_cosmos_account_name, tables_primary_cosmos_account_key) try: entity = self._create_random_entity_dict('001', 'batch_negative_1') self.table.create_entity(entity) received = self.table.get_entity(entity["PartitionKey"], entity["RowKey"]) good_etag = received.metadata["etag"] received.metadata[ "etag"] = u'W/"datetime\'2012-06-15T22%3A51%3A44.9662825Z\'"' batch = [('delete', received, { "match_condition": MatchConditions.IfNotModified })] with pytest.raises(TableTransactionError): self.table.submit_transaction(batch) received.metadata["etag"] = good_etag batch = [('delete', received, { "match_condition": MatchConditions.IfNotModified })] resp = self.table.submit_transaction(batch) assert resp is not None finally: self._tear_down() @pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3") @pytest.mark.live_test_only # Request bodies are very large @cosmos_decorator def test_batch_request_too_large(self, tables_cosmos_account_name, tables_primary_cosmos_account_key): # Arrange self._set_up(tables_cosmos_account_name, tables_primary_cosmos_account_key) try: batch = [] entity = { 'PartitionKey': 'pk001', 'Foo': os.urandom(1024 * 64), 'Bar': os.urandom(1024 * 64), 'Baz': os.urandom(1024 * 64) } for i in range(50): entity['RowKey'] = str(i) batch.append(('create', entity.copy())) with pytest.raises(RequestTooLargeError): self.table.submit_transaction(batch) finally: self._tear_down()
class TestStorageRetry(AzureRecordedTestCase, TableTestCase): def _set_up(self, tables_storage_account_name, tables_primary_storage_account_key, url='table', default_table=True, **kwargs): self.table_name = self.get_resource_name('uttable') self.ts = TableServiceClient( self.account_url(tables_storage_account_name, url), credential=tables_primary_storage_account_key, **kwargs) self.table = self.ts.get_table_client(self.table_name) if self.is_live and default_table: try: self.ts.create_table(self.table_name) except ResourceExistsError: pass self.query_tables = [] # TODO: Figure out why this is needed by the "test_retry_on_socket_timeout" test def _tear_down(self, **kwargs): if self.is_live: try: self.ts.delete_table(self.table_name, **kwargs) except: pass try: for table_name in self.query_tables: try: self.ts.delete_table(table_name, **kwargs) except: pass except AttributeError: pass # --Test Cases -------------------------------------------- @tables_decorator @recorded_by_proxy def test_retry_on_server_error(self, tables_storage_account_name, tables_primary_storage_account_key): self._set_up(tables_storage_account_name, tables_primary_storage_account_key, default_table=False) try: callback = ResponseCallback(status=201, new_status=500).override_status new_table_name = self.get_resource_name('uttable') # The initial create will return 201, but we overwrite it with 500 and retry. # The retry will then get a 409 conflict. with pytest.raises(ResourceExistsError): self.ts.create_table(new_table_name, raw_response_hook=callback) finally: self.ts.delete_table(new_table_name) self._tear_down() @tables_decorator @recorded_by_proxy def test_retry_on_timeout(self, tables_storage_account_name, tables_primary_storage_account_key): self._set_up(tables_storage_account_name, tables_primary_storage_account_key, default_table=False, retry_mode=RetryMode.Exponential, retry_backoff_factor=1) callback = ResponseCallback(status=200, new_status=408).override_first_status try: # The initial get will return 200, but we overwrite it with 408 and retry. # The retry will then succeed. self.ts.get_service_properties(raw_response_hook=callback) finally: self._tear_down() @pytest.mark.live_test_only @tables_decorator def test_retry_on_socket_timeout(self, tables_storage_account_name, tables_primary_storage_account_key): retry_transport = RetryRequestTransport(connection_timeout=11, read_timeout=0.000000000001) self._set_up(tables_storage_account_name, tables_primary_storage_account_key, transport=retry_transport, default_table=False, retry_mode=RetryMode.Fixed, retry_backoff_factor=1) with pytest.raises(AzureError) as error: self.ts.get_service_properties() # 3 retries + 1 original == 4 assert retry_transport.count == 4 # This call should succeed on the server side, but fail on the client side due to socket timeout assert 'read timeout' in str( error.value ), 'Expected socket timeout but got different exception.' @tables_decorator @recorded_by_proxy def test_no_retry(self, tables_storage_account_name, tables_primary_storage_account_key): self._set_up(tables_storage_account_name, tables_primary_storage_account_key, retry_total=0, default_table=False) new_table_name = self.get_resource_name('uttable') # Force the create call to 'timeout' with a 408 callback = ResponseCallback(status=201, new_status=500).override_status try: with pytest.raises(HttpResponseError) as error: self.ts.create_table(new_table_name, raw_response_hook=callback) assert error.value.response.status_code == 500 assert error.value.reason == 'Created' finally: self.ts.delete_table(new_table_name) self._tear_down()