diff --git a/google/cloud/storage/_helpers.py b/google/cloud/storage/_helpers.py index 338b79861..a126c9e02 100644 --- a/google/cloud/storage/_helpers.py +++ b/google/cloud/storage/_helpers.py @@ -215,14 +215,13 @@ def reload( if_metageneration_match=if_metageneration_match, if_metageneration_not_match=if_metageneration_not_match, ) - api_response = client._connection.api_request( - method="GET", - path=self.path, + api_response = client._get_resource( + self.path, query_params=query_params, headers=self._encryption_headers(), - _target_object=self, timeout=timeout, retry=retry, + _target_object=self, ) self._set_properties(api_response) diff --git a/google/cloud/storage/acl.py b/google/cloud/storage/acl.py index 55c12c9b8..b80447b9d 100644 --- a/google/cloud/storage/acl.py +++ b/google/cloud/storage/acl.py @@ -85,6 +85,7 @@ """ from google.cloud.storage.constants import _DEFAULT_TIMEOUT +from google.cloud.storage.retry import DEFAULT_RETRY class _ACLEntity(object): @@ -206,6 +207,7 @@ class ACL(object): # Subclasses must override to provide these attributes (typically, # as properties). + client = None reload_path = None save_path = None user_project = None @@ -430,7 +432,7 @@ def _require_client(self, client): client = self.client return client - def reload(self, client=None, timeout=_DEFAULT_TIMEOUT): + def reload(self, client=None, timeout=_DEFAULT_TIMEOUT, retry=DEFAULT_RETRY): """Reload the ACL data from Cloud Storage. If :attr:`user_project` is set, bills the API request to that project. @@ -445,6 +447,15 @@ def reload(self, client=None, timeout=_DEFAULT_TIMEOUT): Can also be passed as a tuple (connect_timeout, read_timeout). See :meth:`requests.Session.request` documentation for details. + + :type retry: :class:`~google.api_core.retry.Retry` + :param retry: (Optional) How to retry the RPC. + + A None value will disable retries. + + A google.api_core.retry.Retry value will enable retries, + and the object will define retriable response codes and errors + and configure backoff and timeout options. """ path = self.reload_path client = self._require_client(client) @@ -455,10 +466,11 @@ def reload(self, client=None, timeout=_DEFAULT_TIMEOUT): self.entities.clear() - found = client._connection.api_request( - method="GET", path=path, query_params=query_params, timeout=timeout, + found = client._get_resource( + path, query_params=query_params, timeout=timeout, retry=retry, ) self.loaded = True + for entry in found.get("items", ()): self.add_entity(self.entity_from_dict(entry)) diff --git a/google/cloud/storage/blob.py b/google/cloud/storage/blob.py index 66cc1d153..a105053de 100644 --- a/google/cloud/storage/blob.py +++ b/google/cloud/storage/blob.py @@ -704,20 +704,19 @@ def exists( try: # We intentionally pass `_target_object=None` since fields=name # would limit the local properties. - client._connection.api_request( - method="GET", - path=self.path, + client._get_resource( + self.path, query_params=query_params, - _target_object=None, timeout=timeout, retry=retry, + _target_object=None, ) + except NotFound: # NOTE: This will not fail immediately in a batch. However, when # Batch.finish() is called, the resulting `NotFound` will be # raised. - return True - except NotFound: return False + return True def delete( self, @@ -2829,13 +2828,12 @@ def get_iam_policy( if requested_policy_version is not None: query_params["optionsRequestedPolicyVersion"] = requested_policy_version - info = client._connection.api_request( - method="GET", - path="%s/iam" % (self.path,), + info = client._get_resource( + "%s/iam" % (self.path,), query_params=query_params, - _target_object=None, timeout=timeout, retry=retry, + _target_object=None, ) return Policy.from_api_repr(info) @@ -2970,12 +2968,12 @@ def test_iam_permissions( query_params["userProject"] = self.user_project path = "%s/iam/testPermissions" % (self.path,) - resp = client._connection.api_request( - method="GET", - path=path, + resp = client._get_resource( + path, query_params=query_params, timeout=timeout, retry=retry, + _target_object=None, ) return resp.get("permissions", []) diff --git a/google/cloud/storage/bucket.py b/google/cloud/storage/bucket.py index ac38208a3..aacc2efe5 100644 --- a/google/cloud/storage/bucket.py +++ b/google/cloud/storage/bucket.py @@ -786,20 +786,19 @@ def exists( try: # We intentionally pass `_target_object=None` since fields=name # would limit the local properties. - client._connection.api_request( - method="GET", - path=self.path, + client._get_resource( + self.path, query_params=query_params, - _target_object=None, timeout=timeout, retry=retry, + _target_object=None, ) + except NotFound: # NOTE: This will not fail immediately in a batch. However, when # Batch.finish() is called, the resulting `NotFound` will be # raised. - return True - except NotFound: return False + return True def create( self, @@ -2882,13 +2881,12 @@ def get_iam_policy( if requested_policy_version is not None: query_params["optionsRequestedPolicyVersion"] = requested_policy_version - info = client._connection.api_request( - method="GET", - path="%s/iam" % (self.path,), + info = client._get_resource( + "%s/iam" % (self.path,), query_params=query_params, - _target_object=None, timeout=timeout, retry=retry, + _target_object=None, ) return Policy.from_api_repr(info) @@ -3008,12 +3006,12 @@ def test_iam_permissions( query_params["userProject"] = self.user_project path = "%s/iam/testPermissions" % (self.path,) - resp = client._connection.api_request( - method="GET", - path=path, + resp = client._get_resource( + path, query_params=query_params, timeout=timeout, retry=retry, + _target_object=None, ) return resp.get("permissions", []) diff --git a/google/cloud/storage/client.py b/google/cloud/storage/client.py index 858fecdce..848012725 100644 --- a/google/cloud/storage/client.py +++ b/google/cloud/storage/client.py @@ -284,10 +284,9 @@ def get_service_account_email( """ if project is None: project = self.project + path = "/projects/%s/serviceAccount" % (project,) - api_response = self._base_connection.api_request( - method="GET", path=path, timeout=timeout, retry=retry, - ) + api_response = self._get_resource(path, timeout=timeout, retry=retry) return api_response["email_address"] def bucket(self, bucket_name, user_project=None): @@ -321,6 +320,72 @@ def batch(self): """ return Batch(client=self) + def _get_resource( + self, + path, + query_params=None, + headers=None, + timeout=_DEFAULT_TIMEOUT, + retry=DEFAULT_RETRY, + _target_object=None, + ): + """Helper for bucket / blob methods making API 'GET' calls. + + Args: + path str: + The path of the resource to fetch. + + query_params Optional[dict]: + HTTP query parameters to be passed + + headers Optional[dict]: + HTTP headers to be passed + + timeout (Optional[Union[float, Tuple[float, float]]]): + The amount of time, in seconds, to wait for the server response. + + Can also be passed as a tuple (connect_timeout, read_timeout). + See :meth:`requests.Session.request` documentation for details. + + retry (Optional[Union[google.api_core.retry.Retry, google.cloud.storage.retry.ConditionalRetryPolicy]]): + How to retry the RPC. A None value will disable retries. + A google.api_core.retry.Retry value will enable retries, and the object will + define retriable response codes and errors and configure backoff and timeout options. + + A google.cloud.storage.retry.ConditionalRetryPolicy value wraps a Retry object and + activates it only if certain conditions are met. This class exists to provide safe defaults + for RPC calls that are not technically safe to retry normally (due to potential data + duplication or other side-effects) but become safe to retry if a condition such as + if_metageneration_match is set. + + See the retry.py source code and docstrings in this package (google.cloud.storage.retry) for + information on retry types and how to configure them. + + _target_object (Union[ \ + :class:`~google.cloud.storage.bucket.Bucket`, \ + :class:`~google.cloud.storage.bucket.blob`, \ + ]): + Object to which future data is to be applied -- only relevant + in the context of a batch. + + Returns: + dict + The JSON resource fetched + + Raises: + google.cloud.exceptions.NotFound + If the bucket is not found. + """ + return self._connection.api_request( + method="GET", + path=path, + query_params=query_params, + headers=headers, + timeout=timeout, + retry=retry, + _target_object=_target_object, + ) + def get_bucket( self, bucket_or_name, diff --git a/google/cloud/storage/hmac_key.py b/google/cloud/storage/hmac_key.py index 3fd49079e..c6cc17a85 100644 --- a/google/cloud/storage/hmac_key.py +++ b/google/cloud/storage/hmac_key.py @@ -222,12 +222,8 @@ def exists(self, timeout=_DEFAULT_TIMEOUT, retry=DEFAULT_RETRY): if self.user_project is not None: qs_params["userProject"] = self.user_project - self._client._connection.api_request( - method="GET", - path=self.path, - query_params=qs_params, - timeout=timeout, - retry=retry, + self._client._get_resource( + self.path, query_params=qs_params, timeout=timeout, retry=retry, ) except NotFound: return False @@ -266,12 +262,8 @@ def reload(self, timeout=_DEFAULT_TIMEOUT, retry=DEFAULT_RETRY): if self.user_project is not None: qs_params["userProject"] = self.user_project - self._properties = self._client._connection.api_request( - method="GET", - path=self.path, - query_params=qs_params, - timeout=timeout, - retry=retry, + self._properties = self._client._get_resource( + self.path, query_params=qs_params, timeout=timeout, retry=retry, ) def update(self, timeout=_DEFAULT_TIMEOUT, retry=DEFAULT_RETRY_IF_ETAG_IN_JSON): diff --git a/google/cloud/storage/notification.py b/google/cloud/storage/notification.py index fde5e4559..5389ab51e 100644 --- a/google/cloud/storage/notification.py +++ b/google/cloud/storage/notification.py @@ -323,12 +323,8 @@ def exists(self, client=None, timeout=_DEFAULT_TIMEOUT, retry=DEFAULT_RETRY): query_params["userProject"] = self.bucket.user_project try: - client._connection.api_request( - method="GET", - path=self.path, - query_params=query_params, - timeout=timeout, - retry=retry, + client._get_resource( + self.path, query_params=query_params, timeout=timeout, retry=retry, ) except NotFound: return False @@ -381,12 +377,8 @@ def reload(self, client=None, timeout=_DEFAULT_TIMEOUT, retry=DEFAULT_RETRY): if self.bucket.user_project is not None: query_params["userProject"] = self.bucket.user_project - response = client._connection.api_request( - method="GET", - path=self.path, - query_params=query_params, - timeout=timeout, - retry=retry, + response = client._get_resource( + self.path, query_params=query_params, timeout=timeout, retry=retry, ) self._set_properties(response) diff --git a/tests/unit/test__helpers.py b/tests/unit/test__helpers.py index fa989f96e..f1c6b0436 100644 --- a/tests/unit/test__helpers.py +++ b/tests/unit/test__helpers.py @@ -66,6 +66,7 @@ def _derivedClass(self, path=None, user_project=None): class Derived(self._get_target_class()): client = None + _actual_encryption_headers = None @property def path(self): @@ -75,6 +76,9 @@ def path(self): def user_project(self): return user_project + def _encryption_headers(self): + return self._actual_encryption_headers or {} + return Derived def test_path_is_abstract(self): @@ -105,119 +109,130 @@ def test__query_params_w_user_project(self): derived = self._derivedClass("/path", user_project)() self.assertEqual(derived._query_params, {"userProject": user_project}) - def test_reload(self): - connection = _Connection({"foo": "Foo"}) - client = _Client(connection) - derived = self._derivedClass("/path")() + def test_reload_w_defaults(self): + path = "/path" + response = {"foo": "Foo"} + client = mock.Mock(spec=["_get_resource"]) + client._get_resource.return_value = response + derived = self._derivedClass(path)() # Make sure changes is not a set instance before calling reload # (which will clear / replace it with an empty set), checked below. derived._changes = object() - derived.reload(client=client, timeout=42) - self.assertEqual(derived._properties, {"foo": "Foo"}) - kw = connection._requested - self.assertEqual(len(kw), 1) - self.assertEqual( - kw[0], - { - "method": "GET", - "path": "/path", - "query_params": {"projection": "noAcl"}, - "headers": {}, - "_target_object": derived, - "timeout": 42, - "retry": DEFAULT_RETRY, - }, - ) + derived.client = client + + derived.reload() + + self.assertEqual(derived._properties, response) self.assertEqual(derived._changes, set()) - def test_reload_with_generation_match(self): - GENERATION_NUMBER = 9 - METAGENERATION_NUMBER = 6 + expected_query_params = {"projection": "noAcl"} + expected_headers = {} # no encryption headers by default + client._get_resource.assert_called_once_with( + path, + query_params=expected_query_params, + headers=expected_headers, + timeout=self._get_default_timeout(), + retry=DEFAULT_RETRY, + _target_object=derived, + ) - connection = _Connection({"foo": "Foo"}) - client = _Client(connection) - derived = self._derivedClass("/path")() + def test_reload_w_generation_match_w_timeout(self): + generation_number = 9 + metageneration_number = 6 + path = "/path" + timeout = 42 + response = {"foo": "Foo"} + client = mock.Mock(spec=["_get_resource"]) + client._get_resource.return_value = response + derived = self._derivedClass(path)() # Make sure changes is not a set instance before calling reload # (which will clear / replace it with an empty set), checked below. derived._changes = object() + derived.client = client + derived.reload( - client=client, - timeout=42, - if_generation_match=GENERATION_NUMBER, - if_metageneration_match=METAGENERATION_NUMBER, - ) - self.assertEqual(derived._properties, {"foo": "Foo"}) - kw = connection._requested - self.assertEqual(len(kw), 1) - self.assertEqual( - kw[0], - { - "method": "GET", - "path": "/path", - "query_params": { - "projection": "noAcl", - "ifGenerationMatch": GENERATION_NUMBER, - "ifMetagenerationMatch": METAGENERATION_NUMBER, - }, - "headers": {}, - "_target_object": derived, - "timeout": 42, - "retry": DEFAULT_RETRY, - }, + if_generation_match=generation_number, + if_metageneration_match=metageneration_number, + timeout=timeout, ) + + self.assertEqual(derived._properties, response) self.assertEqual(derived._changes, set()) - def test_reload_w_user_project(self): + expected_query_params = { + "projection": "noAcl", + "ifGenerationMatch": generation_number, + "ifMetagenerationMatch": metageneration_number, + } + expected_headers = {} # no encryption headers by default + client._get_resource.assert_called_once_with( + path, + query_params=expected_query_params, + headers=expected_headers, + timeout=timeout, + retry=DEFAULT_RETRY, + _target_object=derived, + ) + + def test_reload_w_user_project_w_retry(self): user_project = "user-project-123" - connection = _Connection({"foo": "Foo"}) - client = _Client(connection) - derived = self._derivedClass("/path", user_project)() + path = "/path" + retry = mock.Mock(spec=[]) + response = {"foo": "Foo"} + client = mock.Mock(spec=["_get_resource"]) + client._get_resource.return_value = response + derived = self._derivedClass(path, user_project)() # Make sure changes is not a set instance before calling reload # (which will clear / replace it with an empty set), checked below. derived._changes = object() - derived.reload(client=client) - self.assertEqual(derived._properties, {"foo": "Foo"}) - kw = connection._requested - self.assertEqual(len(kw), 1) - self.assertEqual( - kw[0], - { - "method": "GET", - "path": "/path", - "query_params": {"projection": "noAcl", "userProject": user_project}, - "headers": {}, - "_target_object": derived, - "timeout": self._get_default_timeout(), - "retry": DEFAULT_RETRY, - }, - ) + derived.client = client + + derived.reload(retry=retry) + + self.assertEqual(derived._properties, response) self.assertEqual(derived._changes, set()) - def test_reload_w_projection(self): - connection = _Connection({"foo": "Foo"}) - client = _Client(connection) - derived = self._derivedClass("/path")() + expected_query_params = { + "projection": "noAcl", + "userProject": user_project, + } + expected_headers = {} # no encryption headers by default + client._get_resource.assert_called_once_with( + path, + query_params=expected_query_params, + headers=expected_headers, + timeout=self._get_default_timeout(), + retry=retry, + _target_object=derived, + ) + + def test_reload_w_projection_w_explicit_client_w_enc_header(self): + path = "/path" + response = {"foo": "Foo"} + encryption_headers = {"bar": "Bar"} + client = mock.Mock(spec=["_get_resource"]) + client._get_resource.return_value = response + derived = self._derivedClass(path)() # Make sure changes is not a set instance before calling reload # (which will clear / replace it with an empty set), checked below. derived._changes = object() - derived.reload(projection="full", client=client, timeout=42) - self.assertEqual(derived._properties, {"foo": "Foo"}) - kw = connection._requested - self.assertEqual(len(kw), 1) - self.assertEqual( - kw[0], - { - "method": "GET", - "path": "/path", - "query_params": {"projection": "full"}, - "headers": {}, - "_target_object": derived, - "timeout": 42, - "retry": DEFAULT_RETRY, - }, - ) + derived._actual_encryption_headers = encryption_headers + + derived.reload(projection="full", client=client) + + self.assertEqual(derived._properties, response) self.assertEqual(derived._changes, set()) + expected_query_params = {"projection": "full"} + client._get_resource.assert_called_once_with( + path, + query_params=expected_query_params, + headers=encryption_headers, + timeout=self._get_default_timeout(), + retry=DEFAULT_RETRY, + _target_object=derived, + ) + def test__set_properties(self): mixin = self._make_one() self.assertEqual(mixin._properties, {}) diff --git a/tests/unit/test_acl.py b/tests/unit/test_acl.py index 47400f1ef..7cea5c8fc 100644 --- a/tests/unit/test_acl.py +++ b/tests/unit/test_acl.py @@ -14,6 +14,10 @@ import unittest +import mock + +from google.cloud.storage.retry import DEFAULT_RETRY + class Test_ACLEntity(unittest.TestCase): @staticmethod @@ -530,78 +534,82 @@ def test_get_entities_nonempty(self): entity = acl.entity(TYPE, ID) self.assertEqual(acl.get_entities(), [entity]) - def test_reload_missing(self): + def test_reload_missing_w_defaults(self): # https://github.com/GoogleCloudPlatform/google-cloud-python/issues/652 - ROLE = "role" - connection = _Connection({}) - client = _Client(connection) - acl = self._make_one() - acl.reload_path = "/testing/acl" + class Derived(self._get_target_class()): + client = None + + role = "role" + reload_path = "/testing/acl" + api_response = {} + acl = Derived() + acl.reload_path = reload_path acl.loaded = True - acl.entity("allUsers", ROLE) - acl.reload(client=client, timeout=42) + acl.entity("allUsers", role) + client = acl.client = mock.Mock(spec=["_get_resource"]) + client._get_resource.return_value = api_response + + acl.reload() + self.assertEqual(list(acl), []) - kw = connection._requested - self.assertEqual(len(kw), 1) - self.assertEqual( - kw[0], - { - "method": "GET", - "path": "/testing/acl", - "query_params": {}, - "timeout": 42, - }, + + expected_query_params = {} + client._get_resource.assert_called_once_with( + reload_path, + query_params=expected_query_params, + timeout=self._get_default_timeout(), + retry=DEFAULT_RETRY, ) - def test_reload_empty_result_clears_local(self): - ROLE = "role" - connection = _Connection({"items": []}) - client = _Client(connection) + def test_reload_w_empty_result_w_timeout_w_retry_w_explicit_client(self): + role = "role" + reload_path = "/testing/acl" + timeout = 42 + retry = mock.Mock(spec=[]) + api_response = {"items": []} acl = self._make_one() - acl.reload_path = "/testing/acl" + acl.reload_path = reload_path acl.loaded = True - acl.entity("allUsers", ROLE) + acl.entity("allUsers", role) + client = mock.Mock(spec=["_get_resource"]) + client._get_resource.return_value = api_response - acl.reload(client=client) + acl.reload(client=client, timeout=timeout, retry=retry) self.assertTrue(acl.loaded) self.assertEqual(list(acl), []) - kw = connection._requested - self.assertEqual(len(kw), 1) - self.assertEqual( - kw[0], - { - "method": "GET", - "path": "/testing/acl", - "query_params": {}, - "timeout": self._get_default_timeout(), - }, + + expected_query_params = {} + client._get_resource.assert_called_once_with( + reload_path, + query_params=expected_query_params, + timeout=timeout, + retry=retry, ) - def test_reload_nonempty_result_w_user_project(self): - ROLE = "role" - USER_PROJECT = "user-project-123" - connection = _Connection({"items": [{"entity": "allUsers", "role": ROLE}]}) - client = _Client(connection) + def test_reload_w_nonempty_result_w_user_project(self): + role = "role" + reload_path = "/testing/acl" + user_project = "user-project-123" + api_response = {"items": [{"entity": "allUsers", "role": role}]} acl = self._make_one() - acl.reload_path = "/testing/acl" + acl.reload_path = reload_path acl.loaded = True - acl.user_project = USER_PROJECT + acl.user_project = user_project + client = mock.Mock(spec=["_get_resource"]) + client._get_resource.return_value = api_response acl.reload(client=client) self.assertTrue(acl.loaded) - self.assertEqual(list(acl), [{"entity": "allUsers", "role": ROLE}]) - kw = connection._requested - self.assertEqual(len(kw), 1) - self.assertEqual( - kw[0], - { - "method": "GET", - "path": "/testing/acl", - "query_params": {"userProject": USER_PROJECT}, - "timeout": self._get_default_timeout(), - }, + self.assertEqual(list(acl), [{"entity": "allUsers", "role": role}]) + + expected_query_params = {"userProject": user_project} + client._get_resource.assert_called_once_with( + reload_path, + query_params=expected_query_params, + timeout=self._get_default_timeout(), + retry=DEFAULT_RETRY, ) def test_save_none_set_none_passed(self): diff --git a/tests/unit/test_blob.py b/tests/unit/test_blob.py index 50732a7f0..6d8fc7b33 100644 --- a/tests/unit/test_blob.py +++ b/tests/unit/test_blob.py @@ -688,117 +688,109 @@ def test_generate_signed_url_v4_w_credentials(self): credentials = object() self._generate_signed_url_v4_helper(credentials=credentials) - def test_exists_miss(self): - NONESUCH = "nonesuch" - not_found_response = ({"status": http_client.NOT_FOUND}, b"") - connection = _Connection(not_found_response) - client = _Client(connection) + def test_exists_miss_w_defaults(self): + from google.cloud.exceptions import NotFound + + blob_name = "nonesuch" + client = mock.Mock(spec=["_get_resource"]) + client._get_resource.side_effect = NotFound("testing") bucket = _Bucket(client) - blob = self._make_one(NONESUCH, bucket=bucket) - self.assertFalse(blob.exists(timeout=42)) - self.assertEqual(len(connection._requested), 1) - self.assertEqual( - connection._requested[0], - { - "method": "GET", - "path": "/b/name/o/{}".format(NONESUCH), - "query_params": {"fields": "name"}, - "_target_object": None, - "timeout": 42, - "retry": DEFAULT_RETRY, - }, + blob = self._make_one(blob_name, bucket=bucket) + + self.assertFalse(blob.exists()) + + expected_query_params = {"fields": "name"} + client._get_resource.assert_called_once_with( + blob.path, + query_params=expected_query_params, + timeout=self._get_default_timeout(), + retry=DEFAULT_RETRY, + _target_object=None, ) - def test_exists_hit_w_user_project(self): - BLOB_NAME = "blob-name" - USER_PROJECT = "user-project-123" - found_response = ({"status": http_client.OK}, b"") - connection = _Connection(found_response) - client = _Client(connection) - bucket = _Bucket(client, user_project=USER_PROJECT) - blob = self._make_one(BLOB_NAME, bucket=bucket) - bucket._blobs[BLOB_NAME] = 1 - self.assertTrue(blob.exists()) - self.assertEqual(len(connection._requested), 1) - self.assertEqual( - connection._requested[0], - { - "method": "GET", - "path": "/b/name/o/{}".format(BLOB_NAME), - "query_params": {"fields": "name", "userProject": USER_PROJECT}, - "_target_object": None, - "timeout": self._get_default_timeout(), - "retry": DEFAULT_RETRY, - }, + def test_exists_hit_w_user_project_w_timeout(self): + blob_name = "blob-name" + user_project = "user-project-123" + timeout = 42 + api_response = {"name": blob_name} + client = mock.Mock(spec=["_get_resource"]) + client._get_resource.return_value = api_response + bucket = _Bucket(client, user_project=user_project) + blob = self._make_one(blob_name, bucket=bucket) + + self.assertTrue(blob.exists(timeout=timeout)) + + expected_query_params = {"fields": "name", "userProject": user_project} + client._get_resource.assert_called_once_with( + blob.path, + query_params=expected_query_params, + timeout=timeout, + retry=DEFAULT_RETRY, + _target_object=None, ) - def test_exists_hit_w_generation(self): - BLOB_NAME = "blob-name" - GENERATION = 123456 - found_response = ({"status": http_client.OK}, b"") - connection = _Connection(found_response) - client = _Client(connection) + def test_exists_hit_w_generation_w_retry(self): + blob_name = "blob-name" + generation = 123456 + api_response = {"name": blob_name} + retry = mock.Mock(spec=[]) + client = mock.Mock(spec=["_get_resource"]) + client._get_resource.return_value = api_response bucket = _Bucket(client) - blob = self._make_one(BLOB_NAME, bucket=bucket, generation=GENERATION) - bucket._blobs[BLOB_NAME] = 1 - self.assertTrue(blob.exists()) - self.assertEqual(len(connection._requested), 1) - self.assertEqual( - connection._requested[0], - { - "method": "GET", - "path": "/b/name/o/{}".format(BLOB_NAME), - "query_params": {"fields": "name", "generation": GENERATION}, - "_target_object": None, - "timeout": self._get_default_timeout(), - "retry": DEFAULT_RETRY, - }, + blob = self._make_one(blob_name, bucket=bucket, generation=generation) + + self.assertTrue(blob.exists(retry=retry)) + + expected_query_params = {"fields": "name", "generation": generation} + client._get_resource.assert_called_once_with( + blob.path, + query_params=expected_query_params, + timeout=self._get_default_timeout(), + retry=retry, + _target_object=None, ) def test_exists_w_generation_match(self): - BLOB_NAME = "blob-name" - GENERATION_NUMBER = 123456 - METAGENERATION_NUMBER = 6 - - found_response = ({"status": http_client.OK}, b"") - connection = _Connection(found_response) - client = _Client(connection) + blob_name = "blob-name" + generation_number = 123456 + metageneration_number = 6 + api_response = {"name": blob_name} + client = mock.Mock(spec=["_get_resource"]) + client._get_resource.return_value = api_response bucket = _Bucket(client) - blob = self._make_one(BLOB_NAME, bucket=bucket) - bucket._blobs[BLOB_NAME] = 1 + blob = self._make_one(blob_name, bucket=bucket) + self.assertTrue( blob.exists( - if_generation_match=GENERATION_NUMBER, - if_metageneration_match=METAGENERATION_NUMBER, + if_generation_match=generation_number, + if_metageneration_match=metageneration_number, + retry=None, ) ) - self.assertEqual(len(connection._requested), 1) - self.assertEqual( - connection._requested[0], - { - "method": "GET", - "path": "/b/name/o/{}".format(BLOB_NAME), - "query_params": { - "fields": "name", - "ifGenerationMatch": GENERATION_NUMBER, - "ifMetagenerationMatch": METAGENERATION_NUMBER, - }, - "_target_object": None, - "timeout": self._get_default_timeout(), - "retry": DEFAULT_RETRY, - }, + + expected_query_params = { + "fields": "name", + "ifGenerationMatch": generation_number, + "ifMetagenerationMatch": metageneration_number, + } + client._get_resource.assert_called_once_with( + blob.path, + query_params=expected_query_params, + timeout=self._get_default_timeout(), + retry=None, + _target_object=None, ) def test_delete_wo_generation(self): BLOB_NAME = "blob-name" - not_found_response = ({"status": http_client.NOT_FOUND}, b"") - connection = _Connection(not_found_response) + connection = _Connection() # no requests will be made client = _Client(connection) bucket = _Bucket(client) blob = self._make_one(BLOB_NAME, bucket=bucket) bucket._blobs[BLOB_NAME] = 1 + blob.delete() - self.assertFalse(blob.exists()) + self.assertEqual( bucket._deleted, [ @@ -819,14 +811,14 @@ def test_delete_wo_generation(self): def test_delete_w_generation(self): BLOB_NAME = "blob-name" GENERATION = 123456 - not_found_response = ({"status": http_client.NOT_FOUND}, b"") - connection = _Connection(not_found_response) + connection = _Connection() # no requests will be made client = _Client(connection) bucket = _Bucket(client) blob = self._make_one(BLOB_NAME, bucket=bucket, generation=GENERATION) bucket._blobs[BLOB_NAME] = 1 + blob.delete(timeout=42) - self.assertFalse(blob.exists()) + self.assertEqual( bucket._deleted, [ @@ -847,14 +839,14 @@ def test_delete_w_generation(self): def test_delete_w_generation_match(self): BLOB_NAME = "blob-name" GENERATION = 123456 - not_found_response = ({"status": http_client.NOT_FOUND}, b"") - connection = _Connection(not_found_response) + connection = _Connection() # no requests will be made client = _Client(connection) bucket = _Bucket(client) blob = self._make_one(BLOB_NAME, bucket=bucket, generation=GENERATION) bucket._blobs[BLOB_NAME] = 1 + blob.delete(timeout=42, if_generation_match=GENERATION) - self.assertFalse(blob.exists()) + self.assertEqual( bucket._deleted, [ @@ -3146,139 +3138,128 @@ def test_create_resumable_upload_session_with_failure(self): self.assertIn(message, exc_info.exception.message) self.assertEqual(exc_info.exception.errors, []) - def test_get_iam_policy(self): + def test_get_iam_policy_defaults(self): from google.cloud.storage.iam import STORAGE_OWNER_ROLE from google.cloud.storage.iam import STORAGE_EDITOR_ROLE from google.cloud.storage.iam import STORAGE_VIEWER_ROLE from google.api_core.iam import Policy - BLOB_NAME = "blob-name" - PATH = "/b/name/o/%s" % (BLOB_NAME,) - ETAG = "DEADBEEF" - VERSION = 1 - OWNER1 = "user:phred@example.com" - OWNER2 = "group:cloud-logs@google.com" - EDITOR1 = "domain:google.com" - EDITOR2 = "user:phred@example.com" - VIEWER1 = "serviceAccount:1234-abcdef@service.example.com" - VIEWER2 = "user:phred@example.com" - RETURNED = { - "resourceId": PATH, - "etag": ETAG, - "version": VERSION, + blob_name = "blob-name" + path = "/b/name/o/%s" % (blob_name,) + etag = "DEADBEEF" + version = 1 + owner1 = "user:phred@example.com" + owner2 = "group:cloud-logs@google.com" + editor1 = "domain:google.com" + editor2 = "user:phred@example.com" + viewer1 = "serviceAccount:1234-abcdef@service.example.com" + viewer2 = "user:phred@example.com" + api_response = { + "resourceId": path, + "etag": etag, + "version": version, "bindings": [ - {"role": STORAGE_OWNER_ROLE, "members": [OWNER1, OWNER2]}, - {"role": STORAGE_EDITOR_ROLE, "members": [EDITOR1, EDITOR2]}, - {"role": STORAGE_VIEWER_ROLE, "members": [VIEWER1, VIEWER2]}, + {"role": STORAGE_OWNER_ROLE, "members": [owner1, owner2]}, + {"role": STORAGE_EDITOR_ROLE, "members": [editor1, editor2]}, + {"role": STORAGE_VIEWER_ROLE, "members": [viewer1, viewer2]}, ], } - after = ({"status": http_client.OK}, RETURNED) - EXPECTED = { - binding["role"]: set(binding["members"]) for binding in RETURNED["bindings"] + expected_policy = { + binding["role"]: set(binding["members"]) + for binding in api_response["bindings"] } - connection = _Connection(after) - client = _Client(connection) + client = mock.Mock(spec=["_get_resource"]) + client._get_resource.return_value = api_response bucket = _Bucket(client=client) - blob = self._make_one(BLOB_NAME, bucket=bucket) + blob = self._make_one(blob_name, bucket=bucket) - policy = blob.get_iam_policy(timeout=42) + policy = blob.get_iam_policy() self.assertIsInstance(policy, Policy) - self.assertEqual(policy.etag, RETURNED["etag"]) - self.assertEqual(policy.version, RETURNED["version"]) - self.assertEqual(dict(policy), EXPECTED) - - kw = connection._requested - self.assertEqual(len(kw), 1) - self.assertEqual( - kw[0], - { - "method": "GET", - "path": "%s/iam" % (PATH,), - "query_params": {}, - "_target_object": None, - "timeout": 42, - "retry": DEFAULT_RETRY, - }, + self.assertEqual(policy.etag, api_response["etag"]) + self.assertEqual(policy.version, api_response["version"]) + self.assertEqual(dict(policy), expected_policy) + + expected_path = "%s/iam" % (path,) + expected_query_params = {} + client._get_resource.assert_called_once_with( + expected_path, + query_params=expected_query_params, + timeout=self._get_default_timeout(), + retry=DEFAULT_RETRY, + _target_object=None, ) - def test_get_iam_policy_w_requested_policy_version(self): - from google.cloud.storage.iam import STORAGE_OWNER_ROLE + def test_get_iam_policy_w_user_project_w_timeout(self): + from google.api_core.iam import Policy - BLOB_NAME = "blob-name" - PATH = "/b/name/o/%s" % (BLOB_NAME,) - ETAG = "DEADBEEF" - VERSION = 1 - OWNER1 = "user:phred@example.com" - OWNER2 = "group:cloud-logs@google.com" - RETURNED = { - "resourceId": PATH, - "etag": ETAG, - "version": VERSION, - "bindings": [{"role": STORAGE_OWNER_ROLE, "members": [OWNER1, OWNER2]}], + blob_name = "blob-name" + user_project = "user-project-123" + timeout = 42 + path = "/b/name/o/%s" % (blob_name,) + etag = "DEADBEEF" + version = 1 + api_response = { + "resourceId": path, + "etag": etag, + "version": version, + "bindings": [], } - after = ({"status": http_client.OK}, RETURNED) - connection = _Connection(after) - client = _Client(connection) - bucket = _Bucket(client=client) - blob = self._make_one(BLOB_NAME, bucket=bucket) + expected_policy = {} + client = mock.Mock(spec=["_get_resource"]) + client._get_resource.return_value = api_response + bucket = _Bucket(client=client, user_project=user_project) + blob = self._make_one(blob_name, bucket=bucket) - blob.get_iam_policy(requested_policy_version=3) + policy = blob.get_iam_policy(timeout=42) - kw = connection._requested - self.assertEqual(len(kw), 1) - self.assertEqual( - kw[0], - { - "method": "GET", - "path": "%s/iam" % (PATH,), - "query_params": {"optionsRequestedPolicyVersion": 3}, - "_target_object": None, - "timeout": self._get_default_timeout(), - "retry": DEFAULT_RETRY, - }, + self.assertIsInstance(policy, Policy) + self.assertEqual(policy.etag, api_response["etag"]) + self.assertEqual(policy.version, api_response["version"]) + self.assertEqual(dict(policy), expected_policy) + + expected_path = "%s/iam" % (path,) + expected_query_params = {"userProject": user_project} + client._get_resource.assert_called_once_with( + expected_path, + query_params=expected_query_params, + timeout=timeout, + retry=DEFAULT_RETRY, + _target_object=None, ) - def test_get_iam_policy_w_user_project(self): - from google.api_core.iam import Policy + def test_get_iam_policy_w_requested_policy_version(self): + from google.cloud.storage.iam import STORAGE_OWNER_ROLE - BLOB_NAME = "blob-name" - USER_PROJECT = "user-project-123" - PATH = "/b/name/o/%s" % (BLOB_NAME,) - ETAG = "DEADBEEF" - VERSION = 1 - RETURNED = { - "resourceId": PATH, - "etag": ETAG, - "version": VERSION, - "bindings": [], + blob_name = "blob-name" + path = "/b/name/o/%s" % (blob_name,) + etag = "DEADBEEF" + version = 3 + owner1 = "user:phred@example.com" + owner2 = "group:cloud-logs@google.com" + api_response = { + "resourceId": path, + "etag": etag, + "version": version, + "bindings": [{"role": STORAGE_OWNER_ROLE, "members": [owner1, owner2]}], } - after = ({"status": http_client.OK}, RETURNED) - EXPECTED = {} - connection = _Connection(after) - client = _Client(connection) - bucket = _Bucket(client=client, user_project=USER_PROJECT) - blob = self._make_one(BLOB_NAME, bucket=bucket) + client = mock.Mock(spec=["_get_resource"]) + client._get_resource.return_value = api_response + bucket = _Bucket(client=client) + blob = self._make_one(blob_name, bucket=bucket) - policy = blob.get_iam_policy() + policy = blob.get_iam_policy(requested_policy_version=version) - self.assertIsInstance(policy, Policy) - self.assertEqual(policy.etag, RETURNED["etag"]) - self.assertEqual(policy.version, RETURNED["version"]) - self.assertEqual(dict(policy), EXPECTED) + self.assertEqual(policy.version, version) - kw = connection._requested - self.assertEqual(len(kw), 1) - self.assertEqual( - kw[0], - { - "method": "GET", - "path": "%s/iam" % (PATH,), - "query_params": {"userProject": USER_PROJECT}, - "_target_object": None, - "timeout": self._get_default_timeout(), - "retry": DEFAULT_RETRY, - }, + expected_path = "%s/iam" % (path,) + expected_query_params = {"optionsRequestedPolicyVersion": version} + client._get_resource.assert_called_once_with( + expected_path, + query_params=expected_query_params, + timeout=self._get_default_timeout(), + retry=DEFAULT_RETRY, + _target_object=None, ) def test_set_iam_policy(self): @@ -3367,71 +3348,75 @@ def test_set_iam_policy_w_user_project(self): self.assertEqual(kw[0]["query_params"], {"userProject": USER_PROJECT}) self.assertEqual(kw[0]["data"], {"resourceId": PATH}) - def test_test_iam_permissions(self): + def test_test_iam_permissions_defaults(self): from google.cloud.storage.iam import STORAGE_OBJECTS_LIST from google.cloud.storage.iam import STORAGE_BUCKETS_GET from google.cloud.storage.iam import STORAGE_BUCKETS_UPDATE - BLOB_NAME = "blob-name" - PATH = "/b/name/o/%s" % (BLOB_NAME,) - PERMISSIONS = [ + blob_name = "blob-name" + permissions = [ STORAGE_OBJECTS_LIST, STORAGE_BUCKETS_GET, STORAGE_BUCKETS_UPDATE, ] - ALLOWED = PERMISSIONS[1:] - RETURNED = {"permissions": ALLOWED} - after = ({"status": http_client.OK}, RETURNED) - connection = _Connection(after) - client = _Client(connection) + expected = permissions[1:] + api_response = {"permissions": expected} + client = mock.Mock(spec=["_get_resource"]) + client._get_resource.return_value = api_response bucket = _Bucket(client=client) - blob = self._make_one(BLOB_NAME, bucket=bucket) + blob = self._make_one(blob_name, bucket=bucket) - allowed = blob.test_iam_permissions(PERMISSIONS, timeout=42) + found = blob.test_iam_permissions(permissions) - self.assertEqual(allowed, ALLOWED) + self.assertEqual(found, expected) - kw = connection._requested - self.assertEqual(len(kw), 1) - self.assertEqual(kw[0]["method"], "GET") - self.assertEqual(kw[0]["path"], "%s/iam/testPermissions" % (PATH,)) - self.assertEqual(kw[0]["query_params"], {"permissions": PERMISSIONS}) - self.assertEqual(kw[0]["timeout"], 42) + expected_path = "/b/name/o/%s/iam/testPermissions" % (blob_name,) + expected_query_params = {"permissions": permissions} + client._get_resource.assert_called_once_with( + expected_path, + query_params=expected_query_params, + timeout=self._get_default_timeout(), + retry=DEFAULT_RETRY, + _target_object=None, + ) - def test_test_iam_permissions_w_user_project(self): + def test_test_iam_permissions_w_user_project_w_timeout_w_retry(self): from google.cloud.storage.iam import STORAGE_OBJECTS_LIST from google.cloud.storage.iam import STORAGE_BUCKETS_GET from google.cloud.storage.iam import STORAGE_BUCKETS_UPDATE - BLOB_NAME = "blob-name" - USER_PROJECT = "user-project-123" - PATH = "/b/name/o/%s" % (BLOB_NAME,) - PERMISSIONS = [ + blob_name = "blob-name" + user_project = "user-project-123" + timeout = 42 + retry = mock.Mock(spec=[]) + permissions = [ STORAGE_OBJECTS_LIST, STORAGE_BUCKETS_GET, STORAGE_BUCKETS_UPDATE, ] - ALLOWED = PERMISSIONS[1:] - RETURNED = {"permissions": ALLOWED} - after = ({"status": http_client.OK}, RETURNED) - connection = _Connection(after) - client = _Client(connection) - bucket = _Bucket(client=client, user_project=USER_PROJECT) - blob = self._make_one(BLOB_NAME, bucket=bucket) + expected = permissions[1:] + api_response = {"permissions": expected} + client = mock.Mock(spec=["_get_resource"]) + client._get_resource.return_value = api_response + bucket = _Bucket(client=client, user_project=user_project) + blob = self._make_one(blob_name, bucket=bucket) - allowed = blob.test_iam_permissions(PERMISSIONS) + found = blob.test_iam_permissions(permissions, timeout=timeout, retry=retry) - self.assertEqual(allowed, ALLOWED) + self.assertEqual(found, expected) - kw = connection._requested - self.assertEqual(len(kw), 1) - self.assertEqual(kw[0]["method"], "GET") - self.assertEqual(kw[0]["path"], "%s/iam/testPermissions" % (PATH,)) - self.assertEqual( - kw[0]["query_params"], - {"permissions": PERMISSIONS, "userProject": USER_PROJECT}, + expected_path = "/b/name/o/%s/iam/testPermissions" % (blob_name,) + expected_query_params = { + "permissions": permissions, + "userProject": user_project, + } + client._get_resource.assert_called_once_with( + expected_path, + query_params=expected_query_params, + timeout=timeout, + retry=retry, + _target_object=None, ) - self.assertEqual(kw[0]["timeout"], self._get_default_timeout()) def test_make_public(self): from google.cloud.storage.acl import _ACLEntity @@ -4899,11 +4884,7 @@ def _respond(self, **kw): return response def api_request(self, **kw): - from google.cloud.exceptions import NotFound - info, content = self._respond(**kw) - if info.get("status") == http_client.NOT_FOUND: - raise NotFound(info) return content diff --git a/tests/unit/test_bucket.py b/tests/unit/test_bucket.py index 22984a343..5ff2209c7 100644 --- a/tests/unit/test_bucket.py +++ b/tests/unit/test_bucket.py @@ -650,95 +650,72 @@ def test_user_project(self): bucket._user_project = USER_PROJECT self.assertEqual(bucket.user_project, USER_PROJECT) - def test_exists_miss(self): + def test_exists_miss_w_defaults(self): from google.cloud.exceptions import NotFound - class _FakeConnection(object): + bucket_name = "bucket-name" + client = mock.Mock(spec=["_get_resource"]) + client._get_resource.side_effect = NotFound("testing") + bucket = self._make_one(client, name=bucket_name) - _called_with = [] + self.assertFalse(bucket.exists()) - @classmethod - def api_request(cls, *args, **kwargs): - cls._called_with.append((args, kwargs)) - raise NotFound(args) - - BUCKET_NAME = "bucket-name" - bucket = self._make_one(name=BUCKET_NAME) - client = _Client(_FakeConnection) - self.assertFalse(bucket.exists(client=client, timeout=42)) - expected_called_kwargs = { - "method": "GET", - "path": bucket.path, - "query_params": {"fields": "name"}, - "_target_object": None, - "timeout": 42, - "retry": DEFAULT_RETRY, - } - expected_cw = [((), expected_called_kwargs)] - self.assertEqual(_FakeConnection._called_with, expected_cw) - - def test_exists_with_metageneration_match(self): - class _FakeConnection(object): - - _called_with = [] - - @classmethod - def api_request(cls, *args, **kwargs): - cls._called_with.append((args, kwargs)) - # exists() does not use the return value - return object() + expected_query_params = {"fields": "name"} + client._get_resource.assert_called_once_with( + bucket.path, + query_params=expected_query_params, + timeout=self._get_default_timeout(), + retry=DEFAULT_RETRY, + _target_object=None, + ) - BUCKET_NAME = "bucket-name" - METAGENERATION_NUMBER = 6 + def test_exists_w_metageneration_match_w_timeout(self): + bucket_name = "bucket-name" + metageneration_number = 6 + timeout = 42 + api_response = {"name": bucket_name} + client = mock.Mock(spec=["_get_resource"]) + client._get_resource.return_value = api_response + bucket = self._make_one(client, name=bucket_name) - bucket = self._make_one(name=BUCKET_NAME) - client = _Client(_FakeConnection) self.assertTrue( - bucket.exists( - client=client, timeout=42, if_metageneration_match=METAGENERATION_NUMBER - ) + bucket.exists(timeout=42, if_metageneration_match=metageneration_number) ) - expected_called_kwargs = { - "method": "GET", - "path": bucket.path, - "query_params": { - "fields": "name", - "ifMetagenerationMatch": METAGENERATION_NUMBER, - }, - "_target_object": None, - "timeout": 42, - "retry": DEFAULT_RETRY, - } - expected_cw = [((), expected_called_kwargs)] - self.assertEqual(_FakeConnection._called_with, expected_cw) - - def test_exists_hit_w_user_project(self): - USER_PROJECT = "user-project-123" - class _FakeConnection(object): + expected_query_params = { + "fields": "name", + "ifMetagenerationMatch": metageneration_number, + } + client._get_resource.assert_called_once_with( + bucket.path, + query_params=expected_query_params, + timeout=timeout, + retry=DEFAULT_RETRY, + _target_object=None, + ) - _called_with = [] + def test_exists_hit_w_user_project_w_retry_w_explicit_client(self): + bucket_name = "bucket-name" + user_project = "user-project-123" + retry = mock.Mock(spec=[]) + api_response = {"name": bucket_name} + client = mock.Mock(spec=["_get_resource"]) + client._get_resource.return_value = api_response + bucket = self._make_one(name=bucket_name, user_project=user_project) - @classmethod - def api_request(cls, *args, **kwargs): - cls._called_with.append((args, kwargs)) - # exists() does not use the return value - return object() + self.assertTrue(bucket.exists(client=client, retry=retry)) - BUCKET_NAME = "bucket-name" - bucket = self._make_one(name=BUCKET_NAME, user_project=USER_PROJECT) - client = _Client(_FakeConnection) - self.assertTrue(bucket.exists(client=client)) - expected_called_kwargs = { - "method": "GET", - "path": bucket.path, - "query_params": {"fields": "name", "userProject": USER_PROJECT}, - "_target_object": None, - "timeout": self._get_default_timeout(), - "retry": DEFAULT_RETRY, + expected_query_params = { + "fields": "name", + "userProject": user_project, } - expected_cw = [((), expected_called_kwargs)] - self.assertEqual(_FakeConnection._called_with, expected_cw) + client._get_resource.assert_called_once_with( + bucket.path, + query_params=expected_query_params, + timeout=self._get_default_timeout(), + retry=retry, + _target_object=None, + ) def test_acl_property(self): from google.cloud.storage.acl import BucketACL @@ -765,97 +742,173 @@ def test_path_w_name(self): bucket = self._make_one(name=NAME) self.assertEqual(bucket.path, "/b/%s" % NAME) - def test_get_blob_miss(self): - NAME = "name" - NONESUCH = "nonesuch" - connection = _Connection() - client = _Client(connection) - bucket = self._make_one(name=NAME) - result = bucket.get_blob(NONESUCH, client=client, timeout=42) + def test_get_blob_miss_w_defaults(self): + from google.cloud.exceptions import NotFound + from google.cloud.storage.blob import Blob + + name = "name" + blob_name = "nonesuch" + client = mock.Mock(spec=["_get_resource"]) + client._get_resource.side_effect = NotFound("testing") + bucket = self._make_one(client, name=name) + + result = bucket.get_blob(blob_name) + self.assertIsNone(result) - (kw,) = connection._requested - self.assertEqual(kw["method"], "GET") - self.assertEqual(kw["path"], "/b/%s/o/%s" % (NAME, NONESUCH)) - self.assertEqual(kw["timeout"], 42) + + expected_path = "/b/%s/o/%s" % (name, blob_name) + expected_query_params = {"projection": "noAcl"} + expected_headers = {} + client._get_resource.assert_called_once_with( + expected_path, + query_params=expected_query_params, + headers=expected_headers, + timeout=self._get_default_timeout(), + retry=DEFAULT_RETRY, + _target_object=mock.ANY, + ) + + target = client._get_resource.call_args[1]["_target_object"] + self.assertIsInstance(target, Blob) + self.assertIs(target.bucket, bucket) + self.assertEqual(target.name, blob_name) def test_get_blob_hit_w_user_project(self): - NAME = "name" - BLOB_NAME = "blob-name" - USER_PROJECT = "user-project-123" - connection = _Connection({"name": BLOB_NAME}) - client = _Client(connection) - bucket = self._make_one(name=NAME, user_project=USER_PROJECT) - blob = bucket.get_blob(BLOB_NAME, client=client) + from google.cloud.storage.blob import Blob + + name = "name" + blob_name = "blob-name" + user_project = "user-project-123" + api_response = {"name": blob_name} + client = mock.Mock(spec=["_get_resource"]) + client._get_resource.return_value = api_response + bucket = self._make_one(client, name=name, user_project=user_project) + + blob = bucket.get_blob(blob_name, client=client) + + self.assertIsInstance(blob, Blob) self.assertIs(blob.bucket, bucket) - self.assertEqual(blob.name, BLOB_NAME) - (kw,) = connection._requested - expected_qp = {"userProject": USER_PROJECT, "projection": "noAcl"} - self.assertEqual(kw["method"], "GET") - self.assertEqual(kw["path"], "/b/%s/o/%s" % (NAME, BLOB_NAME)) - self.assertEqual(kw["query_params"], expected_qp) - self.assertEqual(kw["timeout"], self._get_default_timeout()) + self.assertEqual(blob.name, blob_name) - def test_get_blob_hit_w_generation(self): - NAME = "name" - BLOB_NAME = "blob-name" - GENERATION = 1512565576797178 - connection = _Connection({"name": BLOB_NAME, "generation": GENERATION}) - client = _Client(connection) - bucket = self._make_one(name=NAME) - blob = bucket.get_blob(BLOB_NAME, client=client, generation=GENERATION) + expected_path = "/b/%s/o/%s" % (name, blob_name) + expected_query_params = { + "userProject": user_project, + "projection": "noAcl", + } + expected_headers = {} + client._get_resource.assert_called_once_with( + expected_path, + query_params=expected_query_params, + headers=expected_headers, + timeout=self._get_default_timeout(), + retry=DEFAULT_RETRY, + _target_object=blob, + ) + + def test_get_blob_hit_w_generation_w_timeout(self): + from google.cloud.storage.blob import Blob + + name = "name" + blob_name = "blob-name" + generation = 1512565576797178 + timeout = 42 + api_response = {"name": blob_name, "generation": generation} + client = mock.Mock(spec=["_get_resource"]) + client._get_resource.return_value = api_response + bucket = self._make_one(client, name=name) + + blob = bucket.get_blob(blob_name, generation=generation, timeout=timeout) + + self.assertIsInstance(blob, Blob) self.assertIs(blob.bucket, bucket) - self.assertEqual(blob.name, BLOB_NAME) - self.assertEqual(blob.generation, GENERATION) - (kw,) = connection._requested - expected_qp = {"generation": GENERATION, "projection": "noAcl"} - self.assertEqual(kw["method"], "GET") - self.assertEqual(kw["path"], "/b/%s/o/%s" % (NAME, BLOB_NAME)) - self.assertEqual(kw["query_params"], expected_qp) - self.assertEqual(kw["timeout"], self._get_default_timeout()) + self.assertEqual(blob.name, blob_name) + self.assertEqual(blob.generation, generation) - def test_get_blob_w_generation_match(self): - NAME = "name" - BLOB_NAME = "blob-name" - GENERATION = 1512565576797178 + expected_path = "/b/%s/o/%s" % (name, blob_name) + expected_query_params = { + "generation": generation, + "projection": "noAcl", + } + expected_headers = {} + client._get_resource.assert_called_once_with( + expected_path, + query_params=expected_query_params, + headers=expected_headers, + timeout=timeout, + retry=DEFAULT_RETRY, + _target_object=blob, + ) - connection = _Connection({"name": BLOB_NAME, "generation": GENERATION}) - client = _Client(connection) - bucket = self._make_one(name=NAME) - blob = bucket.get_blob(BLOB_NAME, client=client, if_generation_match=GENERATION) + def test_get_blob_w_generation_match_w_retry(self): + from google.cloud.storage.blob import Blob + name = "name" + blob_name = "blob-name" + generation = 1512565576797178 + retry = mock.Mock(spec=[]) + api_response = {"name": blob_name, "generation": generation} + client = mock.Mock(spec=["_get_resource"]) + client._get_resource.return_value = api_response + bucket = self._make_one(client, name=name) + + blob = bucket.get_blob(blob_name, if_generation_match=generation, retry=retry) + + self.assertIsInstance(blob, Blob) self.assertIs(blob.bucket, bucket) - self.assertEqual(blob.name, BLOB_NAME) - self.assertEqual(blob.generation, GENERATION) - (kw,) = connection._requested - expected_qp = {"ifGenerationMatch": GENERATION, "projection": "noAcl"} - self.assertEqual(kw["method"], "GET") - self.assertEqual(kw["path"], "/b/%s/o/%s" % (NAME, BLOB_NAME)) - self.assertEqual(kw["query_params"], expected_qp) - self.assertEqual(kw["timeout"], self._get_default_timeout()) + self.assertEqual(blob.name, blob_name) + self.assertEqual(blob.generation, generation) - def test_get_blob_hit_with_kwargs(self): + expected_path = "/b/%s/o/%s" % (name, blob_name) + expected_query_params = { + "ifGenerationMatch": generation, + "projection": "noAcl", + } + expected_headers = {} + client._get_resource.assert_called_once_with( + expected_path, + query_params=expected_query_params, + headers=expected_headers, + timeout=self._get_default_timeout(), + retry=retry, + _target_object=blob, + ) + + def test_get_blob_hit_with_kwargs_w_explicit_client(self): + from google.cloud.storage.blob import Blob from google.cloud.storage.blob import _get_encryption_headers - NAME = "name" - BLOB_NAME = "blob-name" - CHUNK_SIZE = 1024 * 1024 - KEY = b"01234567890123456789012345678901" # 32 bytes + name = "name" + blob_name = "blob-name" + chunk_size = 1024 * 1024 + key = b"01234567890123456789012345678901" # 32 bytes + api_response = {"name": blob_name} + client = mock.Mock(spec=["_get_resource"]) + client._get_resource.return_value = api_response + bucket = self._make_one(name=name) - connection = _Connection({"name": BLOB_NAME}) - client = _Client(connection) - bucket = self._make_one(name=NAME) blob = bucket.get_blob( - BLOB_NAME, client=client, encryption_key=KEY, chunk_size=CHUNK_SIZE + blob_name, client=client, encryption_key=key, chunk_size=chunk_size ) + + self.assertIsInstance(blob, Blob) self.assertIs(blob.bucket, bucket) - self.assertEqual(blob.name, BLOB_NAME) - (kw,) = connection._requested - self.assertEqual(kw["method"], "GET") - self.assertEqual(kw["path"], "/b/%s/o/%s" % (NAME, BLOB_NAME)) - self.assertEqual(kw["headers"], _get_encryption_headers(KEY)) - self.assertEqual(kw["timeout"], self._get_default_timeout()) - self.assertEqual(blob.chunk_size, CHUNK_SIZE) - self.assertEqual(blob._encryption_key, KEY) + self.assertEqual(blob.name, blob_name) + self.assertEqual(blob.chunk_size, chunk_size) + self.assertEqual(blob._encryption_key, key) + + expected_path = "/b/%s/o/%s" % (name, blob_name) + expected_query_params = { + "projection": "noAcl", + } + expected_headers = _get_encryption_headers(key) + client._get_resource.assert_called_once_with( + expected_path, + query_params=expected_query_params, + headers=expected_headers, + timeout=self._get_default_timeout(), + retry=DEFAULT_RETRY, + _target_object=blob, + ) def test_list_blobs_defaults(self): NAME = "name" @@ -982,44 +1035,76 @@ def test_list_notifications(self): notification.payload_format, resource.get("payload_format") ) - def test_get_notification(self): + def test_get_notification_miss_w_defaults(self): + from google.cloud.exceptions import NotFound + + project = "my-project-123" + name = "name" + notification_id = "1" + + client = mock.Mock(spec=["_get_resource", "project"]) + client._get_resource.side_effect = NotFound("testing") + client.project = project + bucket = self._make_one(client=client, name=name) + + with self.assertRaises(NotFound): + bucket.get_notification(notification_id=notification_id) + + expected_path = "/b/{}/notificationConfigs/{}".format(name, notification_id) + expected_query_params = {} + client._get_resource.assert_called_once_with( + expected_path, + query_params=expected_query_params, + timeout=self._get_default_timeout(), + retry=DEFAULT_RETRY, + ) + + def test_get_notification_hit_w_explicit_w_user_project(self): + from google.cloud.storage.notification import BucketNotification from google.cloud.storage.notification import _TOPIC_REF_FMT from google.cloud.storage.notification import JSON_API_V1_PAYLOAD_FORMAT - NAME = "name" - ETAG = "FACECABB" - NOTIFICATION_ID = "1" - SELF_LINK = "https://example.com/notification/1" - resources = { + project = "my-project-123" + user_project = "user-project-456" + name = "name" + etag = "FACECABB" + notification_id = "1" + self_link = "https://example.com/notification/1" + api_response = { "topic": _TOPIC_REF_FMT.format("my-project-123", "topic-1"), - "id": NOTIFICATION_ID, - "etag": ETAG, - "selfLink": SELF_LINK, + "id": notification_id, + "etag": etag, + "selfLink": self_link, "payload_format": JSON_API_V1_PAYLOAD_FORMAT, } + timeout = 42 + retry = mock.Mock(spec=[]) + client = mock.Mock(spec=["_get_resource", "project"]) + client._get_resource.return_value = api_response + client.project = project + bucket = self._make_one(client=client, name=name, user_project=user_project) - connection = _make_connection(resources) - client = _Client(connection, project="my-project-123") - bucket = self._make_one(client=client, name=NAME) - notification = bucket.get_notification(notification_id=NOTIFICATION_ID) + notification = bucket.get_notification( + notification_id=notification_id, timeout=timeout, retry=retry, + ) - self.assertEqual(notification.notification_id, NOTIFICATION_ID) - self.assertEqual(notification.etag, ETAG) - self.assertEqual(notification.self_link, SELF_LINK) + self.assertIsInstance(notification, BucketNotification) + self.assertEqual(notification.notification_id, notification_id) + self.assertEqual(notification.etag, etag) + self.assertEqual(notification.self_link, self_link) self.assertIsNone(notification.custom_attributes) self.assertIsNone(notification.event_types) self.assertIsNone(notification.blob_name_prefix) self.assertEqual(notification.payload_format, JSON_API_V1_PAYLOAD_FORMAT) - def test_get_notification_miss(self): - from google.cloud.exceptions import NotFound - - response = NotFound("testing") - connection = _make_connection(response) - client = _Client(connection, project="my-project-123") - bucket = self._make_one(client=client, name="name") - with self.assertRaises(NotFound): - bucket.get_notification(notification_id="1") + expected_path = "/b/{}/notificationConfigs/{}".format(name, notification_id) + expected_query_params = {"userProject": user_project} + client._get_resource.assert_called_once_with( + expected_path, + query_params=expected_query_params, + timeout=timeout, + retry=retry, + ) def test_delete_miss(self): from google.cloud.exceptions import NotFound @@ -1374,27 +1459,32 @@ def test_delete_blobs_miss_w_on_error(self): self.assertEqual(kw[1]["timeout"], self._get_default_timeout()) self.assertEqual(kw[1]["retry"], DEFAULT_RETRY_IF_GENERATION_SPECIFIED) - def test_reload_bucket_w_metageneration_match(self): - NAME = "name" - METAGENERATION_NUMBER = 9 - - connection = _Connection({}) - client = _Client(connection) - bucket = self._make_one(client=client, name=NAME) - - bucket.reload(if_metageneration_match=METAGENERATION_NUMBER) - - self.assertEqual(len(connection._requested), 1) - req = connection._requested[0] - self.assertEqual(req["method"], "GET") - self.assertEqual(req["path"], "/b/%s" % NAME) - self.assertEqual(req["timeout"], self._get_default_timeout()) - self.assertEqual( - req["query_params"], - {"projection": "noAcl", "ifMetagenerationMatch": METAGENERATION_NUMBER}, + def test_reload_w_metageneration_match(self): + name = "name" + metageneration_number = 9 + api_response = {"name": name} + client = mock.Mock(spec=["_get_resource"]) + client._get_resource.return_value = api_response + bucket = self._make_one(client, name=name) + + bucket.reload(if_metageneration_match=metageneration_number) + + expected_path = "/b/%s" % (name,) + expected_query_params = { + "projection": "noAcl", + "ifMetagenerationMatch": metageneration_number, + } + expected_headers = {} + client._get_resource.assert_called_once_with( + expected_path, + query_params=expected_query_params, + headers=expected_headers, + timeout=self._get_default_timeout(), + retry=DEFAULT_RETRY, + _target_object=bucket, ) - def test_reload_bucket_w_generation_match(self): + def test_reload_w_generation_match(self): connection = _Connection({}) client = _Client(connection) bucket = self._make_one(client=client, name="name") @@ -2415,115 +2505,129 @@ def test_disable_website(self): bucket.disable_website() self.assertEqual(bucket._properties, UNSET) - def test_get_iam_policy(self): + def test_get_iam_policy_defaults(self): from google.cloud.storage.iam import STORAGE_OWNER_ROLE from google.cloud.storage.iam import STORAGE_EDITOR_ROLE from google.cloud.storage.iam import STORAGE_VIEWER_ROLE from google.api_core.iam import Policy - NAME = "name" - PATH = "/b/%s" % (NAME,) - ETAG = "DEADBEEF" - VERSION = 1 - OWNER1 = "user:phred@example.com" - OWNER2 = "group:cloud-logs@google.com" - EDITOR1 = "domain:google.com" - EDITOR2 = "user:phred@example.com" - VIEWER1 = "serviceAccount:1234-abcdef@service.example.com" - VIEWER2 = "user:phred@example.com" - RETURNED = { - "resourceId": PATH, - "etag": ETAG, - "version": VERSION, + bucket_name = "name" + path = "/b/%s" % (bucket_name,) + etag = "DEADBEEF" + version = 1 + owner1 = "user:phred@example.com" + owner2 = "group:cloud-logs@google.com" + editor1 = "domain:google.com" + editor2 = "user:phred@example.com" + viewer1 = "serviceAccount:1234-abcdef@service.example.com" + viewer2 = "user:phred@example.com" + api_response = { + "resourceId": path, + "etag": etag, + "version": version, "bindings": [ - {"role": STORAGE_OWNER_ROLE, "members": [OWNER1, OWNER2]}, - {"role": STORAGE_EDITOR_ROLE, "members": [EDITOR1, EDITOR2]}, - {"role": STORAGE_VIEWER_ROLE, "members": [VIEWER1, VIEWER2]}, + {"role": STORAGE_OWNER_ROLE, "members": [owner1, owner2]}, + {"role": STORAGE_EDITOR_ROLE, "members": [editor1, editor2]}, + {"role": STORAGE_VIEWER_ROLE, "members": [viewer1, viewer2]}, ], } - EXPECTED = { - binding["role"]: set(binding["members"]) for binding in RETURNED["bindings"] + expected_policy = { + binding["role"]: set(binding["members"]) + for binding in api_response["bindings"] } - connection = _Connection(RETURNED) - client = _Client(connection, None) - bucket = self._make_one(client=client, name=NAME) + client = mock.Mock(spec=["_get_resource"]) + client._get_resource.return_value = api_response + bucket = self._make_one(client=client, name=bucket_name) - policy = bucket.get_iam_policy(timeout=42) + policy = bucket.get_iam_policy() self.assertIsInstance(policy, Policy) - self.assertEqual(policy.etag, RETURNED["etag"]) - self.assertEqual(policy.version, RETURNED["version"]) - self.assertEqual(dict(policy), EXPECTED) - - kw = connection._requested - self.assertEqual(len(kw), 1) - self.assertEqual(kw[0]["method"], "GET") - self.assertEqual(kw[0]["path"], "%s/iam" % (PATH,)) - self.assertEqual(kw[0]["query_params"], {}) - self.assertEqual(kw[0]["timeout"], 42) + self.assertEqual(policy.etag, api_response["etag"]) + self.assertEqual(policy.version, api_response["version"]) + self.assertEqual(dict(policy), expected_policy) + + expected_path = "/b/%s/iam" % (bucket_name,) + expected_query_params = {} + client._get_resource.assert_called_once_with( + expected_path, + query_params=expected_query_params, + timeout=self._get_default_timeout(), + retry=DEFAULT_RETRY, + _target_object=None, + ) - def test_get_iam_policy_w_user_project(self): + def test_get_iam_policy_w_user_project_w_timeout(self): from google.api_core.iam import Policy - NAME = "name" - USER_PROJECT = "user-project-123" - PATH = "/b/%s" % (NAME,) - ETAG = "DEADBEEF" - VERSION = 1 - RETURNED = { - "resourceId": PATH, - "etag": ETAG, - "version": VERSION, + bucket_name = "name" + timeout = 42 + user_project = "user-project-123" + path = "/b/%s" % (bucket_name,) + etag = "DEADBEEF" + version = 1 + api_response = { + "resourceId": path, + "etag": etag, + "version": version, "bindings": [], } - EXPECTED = {} - connection = _Connection(RETURNED) - client = _Client(connection, None) - bucket = self._make_one(client=client, name=NAME, user_project=USER_PROJECT) + expected_policy = {} + client = mock.Mock(spec=["_get_resource"]) + client._get_resource.return_value = api_response + bucket = self._make_one( + client=client, name=bucket_name, user_project=user_project + ) - policy = bucket.get_iam_policy() + policy = bucket.get_iam_policy(timeout=timeout) self.assertIsInstance(policy, Policy) - self.assertEqual(policy.etag, RETURNED["etag"]) - self.assertEqual(policy.version, RETURNED["version"]) - self.assertEqual(dict(policy), EXPECTED) - - kw = connection._requested - self.assertEqual(len(kw), 1) - self.assertEqual(kw[0]["method"], "GET") - self.assertEqual(kw[0]["path"], "%s/iam" % (PATH,)) - self.assertEqual(kw[0]["query_params"], {"userProject": USER_PROJECT}) - self.assertEqual(kw[0]["timeout"], self._get_default_timeout()) + self.assertEqual(policy.etag, api_response["etag"]) + self.assertEqual(policy.version, api_response["version"]) + self.assertEqual(dict(policy), expected_policy) + + expected_path = "/b/%s/iam" % (bucket_name,) + expected_query_params = {"userProject": user_project} + client._get_resource.assert_called_once_with( + expected_path, + query_params=expected_query_params, + timeout=timeout, + retry=DEFAULT_RETRY, + _target_object=None, + ) - def test_get_iam_policy_w_requested_policy_version(self): + def test_get_iam_policy_w_requested_policy_version_w_retry(self): from google.cloud.storage.iam import STORAGE_OWNER_ROLE - NAME = "name" - PATH = "/b/%s" % (NAME,) - ETAG = "DEADBEEF" - VERSION = 1 - OWNER1 = "user:phred@example.com" - OWNER2 = "group:cloud-logs@google.com" - RETURNED = { - "resourceId": PATH, - "etag": ETAG, - "version": VERSION, - "bindings": [{"role": STORAGE_OWNER_ROLE, "members": [OWNER1, OWNER2]}], + bucket_name = "name" + path = "/b/%s" % (bucket_name,) + etag = "DEADBEEF" + version = 3 + owner1 = "user:phred@example.com" + owner2 = "group:cloud-logs@google.com" + api_response = { + "resourceId": path, + "etag": etag, + "version": version, + "bindings": [{"role": STORAGE_OWNER_ROLE, "members": [owner1, owner2]}], } - connection = _Connection(RETURNED) - client = _Client(connection, None) - bucket = self._make_one(client=client, name=NAME) + retry = mock.Mock(spec=[]) + client = mock.Mock(spec=["_get_resource"]) + client._get_resource.return_value = api_response + bucket = self._make_one(client=client, name=bucket_name) - policy = bucket.get_iam_policy(requested_policy_version=3) + policy = bucket.get_iam_policy(requested_policy_version=3, retry=retry) - self.assertEqual(policy.version, VERSION) + self.assertEqual(policy.version, version) - kw = connection._requested - self.assertEqual(len(kw), 1) - self.assertEqual(kw[0]["method"], "GET") - self.assertEqual(kw[0]["path"], "%s/iam" % (PATH,)) - self.assertEqual(kw[0]["query_params"], {"optionsRequestedPolicyVersion": 3}) - self.assertEqual(kw[0]["timeout"], self._get_default_timeout()) + expected_path = "/b/%s/iam" % (bucket_name,) + expected_query_params = {"optionsRequestedPolicyVersion": version} + client._get_resource.assert_called_once_with( + expected_path, + query_params=expected_query_params, + timeout=self._get_default_timeout(), + retry=retry, + _target_object=None, + ) def test_set_iam_policy(self): import operator @@ -2632,67 +2736,74 @@ def test_set_iam_policy_w_user_project(self): self.assertEqual(found["role"], expected["role"]) self.assertEqual(sorted(found["members"]), sorted(expected["members"])) - def test_test_iam_permissions(self): + def test_test_iam_permissions_defaults(self): from google.cloud.storage.iam import STORAGE_OBJECTS_LIST from google.cloud.storage.iam import STORAGE_BUCKETS_GET from google.cloud.storage.iam import STORAGE_BUCKETS_UPDATE - NAME = "name" - PATH = "/b/%s" % (NAME,) - PERMISSIONS = [ + name = "name" + permissions = [ STORAGE_OBJECTS_LIST, STORAGE_BUCKETS_GET, STORAGE_BUCKETS_UPDATE, ] - ALLOWED = PERMISSIONS[1:] - RETURNED = {"permissions": ALLOWED} - connection = _Connection(RETURNED) - client = _Client(connection, None) - bucket = self._make_one(client=client, name=NAME) + expected = permissions[1:] + api_response = {"permissions": expected} + client = mock.Mock(spec=["_get_resource"]) + client._get_resource.return_value = api_response + bucket = self._make_one(client=client, name=name) - allowed = bucket.test_iam_permissions(PERMISSIONS, timeout=42) + found = bucket.test_iam_permissions(permissions) - self.assertEqual(allowed, ALLOWED) + self.assertEqual(found, expected) - kw = connection._requested - self.assertEqual(len(kw), 1) - self.assertEqual(kw[0]["method"], "GET") - self.assertEqual(kw[0]["path"], "%s/iam/testPermissions" % (PATH,)) - self.assertEqual(kw[0]["query_params"], {"permissions": PERMISSIONS}) - self.assertEqual(kw[0]["timeout"], 42) + expected_path = "/b/%s/iam/testPermissions" % (name,) + expected_query_params = {} + expected_query_params = {"permissions": permissions} + client._get_resource.assert_called_once_with( + expected_path, + query_params=expected_query_params, + timeout=self._get_default_timeout(), + retry=DEFAULT_RETRY, + _target_object=None, + ) - def test_test_iam_permissions_w_user_project(self): + def test_test_iam_permissions_w_user_project_w_timeout_w_retry(self): from google.cloud.storage.iam import STORAGE_OBJECTS_LIST from google.cloud.storage.iam import STORAGE_BUCKETS_GET from google.cloud.storage.iam import STORAGE_BUCKETS_UPDATE - NAME = "name" - USER_PROJECT = "user-project-123" - PATH = "/b/%s" % (NAME,) - PERMISSIONS = [ + name = "name" + user_project = "user-project-123" + timeout = 42 + retry = mock.Mock(spec=[]) + permissions = [ STORAGE_OBJECTS_LIST, STORAGE_BUCKETS_GET, STORAGE_BUCKETS_UPDATE, ] - ALLOWED = PERMISSIONS[1:] - RETURNED = {"permissions": ALLOWED} - connection = _Connection(RETURNED) - client = _Client(connection, None) - bucket = self._make_one(client=client, name=NAME, user_project=USER_PROJECT) + expected = permissions[1:] + api_response = {"permissions": expected} + client = mock.Mock(spec=["_get_resource"]) + client._get_resource.return_value = api_response + bucket = self._make_one(client=client, name=name, user_project=user_project) - allowed = bucket.test_iam_permissions(PERMISSIONS) + found = bucket.test_iam_permissions(permissions, timeout=timeout, retry=retry) - self.assertEqual(allowed, ALLOWED) + self.assertEqual(found, expected) - kw = connection._requested - self.assertEqual(len(kw), 1) - self.assertEqual(kw[0]["method"], "GET") - self.assertEqual(kw[0]["path"], "%s/iam/testPermissions" % (PATH,)) - self.assertEqual( - kw[0]["query_params"], - {"permissions": PERMISSIONS, "userProject": USER_PROJECT}, + expected_path = "/b/%s/iam/testPermissions" % (name,) + expected_query_params = { + "permissions": permissions, + "userProject": user_project, + } + client._get_resource.assert_called_once_with( + expected_path, + query_params=expected_query_params, + timeout=timeout, + retry=retry, + _target_object=None, ) - self.assertEqual(kw[0]["timeout"], self._get_default_timeout()) def test_make_public_defaults(self): from google.cloud.storage.acl import _ACLEntity @@ -2723,15 +2834,12 @@ def _make_public_w_future_helper(self, default_object_acl_loaded=True): permissive = [{"entity": "allUsers", "role": _ACLEntity.READER_ROLE}] after1 = {"acl": permissive, "defaultObjectAcl": []} after2 = {"acl": permissive, "defaultObjectAcl": permissive} - if default_object_acl_loaded: - num_requests = 2 - connection = _Connection(after1, after2) - else: - num_requests = 3 - # We return the same value for default_object_acl.reload() - # to consume. - connection = _Connection(after1, after1, after2) + connection = _Connection(after1, after2) client = _Client(connection) + + # Temporary workaround until we use real mock client + client._get_resource = mock.Mock(return_value={"items": []}) + bucket = self._make_one(client=client, name=NAME) bucket.acl.loaded = True bucket.default_object_acl.loaded = default_object_acl_loaded @@ -2739,21 +2847,27 @@ def _make_public_w_future_helper(self, default_object_acl_loaded=True): self.assertEqual(list(bucket.acl), permissive) self.assertEqual(list(bucket.default_object_acl), permissive) kw = connection._requested - self.assertEqual(len(kw), num_requests) + self.assertEqual(len(kw), 2) self.assertEqual(kw[0]["method"], "PATCH") self.assertEqual(kw[0]["path"], "/b/%s" % NAME) self.assertEqual(kw[0]["data"], {"acl": permissive}) self.assertEqual(kw[0]["query_params"], {"projection": "full"}) self.assertEqual(kw[0]["timeout"], self._get_default_timeout()) + self.assertEqual(kw[1]["method"], "PATCH") + self.assertEqual(kw[1]["path"], "/b/%s" % NAME) + self.assertEqual(kw[1]["data"], {"defaultObjectAcl": permissive}) + self.assertEqual(kw[1]["query_params"], {"projection": "full"}) + self.assertEqual(kw[1]["timeout"], self._get_default_timeout()) + if not default_object_acl_loaded: - self.assertEqual(kw[1]["method"], "GET") - self.assertEqual(kw[1]["path"], "/b/%s/defaultObjectAcl" % NAME) - # Last could be 1 or 2 depending on `default_object_acl_loaded`. - self.assertEqual(kw[-1]["method"], "PATCH") - self.assertEqual(kw[-1]["path"], "/b/%s" % NAME) - self.assertEqual(kw[-1]["data"], {"defaultObjectAcl": permissive}) - self.assertEqual(kw[-1]["query_params"], {"projection": "full"}) - self.assertEqual(kw[-1]["timeout"], self._get_default_timeout()) + expected_path = "/b/%s/defaultObjectAcl" % (NAME,) + expected_query_params = {} + client._get_resource.assert_called_once_with( + expected_path, + query_params=expected_query_params, + timeout=self._get_default_timeout(), + retry=DEFAULT_RETRY, + ) def test_make_public_w_future(self): self._make_public_w_future_helper(default_object_acl_loaded=True) @@ -2871,37 +2985,40 @@ def _make_private_w_future_helper(self, default_object_acl_loaded=True): no_permissions = [] after1 = {"acl": no_permissions, "defaultObjectAcl": []} after2 = {"acl": no_permissions, "defaultObjectAcl": no_permissions} - if default_object_acl_loaded: - num_requests = 2 - connection = _Connection(after1, after2) - else: - num_requests = 3 - # We return the same value for default_object_acl.reload() - # to consume. - connection = _Connection(after1, after1, after2) + connection = _Connection(after1, after2) client = _Client(connection) bucket = self._make_one(client=client, name=NAME) bucket.acl.loaded = True bucket.default_object_acl.loaded = default_object_acl_loaded + + # Temporary workaround until we use real mock client + client._get_resource = mock.Mock(return_value={"items": []}) + bucket.make_private(future=True) self.assertEqual(list(bucket.acl), no_permissions) self.assertEqual(list(bucket.default_object_acl), no_permissions) kw = connection._requested - self.assertEqual(len(kw), num_requests) + self.assertEqual(len(kw), 2) self.assertEqual(kw[0]["method"], "PATCH") self.assertEqual(kw[0]["path"], "/b/%s" % NAME) self.assertEqual(kw[0]["data"], {"acl": no_permissions}) self.assertEqual(kw[0]["query_params"], {"projection": "full"}) self.assertEqual(kw[0]["timeout"], self._get_default_timeout()) + self.assertEqual(kw[1]["method"], "PATCH") + self.assertEqual(kw[1]["path"], "/b/%s" % NAME) + self.assertEqual(kw[1]["data"], {"defaultObjectAcl": no_permissions}) + self.assertEqual(kw[1]["query_params"], {"projection": "full"}) + self.assertEqual(kw[1]["timeout"], self._get_default_timeout()) + if not default_object_acl_loaded: - self.assertEqual(kw[1]["method"], "GET") - self.assertEqual(kw[1]["path"], "/b/%s/defaultObjectAcl" % NAME) - # Last could be 1 or 2 depending on `default_object_acl_loaded`. - self.assertEqual(kw[-1]["method"], "PATCH") - self.assertEqual(kw[-1]["path"], "/b/%s" % NAME) - self.assertEqual(kw[-1]["data"], {"defaultObjectAcl": no_permissions}) - self.assertEqual(kw[-1]["query_params"], {"projection": "full"}) - self.assertEqual(kw[-1]["timeout"], self._get_default_timeout()) + expected_path = "/b/%s/defaultObjectAcl" % (NAME,) + expected_query_params = {} + client._get_resource.assert_called_once_with( + expected_path, + query_params=expected_query_params, + timeout=self._get_default_timeout(), + retry=DEFAULT_RETRY, + ) def test_make_private_w_future(self): self._make_private_w_future_helper(default_object_acl_loaded=True) diff --git a/tests/unit/test_client.py b/tests/unit/test_client.py index 00a9b4913..c6a98951e 100644 --- a/tests/unit/test_client.py +++ b/tests/unit/test_client.py @@ -409,339 +409,360 @@ def test_batch(self): self.assertIsInstance(batch, Batch) self.assertIs(batch._client, client) - def test_get_bucket_with_string_miss(self): + def test__get_resource_miss_w_defaults(self): from google.cloud.exceptions import NotFound PROJECT = "PROJECT" + PATH = "/path/to/something" CREDENTIALS = _make_credentials() - client = self._make_one(project=PROJECT, credentials=CREDENTIALS) - NONESUCH = "nonesuch" - http = _make_requests_session( - [_make_json_response({}, status=http_client.NOT_FOUND)] - ) - client._http_internal = http + client = self._make_one(project=PROJECT, credentials=CREDENTIALS) + connection = client._base_connection = _make_connection() with self.assertRaises(NotFound): - client.get_bucket(NONESUCH, timeout=42) + client._get_resource(PATH) - http.request.assert_called_once_with( - method="GET", url=mock.ANY, data=mock.ANY, headers=mock.ANY, timeout=42 - ) - _, kwargs = http.request.call_args - scheme, netloc, path, qs, _ = urlparse.urlsplit(kwargs.get("url")) - self.assertEqual("%s://%s" % (scheme, netloc), client._connection.API_BASE_URL) - self.assertEqual( - path, - "/".join(["", "storage", client._connection.API_VERSION, "b", NONESUCH]), + connection.api_request.assert_called_once_with( + method="GET", + path=PATH, + query_params=None, + headers=None, + timeout=self._get_default_timeout(), + retry=DEFAULT_RETRY, + _target_object=None, ) - parms = dict(urlparse.parse_qsl(qs)) - self.assertEqual(parms["projection"], "noAcl") - - def test_get_bucket_with_string_hit(self): - from google.cloud.storage.bucket import Bucket + def test__get_resource_hit_w_explicit(self): PROJECT = "PROJECT" + PATH = "/path/to/something" + QUERY_PARAMS = {"foo": "Foo"} + HEADERS = {"bar": "Bar"} + TIMEOUT = 100 + RETRY = mock.Mock(spec=[]) CREDENTIALS = _make_credentials() + client = self._make_one(project=PROJECT, credentials=CREDENTIALS) + expected = mock.Mock(spec={}) + connection = client._base_connection = _make_connection(expected) + target = mock.Mock(spec={}) - BUCKET_NAME = "bucket-name" - data = {"name": BUCKET_NAME} - http = _make_requests_session([_make_json_response(data)]) - client._http_internal = http + found = client._get_resource( + PATH, + query_params=QUERY_PARAMS, + headers=HEADERS, + timeout=TIMEOUT, + retry=RETRY, + _target_object=target, + ) - bucket = client.get_bucket(BUCKET_NAME) + self.assertIs(found, expected) - self.assertIsInstance(bucket, Bucket) - self.assertEqual(bucket.name, BUCKET_NAME) - http.request.assert_called_once_with( + connection.api_request.assert_called_once_with( method="GET", - url=mock.ANY, - data=mock.ANY, - headers=mock.ANY, - timeout=self._get_default_timeout(), + path=PATH, + query_params=QUERY_PARAMS, + headers=HEADERS, + timeout=TIMEOUT, + retry=RETRY, + _target_object=target, ) - _, kwargs = http.request.call_args - scheme, netloc, path, qs, _ = urlparse.urlsplit(kwargs.get("url")) - self.assertEqual("%s://%s" % (scheme, netloc), client._connection.API_BASE_URL) - self.assertEqual( - path, - "/".join(["", "storage", client._connection.API_VERSION, "b", BUCKET_NAME]), + + def test_get_bucket_miss_w_string_w_defaults(self): + from google.cloud.exceptions import NotFound + from google.cloud.storage.bucket import Bucket + + project = "PROJECT" + credentials = _make_credentials() + client = self._make_one(project=project, credentials=credentials) + client._get_resource = mock.Mock() + client._get_resource.side_effect = NotFound("testing") + bucket_name = "nonesuch" + + with self.assertRaises(NotFound): + client.get_bucket(bucket_name) + + expected_path = "/b/%s" % (bucket_name,) + expected_query_params = {"projection": "noAcl"} + expected_headers = {} + client._get_resource.assert_called_once_with( + expected_path, + query_params=expected_query_params, + headers=expected_headers, + timeout=self._get_default_timeout(), + retry=DEFAULT_RETRY, + _target_object=mock.ANY, ) - parms = dict(urlparse.parse_qsl(qs)) - self.assertEqual(parms["projection"], "noAcl") - def test_get_bucket_with_metageneration_match(self): + target = client._get_resource.call_args[1]["_target_object"] + self.assertIsInstance(target, Bucket) + self.assertEqual(target.name, bucket_name) + + def test_get_bucket_hit_w_string_w_timeout(self): from google.cloud.storage.bucket import Bucket - PROJECT = "PROJECT" - CREDENTIALS = _make_credentials() - METAGENERATION_NUMBER = 6 - client = self._make_one(project=PROJECT, credentials=CREDENTIALS) + project = "PROJECT" + bucket_name = "bucket-name" + timeout = 42 + api_response = {"name": bucket_name} + credentials = _make_credentials() + client = self._make_one(project=project, credentials=credentials) + client._get_resource = mock.Mock(return_value=api_response) - BUCKET_NAME = "bucket-name" - data = {"name": BUCKET_NAME} - http = _make_requests_session([_make_json_response(data)]) - client._http_internal = http + bucket = client.get_bucket(bucket_name, timeout=timeout) - bucket = client.get_bucket( - BUCKET_NAME, if_metageneration_match=METAGENERATION_NUMBER - ) self.assertIsInstance(bucket, Bucket) - self.assertEqual(bucket.name, BUCKET_NAME) - http.request.assert_called_once_with( - method="GET", - url=mock.ANY, - data=mock.ANY, - headers=mock.ANY, - timeout=self._get_default_timeout(), - ) - _, kwargs = http.request.call_args - scheme, netloc, path, qs, _ = urlparse.urlsplit(kwargs.get("url")) - self.assertEqual("%s://%s" % (scheme, netloc), client._connection.API_BASE_URL) - self.assertEqual( - path, - "/".join(["", "storage", client._connection.API_VERSION, "b", BUCKET_NAME]), + self.assertEqual(bucket.name, bucket_name) + + expected_path = "/b/%s" % (bucket_name,) + expected_query_params = {"projection": "noAcl"} + expected_headers = {} + client._get_resource.assert_called_once_with( + expected_path, + query_params=expected_query_params, + headers=expected_headers, + timeout=timeout, + retry=DEFAULT_RETRY, + _target_object=bucket, ) - parms = dict(urlparse.parse_qsl(qs)) - self.assertEqual(parms["ifMetagenerationMatch"], str(METAGENERATION_NUMBER)) - self.assertEqual(parms["projection"], "noAcl") - def test_get_bucket_with_object_miss(self): - from google.cloud.exceptions import NotFound + def test_get_bucket_hit_w_string_w_metageneration_match(self): from google.cloud.storage.bucket import Bucket project = "PROJECT" + bucket_name = "bucket-name" + metageneration_number = 6 + api_response = {"name": bucket_name} credentials = _make_credentials() client = self._make_one(project=project, credentials=credentials) + client._get_resource = mock.Mock(return_value=api_response) - nonesuch = "nonesuch" - bucket_obj = Bucket(client, nonesuch) - http = _make_requests_session( - [_make_json_response({}, status=http_client.NOT_FOUND)] + bucket = client.get_bucket( + bucket_name, if_metageneration_match=metageneration_number ) - client._http_internal = http - with self.assertRaises(NotFound): - client.get_bucket(bucket_obj) + self.assertIsInstance(bucket, Bucket) + self.assertEqual(bucket.name, bucket_name) - http.request.assert_called_once_with( - method="GET", - url=mock.ANY, - data=mock.ANY, - headers=mock.ANY, + expected_path = "/b/%s" % (bucket_name,) + expected_query_params = { + "projection": "noAcl", + "ifMetagenerationMatch": metageneration_number, + } + expected_headers = {} + client._get_resource.assert_called_once_with( + expected_path, + query_params=expected_query_params, + headers=expected_headers, timeout=self._get_default_timeout(), + retry=DEFAULT_RETRY, + _target_object=bucket, ) - _, kwargs = http.request.call_args - scheme, netloc, path, qs, _ = urlparse.urlsplit(kwargs.get("url")) - self.assertEqual("%s://%s" % (scheme, netloc), client._connection.API_BASE_URL) - self.assertEqual( - path, - "/".join(["", "storage", client._connection.API_VERSION, "b", nonesuch]), - ) - parms = dict(urlparse.parse_qsl(qs)) - self.assertEqual(parms["projection"], "noAcl") - def test_get_bucket_with_object_hit(self): + def test_get_bucket_miss_w_object_w_retry(self): + from google.cloud.exceptions import NotFound from google.cloud.storage.bucket import Bucket project = "PROJECT" + bucket_name = "nonesuch" + retry = mock.Mock(spec=[]) credentials = _make_credentials() client = self._make_one(project=project, credentials=credentials) - - bucket_name = "bucket-name" + client._get_resource = mock.Mock(side_effect=NotFound("testing")) bucket_obj = Bucket(client, bucket_name) - data = {"name": bucket_name} - http = _make_requests_session([_make_json_response(data)]) - client._http_internal = http - - bucket = client.get_bucket(bucket_obj) - self.assertIsInstance(bucket, Bucket) - self.assertEqual(bucket.name, bucket_name) - http.request.assert_called_once_with( - method="GET", - url=mock.ANY, - data=mock.ANY, - headers=mock.ANY, + with self.assertRaises(NotFound): + client.get_bucket(bucket_obj, retry=retry) + + expected_path = "/b/%s" % (bucket_name,) + expected_query_params = {"projection": "noAcl"} + expected_headers = {} + client._get_resource.assert_called_once_with( + expected_path, + query_params=expected_query_params, + headers=expected_headers, timeout=self._get_default_timeout(), + retry=retry, + _target_object=mock.ANY, ) - _, kwargs = http.request.call_args - scheme, netloc, path, qs, _ = urlparse.urlsplit(kwargs.get("url")) - self.assertEqual("%s://%s" % (scheme, netloc), client._connection.API_BASE_URL) - self.assertEqual( - path, - "/".join(["", "storage", client._connection.API_VERSION, "b", bucket_name]), - ) - parms = dict(urlparse.parse_qsl(qs)) - self.assertEqual(parms["projection"], "noAcl") - def test_get_bucket_default_retry(self): - from google.cloud.storage.bucket import Bucket - from google.cloud.storage._http import Connection + target = client._get_resource.call_args[1]["_target_object"] + self.assertIsInstance(target, Bucket) + self.assertEqual(target.name, bucket_name) - PROJECT = "PROJECT" - CREDENTIALS = _make_credentials() - client = self._make_one(project=PROJECT, credentials=CREDENTIALS) + def test_get_bucket_hit_w_object_defaults(self): + from google.cloud.storage.bucket import Bucket + project = "PROJECT" bucket_name = "bucket-name" + api_response = {"name": bucket_name} + credentials = _make_credentials() + client = self._make_one(project=project, credentials=credentials) + client._get_resource = mock.Mock(return_value=api_response) bucket_obj = Bucket(client, bucket_name) - with mock.patch.object(Connection, "api_request") as req: - client.get_bucket(bucket_obj) + bucket = client.get_bucket(bucket_obj) - req.assert_called_once_with( - method="GET", - path=mock.ANY, - query_params=mock.ANY, - headers=mock.ANY, - _target_object=bucket_obj, - timeout=mock.ANY, + self.assertIsInstance(bucket, Bucket) + self.assertEqual(bucket.name, bucket_name) + + expected_path = "/b/%s" % (bucket_name,) + expected_query_params = {"projection": "noAcl"} + expected_headers = {} + client._get_resource.assert_called_once_with( + expected_path, + query_params=expected_query_params, + headers=expected_headers, + timeout=self._get_default_timeout(), retry=DEFAULT_RETRY, + _target_object=bucket, ) - def test_get_bucket_respects_retry_override(self): + def test_get_bucket_hit_w_object_w_retry_none(self): from google.cloud.storage.bucket import Bucket - from google.cloud.storage._http import Connection - - PROJECT = "PROJECT" - CREDENTIALS = _make_credentials() - client = self._make_one(project=PROJECT, credentials=CREDENTIALS) + project = "PROJECT" bucket_name = "bucket-name" + api_response = {"name": bucket_name} + credentials = _make_credentials() + client = self._make_one(project=project, credentials=credentials) + client._get_resource = mock.Mock(return_value=api_response) bucket_obj = Bucket(client, bucket_name) - with mock.patch.object(Connection, "api_request") as req: - client.get_bucket(bucket_obj, retry=None) + bucket = client.get_bucket(bucket_obj, retry=None) - req.assert_called_once_with( - method="GET", - path=mock.ANY, - query_params=mock.ANY, - headers=mock.ANY, - _target_object=bucket_obj, - timeout=mock.ANY, + self.assertIsInstance(bucket, Bucket) + self.assertEqual(bucket.name, bucket_name) + + expected_path = "/b/%s" % (bucket_name,) + expected_query_params = {"projection": "noAcl"} + expected_headers = {} + client._get_resource.assert_called_once_with( + expected_path, + query_params=expected_query_params, + headers=expected_headers, + timeout=self._get_default_timeout(), retry=None, + _target_object=bucket, ) - def test_lookup_bucket_miss(self): - PROJECT = "PROJECT" - CREDENTIALS = _make_credentials() - client = self._make_one(project=PROJECT, credentials=CREDENTIALS) + def test_lookup_bucket_miss_w_defaults(self): + from google.cloud.exceptions import NotFound + from google.cloud.storage.bucket import Bucket - NONESUCH = "nonesuch" - http = _make_requests_session( - [_make_json_response({}, status=http_client.NOT_FOUND)] - ) - client._http_internal = http + project = "PROJECT" + bucket_name = "nonesuch" + credentials = _make_credentials() + client = self._make_one(project=project, credentials=credentials) + client._get_resource = mock.Mock(side_effect=NotFound("testing")) - bucket = client.lookup_bucket(NONESUCH, timeout=42) + bucket = client.lookup_bucket(bucket_name) self.assertIsNone(bucket) - http.request.assert_called_once_with( - method="GET", url=mock.ANY, data=mock.ANY, headers=mock.ANY, timeout=42 - ) - _, kwargs = http.request.call_args - scheme, netloc, path, qs, _ = urlparse.urlsplit(kwargs.get("url")) - self.assertEqual("%s://%s" % (scheme, netloc), client._connection.API_BASE_URL) - self.assertEqual( - path, - "/".join(["", "storage", client._connection.API_VERSION, "b", NONESUCH]), + + expected_path = "/b/%s" % (bucket_name,) + expected_query_params = {"projection": "noAcl"} + expected_headers = {} + client._get_resource.assert_called_once_with( + expected_path, + query_params=expected_query_params, + headers=expected_headers, + timeout=self._get_default_timeout(), + retry=DEFAULT_RETRY, + _target_object=mock.ANY, ) - parms = dict(urlparse.parse_qsl(qs)) - self.assertEqual(parms["projection"], "noAcl") - def test_lookup_bucket_hit(self): - from google.cloud.storage.bucket import Bucket + target = client._get_resource.call_args[1]["_target_object"] + self.assertIsInstance(target, Bucket) + self.assertEqual(target.name, bucket_name) - PROJECT = "PROJECT" - CREDENTIALS = _make_credentials() - client = self._make_one(project=PROJECT, credentials=CREDENTIALS) + def test_lookup_bucket_hit_w_timeout(self): + from google.cloud.storage.bucket import Bucket - BUCKET_NAME = "bucket-name" - data = {"name": BUCKET_NAME} - http = _make_requests_session([_make_json_response(data)]) - client._http_internal = http + project = "PROJECT" + bucket_name = "bucket-name" + timeout = 42 + api_response = {"name": bucket_name} + credentials = _make_credentials() + client = self._make_one(project=project, credentials=credentials) + client._get_resource = mock.Mock(return_value=api_response) - bucket = client.lookup_bucket(BUCKET_NAME) + bucket = client.lookup_bucket(bucket_name, timeout=timeout) self.assertIsInstance(bucket, Bucket) - self.assertEqual(bucket.name, BUCKET_NAME) - http.request.assert_called_once_with( - method="GET", - url=mock.ANY, - data=mock.ANY, - headers=mock.ANY, - timeout=self._get_default_timeout(), - ) - _, kwargs = http.request.call_args - scheme, netloc, path, qs, _ = urlparse.urlsplit(kwargs.get("url")) - self.assertEqual("%s://%s" % (scheme, netloc), client._connection.API_BASE_URL) - self.assertEqual( - path, - "/".join(["", "storage", client._connection.API_VERSION, "b", BUCKET_NAME]), + self.assertEqual(bucket.name, bucket_name) + + expected_path = "/b/%s" % (bucket_name,) + expected_query_params = {"projection": "noAcl"} + expected_headers = {} + client._get_resource.assert_called_once_with( + expected_path, + query_params=expected_query_params, + headers=expected_headers, + timeout=timeout, + retry=DEFAULT_RETRY, + _target_object=bucket, ) - parms = dict(urlparse.parse_qsl(qs)) - self.assertEqual(parms["projection"], "noAcl") - def test_lookup_bucket_with_metageneration_match(self): + def test_lookup_bucket_hit_w_metageneration_match(self): from google.cloud.storage.bucket import Bucket - PROJECT = "PROJECT" - CREDENTIALS = _make_credentials() - METAGENERATION_NUMBER = 6 - client = self._make_one(project=PROJECT, credentials=CREDENTIALS) - - BUCKET_NAME = "bucket-name" - data = {"name": BUCKET_NAME} - http = _make_requests_session([_make_json_response(data)]) - client._http_internal = http + project = "PROJECT" + bucket_name = "bucket-name" + api_response = {"name": bucket_name} + credentials = _make_credentials() + metageneration_number = 6 + client = self._make_one(project=project, credentials=credentials) + client._get_resource = mock.Mock(return_value=api_response) bucket = client.lookup_bucket( - BUCKET_NAME, if_metageneration_match=METAGENERATION_NUMBER + bucket_name, if_metageneration_match=metageneration_number ) + self.assertIsInstance(bucket, Bucket) - self.assertEqual(bucket.name, BUCKET_NAME) - http.request.assert_called_once_with( - method="GET", - url=mock.ANY, - data=mock.ANY, - headers=mock.ANY, + self.assertEqual(bucket.name, bucket_name) + + expected_path = "/b/%s" % (bucket_name,) + expected_query_params = { + "projection": "noAcl", + "ifMetagenerationMatch": metageneration_number, + } + expected_headers = {} + client._get_resource.assert_called_once_with( + expected_path, + query_params=expected_query_params, + headers=expected_headers, timeout=self._get_default_timeout(), + retry=DEFAULT_RETRY, + _target_object=bucket, ) - _, kwargs = http.request.call_args - scheme, netloc, path, qs, _ = urlparse.urlsplit(kwargs.get("url")) - self.assertEqual("%s://%s" % (scheme, netloc), client._connection.API_BASE_URL) - self.assertEqual( - path, - "/".join(["", "storage", client._connection.API_VERSION, "b", BUCKET_NAME]), - ) - parms = dict(urlparse.parse_qsl(qs)) - self.assertEqual(parms["projection"], "noAcl") - self.assertEqual(parms["ifMetagenerationMatch"], str(METAGENERATION_NUMBER)) - def test_lookup_bucket_default_retry(self): + def test_lookup_bucket_hit_w_retry(self): from google.cloud.storage.bucket import Bucket - from google.cloud.storage._http import Connection - - PROJECT = "PROJECT" - CREDENTIALS = _make_credentials() - client = self._make_one(project=PROJECT, credentials=CREDENTIALS) + project = "PROJECT" bucket_name = "bucket-name" + api_response = {"name": bucket_name} + credentials = _make_credentials() + client = self._make_one(project=project, credentials=credentials) + client._get_resource = mock.Mock(return_value=api_response) bucket_obj = Bucket(client, bucket_name) - with mock.patch.object(Connection, "api_request") as req: - client.lookup_bucket(bucket_obj) - req.assert_called_once_with( - method="GET", - path=mock.ANY, - query_params=mock.ANY, - headers=mock.ANY, - _target_object=bucket_obj, - timeout=mock.ANY, - retry=DEFAULT_RETRY, - ) + bucket = client.lookup_bucket(bucket_obj, retry=None) + + self.assertIsInstance(bucket, Bucket) + self.assertEqual(bucket.name, bucket_name) + + expected_path = "/b/%s" % (bucket_name,) + expected_query_params = {"projection": "noAcl"} + expected_headers = {} + client._get_resource.assert_called_once_with( + expected_path, + query_params=expected_query_params, + headers=expected_headers, + timeout=self._get_default_timeout(), + retry=None, + _target_object=bucket, + ) def test_create_bucket_w_missing_client_project(self): credentials = _make_credentials() diff --git a/tests/unit/test_hmac_key.py b/tests/unit/test_hmac_key.py index 5761f4a96..06b3a65c5 100644 --- a/tests/unit/test_hmac_key.py +++ b/tests/unit/test_hmac_key.py @@ -218,31 +218,29 @@ def test_path_w_access_id_w_explicit_project(self): expected_path = "/projects/{}/hmacKeys/{}".format(project, access_id) self.assertEqual(metadata.path, expected_path) - def test_exists_miss_no_project_set(self): + def test_exists_miss_w_defaults(self): from google.cloud.exceptions import NotFound access_id = "ACCESS-ID" - connection = mock.Mock(spec=["api_request"]) - connection.api_request.side_effect = NotFound("testing") - client = _Client(connection) + project = "PROJECT" + client = mock.Mock(spec=["_get_resource", "project"]) + client._get_resource.side_effect = NotFound("testing") + client.project = project metadata = self._make_one(client) metadata._properties["accessId"] = access_id - self.assertFalse(metadata.exists(timeout=42)) + self.assertFalse(metadata.exists()) - expected_path = "/projects/{}/hmacKeys/{}".format( - client.DEFAULT_PROJECT, access_id + expected_path = "/projects/{}/hmacKeys/{}".format(project, access_id) + expected_query_params = {} + client._get_resource.assert_called_once_with( + expected_path, + query_params=expected_query_params, + timeout=self._get_default_timeout(), + retry=DEFAULT_RETRY, ) - expected_kwargs = { - "method": "GET", - "path": expected_path, - "query_params": {}, - "timeout": 42, - "retry": DEFAULT_RETRY, - } - connection.api_request.assert_called_once_with(**expected_kwargs) - def test_exists_hit_w_project_set(self): + def test_exists_hit_w_explicit_w_user_project(self): project = "PROJECT-ID" access_id = "ACCESS-ID" user_project = "billed-project" @@ -252,49 +250,47 @@ def test_exists_hit_w_project_set(self): "accessId": access_id, "serviceAccountEmail": email, } - connection = mock.Mock(spec=["api_request"]) - connection.api_request.return_value = resource - client = _Client(connection) + timeout = 42 + retry = mock.Mock(spec=[]) + client = mock.Mock(spec=["_get_resource"]) + client._get_resource.return_value = resource metadata = self._make_one(client, user_project=user_project) metadata._properties["accessId"] = access_id metadata._properties["projectId"] = project - self.assertTrue(metadata.exists()) + self.assertTrue(metadata.exists(timeout=timeout, retry=retry)) expected_path = "/projects/{}/hmacKeys/{}".format(project, access_id) - expected_kwargs = { - "method": "GET", - "path": expected_path, - "query_params": {"userProject": user_project}, - "timeout": self._get_default_timeout(), - "retry": DEFAULT_RETRY, - } - connection.api_request.assert_called_once_with(**expected_kwargs) + expected_query_params = {"userProject": user_project} + client._get_resource.assert_called_once_with( + expected_path, + query_params=expected_query_params, + timeout=timeout, + retry=retry, + ) - def test_reload_miss_no_project_set(self): + def test_reload_miss_w_defaults(self): from google.cloud.exceptions import NotFound access_id = "ACCESS-ID" - connection = mock.Mock(spec=["api_request"]) - connection.api_request.side_effect = NotFound("testing") - client = _Client(connection) + project = "PROJECT" + client = mock.Mock(spec=["_get_resource", "project"]) + client._get_resource.side_effect = NotFound("testing") + client.project = project metadata = self._make_one(client) metadata._properties["accessId"] = access_id with self.assertRaises(NotFound): - metadata.reload(timeout=42) + metadata.reload() - expected_path = "/projects/{}/hmacKeys/{}".format( - client.DEFAULT_PROJECT, access_id + expected_path = "/projects/{}/hmacKeys/{}".format(project, access_id) + expected_query_params = {} + client._get_resource.assert_called_once_with( + expected_path, + query_params=expected_query_params, + timeout=self._get_default_timeout(), + retry=DEFAULT_RETRY, ) - expected_kwargs = { - "method": "GET", - "path": expected_path, - "query_params": {}, - "timeout": 42, - "retry": DEFAULT_RETRY, - } - connection.api_request.assert_called_once_with(**expected_kwargs) def test_reload_hit_w_project_set(self): project = "PROJECT-ID" @@ -306,26 +302,26 @@ def test_reload_hit_w_project_set(self): "accessId": access_id, "serviceAccountEmail": email, } - connection = mock.Mock(spec=["api_request"]) - connection.api_request.return_value = resource - client = _Client(connection) + timeout = 42 + retry = mock.Mock(spec=[]) + client = mock.Mock(spec=["_get_resource"]) + client._get_resource.return_value = resource metadata = self._make_one(client, user_project=user_project) metadata._properties["accessId"] = access_id metadata._properties["projectId"] = project - metadata.reload() + metadata.reload(timeout=timeout, retry=retry) self.assertEqual(metadata._properties, resource) expected_path = "/projects/{}/hmacKeys/{}".format(project, access_id) - expected_kwargs = { - "method": "GET", - "path": expected_path, - "query_params": {"userProject": user_project}, - "timeout": self._get_default_timeout(), - "retry": DEFAULT_RETRY, - } - connection.api_request.assert_called_once_with(**expected_kwargs) + expected_query_params = {"userProject": user_project} + client._get_resource.assert_called_once_with( + expected_path, + query_params=expected_query_params, + timeout=timeout, + retry=retry, + ) def test_update_miss_no_project_set(self): from google.cloud.exceptions import NotFound diff --git a/tests/unit/test_notification.py b/tests/unit/test_notification.py index 7ecabfa3a..e8cee0478 100644 --- a/tests/unit/test_notification.py +++ b/tests/unit/test_notification.py @@ -327,104 +327,118 @@ def test_create_w_explicit_client(self): ) def test_exists_wo_notification_id(self): - client = self._make_client() + client = mock.Mock(spec=["_get_resource", "project"]) + client.project = self.BUCKET_PROJECT bucket = self._make_bucket(client) notification = self._make_one(bucket, self.TOPIC_NAME) with self.assertRaises(ValueError): notification.exists() - def test_exists_miss(self): + client._get_resource.assert_not_called() + + def test_exists_miss_w_defaults(self): from google.cloud.exceptions import NotFound - client = self._make_client() + client = mock.Mock(spec=["_get_resource", "project"]) + client._get_resource.side_effect = NotFound("testing") + client.project = self.BUCKET_PROJECT bucket = self._make_bucket(client) notification = self._make_one(bucket, self.TOPIC_NAME) notification._properties["id"] = self.NOTIFICATION_ID - api_request = client._connection.api_request - api_request.side_effect = NotFound("testing") - self.assertFalse(notification.exists(timeout=42)) + self.assertFalse(notification.exists()) - api_request.assert_called_once_with( - method="GET", - path=self.NOTIFICATION_PATH, - query_params={}, - timeout=42, + expected_query_params = {} + client._get_resource.assert_called_once_with( + self.NOTIFICATION_PATH, + query_params=expected_query_params, + timeout=self._get_default_timeout(), retry=DEFAULT_RETRY, ) - def test_exists_hit(self): - USER_PROJECT = "user-project-123" - client = self._make_client() - bucket = self._make_bucket(client, user_project=USER_PROJECT) - notification = self._make_one(bucket, self.TOPIC_NAME) - notification._properties["id"] = self.NOTIFICATION_ID - api_request = client._connection.api_request - api_request.return_value = { + def test_exists_hit_w_explicit_w_user_project(self): + user_project = "user-project-123" + api_response = { "topic": self.TOPIC_REF, "id": self.NOTIFICATION_ID, "etag": self.ETAG, "selfLink": self.SELF_LINK, } + client = mock.Mock(spec=["_get_resource", "project"]) + client._get_resource.return_vale = api_response + client.project = self.BUCKET_PROJECT + bucket = self._make_bucket(client, user_project=user_project) + notification = self._make_one(bucket, self.TOPIC_NAME) + notification._properties["id"] = self.NOTIFICATION_ID + timeout = 42 + retry = mock.Mock(spec=[]) - self.assertTrue(notification.exists(client=client)) + self.assertTrue( + notification.exists(client=client, timeout=timeout, retry=retry) + ) - api_request.assert_called_once_with( - method="GET", - path=self.NOTIFICATION_PATH, - query_params={"userProject": USER_PROJECT}, - timeout=self._get_default_timeout(), - retry=DEFAULT_RETRY, + expected_query_params = {"userProject": user_project} + client._get_resource.assert_called_once_with( + self.NOTIFICATION_PATH, + query_params=expected_query_params, + timeout=timeout, + retry=retry, ) def test_reload_wo_notification_id(self): - client = self._make_client() + client = mock.Mock(spec=["_get_resource", "project"]) + client.project = self.BUCKET_PROJECT bucket = self._make_bucket(client) notification = self._make_one(bucket, self.TOPIC_NAME) with self.assertRaises(ValueError): notification.reload() - def test_reload_miss(self): + client._get_resource.assert_not_called() + + def test_reload_miss_w_defaults(self): from google.cloud.exceptions import NotFound - client = self._make_client() + client = mock.Mock(spec=["_get_resource", "project"]) + client._get_resource.side_effect = NotFound("testing") + client.project = self.BUCKET_PROJECT bucket = self._make_bucket(client) notification = self._make_one(bucket, self.TOPIC_NAME) notification._properties["id"] = self.NOTIFICATION_ID - api_request = client._connection.api_request - api_request.side_effect = NotFound("testing") with self.assertRaises(NotFound): - notification.reload(timeout=42) + notification.reload() - api_request.assert_called_once_with( - method="GET", - path=self.NOTIFICATION_PATH, - query_params={}, - timeout=42, + expected_query_params = {} + client._get_resource.assert_called_once_with( + self.NOTIFICATION_PATH, + query_params=expected_query_params, + timeout=self._get_default_timeout(), retry=DEFAULT_RETRY, ) - def test_reload_hit(self): + def test_reload_hit_w_explicit_w_user_project(self): from google.cloud.storage.notification import NONE_PAYLOAD_FORMAT - USER_PROJECT = "user-project-123" - client = self._make_client() - bucket = self._make_bucket(client, user_project=USER_PROJECT) - notification = self._make_one(bucket, self.TOPIC_NAME) - notification._properties["id"] = self.NOTIFICATION_ID - api_request = client._connection.api_request - api_request.return_value = { + user_project = "user-project-123" + api_response = { "topic": self.TOPIC_REF, "id": self.NOTIFICATION_ID, "etag": self.ETAG, "selfLink": self.SELF_LINK, "payload_format": NONE_PAYLOAD_FORMAT, } + client = mock.Mock(spec=["_get_resource", "project"]) + client._get_resource.return_value = api_response + client.project = self.BUCKET_PROJECT + bucket = self._make_bucket(client, user_project=user_project) + notification = self._make_one(bucket, self.TOPIC_NAME) + notification._properties["id"] = self.NOTIFICATION_ID + timeout = 42 + retry = mock.Mock(spec=[]) - notification.reload(client=client) + notification.reload(client=client, timeout=timeout, retry=retry) self.assertEqual(notification.etag, self.ETAG) self.assertEqual(notification.self_link, self.SELF_LINK) @@ -433,12 +447,12 @@ def test_reload_hit(self): self.assertIsNone(notification.blob_name_prefix) self.assertEqual(notification.payload_format, NONE_PAYLOAD_FORMAT) - api_request.assert_called_once_with( - method="GET", - path=self.NOTIFICATION_PATH, - query_params={"userProject": USER_PROJECT}, - timeout=self._get_default_timeout(), - retry=DEFAULT_RETRY, + expected_query_params = {"userProject": user_project} + client._get_resource.assert_called_once_with( + self.NOTIFICATION_PATH, + query_params=expected_query_params, + timeout=timeout, + retry=retry, ) def test_delete_wo_notification_id(self):