1# -*- coding: utf-8 -*-
2# Copyright 2020 Google LLC
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#     http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15#
16import os
17
18import mock
19import pytest
20
21try:
22    import grpc  # noqa: F401
23except ImportError:
24    pytest.skip("No GRPC", allow_module_level=True)
25from requests import Response  # noqa I201
26from requests.sessions import Session
27
28from google.api_core import client_options
29from google.api_core import exceptions as core_exceptions
30from google.api_core import gapic_v1
31from google.api_core.operations_v1 import AbstractOperationsClient
32from google.api_core.operations_v1 import pagers
33from google.api_core.operations_v1 import transports
34import google.auth
35from google.auth import credentials as ga_credentials
36from google.auth.exceptions import MutualTLSChannelError
37from google.longrunning import operations_pb2
38from google.oauth2 import service_account
39from google.protobuf import json_format  # type: ignore
40from google.rpc import status_pb2  # type: ignore
41
42
43HTTP_OPTIONS = {
44    "google.longrunning.Operations.CancelOperation": [
45        {"method": "post", "uri": "/v3/{name=operations/*}:cancel", "body": "*"},
46    ],
47    "google.longrunning.Operations.DeleteOperation": [
48        {"method": "delete", "uri": "/v3/{name=operations/*}"},
49    ],
50    "google.longrunning.Operations.GetOperation": [
51        {"method": "get", "uri": "/v3/{name=operations/*}"},
52    ],
53    "google.longrunning.Operations.ListOperations": [
54        {"method": "get", "uri": "/v3/{name=operations}"},
55    ],
56}
57
58
59def client_cert_source_callback():
60    return b"cert bytes", b"key bytes"
61
62
63def _get_operations_client(http_options=HTTP_OPTIONS):
64    transport = transports.rest.OperationsRestTransport(
65        credentials=ga_credentials.AnonymousCredentials(), http_options=http_options
66    )
67
68    return AbstractOperationsClient(transport=transport)
69
70
71# If default endpoint is localhost, then default mtls endpoint will be the same.
72# This method modifies the default endpoint so the client can produce a different
73# mtls endpoint for endpoint testing purposes.
74def modify_default_endpoint(client):
75    return (
76        "foo.googleapis.com"
77        if ("localhost" in client.DEFAULT_ENDPOINT)
78        else client.DEFAULT_ENDPOINT
79    )
80
81
82def test__get_default_mtls_endpoint():
83    api_endpoint = "example.googleapis.com"
84    api_mtls_endpoint = "example.mtls.googleapis.com"
85    sandbox_endpoint = "example.sandbox.googleapis.com"
86    sandbox_mtls_endpoint = "example.mtls.sandbox.googleapis.com"
87    non_googleapi = "api.example.com"
88
89    assert AbstractOperationsClient._get_default_mtls_endpoint(None) is None
90    assert (
91        AbstractOperationsClient._get_default_mtls_endpoint(api_endpoint)
92        == api_mtls_endpoint
93    )
94    assert (
95        AbstractOperationsClient._get_default_mtls_endpoint(api_mtls_endpoint)
96        == api_mtls_endpoint
97    )
98    assert (
99        AbstractOperationsClient._get_default_mtls_endpoint(sandbox_endpoint)
100        == sandbox_mtls_endpoint
101    )
102    assert (
103        AbstractOperationsClient._get_default_mtls_endpoint(sandbox_mtls_endpoint)
104        == sandbox_mtls_endpoint
105    )
106    assert (
107        AbstractOperationsClient._get_default_mtls_endpoint(non_googleapi)
108        == non_googleapi
109    )
110
111
112@pytest.mark.parametrize("client_class", [AbstractOperationsClient])
113def test_operations_client_from_service_account_info(client_class):
114    creds = ga_credentials.AnonymousCredentials()
115    with mock.patch.object(
116        service_account.Credentials, "from_service_account_info"
117    ) as factory:
118        factory.return_value = creds
119        info = {"valid": True}
120        client = client_class.from_service_account_info(info)
121        assert client.transport._credentials == creds
122        assert isinstance(client, client_class)
123
124        assert client.transport._host == "longrunning.googleapis.com:443"
125
126
127@pytest.mark.parametrize(
128    "transport_class,transport_name", [(transports.OperationsRestTransport, "rest")]
129)
130def test_operations_client_service_account_always_use_jwt(
131    transport_class, transport_name
132):
133    with mock.patch.object(
134        service_account.Credentials, "with_always_use_jwt_access", create=True
135    ) as use_jwt:
136        creds = service_account.Credentials(None, None, None)
137        transport_class(credentials=creds, always_use_jwt_access=True)
138        use_jwt.assert_called_once_with(True)
139
140    with mock.patch.object(
141        service_account.Credentials, "with_always_use_jwt_access", create=True
142    ) as use_jwt:
143        creds = service_account.Credentials(None, None, None)
144        transport_class(credentials=creds, always_use_jwt_access=False)
145        use_jwt.assert_not_called()
146
147
148@pytest.mark.parametrize("client_class", [AbstractOperationsClient])
149def test_operations_client_from_service_account_file(client_class):
150    creds = ga_credentials.AnonymousCredentials()
151    with mock.patch.object(
152        service_account.Credentials, "from_service_account_file"
153    ) as factory:
154        factory.return_value = creds
155        client = client_class.from_service_account_file("dummy/file/path.json")
156        assert client.transport._credentials == creds
157        assert isinstance(client, client_class)
158
159        client = client_class.from_service_account_json("dummy/file/path.json")
160        assert client.transport._credentials == creds
161        assert isinstance(client, client_class)
162
163        assert client.transport._host == "longrunning.googleapis.com:443"
164
165
166def test_operations_client_get_transport_class():
167    transport = AbstractOperationsClient.get_transport_class()
168    available_transports = [
169        transports.OperationsRestTransport,
170    ]
171    assert transport in available_transports
172
173    transport = AbstractOperationsClient.get_transport_class("rest")
174    assert transport == transports.OperationsRestTransport
175
176
177@pytest.mark.parametrize(
178    "client_class,transport_class,transport_name",
179    [(AbstractOperationsClient, transports.OperationsRestTransport, "rest")],
180)
181@mock.patch.object(
182    AbstractOperationsClient,
183    "DEFAULT_ENDPOINT",
184    modify_default_endpoint(AbstractOperationsClient),
185)
186def test_operations_client_client_options(
187    client_class, transport_class, transport_name
188):
189    # Check that if channel is provided we won't create a new one.
190    with mock.patch.object(AbstractOperationsClient, "get_transport_class") as gtc:
191        transport = transport_class(credentials=ga_credentials.AnonymousCredentials())
192        client = client_class(transport=transport)
193        gtc.assert_not_called()
194
195    # Check that if channel is provided via str we will create a new one.
196    with mock.patch.object(AbstractOperationsClient, "get_transport_class") as gtc:
197        client = client_class(transport=transport_name)
198        gtc.assert_called()
199
200    # Check the case api_endpoint is provided.
201    options = client_options.ClientOptions(api_endpoint="squid.clam.whelk")
202    with mock.patch.object(transport_class, "__init__") as patched:
203        patched.return_value = None
204        client = client_class(client_options=options)
205        patched.assert_called_once_with(
206            credentials=None,
207            credentials_file=None,
208            host="squid.clam.whelk",
209            scopes=None,
210            client_cert_source_for_mtls=None,
211            quota_project_id=None,
212            client_info=transports.base.DEFAULT_CLIENT_INFO,
213            always_use_jwt_access=True,
214        )
215
216    # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is
217    # "never".
218    with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}):
219        with mock.patch.object(transport_class, "__init__") as patched:
220            patched.return_value = None
221            client = client_class()
222            patched.assert_called_once_with(
223                credentials=None,
224                credentials_file=None,
225                host=client.DEFAULT_ENDPOINT,
226                scopes=None,
227                client_cert_source_for_mtls=None,
228                quota_project_id=None,
229                client_info=transports.base.DEFAULT_CLIENT_INFO,
230                always_use_jwt_access=True,
231            )
232
233    # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is
234    # "always".
235    with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "always"}):
236        with mock.patch.object(transport_class, "__init__") as patched:
237            patched.return_value = None
238            client = client_class()
239            patched.assert_called_once_with(
240                credentials=None,
241                credentials_file=None,
242                host=client.DEFAULT_MTLS_ENDPOINT,
243                scopes=None,
244                client_cert_source_for_mtls=None,
245                quota_project_id=None,
246                client_info=transports.base.DEFAULT_CLIENT_INFO,
247                always_use_jwt_access=True,
248            )
249
250    # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT has
251    # unsupported value.
252    with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "Unsupported"}):
253        with pytest.raises(MutualTLSChannelError):
254            client = client_class()
255
256    # Check the case GOOGLE_API_USE_CLIENT_CERTIFICATE has unsupported value.
257    with mock.patch.dict(
258        os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"}
259    ):
260        with pytest.raises(ValueError):
261            client = client_class()
262
263    # Check the case quota_project_id is provided
264    options = client_options.ClientOptions(quota_project_id="octopus")
265    with mock.patch.object(transport_class, "__init__") as patched:
266        patched.return_value = None
267        client = client_class(client_options=options)
268        patched.assert_called_once_with(
269            credentials=None,
270            credentials_file=None,
271            host=client.DEFAULT_ENDPOINT,
272            scopes=None,
273            client_cert_source_for_mtls=None,
274            quota_project_id="octopus",
275            client_info=transports.base.DEFAULT_CLIENT_INFO,
276            always_use_jwt_access=True,
277        )
278
279
280@pytest.mark.parametrize(
281    "client_class,transport_class,transport_name,use_client_cert_env",
282    [
283        (AbstractOperationsClient, transports.OperationsRestTransport, "rest", "true"),
284        (AbstractOperationsClient, transports.OperationsRestTransport, "rest", "false"),
285    ],
286)
287@mock.patch.object(
288    AbstractOperationsClient,
289    "DEFAULT_ENDPOINT",
290    modify_default_endpoint(AbstractOperationsClient),
291)
292@mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"})
293def test_operations_client_mtls_env_auto(
294    client_class, transport_class, transport_name, use_client_cert_env
295):
296    # This tests the endpoint autoswitch behavior. Endpoint is autoswitched to the default
297    # mtls endpoint, if GOOGLE_API_USE_CLIENT_CERTIFICATE is "true" and client cert exists.
298
299    # Check the case client_cert_source is provided. Whether client cert is used depends on
300    # GOOGLE_API_USE_CLIENT_CERTIFICATE value.
301    with mock.patch.dict(
302        os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env}
303    ):
304        options = client_options.ClientOptions(
305            client_cert_source=client_cert_source_callback
306        )
307
308        def fake_init(client_cert_source_for_mtls=None, **kwargs):
309            """Invoke client_cert source if provided."""
310
311            if client_cert_source_for_mtls:
312                client_cert_source_for_mtls()
313                return None
314
315        with mock.patch.object(transport_class, "__init__") as patched:
316            patched.side_effect = fake_init
317            client = client_class(client_options=options)
318
319            if use_client_cert_env == "false":
320                expected_client_cert_source = None
321                expected_host = client.DEFAULT_ENDPOINT
322            else:
323                expected_client_cert_source = client_cert_source_callback
324                expected_host = client.DEFAULT_MTLS_ENDPOINT
325
326            patched.assert_called_once_with(
327                credentials=None,
328                credentials_file=None,
329                host=expected_host,
330                scopes=None,
331                client_cert_source_for_mtls=expected_client_cert_source,
332                quota_project_id=None,
333                client_info=transports.base.DEFAULT_CLIENT_INFO,
334                always_use_jwt_access=True,
335            )
336
337    # Check the case ADC client cert is provided. Whether client cert is used depends on
338    # GOOGLE_API_USE_CLIENT_CERTIFICATE value.
339    with mock.patch.dict(
340        os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env}
341    ):
342        with mock.patch.object(transport_class, "__init__") as patched:
343            with mock.patch(
344                "google.auth.transport.mtls.has_default_client_cert_source",
345                return_value=True,
346            ):
347                with mock.patch(
348                    "google.auth.transport.mtls.default_client_cert_source",
349                    return_value=client_cert_source_callback,
350                ):
351                    if use_client_cert_env == "false":
352                        expected_host = client.DEFAULT_ENDPOINT
353                        expected_client_cert_source = None
354                    else:
355                        expected_host = client.DEFAULT_MTLS_ENDPOINT
356                        expected_client_cert_source = client_cert_source_callback
357
358                    patched.return_value = None
359                    client = client_class()
360                    patched.assert_called_once_with(
361                        credentials=None,
362                        credentials_file=None,
363                        host=expected_host,
364                        scopes=None,
365                        client_cert_source_for_mtls=expected_client_cert_source,
366                        quota_project_id=None,
367                        client_info=transports.base.DEFAULT_CLIENT_INFO,
368                        always_use_jwt_access=True,
369                    )
370
371    # Check the case client_cert_source and ADC client cert are not provided.
372    with mock.patch.dict(
373        os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env}
374    ):
375        with mock.patch.object(transport_class, "__init__") as patched:
376            with mock.patch(
377                "google.auth.transport.mtls.has_default_client_cert_source",
378                return_value=False,
379            ):
380                patched.return_value = None
381                client = client_class()
382                patched.assert_called_once_with(
383                    credentials=None,
384                    credentials_file=None,
385                    host=client.DEFAULT_ENDPOINT,
386                    scopes=None,
387                    client_cert_source_for_mtls=None,
388                    quota_project_id=None,
389                    client_info=transports.base.DEFAULT_CLIENT_INFO,
390                    always_use_jwt_access=True,
391                )
392
393
394@pytest.mark.parametrize(
395    "client_class,transport_class,transport_name",
396    [(AbstractOperationsClient, transports.OperationsRestTransport, "rest")],
397)
398def test_operations_client_client_options_scopes(
399    client_class, transport_class, transport_name
400):
401    # Check the case scopes are provided.
402    options = client_options.ClientOptions(scopes=["1", "2"],)
403    with mock.patch.object(transport_class, "__init__") as patched:
404        patched.return_value = None
405        client = client_class(client_options=options)
406        patched.assert_called_once_with(
407            credentials=None,
408            credentials_file=None,
409            host=client.DEFAULT_ENDPOINT,
410            scopes=["1", "2"],
411            client_cert_source_for_mtls=None,
412            quota_project_id=None,
413            client_info=transports.base.DEFAULT_CLIENT_INFO,
414            always_use_jwt_access=True,
415        )
416
417
418@pytest.mark.parametrize(
419    "client_class,transport_class,transport_name",
420    [(AbstractOperationsClient, transports.OperationsRestTransport, "rest")],
421)
422def test_operations_client_client_options_credentials_file(
423    client_class, transport_class, transport_name
424):
425    # Check the case credentials file is provided.
426    options = client_options.ClientOptions(credentials_file="credentials.json")
427    with mock.patch.object(transport_class, "__init__") as patched:
428        patched.return_value = None
429        client = client_class(client_options=options)
430        patched.assert_called_once_with(
431            credentials=None,
432            credentials_file="credentials.json",
433            host=client.DEFAULT_ENDPOINT,
434            scopes=None,
435            client_cert_source_for_mtls=None,
436            quota_project_id=None,
437            client_info=transports.base.DEFAULT_CLIENT_INFO,
438            always_use_jwt_access=True,
439        )
440
441
442def test_list_operations_rest(
443    transport: str = "rest", request_type=operations_pb2.ListOperationsRequest
444):
445    client = _get_operations_client()
446
447    # Mock the http request call within the method and fake a response.
448    with mock.patch.object(Session, "request") as req:
449        # Designate an appropriate value for the returned response.
450        return_value = operations_pb2.ListOperationsResponse(
451            next_page_token="next_page_token_value",
452        )
453
454        # Wrap the value into a proper Response obj
455        response_value = Response()
456        response_value.status_code = 200
457        json_return_value = json_format.MessageToJson(return_value)
458        response_value._content = json_return_value.encode("UTF-8")
459        req.return_value = response_value
460        response = client.list_operations(
461            name="operations", filter_="my_filter", page_size=10, page_token="abc"
462        )
463
464        actual_args = req.call_args
465        assert actual_args.args[0] == "GET"
466        assert (
467            actual_args.args[1]
468            == "https://longrunning.googleapis.com:443/v3/operations"
469        )
470        assert actual_args.kwargs["params"] == [
471            ("filter", "my_filter"),
472            ("pageSize", 10),
473            ("pageToken", "abc"),
474        ]
475
476    # Establish that the response is the type that we expect.
477    assert isinstance(response, pagers.ListOperationsPager)
478    assert response.next_page_token == "next_page_token_value"
479
480
481def test_list_operations_rest_failure():
482    client = _get_operations_client(http_options=None)
483
484    with mock.patch.object(Session, "request") as req:
485        response_value = Response()
486        response_value.status_code = 400
487        mock_request = mock.MagicMock()
488        mock_request.method = "GET"
489        mock_request.url = "https://longrunning.googleapis.com:443/v1/operations"
490        response_value.request = mock_request
491        req.return_value = response_value
492        with pytest.raises(core_exceptions.GoogleAPIError):
493            client.list_operations(name="operations")
494
495
496def test_list_operations_rest_pager():
497    client = AbstractOperationsClient(
498        credentials=ga_credentials.AnonymousCredentials(),
499    )
500
501    # Mock the http request call within the method and fake a response.
502    with mock.patch.object(Session, "request") as req:
503        # TODO(kbandes): remove this mock unless there's a good reason for it.
504        # with mock.patch.object(path_template, 'transcode') as transcode:
505        # Set the response as a series of pages
506        response = (
507            operations_pb2.ListOperationsResponse(
508                operations=[
509                    operations_pb2.Operation(),
510                    operations_pb2.Operation(),
511                    operations_pb2.Operation(),
512                ],
513                next_page_token="abc",
514            ),
515            operations_pb2.ListOperationsResponse(
516                operations=[], next_page_token="def",
517            ),
518            operations_pb2.ListOperationsResponse(
519                operations=[operations_pb2.Operation()], next_page_token="ghi",
520            ),
521            operations_pb2.ListOperationsResponse(
522                operations=[operations_pb2.Operation(), operations_pb2.Operation()],
523            ),
524        )
525        # Two responses for two calls
526        response = response + response
527
528        # Wrap the values into proper Response objs
529        response = tuple(json_format.MessageToJson(x) for x in response)
530        return_values = tuple(Response() for i in response)
531        for return_val, response_val in zip(return_values, response):
532            return_val._content = response_val.encode("UTF-8")
533            return_val.status_code = 200
534        req.side_effect = return_values
535
536        pager = client.list_operations(name="operations")
537
538        results = list(pager)
539        assert len(results) == 6
540        assert all(isinstance(i, operations_pb2.Operation) for i in results)
541
542        pages = list(client.list_operations(name="operations").pages)
543        for page_, token in zip(pages, ["abc", "def", "ghi", ""]):
544            assert page_.next_page_token == token
545
546
547def test_get_operation_rest(
548    transport: str = "rest", request_type=operations_pb2.GetOperationRequest
549):
550    client = _get_operations_client()
551
552    # Mock the http request call within the method and fake a response.
553    with mock.patch.object(Session, "request") as req:
554        # Designate an appropriate value for the returned response.
555        return_value = operations_pb2.Operation(
556            name="operations/sample1", done=True, error=status_pb2.Status(code=411),
557        )
558
559        # Wrap the value into a proper Response obj
560        response_value = Response()
561        response_value.status_code = 200
562        json_return_value = json_format.MessageToJson(return_value)
563        response_value._content = json_return_value.encode("UTF-8")
564        req.return_value = response_value
565        response = client.get_operation("operations/sample1")
566
567    actual_args = req.call_args
568    assert actual_args.args[0] == "GET"
569    assert (
570        actual_args.args[1]
571        == "https://longrunning.googleapis.com:443/v3/operations/sample1"
572    )
573
574    # Establish that the response is the type that we expect.
575    assert isinstance(response, operations_pb2.Operation)
576    assert response.name == "operations/sample1"
577    assert response.done is True
578
579
580def test_get_operation_rest_failure():
581    client = _get_operations_client(http_options=None)
582
583    with mock.patch.object(Session, "request") as req:
584        response_value = Response()
585        response_value.status_code = 400
586        mock_request = mock.MagicMock()
587        mock_request.method = "GET"
588        mock_request.url = (
589            "https://longrunning.googleapis.com:443/v1/operations/sample1"
590        )
591        response_value.request = mock_request
592        req.return_value = response_value
593        with pytest.raises(core_exceptions.GoogleAPIError):
594            client.get_operation("operations/sample1")
595
596
597def test_delete_operation_rest(
598    transport: str = "rest", request_type=operations_pb2.DeleteOperationRequest
599):
600    client = _get_operations_client()
601
602    # Mock the http request call within the method and fake a response.
603    with mock.patch.object(Session, "request") as req:
604        # Wrap the value into a proper Response obj
605        response_value = Response()
606        response_value.status_code = 200
607        json_return_value = ""
608        response_value._content = json_return_value.encode("UTF-8")
609        req.return_value = response_value
610        client.delete_operation(name="operations/sample1")
611        assert req.call_count == 1
612        actual_args = req.call_args
613        assert actual_args.args[0] == "DELETE"
614        assert (
615            actual_args.args[1]
616            == "https://longrunning.googleapis.com:443/v3/operations/sample1"
617        )
618
619
620def test_delete_operation_rest_failure():
621    client = _get_operations_client(http_options=None)
622
623    with mock.patch.object(Session, "request") as req:
624        response_value = Response()
625        response_value.status_code = 400
626        mock_request = mock.MagicMock()
627        mock_request.method = "DELETE"
628        mock_request.url = (
629            "https://longrunning.googleapis.com:443/v1/operations/sample1"
630        )
631        response_value.request = mock_request
632        req.return_value = response_value
633        with pytest.raises(core_exceptions.GoogleAPIError):
634            client.delete_operation(name="operations/sample1")
635
636
637def test_cancel_operation_rest(transport: str = "rest"):
638    client = _get_operations_client()
639
640    # Mock the http request call within the method and fake a response.
641    with mock.patch.object(Session, "request") as req:
642        # Wrap the value into a proper Response obj
643        response_value = Response()
644        response_value.status_code = 200
645        json_return_value = ""
646        response_value._content = json_return_value.encode("UTF-8")
647        req.return_value = response_value
648        client.cancel_operation(name="operations/sample1")
649        assert req.call_count == 1
650        actual_args = req.call_args
651        assert actual_args.args[0] == "POST"
652        assert (
653            actual_args.args[1]
654            == "https://longrunning.googleapis.com:443/v3/operations/sample1:cancel"
655        )
656
657
658def test_cancel_operation_rest_failure():
659    client = _get_operations_client(http_options=None)
660
661    with mock.patch.object(Session, "request") as req:
662        response_value = Response()
663        response_value.status_code = 400
664        mock_request = mock.MagicMock()
665        mock_request.method = "POST"
666        mock_request.url = (
667            "https://longrunning.googleapis.com:443/v1/operations/sample1:cancel"
668        )
669        response_value.request = mock_request
670        req.return_value = response_value
671        with pytest.raises(core_exceptions.GoogleAPIError):
672            client.cancel_operation(name="operations/sample1")
673
674
675def test_credentials_transport_error():
676    # It is an error to provide credentials and a transport instance.
677    transport = transports.OperationsRestTransport(
678        credentials=ga_credentials.AnonymousCredentials(),
679    )
680    with pytest.raises(ValueError):
681        AbstractOperationsClient(
682            credentials=ga_credentials.AnonymousCredentials(), transport=transport,
683        )
684
685    # It is an error to provide a credentials file and a transport instance.
686    transport = transports.OperationsRestTransport(
687        credentials=ga_credentials.AnonymousCredentials(),
688    )
689    with pytest.raises(ValueError):
690        AbstractOperationsClient(
691            client_options={"credentials_file": "credentials.json"},
692            transport=transport,
693        )
694
695    # It is an error to provide scopes and a transport instance.
696    transport = transports.OperationsRestTransport(
697        credentials=ga_credentials.AnonymousCredentials(),
698    )
699    with pytest.raises(ValueError):
700        AbstractOperationsClient(
701            client_options={"scopes": ["1", "2"]}, transport=transport,
702        )
703
704
705def test_transport_instance():
706    # A client may be instantiated with a custom transport instance.
707    transport = transports.OperationsRestTransport(
708        credentials=ga_credentials.AnonymousCredentials(),
709    )
710    client = AbstractOperationsClient(transport=transport)
711    assert client.transport is transport
712
713
714@pytest.mark.parametrize("transport_class", [transports.OperationsRestTransport])
715def test_transport_adc(transport_class):
716    # Test default credentials are used if not provided.
717    with mock.patch.object(google.auth, "default") as adc:
718        adc.return_value = (ga_credentials.AnonymousCredentials(), None)
719        transport_class()
720        adc.assert_called_once()
721
722
723def test_operations_base_transport_error():
724    # Passing both a credentials object and credentials_file should raise an error
725    with pytest.raises(core_exceptions.DuplicateCredentialArgs):
726        transports.OperationsTransport(
727            credentials=ga_credentials.AnonymousCredentials(),
728            credentials_file="credentials.json",
729        )
730
731
732def test_operations_base_transport():
733    # Instantiate the base transport.
734    with mock.patch(
735        "google.api_core.operations_v1.transports.OperationsTransport.__init__"
736    ) as Transport:
737        Transport.return_value = None
738        transport = transports.OperationsTransport(
739            credentials=ga_credentials.AnonymousCredentials(),
740        )
741
742    # Every method on the transport should just blindly
743    # raise NotImplementedError.
744    methods = (
745        "list_operations",
746        "get_operation",
747        "delete_operation",
748        "cancel_operation",
749    )
750    for method in methods:
751        with pytest.raises(NotImplementedError):
752            getattr(transport, method)(request=object())
753
754    with pytest.raises(NotImplementedError):
755        transport.close()
756
757
758def test_operations_base_transport_with_credentials_file():
759    # Instantiate the base transport with a credentials file
760    with mock.patch.object(
761        google.auth, "load_credentials_from_file", autospec=True
762    ) as load_creds, mock.patch(
763        "google.api_core.operations_v1.transports.OperationsTransport._prep_wrapped_messages"
764    ) as Transport:
765        Transport.return_value = None
766        load_creds.return_value = (ga_credentials.AnonymousCredentials(), None)
767        transports.OperationsTransport(
768            credentials_file="credentials.json", quota_project_id="octopus",
769        )
770        load_creds.assert_called_once_with(
771            "credentials.json",
772            scopes=None,
773            default_scopes=(),
774            quota_project_id="octopus",
775        )
776
777
778def test_operations_base_transport_with_adc():
779    # Test the default credentials are used if credentials and credentials_file are None.
780    with mock.patch.object(google.auth, "default", autospec=True) as adc, mock.patch(
781        "google.api_core.operations_v1.transports.OperationsTransport._prep_wrapped_messages"
782    ) as Transport:
783        Transport.return_value = None
784        adc.return_value = (ga_credentials.AnonymousCredentials(), None)
785        transports.OperationsTransport()
786        adc.assert_called_once()
787
788
789def test_operations_auth_adc():
790    # If no credentials are provided, we should use ADC credentials.
791    with mock.patch.object(google.auth, "default", autospec=True) as adc:
792        adc.return_value = (ga_credentials.AnonymousCredentials(), None)
793        AbstractOperationsClient()
794        adc.assert_called_once_with(
795            scopes=None, default_scopes=(), quota_project_id=None,
796        )
797
798
799def test_operations_http_transport_client_cert_source_for_mtls():
800    cred = ga_credentials.AnonymousCredentials()
801    with mock.patch(
802        "google.auth.transport.requests.AuthorizedSession.configure_mtls_channel"
803    ) as mock_configure_mtls_channel:
804        transports.OperationsRestTransport(
805            credentials=cred, client_cert_source_for_mtls=client_cert_source_callback
806        )
807        mock_configure_mtls_channel.assert_called_once_with(client_cert_source_callback)
808
809
810def test_operations_host_no_port():
811    client = AbstractOperationsClient(
812        credentials=ga_credentials.AnonymousCredentials(),
813        client_options=client_options.ClientOptions(
814            api_endpoint="longrunning.googleapis.com"
815        ),
816    )
817    assert client.transport._host == "longrunning.googleapis.com:443"
818
819
820def test_operations_host_with_port():
821    client = AbstractOperationsClient(
822        credentials=ga_credentials.AnonymousCredentials(),
823        client_options=client_options.ClientOptions(
824            api_endpoint="longrunning.googleapis.com:8000"
825        ),
826    )
827    assert client.transport._host == "longrunning.googleapis.com:8000"
828
829
830def test_common_billing_account_path():
831    billing_account = "squid"
832    expected = "billingAccounts/{billing_account}".format(
833        billing_account=billing_account,
834    )
835    actual = AbstractOperationsClient.common_billing_account_path(billing_account)
836    assert expected == actual
837
838
839def test_parse_common_billing_account_path():
840    expected = {
841        "billing_account": "clam",
842    }
843    path = AbstractOperationsClient.common_billing_account_path(**expected)
844
845    # Check that the path construction is reversible.
846    actual = AbstractOperationsClient.parse_common_billing_account_path(path)
847    assert expected == actual
848
849
850def test_common_folder_path():
851    folder = "whelk"
852    expected = "folders/{folder}".format(folder=folder,)
853    actual = AbstractOperationsClient.common_folder_path(folder)
854    assert expected == actual
855
856
857def test_parse_common_folder_path():
858    expected = {
859        "folder": "octopus",
860    }
861    path = AbstractOperationsClient.common_folder_path(**expected)
862
863    # Check that the path construction is reversible.
864    actual = AbstractOperationsClient.parse_common_folder_path(path)
865    assert expected == actual
866
867
868def test_common_organization_path():
869    organization = "oyster"
870    expected = "organizations/{organization}".format(organization=organization,)
871    actual = AbstractOperationsClient.common_organization_path(organization)
872    assert expected == actual
873
874
875def test_parse_common_organization_path():
876    expected = {
877        "organization": "nudibranch",
878    }
879    path = AbstractOperationsClient.common_organization_path(**expected)
880
881    # Check that the path construction is reversible.
882    actual = AbstractOperationsClient.parse_common_organization_path(path)
883    assert expected == actual
884
885
886def test_common_project_path():
887    project = "cuttlefish"
888    expected = "projects/{project}".format(project=project,)
889    actual = AbstractOperationsClient.common_project_path(project)
890    assert expected == actual
891
892
893def test_parse_common_project_path():
894    expected = {
895        "project": "mussel",
896    }
897    path = AbstractOperationsClient.common_project_path(**expected)
898
899    # Check that the path construction is reversible.
900    actual = AbstractOperationsClient.parse_common_project_path(path)
901    assert expected == actual
902
903
904def test_common_location_path():
905    project = "winkle"
906    location = "nautilus"
907    expected = "projects/{project}/locations/{location}".format(
908        project=project, location=location,
909    )
910    actual = AbstractOperationsClient.common_location_path(project, location)
911    assert expected == actual
912
913
914def test_parse_common_location_path():
915    expected = {
916        "project": "scallop",
917        "location": "abalone",
918    }
919    path = AbstractOperationsClient.common_location_path(**expected)
920
921    # Check that the path construction is reversible.
922    actual = AbstractOperationsClient.parse_common_location_path(path)
923    assert expected == actual
924
925
926def test_client_withDEFAULT_CLIENT_INFO():
927    client_info = gapic_v1.client_info.ClientInfo()
928
929    with mock.patch.object(
930        transports.OperationsTransport, "_prep_wrapped_messages"
931    ) as prep:
932        AbstractOperationsClient(
933            credentials=ga_credentials.AnonymousCredentials(), client_info=client_info,
934        )
935        prep.assert_called_once_with(client_info)
936
937    with mock.patch.object(
938        transports.OperationsTransport, "_prep_wrapped_messages"
939    ) as prep:
940        transport_class = AbstractOperationsClient.get_transport_class()
941        transport_class(
942            credentials=ga_credentials.AnonymousCredentials(), client_info=client_info,
943        )
944        prep.assert_called_once_with(client_info)
945