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