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 mock 16import pytest 17 18try: 19 import grpc 20except ImportError: 21 pytest.skip("No GRPC", allow_module_level=True) 22 23from google.api_core import exceptions 24from google.api_core import grpc_helpers 25import google.auth.credentials 26from google.longrunning import operations_pb2 27 28 29def test__patch_callable_name(): 30 callable = mock.Mock(spec=["__class__"]) 31 callable.__class__ = mock.Mock(spec=["__name__"]) 32 callable.__class__.__name__ = "TestCallable" 33 34 grpc_helpers._patch_callable_name(callable) 35 36 assert callable.__name__ == "TestCallable" 37 38 39def test__patch_callable_name_no_op(): 40 callable = mock.Mock(spec=["__name__"]) 41 callable.__name__ = "test_callable" 42 43 grpc_helpers._patch_callable_name(callable) 44 45 assert callable.__name__ == "test_callable" 46 47 48class RpcErrorImpl(grpc.RpcError, grpc.Call): 49 def __init__(self, code): 50 super(RpcErrorImpl, self).__init__() 51 self._code = code 52 53 def code(self): 54 return self._code 55 56 def details(self): 57 return None 58 59 def trailing_metadata(self): 60 return None 61 62 63def test_wrap_unary_errors(): 64 grpc_error = RpcErrorImpl(grpc.StatusCode.INVALID_ARGUMENT) 65 callable_ = mock.Mock(spec=["__call__"], side_effect=grpc_error) 66 67 wrapped_callable = grpc_helpers._wrap_unary_errors(callable_) 68 69 with pytest.raises(exceptions.InvalidArgument) as exc_info: 70 wrapped_callable(1, 2, three="four") 71 72 callable_.assert_called_once_with(1, 2, three="four") 73 assert exc_info.value.response == grpc_error 74 75 76class Test_StreamingResponseIterator: 77 @staticmethod 78 def _make_wrapped(*items): 79 return iter(items) 80 81 @staticmethod 82 def _make_one(wrapped, **kw): 83 return grpc_helpers._StreamingResponseIterator(wrapped, **kw) 84 85 def test_ctor_defaults(self): 86 wrapped = self._make_wrapped("a", "b", "c") 87 iterator = self._make_one(wrapped) 88 assert iterator._stored_first_result == "a" 89 assert list(wrapped) == ["b", "c"] 90 91 def test_ctor_explicit(self): 92 wrapped = self._make_wrapped("a", "b", "c") 93 iterator = self._make_one(wrapped, prefetch_first_result=False) 94 assert getattr(iterator, "_stored_first_result", self) is self 95 assert list(wrapped) == ["a", "b", "c"] 96 97 def test_ctor_w_rpc_error_on_prefetch(self): 98 wrapped = mock.MagicMock() 99 wrapped.__next__.side_effect = grpc.RpcError() 100 101 with pytest.raises(grpc.RpcError): 102 self._make_one(wrapped) 103 104 def test___iter__(self): 105 wrapped = self._make_wrapped("a", "b", "c") 106 iterator = self._make_one(wrapped) 107 assert iter(iterator) is iterator 108 109 def test___next___w_cached_first_result(self): 110 wrapped = self._make_wrapped("a", "b", "c") 111 iterator = self._make_one(wrapped) 112 assert next(iterator) == "a" 113 iterator = self._make_one(wrapped, prefetch_first_result=False) 114 assert next(iterator) == "b" 115 assert next(iterator) == "c" 116 117 def test___next___wo_cached_first_result(self): 118 wrapped = self._make_wrapped("a", "b", "c") 119 iterator = self._make_one(wrapped, prefetch_first_result=False) 120 assert next(iterator) == "a" 121 assert next(iterator) == "b" 122 assert next(iterator) == "c" 123 124 def test___next___w_rpc_error(self): 125 wrapped = mock.MagicMock() 126 wrapped.__next__.side_effect = grpc.RpcError() 127 iterator = self._make_one(wrapped, prefetch_first_result=False) 128 129 with pytest.raises(exceptions.GoogleAPICallError): 130 next(iterator) 131 132 def test_add_callback(self): 133 wrapped = mock.MagicMock() 134 callback = mock.Mock(spec={}) 135 iterator = self._make_one(wrapped, prefetch_first_result=False) 136 137 assert iterator.add_callback(callback) is wrapped.add_callback.return_value 138 139 wrapped.add_callback.assert_called_once_with(callback) 140 141 def test_cancel(self): 142 wrapped = mock.MagicMock() 143 iterator = self._make_one(wrapped, prefetch_first_result=False) 144 145 assert iterator.cancel() is wrapped.cancel.return_value 146 147 wrapped.cancel.assert_called_once_with() 148 149 def test_code(self): 150 wrapped = mock.MagicMock() 151 iterator = self._make_one(wrapped, prefetch_first_result=False) 152 153 assert iterator.code() is wrapped.code.return_value 154 155 wrapped.code.assert_called_once_with() 156 157 def test_details(self): 158 wrapped = mock.MagicMock() 159 iterator = self._make_one(wrapped, prefetch_first_result=False) 160 161 assert iterator.details() is wrapped.details.return_value 162 163 wrapped.details.assert_called_once_with() 164 165 def test_initial_metadata(self): 166 wrapped = mock.MagicMock() 167 iterator = self._make_one(wrapped, prefetch_first_result=False) 168 169 assert iterator.initial_metadata() is wrapped.initial_metadata.return_value 170 171 wrapped.initial_metadata.assert_called_once_with() 172 173 def test_is_active(self): 174 wrapped = mock.MagicMock() 175 iterator = self._make_one(wrapped, prefetch_first_result=False) 176 177 assert iterator.is_active() is wrapped.is_active.return_value 178 179 wrapped.is_active.assert_called_once_with() 180 181 def test_time_remaining(self): 182 wrapped = mock.MagicMock() 183 iterator = self._make_one(wrapped, prefetch_first_result=False) 184 185 assert iterator.time_remaining() is wrapped.time_remaining.return_value 186 187 wrapped.time_remaining.assert_called_once_with() 188 189 def test_trailing_metadata(self): 190 wrapped = mock.MagicMock() 191 iterator = self._make_one(wrapped, prefetch_first_result=False) 192 193 assert iterator.trailing_metadata() is wrapped.trailing_metadata.return_value 194 195 wrapped.trailing_metadata.assert_called_once_with() 196 197 198def test_wrap_stream_okay(): 199 expected_responses = [1, 2, 3] 200 callable_ = mock.Mock(spec=["__call__"], return_value=iter(expected_responses)) 201 202 wrapped_callable = grpc_helpers._wrap_stream_errors(callable_) 203 204 got_iterator = wrapped_callable(1, 2, three="four") 205 206 responses = list(got_iterator) 207 208 callable_.assert_called_once_with(1, 2, three="four") 209 assert responses == expected_responses 210 211 212def test_wrap_stream_prefetch_disabled(): 213 responses = [1, 2, 3] 214 iter_responses = iter(responses) 215 callable_ = mock.Mock(spec=["__call__"], return_value=iter_responses) 216 callable_._prefetch_first_result_ = False 217 218 wrapped_callable = grpc_helpers._wrap_stream_errors(callable_) 219 wrapped_callable(1, 2, three="four") 220 221 assert list(iter_responses) == responses # no items should have been pre-fetched 222 callable_.assert_called_once_with(1, 2, three="four") 223 224 225def test_wrap_stream_iterable_iterface(): 226 response_iter = mock.create_autospec(grpc.Call, instance=True) 227 callable_ = mock.Mock(spec=["__call__"], return_value=response_iter) 228 229 wrapped_callable = grpc_helpers._wrap_stream_errors(callable_) 230 231 got_iterator = wrapped_callable() 232 233 callable_.assert_called_once_with() 234 235 # Check each aliased method in the grpc.Call interface 236 got_iterator.add_callback(mock.sentinel.callback) 237 response_iter.add_callback.assert_called_once_with(mock.sentinel.callback) 238 239 got_iterator.cancel() 240 response_iter.cancel.assert_called_once_with() 241 242 got_iterator.code() 243 response_iter.code.assert_called_once_with() 244 245 got_iterator.details() 246 response_iter.details.assert_called_once_with() 247 248 got_iterator.initial_metadata() 249 response_iter.initial_metadata.assert_called_once_with() 250 251 got_iterator.is_active() 252 response_iter.is_active.assert_called_once_with() 253 254 got_iterator.time_remaining() 255 response_iter.time_remaining.assert_called_once_with() 256 257 got_iterator.trailing_metadata() 258 response_iter.trailing_metadata.assert_called_once_with() 259 260 261def test_wrap_stream_errors_invocation(): 262 grpc_error = RpcErrorImpl(grpc.StatusCode.INVALID_ARGUMENT) 263 callable_ = mock.Mock(spec=["__call__"], side_effect=grpc_error) 264 265 wrapped_callable = grpc_helpers._wrap_stream_errors(callable_) 266 267 with pytest.raises(exceptions.InvalidArgument) as exc_info: 268 wrapped_callable(1, 2, three="four") 269 270 callable_.assert_called_once_with(1, 2, three="four") 271 assert exc_info.value.response == grpc_error 272 273 274def test_wrap_stream_empty_iterator(): 275 expected_responses = [] 276 callable_ = mock.Mock(spec=["__call__"], return_value=iter(expected_responses)) 277 278 wrapped_callable = grpc_helpers._wrap_stream_errors(callable_) 279 280 got_iterator = wrapped_callable() 281 282 responses = list(got_iterator) 283 284 callable_.assert_called_once_with() 285 assert responses == expected_responses 286 287 288class RpcResponseIteratorImpl(object): 289 def __init__(self, iterable): 290 self._iterable = iter(iterable) 291 292 def next(self): 293 next_item = next(self._iterable) 294 if isinstance(next_item, RpcErrorImpl): 295 raise next_item 296 return next_item 297 298 __next__ = next 299 300 301def test_wrap_stream_errors_iterator_initialization(): 302 grpc_error = RpcErrorImpl(grpc.StatusCode.UNAVAILABLE) 303 response_iter = RpcResponseIteratorImpl([grpc_error]) 304 callable_ = mock.Mock(spec=["__call__"], return_value=response_iter) 305 306 wrapped_callable = grpc_helpers._wrap_stream_errors(callable_) 307 308 with pytest.raises(exceptions.ServiceUnavailable) as exc_info: 309 wrapped_callable(1, 2, three="four") 310 311 callable_.assert_called_once_with(1, 2, three="four") 312 assert exc_info.value.response == grpc_error 313 314 315def test_wrap_stream_errors_during_iteration(): 316 grpc_error = RpcErrorImpl(grpc.StatusCode.UNAVAILABLE) 317 response_iter = RpcResponseIteratorImpl([1, grpc_error]) 318 callable_ = mock.Mock(spec=["__call__"], return_value=response_iter) 319 320 wrapped_callable = grpc_helpers._wrap_stream_errors(callable_) 321 got_iterator = wrapped_callable(1, 2, three="four") 322 next(got_iterator) 323 324 with pytest.raises(exceptions.ServiceUnavailable) as exc_info: 325 next(got_iterator) 326 327 callable_.assert_called_once_with(1, 2, three="four") 328 assert exc_info.value.response == grpc_error 329 330 331@mock.patch("google.api_core.grpc_helpers._wrap_unary_errors") 332def test_wrap_errors_non_streaming(wrap_unary_errors): 333 callable_ = mock.create_autospec(grpc.UnaryUnaryMultiCallable) 334 335 result = grpc_helpers.wrap_errors(callable_) 336 337 assert result == wrap_unary_errors.return_value 338 wrap_unary_errors.assert_called_once_with(callable_) 339 340 341@mock.patch("google.api_core.grpc_helpers._wrap_stream_errors") 342def test_wrap_errors_streaming(wrap_stream_errors): 343 callable_ = mock.create_autospec(grpc.UnaryStreamMultiCallable) 344 345 result = grpc_helpers.wrap_errors(callable_) 346 347 assert result == wrap_stream_errors.return_value 348 wrap_stream_errors.assert_called_once_with(callable_) 349 350 351@mock.patch("grpc.composite_channel_credentials") 352@mock.patch( 353 "google.auth.default", 354 autospec=True, 355 return_value=(mock.sentinel.credentials, mock.sentinel.projet), 356) 357@mock.patch("grpc.secure_channel") 358def test_create_channel_implicit(grpc_secure_channel, default, composite_creds_call): 359 target = "example.com:443" 360 composite_creds = composite_creds_call.return_value 361 362 channel = grpc_helpers.create_channel(target) 363 364 assert channel is grpc_secure_channel.return_value 365 366 default.assert_called_once_with(scopes=None, default_scopes=None) 367 368 if grpc_helpers.HAS_GRPC_GCP: 369 grpc_secure_channel.assert_called_once_with(target, composite_creds, None) 370 else: 371 grpc_secure_channel.assert_called_once_with(target, composite_creds) 372 373 374@mock.patch("google.auth.transport.grpc.AuthMetadataPlugin", autospec=True) 375@mock.patch( 376 "google.auth.transport.requests.Request", 377 autospec=True, 378 return_value=mock.sentinel.Request, 379) 380@mock.patch("grpc.composite_channel_credentials") 381@mock.patch( 382 "google.auth.default", 383 autospec=True, 384 return_value=(mock.sentinel.credentials, mock.sentinel.project), 385) 386@mock.patch("grpc.secure_channel") 387def test_create_channel_implicit_with_default_host( 388 grpc_secure_channel, default, composite_creds_call, request, auth_metadata_plugin 389): 390 target = "example.com:443" 391 default_host = "example.com" 392 composite_creds = composite_creds_call.return_value 393 394 channel = grpc_helpers.create_channel(target, default_host=default_host) 395 396 assert channel is grpc_secure_channel.return_value 397 398 default.assert_called_once_with(scopes=None, default_scopes=None) 399 auth_metadata_plugin.assert_called_once_with( 400 mock.sentinel.credentials, mock.sentinel.Request, default_host=default_host 401 ) 402 403 if grpc_helpers.HAS_GRPC_GCP: 404 grpc_secure_channel.assert_called_once_with(target, composite_creds, None) 405 else: 406 grpc_secure_channel.assert_called_once_with(target, composite_creds) 407 408 409@mock.patch("grpc.composite_channel_credentials") 410@mock.patch( 411 "google.auth.default", 412 autospec=True, 413 return_value=(mock.sentinel.credentials, mock.sentinel.projet), 414) 415@mock.patch("grpc.secure_channel") 416def test_create_channel_implicit_with_ssl_creds( 417 grpc_secure_channel, default, composite_creds_call 418): 419 target = "example.com:443" 420 421 ssl_creds = grpc.ssl_channel_credentials() 422 423 grpc_helpers.create_channel(target, ssl_credentials=ssl_creds) 424 425 default.assert_called_once_with(scopes=None, default_scopes=None) 426 427 composite_creds_call.assert_called_once_with(ssl_creds, mock.ANY) 428 composite_creds = composite_creds_call.return_value 429 if grpc_helpers.HAS_GRPC_GCP: 430 grpc_secure_channel.assert_called_once_with(target, composite_creds, None) 431 else: 432 grpc_secure_channel.assert_called_once_with(target, composite_creds) 433 434 435@mock.patch("grpc.composite_channel_credentials") 436@mock.patch( 437 "google.auth.default", 438 autospec=True, 439 return_value=(mock.sentinel.credentials, mock.sentinel.projet), 440) 441@mock.patch("grpc.secure_channel") 442def test_create_channel_implicit_with_scopes( 443 grpc_secure_channel, default, composite_creds_call 444): 445 target = "example.com:443" 446 composite_creds = composite_creds_call.return_value 447 448 channel = grpc_helpers.create_channel(target, scopes=["one", "two"]) 449 450 assert channel is grpc_secure_channel.return_value 451 452 default.assert_called_once_with(scopes=["one", "two"], default_scopes=None) 453 454 if grpc_helpers.HAS_GRPC_GCP: 455 grpc_secure_channel.assert_called_once_with(target, composite_creds, None) 456 else: 457 grpc_secure_channel.assert_called_once_with(target, composite_creds) 458 459 460@mock.patch("grpc.composite_channel_credentials") 461@mock.patch( 462 "google.auth.default", 463 autospec=True, 464 return_value=(mock.sentinel.credentials, mock.sentinel.projet), 465) 466@mock.patch("grpc.secure_channel") 467def test_create_channel_implicit_with_default_scopes( 468 grpc_secure_channel, default, composite_creds_call 469): 470 target = "example.com:443" 471 composite_creds = composite_creds_call.return_value 472 473 channel = grpc_helpers.create_channel(target, default_scopes=["three", "four"]) 474 475 assert channel is grpc_secure_channel.return_value 476 477 default.assert_called_once_with(scopes=None, default_scopes=["three", "four"]) 478 479 if grpc_helpers.HAS_GRPC_GCP: 480 grpc_secure_channel.assert_called_once_with(target, composite_creds, None) 481 else: 482 grpc_secure_channel.assert_called_once_with(target, composite_creds) 483 484 485def test_create_channel_explicit_with_duplicate_credentials(): 486 target = "example.com:443" 487 488 with pytest.raises(exceptions.DuplicateCredentialArgs): 489 grpc_helpers.create_channel( 490 target, 491 credentials_file="credentials.json", 492 credentials=mock.sentinel.credentials, 493 ) 494 495 496@mock.patch("grpc.composite_channel_credentials") 497@mock.patch("google.auth.credentials.with_scopes_if_required", autospec=True) 498@mock.patch("grpc.secure_channel") 499def test_create_channel_explicit(grpc_secure_channel, auth_creds, composite_creds_call): 500 target = "example.com:443" 501 composite_creds = composite_creds_call.return_value 502 503 channel = grpc_helpers.create_channel(target, credentials=mock.sentinel.credentials) 504 505 auth_creds.assert_called_once_with( 506 mock.sentinel.credentials, scopes=None, default_scopes=None 507 ) 508 509 assert channel is grpc_secure_channel.return_value 510 if grpc_helpers.HAS_GRPC_GCP: 511 grpc_secure_channel.assert_called_once_with(target, composite_creds, None) 512 else: 513 grpc_secure_channel.assert_called_once_with(target, composite_creds) 514 515 516@mock.patch("grpc.composite_channel_credentials") 517@mock.patch("grpc.secure_channel") 518def test_create_channel_explicit_scoped(grpc_secure_channel, composite_creds_call): 519 target = "example.com:443" 520 scopes = ["1", "2"] 521 composite_creds = composite_creds_call.return_value 522 523 credentials = mock.create_autospec(google.auth.credentials.Scoped, instance=True) 524 credentials.requires_scopes = True 525 526 channel = grpc_helpers.create_channel( 527 target, credentials=credentials, scopes=scopes 528 ) 529 530 credentials.with_scopes.assert_called_once_with(scopes, default_scopes=None) 531 532 assert channel is grpc_secure_channel.return_value 533 if grpc_helpers.HAS_GRPC_GCP: 534 grpc_secure_channel.assert_called_once_with(target, composite_creds, None) 535 else: 536 grpc_secure_channel.assert_called_once_with(target, composite_creds) 537 538 539@mock.patch("grpc.composite_channel_credentials") 540@mock.patch("grpc.secure_channel") 541def test_create_channel_explicit_default_scopes( 542 grpc_secure_channel, composite_creds_call 543): 544 target = "example.com:443" 545 default_scopes = ["3", "4"] 546 composite_creds = composite_creds_call.return_value 547 548 credentials = mock.create_autospec(google.auth.credentials.Scoped, instance=True) 549 credentials.requires_scopes = True 550 551 channel = grpc_helpers.create_channel( 552 target, credentials=credentials, default_scopes=default_scopes 553 ) 554 555 credentials.with_scopes.assert_called_once_with( 556 scopes=None, default_scopes=default_scopes 557 ) 558 559 assert channel is grpc_secure_channel.return_value 560 if grpc_helpers.HAS_GRPC_GCP: 561 grpc_secure_channel.assert_called_once_with(target, composite_creds, None) 562 else: 563 grpc_secure_channel.assert_called_once_with(target, composite_creds) 564 565 566@mock.patch("grpc.composite_channel_credentials") 567@mock.patch("grpc.secure_channel") 568def test_create_channel_explicit_with_quota_project( 569 grpc_secure_channel, composite_creds_call 570): 571 target = "example.com:443" 572 composite_creds = composite_creds_call.return_value 573 574 credentials = mock.create_autospec( 575 google.auth.credentials.CredentialsWithQuotaProject, instance=True 576 ) 577 578 channel = grpc_helpers.create_channel( 579 target, credentials=credentials, quota_project_id="project-foo" 580 ) 581 582 credentials.with_quota_project.assert_called_once_with("project-foo") 583 584 assert channel is grpc_secure_channel.return_value 585 if grpc_helpers.HAS_GRPC_GCP: 586 grpc_secure_channel.assert_called_once_with(target, composite_creds, None) 587 else: 588 grpc_secure_channel.assert_called_once_with(target, composite_creds) 589 590 591@mock.patch("grpc.composite_channel_credentials") 592@mock.patch("grpc.secure_channel") 593@mock.patch( 594 "google.auth.load_credentials_from_file", 595 autospec=True, 596 return_value=(mock.sentinel.credentials, mock.sentinel.project), 597) 598def test_create_channel_with_credentials_file( 599 load_credentials_from_file, grpc_secure_channel, composite_creds_call 600): 601 target = "example.com:443" 602 603 credentials_file = "/path/to/credentials/file.json" 604 composite_creds = composite_creds_call.return_value 605 606 channel = grpc_helpers.create_channel(target, credentials_file=credentials_file) 607 608 google.auth.load_credentials_from_file.assert_called_once_with( 609 credentials_file, scopes=None, default_scopes=None 610 ) 611 612 assert channel is grpc_secure_channel.return_value 613 if grpc_helpers.HAS_GRPC_GCP: 614 grpc_secure_channel.assert_called_once_with(target, composite_creds, None) 615 else: 616 grpc_secure_channel.assert_called_once_with(target, composite_creds) 617 618 619@mock.patch("grpc.composite_channel_credentials") 620@mock.patch("grpc.secure_channel") 621@mock.patch( 622 "google.auth.load_credentials_from_file", 623 autospec=True, 624 return_value=(mock.sentinel.credentials, mock.sentinel.project), 625) 626def test_create_channel_with_credentials_file_and_scopes( 627 load_credentials_from_file, grpc_secure_channel, composite_creds_call 628): 629 target = "example.com:443" 630 scopes = ["1", "2"] 631 632 credentials_file = "/path/to/credentials/file.json" 633 composite_creds = composite_creds_call.return_value 634 635 channel = grpc_helpers.create_channel( 636 target, credentials_file=credentials_file, scopes=scopes 637 ) 638 639 google.auth.load_credentials_from_file.assert_called_once_with( 640 credentials_file, scopes=scopes, default_scopes=None 641 ) 642 643 assert channel is grpc_secure_channel.return_value 644 if grpc_helpers.HAS_GRPC_GCP: 645 grpc_secure_channel.assert_called_once_with(target, composite_creds, None) 646 else: 647 grpc_secure_channel.assert_called_once_with(target, composite_creds) 648 649 650@mock.patch("grpc.composite_channel_credentials") 651@mock.patch("grpc.secure_channel") 652@mock.patch( 653 "google.auth.load_credentials_from_file", 654 autospec=True, 655 return_value=(mock.sentinel.credentials, mock.sentinel.project), 656) 657def test_create_channel_with_credentials_file_and_default_scopes( 658 load_credentials_from_file, grpc_secure_channel, composite_creds_call 659): 660 target = "example.com:443" 661 default_scopes = ["3", "4"] 662 663 credentials_file = "/path/to/credentials/file.json" 664 composite_creds = composite_creds_call.return_value 665 666 channel = grpc_helpers.create_channel( 667 target, credentials_file=credentials_file, default_scopes=default_scopes 668 ) 669 670 load_credentials_from_file.assert_called_once_with( 671 credentials_file, scopes=None, default_scopes=default_scopes 672 ) 673 674 assert channel is grpc_secure_channel.return_value 675 if grpc_helpers.HAS_GRPC_GCP: 676 grpc_secure_channel.assert_called_once_with(target, composite_creds, None) 677 else: 678 grpc_secure_channel.assert_called_once_with(target, composite_creds) 679 680 681@pytest.mark.skipif( 682 not grpc_helpers.HAS_GRPC_GCP, reason="grpc_gcp module not available" 683) 684@mock.patch("grpc_gcp.secure_channel") 685def test_create_channel_with_grpc_gcp(grpc_gcp_secure_channel): 686 target = "example.com:443" 687 scopes = ["test_scope"] 688 689 credentials = mock.create_autospec(google.auth.credentials.Scoped, instance=True) 690 credentials.requires_scopes = True 691 692 grpc_helpers.create_channel(target, credentials=credentials, scopes=scopes) 693 grpc_gcp_secure_channel.assert_called() 694 695 credentials.with_scopes.assert_called_once_with(scopes, default_scopes=None) 696 697 698@pytest.mark.skipif(grpc_helpers.HAS_GRPC_GCP, reason="grpc_gcp module not available") 699@mock.patch("grpc.secure_channel") 700def test_create_channel_without_grpc_gcp(grpc_secure_channel): 701 target = "example.com:443" 702 scopes = ["test_scope"] 703 704 credentials = mock.create_autospec(google.auth.credentials.Scoped, instance=True) 705 credentials.requires_scopes = True 706 707 grpc_helpers.create_channel(target, credentials=credentials, scopes=scopes) 708 grpc_secure_channel.assert_called() 709 710 credentials.with_scopes.assert_called_once_with(scopes, default_scopes=None) 711 712 713class TestChannelStub(object): 714 def test_single_response(self): 715 channel = grpc_helpers.ChannelStub() 716 stub = operations_pb2.OperationsStub(channel) 717 expected_request = operations_pb2.GetOperationRequest(name="meep") 718 expected_response = operations_pb2.Operation(name="moop") 719 720 channel.GetOperation.response = expected_response 721 722 response = stub.GetOperation(expected_request) 723 724 assert response == expected_response 725 assert channel.requests == [("GetOperation", expected_request)] 726 assert channel.GetOperation.requests == [expected_request] 727 728 def test_no_response(self): 729 channel = grpc_helpers.ChannelStub() 730 stub = operations_pb2.OperationsStub(channel) 731 expected_request = operations_pb2.GetOperationRequest(name="meep") 732 733 with pytest.raises(ValueError) as exc_info: 734 stub.GetOperation(expected_request) 735 736 assert exc_info.match("GetOperation") 737 738 def test_missing_method(self): 739 channel = grpc_helpers.ChannelStub() 740 741 with pytest.raises(AttributeError): 742 channel.DoesNotExist.response 743 744 def test_exception_response(self): 745 channel = grpc_helpers.ChannelStub() 746 stub = operations_pb2.OperationsStub(channel) 747 expected_request = operations_pb2.GetOperationRequest(name="meep") 748 749 channel.GetOperation.response = RuntimeError() 750 751 with pytest.raises(RuntimeError): 752 stub.GetOperation(expected_request) 753 754 def test_callable_response(self): 755 channel = grpc_helpers.ChannelStub() 756 stub = operations_pb2.OperationsStub(channel) 757 expected_request = operations_pb2.GetOperationRequest(name="meep") 758 expected_response = operations_pb2.Operation(name="moop") 759 760 on_get_operation = mock.Mock(spec=("__call__",), return_value=expected_response) 761 762 channel.GetOperation.response = on_get_operation 763 764 response = stub.GetOperation(expected_request) 765 766 assert response == expected_response 767 on_get_operation.assert_called_once_with(expected_request) 768 769 def test_multiple_responses(self): 770 channel = grpc_helpers.ChannelStub() 771 stub = operations_pb2.OperationsStub(channel) 772 expected_request = operations_pb2.GetOperationRequest(name="meep") 773 expected_responses = [ 774 operations_pb2.Operation(name="foo"), 775 operations_pb2.Operation(name="bar"), 776 operations_pb2.Operation(name="baz"), 777 ] 778 779 channel.GetOperation.responses = iter(expected_responses) 780 781 response1 = stub.GetOperation(expected_request) 782 response2 = stub.GetOperation(expected_request) 783 response3 = stub.GetOperation(expected_request) 784 785 assert response1 == expected_responses[0] 786 assert response2 == expected_responses[1] 787 assert response3 == expected_responses[2] 788 assert channel.requests == [("GetOperation", expected_request)] * 3 789 assert channel.GetOperation.requests == [expected_request] * 3 790 791 with pytest.raises(StopIteration): 792 stub.GetOperation(expected_request) 793 794 def test_multiple_responses_and_single_response_error(self): 795 channel = grpc_helpers.ChannelStub() 796 stub = operations_pb2.OperationsStub(channel) 797 channel.GetOperation.responses = [] 798 channel.GetOperation.response = mock.sentinel.response 799 800 with pytest.raises(ValueError): 801 stub.GetOperation(operations_pb2.GetOperationRequest()) 802 803 def test_call_info(self): 804 channel = grpc_helpers.ChannelStub() 805 stub = operations_pb2.OperationsStub(channel) 806 expected_request = operations_pb2.GetOperationRequest(name="meep") 807 expected_response = operations_pb2.Operation(name="moop") 808 expected_metadata = [("red", "blue"), ("two", "shoe")] 809 expected_credentials = mock.sentinel.credentials 810 channel.GetOperation.response = expected_response 811 812 response = stub.GetOperation( 813 expected_request, 814 timeout=42, 815 metadata=expected_metadata, 816 credentials=expected_credentials, 817 ) 818 819 assert response == expected_response 820 assert channel.requests == [("GetOperation", expected_request)] 821 assert channel.GetOperation.calls == [ 822 (expected_request, 42, expected_metadata, expected_credentials) 823 ] 824 825 def test_unary_unary(self): 826 channel = grpc_helpers.ChannelStub() 827 method_name = "GetOperation" 828 callable_stub = channel.unary_unary(method_name) 829 assert callable_stub._method == method_name 830 assert callable_stub._channel == channel 831 832 def test_unary_stream(self): 833 channel = grpc_helpers.ChannelStub() 834 method_name = "GetOperation" 835 callable_stub = channel.unary_stream(method_name) 836 assert callable_stub._method == method_name 837 assert callable_stub._channel == channel 838 839 def test_stream_unary(self): 840 channel = grpc_helpers.ChannelStub() 841 method_name = "GetOperation" 842 callable_stub = channel.stream_unary(method_name) 843 assert callable_stub._method == method_name 844 assert callable_stub._channel == channel 845 846 def test_stream_stream(self): 847 channel = grpc_helpers.ChannelStub() 848 method_name = "GetOperation" 849 callable_stub = channel.stream_stream(method_name) 850 assert callable_stub._method == method_name 851 assert callable_stub._channel == channel 852 853 def test_subscribe_unsubscribe(self): 854 channel = grpc_helpers.ChannelStub() 855 assert channel.subscribe(None) is None 856 assert channel.unsubscribe(None) is None 857 858 def test_close(self): 859 channel = grpc_helpers.ChannelStub() 860 assert channel.close() is None 861