diff options
author | Jon Wayne Parrott <jonwayne@google.com> | 2017-10-18 12:52:35 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-10-18 12:52:35 -0700 |
commit | 77fb0f23da16f9d59062b69daf13b50c9dc0d5ff (patch) | |
tree | 3643765a2c1a0767e3a204cbfb16a08b23ef5026 /tests/unit/gapic | |
download | python-api-core-77fb0f23da16f9d59062b69daf13b50c9dc0d5ff.tar.gz |
Add api_core package (#4210)
* Add api_core package
* Address review comments
Diffstat (limited to 'tests/unit/gapic')
-rw-r--r-- | tests/unit/gapic/test_config.py | 89 | ||||
-rw-r--r-- | tests/unit/gapic/test_method.py | 226 |
2 files changed, 315 insertions, 0 deletions
diff --git a/tests/unit/gapic/test_config.py b/tests/unit/gapic/test_config.py new file mode 100644 index 0000000..75a6e1c --- /dev/null +++ b/tests/unit/gapic/test_config.py @@ -0,0 +1,89 @@ +# Copyright 2017 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from google.api_core import exceptions +from google.api_core.gapic_v1 import config + + +INTERFACE_CONFIG = { + 'retry_codes': { + 'idempotent': ['DEADLINE_EXCEEDED', 'UNAVAILABLE'], + 'other': ['FAILED_PRECONDITION'], + 'non_idempotent': [] + }, + 'retry_params': { + 'default': { + 'initial_retry_delay_millis': 1000, + 'retry_delay_multiplier': 2.5, + 'max_retry_delay_millis': 120000, + 'initial_rpc_timeout_millis': 120000, + 'rpc_timeout_multiplier': 1.0, + 'max_rpc_timeout_millis': 120000, + 'total_timeout_millis': 600000 + }, + 'other': { + 'initial_retry_delay_millis': 1000, + 'retry_delay_multiplier': 1, + 'max_retry_delay_millis': 1000, + 'initial_rpc_timeout_millis': 1000, + 'rpc_timeout_multiplier': 1, + 'max_rpc_timeout_millis': 1000, + 'total_timeout_millis': 1000 + }, + }, + 'methods': { + 'AnnotateVideo': { + 'timeout_millis': 60000, + 'retry_codes_name': 'idempotent', + 'retry_params_name': 'default' + }, + 'Other': { + 'timeout_millis': 60000, + 'retry_codes_name': 'other', + 'retry_params_name': 'other' + }, + 'Plain': { + 'timeout_millis': 30000 + } + } +} + + +def test_create_method_configs(): + method_configs = config.parse_method_configs(INTERFACE_CONFIG) + + retry, timeout = method_configs['AnnotateVideo'] + assert retry._predicate(exceptions.DeadlineExceeded(None)) + assert retry._predicate(exceptions.ServiceUnavailable(None)) + assert retry._initial == 1.0 + assert retry._multiplier == 2.5 + assert retry._maximum == 120.0 + assert retry._deadline == 600.0 + assert timeout._initial == 120.0 + assert timeout._multiplier == 1.0 + assert timeout._maximum == 120.0 + + retry, timeout = method_configs['Other'] + assert retry._predicate(exceptions.FailedPrecondition(None)) + assert retry._initial == 1.0 + assert retry._multiplier == 1.0 + assert retry._maximum == 1.0 + assert retry._deadline == 1.0 + assert timeout._initial == 1.0 + assert timeout._multiplier == 1.0 + assert timeout._maximum == 1.0 + + retry, timeout = method_configs['Plain'] + assert retry is None + assert timeout._timeout == 30.0 diff --git a/tests/unit/gapic/test_method.py b/tests/unit/gapic/test_method.py new file mode 100644 index 0000000..35ac144 --- /dev/null +++ b/tests/unit/gapic/test_method.py @@ -0,0 +1,226 @@ +# Copyright 2017 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import datetime + +import mock + +from google.api_core import exceptions +from google.api_core import retry +from google.api_core import timeout +import google.api_core.gapic_v1.method +import google.api_core.page_iterator + + +def _utcnow_monotonic(): + curr_value = datetime.datetime.min + delta = datetime.timedelta(seconds=0.5) + while True: + yield curr_value + curr_value += delta + + +def test_wrap_method_basic(): + method = mock.Mock(spec=['__call__'], return_value=42) + + wrapped_method = google.api_core.gapic_v1.method.wrap_method( + method, metadata=None) + + result = wrapped_method(1, 2, meep='moop') + + assert result == 42 + method.assert_called_once_with(1, 2, meep='moop') + + +def test_wrap_method_with_default_metadata(): + method = mock.Mock(spec=['__call__']) + + wrapped_method = google.api_core.gapic_v1.method.wrap_method(method) + + wrapped_method(1, 2, meep='moop') + + method.assert_called_once_with(1, 2, meep='moop', metadata=mock.ANY) + + metadata = method.call_args[1]['metadata'] + assert len(metadata) == 1 + assert metadata[0][0] == 'x-goog-api-client' + assert 'api-core' in metadata[0][1] + + +def test_wrap_method_with_custom_metadata(): + method = mock.Mock(spec=['__call__']) + + wrapped_method = google.api_core.gapic_v1.method.wrap_method( + method, metadata={'foo': 'bar'}) + + wrapped_method(1, 2, meep='moop') + + method.assert_called_once_with(1, 2, meep='moop', metadata=mock.ANY) + + metadata = method.call_args[1]['metadata'] + assert len(metadata) == 2 + assert ('foo', 'bar') in metadata + + +def test_wrap_method_with_merged_metadata(): + method = mock.Mock(spec=['__call__']) + + wrapped_method = google.api_core.gapic_v1.method.wrap_method( + method, metadata={'x-goog-api-client': 'foo/1.2.3'}) + + wrapped_method(1, 2, meep='moop') + + method.assert_called_once_with(1, 2, meep='moop', metadata=mock.ANY) + + metadata = method.call_args[1]['metadata'] + assert len(metadata) == 1 + assert metadata[0][0] == 'x-goog-api-client' + assert metadata[0][1].endswith(' foo/1.2.3') + + +@mock.patch('time.sleep') +def test_wrap_method_with_default_retry_and_timeout(unusued_sleep): + method = mock.Mock( + spec=['__call__'], + side_effect=[exceptions.InternalServerError(None), 42] + ) + default_retry = retry.Retry() + default_timeout = timeout.ConstantTimeout(60) + wrapped_method = google.api_core.gapic_v1.method.wrap_method( + method, default_retry, default_timeout) + + result = wrapped_method() + + assert result == 42 + assert method.call_count == 2 + method.assert_called_with(timeout=60, metadata=mock.ANY) + + +@mock.patch('time.sleep') +def test_wrap_method_with_default_retry_and_timeout_using_sentinel( + unusued_sleep): + method = mock.Mock( + spec=['__call__'], + side_effect=[exceptions.InternalServerError(None), 42] + ) + default_retry = retry.Retry() + default_timeout = timeout.ConstantTimeout(60) + wrapped_method = google.api_core.gapic_v1.method.wrap_method( + method, default_retry, default_timeout) + + result = wrapped_method( + retry=google.api_core.gapic_v1.method.DEFAULT, + timeout=google.api_core.gapic_v1.method.DEFAULT) + + assert result == 42 + assert method.call_count == 2 + method.assert_called_with(timeout=60, metadata=mock.ANY) + + +@mock.patch('time.sleep') +def test_wrap_method_with_overriding_retry_and_timeout(unusued_sleep): + method = mock.Mock( + spec=['__call__'], + side_effect=[exceptions.NotFound(None), 42] + ) + default_retry = retry.Retry() + default_timeout = timeout.ConstantTimeout(60) + wrapped_method = google.api_core.gapic_v1.method.wrap_method( + method, default_retry, default_timeout) + + result = wrapped_method( + retry=retry.Retry(retry.if_exception_type(exceptions.NotFound)), + timeout=timeout.ConstantTimeout(22)) + + assert result == 42 + assert method.call_count == 2 + method.assert_called_with(timeout=22, metadata=mock.ANY) + + +@mock.patch('time.sleep') +@mock.patch( + 'google.api_core.datetime_helpers.utcnow', + side_effect=_utcnow_monotonic(), + autospec=True) +def test_wrap_method_with_overriding_retry_deadline(utcnow, unused_sleep): + method = mock.Mock( + spec=['__call__'], + side_effect=([exceptions.InternalServerError(None)] * 4) + [42] + ) + default_retry = retry.Retry() + default_timeout = timeout.ExponentialTimeout(deadline=60) + wrapped_method = google.api_core.gapic_v1.method.wrap_method( + method, default_retry, default_timeout) + + # Overriding only the retry's deadline should also override the timeout's + # deadline. + result = wrapped_method( + retry=default_retry.with_deadline(30)) + + assert result == 42 + timeout_args = [call[1]['timeout'] for call in method.call_args_list] + assert timeout_args == [5.0, 10.0, 20.0, 26.0, 25.0] + assert utcnow.call_count == ( + 1 + # First to set the deadline. + 5 + # One for each min(timeout, maximum, (DEADLINE - NOW).seconds) + 5 + ) + + +def test_wrap_method_with_overriding_timeout_as_a_number(): + method = mock.Mock(spec=['__call__'], return_value=42) + default_retry = retry.Retry() + default_timeout = timeout.ConstantTimeout(60) + wrapped_method = google.api_core.gapic_v1.method.wrap_method( + method, default_retry, default_timeout) + + result = wrapped_method(timeout=22) + + assert result == 42 + method.assert_called_once_with(timeout=22, metadata=mock.ANY) + + +def test_wrap_with_paging(): + page_one = mock.Mock( + spec=['items', 'page_token', 'next_page_token'], + items=[1, 2], + next_page_token='icanhasnextpls') + page_two = mock.Mock( + spec=['items', 'page_token', 'next_page_token'], + items=[3, 4], + next_page_token=None) + method = mock.Mock( + spec=['__call__', '__name__'], side_effect=(page_one, page_two)) + method.__name__ = 'mockmethod' + + wrapped_method = google.api_core.gapic_v1.method.wrap_with_paging( + method, 'items', 'page_token', 'next_page_token') + + request = mock.Mock(spec=['page_token'], page_token=None) + result = wrapped_method(request, extra='param') + + # Should return an iterator and should not have actually called the + # method yet. + assert isinstance(result, google.api_core.page_iterator.Iterator) + method.assert_not_called() + assert request.page_token is None + + # Draining the iterator should call the method until no more pages are + # returned. + results = list(result) + + assert results == [1, 2, 3, 4] + assert method.call_count == 2 + method.assert_called_with(request, extra='param') + assert request.page_token == 'icanhasnextpls' |