1# Copyright 2017 Google LLC 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15import datetime 16 17import mock 18import pytest 19 20try: 21 import grpc # noqa: F401 22except ImportError: 23 pytest.skip("No GRPC", allow_module_level=True) 24 25 26from google.api_core import exceptions 27from google.api_core import retry 28from google.api_core import timeout 29import google.api_core.gapic_v1.client_info 30import google.api_core.gapic_v1.method 31import google.api_core.page_iterator 32 33 34def _utcnow_monotonic(): 35 curr_value = datetime.datetime.min 36 delta = datetime.timedelta(seconds=0.5) 37 while True: 38 yield curr_value 39 curr_value += delta 40 41 42def test__determine_timeout(): 43 # Check _determine_timeout always returns a Timeout object. 44 timeout_type_timeout = timeout.ConstantTimeout(600.0) 45 returned_timeout = google.api_core.gapic_v1.method._determine_timeout( 46 600.0, 600.0, None 47 ) 48 assert isinstance(returned_timeout, timeout.ConstantTimeout) 49 returned_timeout = google.api_core.gapic_v1.method._determine_timeout( 50 600.0, timeout_type_timeout, None 51 ) 52 assert isinstance(returned_timeout, timeout.ConstantTimeout) 53 returned_timeout = google.api_core.gapic_v1.method._determine_timeout( 54 timeout_type_timeout, 600.0, None 55 ) 56 assert isinstance(returned_timeout, timeout.ConstantTimeout) 57 returned_timeout = google.api_core.gapic_v1.method._determine_timeout( 58 timeout_type_timeout, timeout_type_timeout, None 59 ) 60 assert isinstance(returned_timeout, timeout.ConstantTimeout) 61 62 63def test_wrap_method_basic(): 64 method = mock.Mock(spec=["__call__"], return_value=42) 65 66 wrapped_method = google.api_core.gapic_v1.method.wrap_method(method) 67 68 result = wrapped_method(1, 2, meep="moop") 69 70 assert result == 42 71 method.assert_called_once_with(1, 2, meep="moop", metadata=mock.ANY) 72 73 # Check that the default client info was specified in the metadata. 74 metadata = method.call_args[1]["metadata"] 75 assert len(metadata) == 1 76 client_info = google.api_core.gapic_v1.client_info.DEFAULT_CLIENT_INFO 77 user_agent_metadata = client_info.to_grpc_metadata() 78 assert user_agent_metadata in metadata 79 80 81def test_wrap_method_with_no_client_info(): 82 method = mock.Mock(spec=["__call__"]) 83 84 wrapped_method = google.api_core.gapic_v1.method.wrap_method( 85 method, client_info=None 86 ) 87 88 wrapped_method(1, 2, meep="moop") 89 90 method.assert_called_once_with(1, 2, meep="moop") 91 92 93def test_wrap_method_with_custom_client_info(): 94 client_info = google.api_core.gapic_v1.client_info.ClientInfo( 95 python_version=1, 96 grpc_version=2, 97 api_core_version=3, 98 gapic_version=4, 99 client_library_version=5, 100 ) 101 method = mock.Mock(spec=["__call__"]) 102 103 wrapped_method = google.api_core.gapic_v1.method.wrap_method( 104 method, client_info=client_info 105 ) 106 107 wrapped_method(1, 2, meep="moop") 108 109 method.assert_called_once_with(1, 2, meep="moop", metadata=mock.ANY) 110 111 # Check that the custom client info was specified in the metadata. 112 metadata = method.call_args[1]["metadata"] 113 assert client_info.to_grpc_metadata() in metadata 114 115 116def test_invoke_wrapped_method_with_metadata(): 117 method = mock.Mock(spec=["__call__"]) 118 119 wrapped_method = google.api_core.gapic_v1.method.wrap_method(method) 120 121 wrapped_method(mock.sentinel.request, metadata=[("a", "b")]) 122 123 method.assert_called_once_with(mock.sentinel.request, metadata=mock.ANY) 124 metadata = method.call_args[1]["metadata"] 125 # Metadata should have two items: the client info metadata and our custom 126 # metadata. 127 assert len(metadata) == 2 128 assert ("a", "b") in metadata 129 130 131def test_invoke_wrapped_method_with_metadata_as_none(): 132 method = mock.Mock(spec=["__call__"]) 133 134 wrapped_method = google.api_core.gapic_v1.method.wrap_method(method) 135 136 wrapped_method(mock.sentinel.request, metadata=None) 137 138 method.assert_called_once_with(mock.sentinel.request, metadata=mock.ANY) 139 metadata = method.call_args[1]["metadata"] 140 # Metadata should have just one items: the client info metadata. 141 assert len(metadata) == 1 142 143 144@mock.patch("time.sleep") 145def test_wrap_method_with_default_retry_and_timeout(unusued_sleep): 146 method = mock.Mock( 147 spec=["__call__"], side_effect=[exceptions.InternalServerError(None), 42] 148 ) 149 default_retry = retry.Retry() 150 default_timeout = timeout.ConstantTimeout(60) 151 wrapped_method = google.api_core.gapic_v1.method.wrap_method( 152 method, default_retry, default_timeout 153 ) 154 155 result = wrapped_method() 156 157 assert result == 42 158 assert method.call_count == 2 159 method.assert_called_with(timeout=60, metadata=mock.ANY) 160 161 162@mock.patch("time.sleep") 163def test_wrap_method_with_default_retry_and_timeout_using_sentinel(unusued_sleep): 164 method = mock.Mock( 165 spec=["__call__"], side_effect=[exceptions.InternalServerError(None), 42] 166 ) 167 default_retry = retry.Retry() 168 default_timeout = timeout.ConstantTimeout(60) 169 wrapped_method = google.api_core.gapic_v1.method.wrap_method( 170 method, default_retry, default_timeout 171 ) 172 173 result = wrapped_method( 174 retry=google.api_core.gapic_v1.method.DEFAULT, 175 timeout=google.api_core.gapic_v1.method.DEFAULT, 176 ) 177 178 assert result == 42 179 assert method.call_count == 2 180 method.assert_called_with(timeout=60, metadata=mock.ANY) 181 182 183@mock.patch("time.sleep") 184def test_wrap_method_with_overriding_retry_and_timeout(unusued_sleep): 185 method = mock.Mock(spec=["__call__"], side_effect=[exceptions.NotFound(None), 42]) 186 default_retry = retry.Retry() 187 default_timeout = timeout.ConstantTimeout(60) 188 wrapped_method = google.api_core.gapic_v1.method.wrap_method( 189 method, default_retry, default_timeout 190 ) 191 192 result = wrapped_method( 193 retry=retry.Retry(retry.if_exception_type(exceptions.NotFound)), 194 timeout=timeout.ConstantTimeout(22), 195 ) 196 197 assert result == 42 198 assert method.call_count == 2 199 method.assert_called_with(timeout=22, metadata=mock.ANY) 200 201 202@mock.patch("time.sleep") 203@mock.patch( 204 "google.api_core.datetime_helpers.utcnow", 205 side_effect=_utcnow_monotonic(), 206 autospec=True, 207) 208def test_wrap_method_with_overriding_retry_deadline(utcnow, unused_sleep): 209 method = mock.Mock( 210 spec=["__call__"], 211 side_effect=([exceptions.InternalServerError(None)] * 4) + [42], 212 ) 213 default_retry = retry.Retry() 214 default_timeout = timeout.ExponentialTimeout(deadline=60) 215 wrapped_method = google.api_core.gapic_v1.method.wrap_method( 216 method, default_retry, default_timeout 217 ) 218 219 # Overriding only the retry's deadline should also override the timeout's 220 # deadline. 221 result = wrapped_method(retry=default_retry.with_deadline(30)) 222 223 assert result == 42 224 timeout_args = [call[1]["timeout"] for call in method.call_args_list] 225 assert timeout_args == [5.0, 10.0, 20.0, 26.0, 25.0] 226 assert utcnow.call_count == ( 227 1 228 + 5 # First to set the deadline. 229 + 5 # One for each min(timeout, maximum, (DEADLINE - NOW).seconds) 230 ) 231 232 233def test_wrap_method_with_overriding_timeout_as_a_number(): 234 method = mock.Mock(spec=["__call__"], return_value=42) 235 default_retry = retry.Retry() 236 default_timeout = timeout.ConstantTimeout(60) 237 wrapped_method = google.api_core.gapic_v1.method.wrap_method( 238 method, default_retry, default_timeout 239 ) 240 241 result = wrapped_method(timeout=22) 242 243 assert result == 42 244 method.assert_called_once_with(timeout=22, metadata=mock.ANY) 245