async def _test_invalid_account_key_async(self):
        # Arrange
        container_name = self.get_resource_name()
        retry = ExponentialRetry(initial_backoff=1,
                                 increment_base=3,
                                 retry_total=3)
        service = self._create_storage_service(
            BlobServiceClient,
            self.settings,
            retry_policy=retry,
            transport=AiohttpTestTransport())
        service.credential.account_name = "dummy_account_name"
        service.credential.account_key = "dummy_account_key"

        # Shorten retries and add counter
        retry_counter = RetryCounter()
        retry_callback = retry_counter.simple_count

        # Act
        with self.assertRaises(ClientAuthenticationError):
            await service.create_container(container_name,
                                           retry_callback=retry_callback)

        # Assert
        # No retry should be performed since the signing error is fatal
        self.assertEqual(retry_counter.count, 0)
    async def _test_retry_to_secondary_with_get_async(self):
        # Arrange
        container_name = self.get_resource_name()
        retry = ExponentialRetry(retry_to_secondary=True,
                                 initial_backoff=1,
                                 increment_base=2)
        service = self._create_storage_service(
            BlobServiceClient,
            self.settings,
            retry_policy=retry,
            transport=AiohttpTestTransport())

        # Act
        try:
            container = await service.create_container(container_name)
            response_callback = ResponseCallback(
                status=200, new_status=408).override_first_status

            # Assert
            # Confirm that the get request gets retried to secondary
            def retry_callback(retry_count=None, location_mode=None, **kwargs):
                # Only check this every other time, sometimes the secondary location fails due to delay
                if retry_count % 2 == 0:
                    self.assertEqual(LocationMode.SECONDARY, location_mode)

            await container.get_container_properties(
                raw_response_hook=response_callback, retry_hook=retry_callback)
        finally:
            await service.delete_container(container_name)
    async def _test_secondary_location_mode_async(self):
        # Arrange
        container_name = self.get_resource_name()
        retry = ExponentialRetry(initial_backoff=1, increment_base=2)
        service = self._create_storage_service(
            BlobServiceClient,
            self.settings,
            retry_policy=retry,
            transport=AiohttpTestTransport())

        # Act
        try:
            container = await service.create_container(container_name)
            container.location_mode = LocationMode.SECONDARY

            # Override the response from secondary if it's 404 as that simply means
            # the container hasn't replicated. We're just testing we try secondary,
            # so that's fine.
            response_callback = ResponseCallback(
                status=404, new_status=200).override_first_status

            # Assert
            def request_callback(request):
                self.assertNotEqual(
                    -1, request.http_request.url.find('-secondary'))

            request_callback = request_callback
            await container.get_container_properties(
                raw_request_hook=request_callback,
                raw_response_hook=response_callback)
        finally:
            # Delete will go to primary, so disable the request validation
            await service.delete_container(container_name)
    async def _test_retry_to_secondary_with_put_async(self):
        # Arrange
        container_name = self.get_resource_name()
        retry = ExponentialRetry(retry_to_secondary=True,
                                 initial_backoff=1,
                                 increment_base=2)
        service = self._create_storage_service(
            BlobServiceClient,
            self.settings,
            retry_policy=retry,
            transport=AiohttpTestTransport())

        # Act
        try:
            # Fail the first create attempt
            response_callback = ResponseCallback(
                status=201, new_status=408).override_first_status

            # Assert
            # Confirm that the create request does *not* get retried to secondary
            # This should actually throw InvalidPermissions if sent to secondary,
            # but validate the location_mode anyways.
            def retry_callback(location_mode=None, **kwargs):
                self.assertEqual(LocationMode.PRIMARY, location_mode)

            with self.assertRaises(ResourceExistsError):
                await service.create_container(
                    container_name,
                    raw_response_hook=response_callback,
                    retry_hook=retry_callback)

        finally:
            await service.delete_container(container_name)
    async def _test_retry_with_deserialization_async(self):
        # Arrange
        container_name = self.get_resource_name(prefix='retry')
        retry = ExponentialRetry(initial_backoff=1, increment_base=2)
        service = self._create_storage_service(
            BlobServiceClient,
            self.settings,
            retry_policy=retry,
            transport=AiohttpTestTransport())

        try:
            created = await service.create_container(container_name)

            # Act
            callback = ResponseCallback(status=200,
                                        new_status=408).override_first_status
            containers = service.list_containers(name_starts_with='retry',
                                                 raw_response_hook=callback)

            # Assert
            listed = []
            async for c in containers:
                listed.append(c)
            self.assertTrue(len(listed) >= 1)
        finally:
            await service.delete_container(container_name)
    async def _test_exponential_retry_async(self):
        # Arrange
        container_name = self.get_resource_name()
        retry = ExponentialRetry(initial_backoff=1,
                                 increment_base=3,
                                 retry_total=3)
        service = self._create_storage_service(
            BlobServiceClient,
            self.settings,
            retry_policy=retry,
            transport=AiohttpTestTransport())

        try:
            container = await service.create_container(container_name)

            # Force the create call to 'timeout' with a 408
            callback = ResponseCallback(status=200, new_status=408)

            # Act
            with self.assertRaises(HttpResponseError):
                await container.get_container_properties(
                    raw_response_hook=callback.override_status)

            # Assert the response was called the right number of times (1 initial request + 3 retries)
            self.assertEqual(callback.count, 1 + 3)
        finally:
            # Clean up
            await service.delete_container(container_name)
    def test_exponential_retry_interval_async(self):
        # Arrange
        retry_policy = ExponentialRetry(initial_backoff=1,
                                        increment_base=3,
                                        random_jitter_range=3)
        context_stub = {}

        for i in range(10):
            # Act
            context_stub['count'] = 0
            backoff = retry_policy.get_backoff_time(context_stub)

            # Assert backoff interval is within +/- 3 of 1
            self.assertTrue(0 <= backoff <= 4)

            # Act
            context_stub['count'] = 1
            backoff = retry_policy.get_backoff_time(context_stub)

            # Assert backoff interval is within +/- 3 of 4(1+3^1)
            self.assertTrue(1 <= backoff <= 7)

            # Act
            context_stub['count'] = 2
            backoff = retry_policy.get_backoff_time(context_stub)

            # Assert backoff interval is within +/- 3 of 10(1+3^2)
            self.assertTrue(7 <= backoff <= 13)

            # Act
            context_stub['count'] = 3
            backoff = retry_policy.get_backoff_time(context_stub)

            # Assert backoff interval is within +/- 3 of 28(1+3^3)
            self.assertTrue(25 <= backoff <= 31)
    def setUp(self):
        super(StorageBlobRetryTestAsync, self).setUp()

        url = self._get_account_url()
        credential = self._get_shared_key_credential()
        retry = ExponentialRetry(initial_backoff=1,
                                 increment_base=2,
                                 retry_total=3)

        self.bs = BlobServiceClient(url,
                                    credential=credential,
                                    retry_policy=retry)
        self.container_name = self.get_resource_name('utcontainer')
    async def _test_retry_on_timeout_async(self):
        # Arrange
        container_name = self.get_resource_name()
        retry = ExponentialRetry(initial_backoff=1, increment_base=2)
        service = self._create_storage_service(
            BlobServiceClient,
            self.settings,
            retry_policy=retry,
            transport=AiohttpTestTransport())

        callback = ResponseCallback(status=201, new_status=408).override_status

        # Act
        try:
            # The initial create will return 201, but we overwrite it and retry.
            # The retry will then get a 409 and return false.
            with self.assertRaises(ResourceExistsError):
                await service.create_container(container_name,
                                               raw_response_hook=callback)
        finally:
            await service.delete_container(container_name)
    async def _test_location_lock_async(self):
        # Arrange
        retry = ExponentialRetry(retry_to_secondary=True,
                                 initial_backoff=1,
                                 increment_base=2)
        service = self._create_storage_service(
            BlobServiceClient,
            self.settings,
            retry_policy=retry,
            transport=AiohttpTestTransport())

        # Act
        # Fail the first request and set the retry policy to retry to secondary
        response_callback = ResponseCallback(
            status=200, new_status=408).override_first_status

        #context = _OperationContext(location_lock=True)

        # Assert
        # Confirm that the first request gets retried to secondary
        # The given test account must be GRS
        def retry_callback(retry_count=None, location_mode=None, **kwargs):
            self.assertEqual(LocationMode.SECONDARY, location_mode)

        # Confirm that the second list request done with the same context sticks
        # to the final location of the first list request (aka secondary) despite
        # the client normally trying primary first
        requests = []

        def request_callback(request):
            if not requests:
                requests.append(request)
            else:
                self.assertNotEqual(
                    -1, request.http_request.url.find('-secondary'))

        containers = service.list_containers(results_per_page=1,
                                             retry_hook=retry_callback)
        await containers.__anext__()
        await containers.__anext__()
    async def _test_invalid_retry_async(self):
        # Arrange
        container_name = self.get_resource_name()
        retry = ExponentialRetry(initial_backoff=1, increment_base=2)
        service = self._create_storage_service(
            BlobServiceClient,
            self.settings,
            retry_policy=retry,
            transport=AiohttpTestTransport())

        # Force the create call to fail by pretending it's a teapot
        callback = ResponseCallback(status=201, new_status=418).override_status

        # Act
        try:
            with self.assertRaises(HttpResponseError) as error:
                await service.create_container(container_name,
                                               raw_response_hook=callback)
            self.assertEqual(error.exception.response.status_code, 418)
            self.assertEqual(error.exception.reason, 'Created')
        finally:
            await service.delete_container(container_name)