Skip to content

tests: refactor systests using pytest #476

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 54 commits into from
Jun 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
8a35175
tests: move systest for service acct email to new module
tseaver Jun 21, 2021
44d70d0
tests: move systest for hmac keys to new module
tseaver Jun 21, 2021
a08b702
tests: skip test if not service account
tseaver Jun 21, 2021
ffb140e
tests: move test for 'Client.create_bucket' to new module
tseaver Jun 21, 2021
e647017
tests: move test for 'Bucket.create' to new module
tseaver Jun 21, 2021
ebfd433
tests: move test for 'Bucket.lifecycle_rules' to new module
tseaver Jun 21, 2021
89edc92
tests: move test for 'Client.list_buckets' to new module
tseaver Jun 21, 2021
6ce2487
tests: move test for updating 'Bucket.labels' to new module
tseaver Jun 22, 2021
f06706b
tests: move test for bucket get / set IAM policy to new module
tseaver Jun 22, 2021
244359c
tests: move test for bucket CRUD w/ requester pays to new module
tseaver Jun 22, 2021
269aebe
tests: move test for bucket ACL/IAM w/ requester pays to new module
tseaver Jun 22, 2021
7635f34
tests: move test for 'Bucket.copy_blob' w/ requester pays to new module
tseaver Jun 22, 2021
476ec68
tests: move test for 'Bucket.copy_blob' w/ generation_match to new mo…
tseaver Jun 22, 2021
43604e4
tests: move test for 'Bucket.copy_blob' w/ metageneration_match to ne…
tseaver Jun 22, 2021
459e4d3
tests: move test for 'Bucket.get_blob' w/ user project to new module
tseaver Jun 22, 2021
14d5430
tests: make 'require_service_account' a fixture
tseaver Jun 22, 2021
f46f549
tests: add 'shared_bucket' fixture
tseaver Jun 22, 2021
109ba48
tests: move tests for large / samll file writes to new module
tseaver Jun 22, 2021
9788a13
tests: make 'user_project' a fixture
tseaver Jun 22, 2021
4a35440
tests: rename 'require_service_account' fixture to 'service_account'
tseaver Jun 22, 2021
445ded8
tests: move tests for blob CRUD to separate module
tseaver Jun 22, 2021
06ef8e5
tests: move tests for blob ACL to separate module
tseaver Jun 22, 2021
d36da5f
tests: move more tests for blob write to separate module
tseaver Jun 22, 2021
4e5f4d5
test: move conditional blob download tests to new module
tseaver Jun 22, 2021
6b1f81f
tests: move tests for download as URI to new module
tseaver Jun 22, 2021
283ebc0
tests: move test for 'Bucket.copy_blob' to new module
tseaver Jun 22, 2021
d7d6e23
tests: move test for 'Blob.download_as_text' to new module
tseaver Jun 22, 2021
c6d25ec
tests: move test for upload of gzip-encoded file to new module
tseaver Jun 22, 2021
c2c193d
tests: move test for resumable upload w/ genration to new module
tseaver Jun 22, 2021
a48acc9
tests: move remaining 'Blob.upload_from' tests to new module
tseaver Jun 22, 2021
9625fe5
tests: share 'file_data' fixture
tseaver Jun 22, 2021
1920a89
tests: move tests for 'BlobReader'/'BlobWriter' to new module
tseaver Jun 22, 2021
f1d12ba
tests: move test for blob unicode names to new module
tseaver Jun 22, 2021
e8c4391
tests: move tests for 'Bucket.list_blobs' to new module
tseaver Jun 22, 2021
0727bbe
tests: move tests for hierarchy 'Bucket.list_blobs' to new module
tseaver Jun 22, 2021
a0ebd8b
tests: move tests for signe list_blobs URL to new module
tseaver Jun 22, 2021
f498fcc
tests: move tests for create signed read URLs to new module
tseaver Jun 22, 2021
2b0799d
tests: move tests for create signed delete URLs to new module
tseaver Jun 22, 2021
4378fd6
tests: move tests for create signed resumable upload URLs to new module
tseaver Jun 22, 2021
eb5863b
tests: move tests for 'Blob.compose' to new module
tseaver Jun 22, 2021
4ad2e21
tests: move tests for 'Blob.rewrite' to new module
tseaver Jun 22, 2021
0b18610
tests: move tests for 'Blob.update_storage_class' to new module
tseaver Jun 22, 2021
4f6ebe9
tests: add fixture for skipping testcases if testing mTLS
tseaver Jun 23, 2021
d437b03
tests: move tests for 'BucketNotification' to new module
tseaver Jun 23, 2021
a3288ce
tests: move tests for anonymous client to new module
tseaver Jun 23, 2021
d7bb555
tests: move tests for KMS integration to new module
tseaver Jun 23, 2021
4c13e32
tests: move tests for bucket retention policy to new module
tseaver Jun 23, 2021
9f1eec9
tests: move tests for bucket IAM policy to new module
tseaver Jun 23, 2021
97fc701
tests: move tests for signed post policies to new module
tseaver Jun 23, 2021
46b9975
chore: add file encoding FBO Python 2.7
tseaver Jun 23, 2021
34466b6
chore: address review comments
tseaver Jun 25, 2021
8cf30fc
tests: work around failures under Python 2.7
tseaver Jun 25, 2021
6b93e1d
tests: simulate Python 3.x 'functools.wraps' under 2.7
tseaver Jun 25, 2021
e7bd20d
tests: move Python 2.7 workaround to '_helpers'
tseaver Jun 25, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file added tests/system/__init__.py
Empty file.
86 changes: 86 additions & 0 deletions tests/system/_helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import os

import six

from google.api_core import exceptions

from test_utils.retry import RetryErrors
from test_utils.retry import RetryInstanceState
from test_utils.system import unique_resource_id

retry_429 = RetryErrors(exceptions.TooManyRequests)
retry_429_harder = RetryErrors(exceptions.TooManyRequests, max_tries=10)
retry_429_503 = RetryErrors(
[exceptions.TooManyRequests, exceptions.ServiceUnavailable], max_tries=10
)

# Work around https://github.com/googleapis/python-test-utils/issues/36
if six.PY3:
retry_failures = RetryErrors(AssertionError)
else:

def retry_failures(decorated): # no-op
wrapped = RetryErrors(AssertionError)(decorated)
wrapped.__wrapped__ = decorated
return wrapped


user_project = os.environ.get("GOOGLE_CLOUD_TESTS_USER_PROJECT")
testing_mtls = os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE") == "true"
signing_blob_content = b"This time for sure, Rocky!"


def _bad_copy(bad_request):
"""Predicate: pass only exceptions for a failed copyTo."""
err_msg = bad_request.message
return err_msg.startswith("No file found in request. (POST") and "copyTo" in err_msg


def _no_event_based_hold(blob):
return not blob.event_based_hold


retry_bad_copy = RetryErrors(exceptions.BadRequest, error_predicate=_bad_copy)
retry_no_event_based_hold = RetryInstanceState(_no_event_based_hold)


def unique_name(prefix):
return prefix + unique_resource_id("-")


def empty_bucket(bucket):
for blob in list(bucket.list_blobs(versions=True)):
try:
blob.delete()
except exceptions.NotFound:
pass


def delete_blob(blob):
errors = (exceptions.Conflict, exceptions.TooManyRequests)
retry = RetryErrors(errors)
try:
retry(blob.delete)()
except exceptions.NotFound: # race
pass


def delete_bucket(bucket):
errors = (exceptions.Conflict, exceptions.TooManyRequests)
retry = RetryErrors(errors, max_tries=15)
retry(empty_bucket)(bucket)
retry(bucket.delete)(force=True)
192 changes: 192 additions & 0 deletions tests/system/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import contextlib
import os

import pytest

from google.cloud.storage._helpers import _base64_md5hash
from . import _helpers


dirname = os.path.realpath(os.path.dirname(__file__))
data_dirname = os.path.abspath(os.path.join(dirname, "..", "data"))
_filenames = [
("logo", "CloudPlatform_128px_Retina.png"),
("big", "five-point-one-mb-file.zip"),
("simple", "simple.txt"),
]
_file_data = {
key: {"path": os.path.join(data_dirname, file_name)}
for key, file_name in _filenames
}

_listable_filenames = ["CloudLogo1", "CloudLogo2", "CloudLogo3", "CloudLogo4"]
_hierarchy_filenames = [
"file01.txt",
"parent/",
"parent/file11.txt",
"parent/child/file21.txt",
"parent/child/file22.txt",
"parent/child/grand/file31.txt",
"parent/child/other/file32.txt",
]


@pytest.fixture(scope="session")
def storage_client():
from google.cloud.storage import Client

client = Client()
with contextlib.closing(client):
yield client


@pytest.fixture(scope="session")
def user_project():
if _helpers.user_project is None:
pytest.skip("USER_PROJECT not set in environment.")
return _helpers.user_project


@pytest.fixture(scope="session")
def no_mtls():
if _helpers.testing_mtls:
pytest.skip("Test incompatible with mTLS.")


@pytest.fixture(scope="session")
def service_account(storage_client):
from google.oauth2.service_account import Credentials

if not isinstance(storage_client._credentials, Credentials):
pytest.skip("These tests require a service account credential")
return storage_client._credentials


@pytest.fixture(scope="session")
def shared_bucket_name():
return _helpers.unique_name("gcp-systest")


@pytest.fixture(scope="session")
def shared_bucket(storage_client, shared_bucket_name):
bucket = storage_client.bucket(shared_bucket_name)
bucket.versioning_enabled = True
_helpers.retry_429_503(bucket.create)()

yield bucket

_helpers.delete_bucket(bucket)


@pytest.fixture(scope="session")
def listable_bucket_name():
return _helpers.unique_name("gcp-systest-listable")


@pytest.fixture(scope="session")
def listable_bucket(storage_client, listable_bucket_name, file_data):
bucket = storage_client.bucket(listable_bucket_name)
_helpers.retry_429_503(bucket.create)()

info = file_data["logo"]
source_blob = bucket.blob(_listable_filenames[0])
source_blob.upload_from_filename(info["path"])

for filename in _listable_filenames[1:]:
_helpers.retry_bad_copy(bucket.copy_blob)(
source_blob, bucket, filename,
)

yield bucket

_helpers.delete_bucket(bucket)


@pytest.fixture(scope="session")
def listable_filenames():
return _listable_filenames


@pytest.fixture(scope="session")
def hierarchy_bucket_name():
return _helpers.unique_name("gcp-systest-hierarchy")


@pytest.fixture(scope="session")
def hierarchy_bucket(storage_client, hierarchy_bucket_name, file_data):
bucket = storage_client.bucket(hierarchy_bucket_name)
_helpers.retry_429_503(bucket.create)()

simple_path = _file_data["simple"]["path"]
for filename in _hierarchy_filenames:
blob = bucket.blob(filename)
blob.upload_from_filename(simple_path)

yield bucket

_helpers.delete_bucket(bucket)


@pytest.fixture(scope="session")
def hierarchy_filenames():
return _hierarchy_filenames


@pytest.fixture(scope="session")
def signing_bucket_name():
return _helpers.unique_name("gcp-systest-signing")


@pytest.fixture(scope="session")
def signing_bucket(storage_client, signing_bucket_name):
bucket = storage_client.bucket(signing_bucket_name)
_helpers.retry_429_503(bucket.create)()
blob = bucket.blob("README.txt")
blob.upload_from_string(_helpers.signing_blob_content)

yield bucket

_helpers.delete_bucket(bucket)


@pytest.fixture(scope="function")
def buckets_to_delete():
buckets_to_delete = []

yield buckets_to_delete

for bucket in buckets_to_delete:
_helpers.delete_bucket(bucket)


@pytest.fixture(scope="function")
def blobs_to_delete():
blobs_to_delete = []

yield blobs_to_delete

for blob in blobs_to_delete:
_helpers.delete_blob(blob)


@pytest.fixture(scope="session")
def file_data():
for file_data in _file_data.values():
with open(file_data["path"], "rb") as file_obj:
file_data["hash"] = _base64_md5hash(file_obj)

return _file_data
Loading