diff --git a/.forgejo/workflows/test.yml b/.forgejo/workflows/test.yml new file mode 100644 index 0000000..976726b --- /dev/null +++ b/.forgejo/workflows/test.yml @@ -0,0 +1,18 @@ +on: + pull_request: + types: [opened, synchronize] + push: +jobs: + test: + runs-on: podman + container: + image: quay.io/fedora/fedora:43 + steps: + - run: dnf install hatch cairo-devel cairo-gobject-devel gobject-introspection-devel pkgconf-pkg-config python3-devel -y + - uses: actions/checkout@v6 + - run: hatch test + working-directory: fedora-image-tester + - run: hatch test + working-directory: fedora-image-uploader + - run: hatch test + working-directory: fedora-image-uploader-messages diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f739f96 --- /dev/null +++ b/.gitignore @@ -0,0 +1,211 @@ +# Created by https://www.toptal.com/developers/gitignore/api/linux,python,vim +# Edit at https://www.toptal.com/developers/gitignore?templates=linux,python,vim + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +### Python Patch ### +# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration +poetry.toml + +# ruff +.ruff_cache/ + +# LSP config files +pyrightconfig.json + +### Vim ### +# Swap +[._]*.s[a-v][a-z] +!*.svg # comment out if you don't need vector files +[._]*.sw[a-p] +[._]s[a-rt-v][a-z] +[._]ss[a-gi-z] +[._]sw[a-p] + +# Session +Session.vim +Sessionx.vim + +# Temporary +.netrwhist +# Auto-generated tag files +tags +# Persistent undo +[._]*.un~ + +# End of https://www.toptal.com/developers/gitignore/api/linux,python,vim diff --git a/Containerfile.fit b/Containerfile.fit index 276d3db..e61a01f 100644 --- a/Containerfile.fit +++ b/Containerfile.fit @@ -15,7 +15,7 @@ RUN cd /srv/fedora-image-uploader-messages && hatchling build -d /srv/dist/ --ta # Currently LISA isn't on PyPI, but I'm working on it RUN git clone https://github.com/microsoft/lisa.git /srv/lisa WORKDIR /srv/lisa -RUN git checkout 20260330.1 && \ +RUN git checkout 20260521.1 && \ git config --global user.email "cloud@lists.fedoraproject.org" && \ git config --global user.name "Fedora Cloud SIG" && \ sed -i 's/PyGObject <= 3.50.0/PyGObject <= 3.55.0/g' pyproject.toml && \ diff --git a/fedora-image-tester/pyproject.toml b/fedora-image-tester/pyproject.toml index 753d990..e2bf9b7 100644 --- a/fedora-image-tester/pyproject.toml +++ b/fedora-image-tester/pyproject.toml @@ -33,6 +33,21 @@ path = "fedora_image_tester/__init__.py" # Required for LISA since they don't publish releases to PyPI yet allow-direct-references = true +[tool.hatch.envs.hatch-test] +features = ["test"] +extra-dependencies = [ + "fedora-image-uploader-messages @ {root:uri}/../fedora-image-uploader-messages", + "mslisa[azure,aws] @ git+https://github.com/microsoft/lisa.git", +] + +# mslisa pins pluggy<0.14 which conflicts with the pluggy>=1.4 required by +# modern pytest. Tell uv (hatch's resolver) to ignore mslisa's pluggy pin so +# the test environment can resolve. +[tool.uv] +override-dependencies = [ + "pluggy>=1.4", +] + [tool.black] line-length = 100 diff --git a/fedora-image-tester/tests/test_azure.py b/fedora-image-tester/tests/test_azure.py deleted file mode 100644 index b2c80b0..0000000 --- a/fedora-image-tester/tests/test_azure.py +++ /dev/null @@ -1,237 +0,0 @@ -"""Unit tests for the Consumer class in azure.py.""" - -import os -import subprocess -from tempfile import TemporaryDirectory -from unittest.mock import MagicMock, Mock, patch - -import pytest -from fedora_image_uploader_messages.publish import AzurePublishedV1 -from fedora_messaging import config as fm_config - -from fedora_image_tester.azure import Consumer - - -@pytest.fixture(scope="module") -def azure_conf(): - """Provide a minimal config for Azure in the fedora-messaging configuration dictionary.""" - with patch.dict( - fm_config.conf["consumer_config"], - { - "azure": { - "region": "westus3", - "subscription_id": "00000000-0000-0000-0000-000000000000", - } - }, - ): - yield - - -@pytest.fixture -def consumer(azure_conf): # pylint: disable=unused-argument - """Create an Consumer instance for testing.""" - return Consumer() - - -@pytest.fixture -def valid_message(): - """Create a valid mock AzurePublishedV1 message.""" - message = Mock(spec=AzurePublishedV1) - message.topic = "org.fedoraproject.prod.fedora_image_uploader.published.v1.azure.test" - message.body = { - "image_definition_name": "Fedora-Cloud-Rawhide-x64", - "image_version_name": "20250101.0", - "image_resource_id": ( - "/subscriptions/test-sub/resourceGroups/test-rg/providers" - "/Microsoft.Compute/galleries/test-gallery" - ), - } - return message - - -class TestConsumer: - # pylint: disable=protected-access - """Test class for Consumer.""" - - def test_get_image_definition_name_success(self, consumer, valid_message): - """Test successful extraction of image definition name.""" - result = consumer._get_image_definition_name(valid_message) - assert result == "Fedora-Cloud-Rawhide-x64" - - def test_get_image_definition_name_invalid_data(self, consumer): - """Test handling of invalid image definition name data.""" - # Test missing field - message = Mock() - message.body = {} - assert consumer._get_image_definition_name(message) is None - - # Test non-string value - message.body = {"image_definition_name": 123} - assert consumer._get_image_definition_name(message) is None - - # Test missing body attribute - del message.body - assert consumer._get_image_definition_name(message) is None - - @patch("fedora_cloud_tests.azure.subprocess.run") - @patch("os.chmod") - def test_generate_ssh_key_pair_success(self, mock_chmod, mock_subprocess, consumer): - """Test successful SSH key pair generation.""" - # Mock subprocess.run to simulate successful ssh-keygen - mock_subprocess.return_value = MagicMock(stdout="Key generated successfully") - - with TemporaryDirectory() as temp_dir: - with patch("os.path.exists", return_value=True): - result = consumer._generate_ssh_key_pair(temp_dir) - - # Verify the method returns the expected private key path - expected_path = os.path.join(temp_dir, "id_ed25519") - assert result == expected_path - - # Verify ssh-keygen was called with correct parameters - mock_subprocess.assert_called_once() - call_args = mock_subprocess.call_args[0][0] - assert "ssh-keygen" in call_args - assert "-t" in call_args and "ed25519" in call_args - assert "-f" in call_args - - # Verify file permissions were set - mock_chmod.assert_called_once_with(expected_path, 0o600) - - @patch("fedora_cloud_tests.azure.subprocess.run") - def test_generate_ssh_key_pair_failures(self, mock_subprocess, consumer): - """Test SSH key pair generation failure cases.""" - with TemporaryDirectory() as temp_dir: - # Test subprocess failure - mock_subprocess.side_effect = subprocess.CalledProcessError(1, "ssh-keygen") - result = consumer._generate_ssh_key_pair(temp_dir) - assert result is None - - # Reset mock for next test - mock_subprocess.side_effect = None - mock_subprocess.return_value = MagicMock(stdout="Key generated") - - # Test file not created scenario - with patch("os.path.exists", return_value=False): - result = consumer._generate_ssh_key_pair(temp_dir) - assert result is None - - def test_get_community_gallery_image_success(self, consumer, valid_message): - """Test successful community gallery image construction.""" - result = consumer.get_community_gallery_image(valid_message) - expected = "westus3/test-sub/Fedora-Cloud-Rawhide-x64/20250101.0" - assert result == expected - - def test_get_community_gallery_image_invalid_cases(self, consumer): - """Test community gallery image extraction with invalid inputs.""" - # Test unsupported Fedora version - message = Mock() - message.body = { - "image_definition_name": "Fedora-Cloud-Unsupported-x64", - "image_version_name": "20250101.0", - "image_resource_id": ( - "/subscriptions/test-sub/resourceGroups/test-rg/providers" - "/Microsoft.Compute/galleries/test-gallery" - ), - } - assert consumer.get_community_gallery_image(message) is None - - # Test invalid message body type - message.body = "not_a_dict" - assert consumer.get_community_gallery_image(message) is None - - # Test missing required fields - message.body = {"image_definition_name": "Fedora-Cloud-Rawhide-x64"} - assert consumer.get_community_gallery_image(message) is None - - # Test invalid resource ID format - message.body = { - "image_definition_name": "Fedora-Cloud-Rawhide-x64", - "image_version_name": "20250101.0", - "image_resource_id": "invalid/format", - } - assert consumer.get_community_gallery_image(message) is None - - # Test resource_id with insufficient parts (code allows empty parts[2]) - message.body = { - "image_definition_name": "Fedora-Cloud-Rawhide-x64", - "image_version_name": "20250101.0", - "image_resource_id": "//", # Results in empty parts[2] but still valid - } - result = consumer.get_community_gallery_image(message) - # The current code allows this and creates: "westus3//Fedora-Cloud-Rawhide-x64/20250101.0" - assert result == "westus3//Fedora-Cloud-Rawhide-x64/20250101.0" - - @patch("fedora_cloud_tests.azure.asyncio.run") - @patch("fedora_cloud_tests.azure.LisaRunner") - @patch.object(Consumer, "_generate_ssh_key_pair") - def test_azure_published_callback_success( - self, - mock_ssh_keygen, - mock_lisa_runner, - mock_asyncio_run, - consumer, - valid_message, - ): # pylint: disable=R0913,R0917 - """Test successful message processing and LISA trigger.""" - mock_runner_instance = MagicMock() - mock_lisa_runner.return_value = mock_runner_instance - mock_ssh_keygen.return_value = "/tmp/test_key" - - consumer.azure_published_callback(valid_message) - mock_lisa_runner.assert_called_once_with() - mock_asyncio_run.assert_called_once() - - @patch("fedora_cloud_tests.azure.asyncio.run") - @patch("fedora_cloud_tests.azure.LisaRunner") - def test_azure_published_callback_unsupported_image( - self, mock_lisa_runner, mock_asyncio_run, consumer - ): - """Test handling when community gallery image cannot be processed.""" - message = Mock() - message.topic = "test.topic" - message.body = {"image_definition_name": "Fedora-Cloud-Unsupported-x64"} - - consumer.azure_published_callback(message) - mock_lisa_runner.assert_not_called() - mock_asyncio_run.assert_not_called() - - @patch( - "fedora_cloud_tests.azure.asyncio.run", - side_effect=OSError("LISA execution failed"), - ) - @patch("fedora_cloud_tests.azure.LisaRunner") - @patch.object(Consumer, "_generate_ssh_key_pair") - def test_azure_published_callback_lisa_exception( - self, - mock_ssh_keygen, - mock_lisa_runner, - mock_asyncio_run, - consumer, - valid_message, - ): # pylint: disable=R0913,R0917 - """Test exception handling when LISA execution fails.""" - mock_runner_instance = MagicMock() - mock_lisa_runner.return_value = mock_runner_instance - mock_ssh_keygen.return_value = "/tmp/test_key" - - # Should not raise exception, just log it - consumer.azure_published_callback(valid_message) - mock_asyncio_run.assert_called_once() - - def test_azure_published_callback_message_validation_exception(self, consumer): - """Test exception handling during message type validation.""" - # Create a message that will cause a TypeError during isinstance check - # by making it not a proper message type - invalid_message = Mock() - invalid_message.topic = "test.topic" - invalid_message.body = "not_a_dict" # This will cause isinstance issues - - # This should not crash but should log errors and return early - consumer.azure_published_callback(invalid_message) - - def test_call_method_delegates_to_callback(self, consumer, valid_message): - """Test that __call__ method properly delegates to azure_published_callback.""" - with patch.object(consumer, "azure_published_callback") as mock_callback: - consumer(valid_message) - mock_callback.assert_called_once_with(valid_message) diff --git a/fedora-image-uploader-messages/pyproject.toml b/fedora-image-uploader-messages/pyproject.toml index 117af74..e41b253 100644 --- a/fedora-image-uploader-messages/pyproject.toml +++ b/fedora-image-uploader-messages/pyproject.toml @@ -52,6 +52,9 @@ test = [ [tool.hatch.version] path = "fedora_image_uploader_messages/__init__.py" +[tool.hatch.envs.hatch-test] +features = ["test"] + [tool.black] line-length = 100 diff --git a/fedora-image-uploader/pyproject.toml b/fedora-image-uploader/pyproject.toml index 182d4b5..191709e 100644 --- a/fedora-image-uploader/pyproject.toml +++ b/fedora-image-uploader/pyproject.toml @@ -27,7 +27,7 @@ aws = [ ] azure = [ "azure-identity", - "azure-mgmt-compute", + "azure-mgmt-compute ~= 37.2.0", "azure-storage-blob", ] gcp = [ @@ -54,6 +54,12 @@ fedora-image-uploader = "fedora_image_uploader.cli:main" [tool.hatch.version] path = "fedora_image_uploader/__init__.py" +[tool.hatch.envs.hatch-test] +features = ["test", "aws", "azure", "gcp"] +extra-dependencies = [ + "fedora-image-uploader-messages @ {root:uri}/../fedora-image-uploader-messages", +] + [tool.black] line-length = 100 diff --git a/fedora-image-uploader/tests/test_aws.py b/fedora-image-uploader/tests/test_aws.py index b1245b3..5dd8023 100644 --- a/fedora-image-uploader/tests/test_aws.py +++ b/fedora-image-uploader/tests/test_aws.py @@ -644,18 +644,15 @@ def test_aws_filters(): aws_handler(image, ffrel) assert aws_handler.aws_register_image.call_count == 0 + # No EC2 in path image["arch"] = "x86_64" aws_handler(image, ffrel) - assert aws_handler.aws_register_image.call_count == 1 - - # No EC2 in path - aws_handler(image, ffrel) - assert aws_handler.aws_register_image.call_count == 1 + assert aws_handler.aws_register_image.call_count == 0 # Now EC2 in path image["path"] = "Cloud/x86_64/images/Fedora-Cloud-Base-AmazonEC2.x86_64.raw.xz" aws_handler(image, ffrel) - assert aws_handler.aws_register_image.call_count == 2 + assert aws_handler.aws_register_image.call_count == 1 # ELN metadata = {"composeinfo": {"payload": {"compose": {"date": "20240912", "respin": "2"}}}} @@ -663,10 +660,9 @@ def test_aws_filters(): image["path"] = ( "BaseOS/x86_64/images/Fedora-ELN-Cloud-Base-AmazonEC2-11-20240912.n.0.x86_64.raw.xz" ) - image["subvariant"] = "BaseOS" aws_handler(image, ffrel) - assert aws_handler.aws_register_image.call_count == 3 + assert aws_handler.aws_register_image.call_count == 2 assert ( - aws_handler.aws_register_image.call_args_list[2][0][3] + aws_handler.aws_register_image.call_args_list[1][0][3] == "Fedora-Cloud-Base-AmazonEC2.x86_64-ELN-20240912.2" ) diff --git a/fedora-image-uploader/tests/test_azure.py b/fedora-image-uploader/tests/test_azure.py index 46e1907..8b59e47 100644 --- a/fedora-image-uploader/tests/test_azure.py +++ b/fedora-image-uploader/tests/test_azure.py @@ -17,6 +17,7 @@ from fedora_messaging import testing as fm_testing from freezegun import freeze_time from fedora_image_uploader import Uploader +from fedora_image_uploader.azure import OWNER @pytest.mark.vcr @@ -181,7 +182,7 @@ def test_azure_old_excluded_images(azure_env_vars, azure_fm_conf): azure_handler = consumer.handlers["azure"] azure_handler.azure_compute_client = mock.Mock() azure_handler.azure_blob_client = mock.Mock() - image_definition = GalleryImage(location="eastus") + image_definition = GalleryImage(location="eastus", tags={"owner": OWNER}) image_definition.name = "Fedora-40" now = datetime.datetime.now(datetime.UTC) image_versions = [] @@ -190,7 +191,9 @@ def test_azure_old_excluded_images(azure_env_vars, azure_fm_conf): exclude_from_latest=True, end_of_life_date=now + timedelta(days=7) ) publish_profile.published_date = now + timedelta(seconds=v) - version = GalleryImageVersion(location="eastus", publishing_profile=publish_profile) + version = GalleryImageVersion( + location="eastus", publishing_profile=publish_profile, tags={"owner": OWNER} + ) version.name = f"40.1.{v}" image_versions.append(version) azure_handler.azure_compute_client.gallery_images.list_by_gallery.return_value = [ @@ -218,7 +221,7 @@ def test_azure_end_of_life(azure_env_vars, azure_fm_conf): azure_handler = consumer.handlers["azure"] azure_handler.azure_compute_client = mock.Mock() azure_handler.azure_blob_client = mock.Mock() - image_definition = GalleryImage(location="eastus") + image_definition = GalleryImage(location="eastus", tags={"owner": OWNER}) image_definition.name = "Fedora-40" now = datetime.datetime.now(datetime.UTC) image_versions = [] @@ -227,7 +230,9 @@ def test_azure_end_of_life(azure_env_vars, azure_fm_conf): exclude_from_latest=False, end_of_life_date=now + timedelta(days=v - 2) ) publish_profile.published_date = now - timedelta(days=v) - version = GalleryImageVersion(location="eastus", publishing_profile=publish_profile) + version = GalleryImageVersion( + location="eastus", publishing_profile=publish_profile, tags={"owner": OWNER} + ) version.name = f"40.1.{v}" image_versions.append(version) azure_handler.azure_compute_client.gallery_images.list_by_gallery.return_value = [ @@ -260,7 +265,7 @@ def test_azure_empty_definitions(azure_env_vars, azure_fm_conf): azure_handler = consumer.handlers["azure"] azure_handler.azure_compute_client = mock.Mock() azure_handler.azure_blob_client = mock.Mock() - image_definition = GalleryImage(location="eastus") + image_definition = GalleryImage(location="eastus", tags={"owner": OWNER}) image_definition.name = "Fedora-40" azure_handler.azure_compute_client.gallery_images.list_by_gallery.return_value = [ image_definition @@ -284,7 +289,7 @@ def test_azure_old_included_images(azure_env_vars, azure_fm_conf): azure_handler = consumer.handlers["azure"] azure_handler.azure_compute_client = mock.Mock() azure_handler.azure_blob_client = mock.Mock() - image_definition = GalleryImage(location="eastus") + image_definition = GalleryImage(location="eastus", tags={"owner": OWNER}) image_definition.name = "Fedora-40" now = datetime.datetime.now(datetime.UTC) image_versions = [] @@ -293,7 +298,9 @@ def test_azure_old_included_images(azure_env_vars, azure_fm_conf): exclude_from_latest=False, end_of_life_date=now + timedelta(days=7) ) publish_profile.published_date = now + timedelta(seconds=v) - version = GalleryImageVersion(location="eastus", publishing_profile=publish_profile) + version = GalleryImageVersion( + location="eastus", publishing_profile=publish_profile, tags={"owner": OWNER} + ) version.name = f"40.1.{v}" image_versions.append(version) azure_handler.azure_compute_client.gallery_images.list_by_gallery.return_value = [ @@ -317,7 +324,7 @@ def test_azure_rolling_images(azure_env_vars, azure_fm_conf, rolling_image): azure_handler = consumer.handlers["azure"] azure_handler.azure_compute_client = mock.Mock() azure_handler.azure_blob_client = mock.Mock() - image_definition = GalleryImage(location="eastus") + image_definition = GalleryImage(location="eastus", tags={"owner": OWNER}) image_definition.name = f"Fedora-{rolling_image}" now = datetime.datetime.now(datetime.UTC) image_versions = [] @@ -326,7 +333,9 @@ def test_azure_rolling_images(azure_env_vars, azure_fm_conf, rolling_image): exclude_from_latest=False, end_of_life_date=now + timedelta(days=7) ) publish_profile.published_date = now + timedelta(seconds=v) - version = GalleryImageVersion(location="eastus", publishing_profile=publish_profile) + version = GalleryImageVersion( + location="eastus", publishing_profile=publish_profile, tags={"owner": OWNER} + ) version.name = f"41.1.{v}" image_versions.append(version) azure_handler.azure_compute_client.gallery_images.list_by_gallery.return_value = [ diff --git a/fedora-image-uploader/tests/test_gcp.py b/fedora-image-uploader/tests/test_gcp.py index a2493b0..3ae617b 100644 --- a/fedora-image-uploader/tests/test_gcp.py +++ b/fedora-image-uploader/tests/test_gcp.py @@ -24,6 +24,7 @@ from fedora_image_uploader import Uploader }, }, ) +@pytest.mark.skipif(not os.environ.get("GOOGLE_APPLICATION_CREDENTIALS"), reason="GCP tests require GCP credentials") def test_image_filter(fixtures_dir): consumer = Uploader() handler = consumer.handlers["gcp"] @@ -66,6 +67,7 @@ def test_image_filter(fixtures_dir): }, }, ) +@pytest.mark.skipif(not os.environ.get("GOOGLE_APPLICATION_CREDENTIALS"), reason="GCP tests require GCP credentials") def test_messages(fixtures_dir): with open(os.path.join(fixtures_dir, "messages/rc_compose_40.json")) as fd: msg = message.load_message(json.load(fd)) @@ -138,6 +140,7 @@ def test_messages(fixtures_dir): }, }, ) +@pytest.mark.skipif(not os.environ.get("GOOGLE_APPLICATION_CREDENTIALS"), reason="GCP tests require GCP credentials") def test_import_image(fixtures_dir): consumer = Uploader() handler = consumer.handlers["gcp"] @@ -252,6 +255,7 @@ def test_import_image(fixtures_dir): }, }, ) +@pytest.mark.skipif(not os.environ.get("GOOGLE_APPLICATION_CREDENTIALS"), reason="GCP tests require GCP credentials") def test_promote_image_eln_rawhide(): """Assert this is a no-op for eln and rawhide""" consumer = Uploader() @@ -304,6 +308,7 @@ def test_promote_image_eln_rawhide(): }, }, ) +@pytest.mark.skipif(not os.environ.get("GOOGLE_APPLICATION_CREDENTIALS"), reason="GCP tests require GCP credentials") def test_needs_promotion(): consumer = Uploader() handler = consumer.handlers["gcp"] @@ -365,6 +370,7 @@ def test_needs_promotion(): }, }, ) +@pytest.mark.skipif(not os.environ.get("GOOGLE_APPLICATION_CREDENTIALS"), reason="GCP tests require GCP credentials") def test_no_promotion(): consumer = Uploader() handler = consumer.handlers["gcp"] @@ -417,6 +423,7 @@ def test_no_promotion(): }, }, ) +@pytest.mark.skipif(not os.environ.get("GOOGLE_APPLICATION_CREDENTIALS"), reason="GCP tests require GCP credentials") def test_cleanup_skips_unmanaged_images(): consumer = Uploader() handler = consumer.handlers["gcp"] @@ -447,6 +454,7 @@ def test_cleanup_skips_unmanaged_images(): }, }, ) +@pytest.mark.skipif(not os.environ.get("GOOGLE_APPLICATION_CREDENTIALS"), reason="GCP tests require GCP credentials") def test_cleanup_rawhide(): consumer = Uploader() handler = consumer.handlers["gcp"] @@ -507,6 +515,7 @@ def test_cleanup_rawhide(): }, }, ) +@pytest.mark.skipif(not os.environ.get("GOOGLE_APPLICATION_CREDENTIALS"), reason="GCP tests require GCP credentials") def test_cleanup_eol(): consumer = Uploader() handler = consumer.handlers["gcp"]