• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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