diff --git a/Makefile b/Makefile index 6e7d605e..d8d765a3 100644 --- a/Makefile +++ b/Makefile @@ -13,6 +13,14 @@ build: venv rm -f README.rst . venv/bin/activate && python -m build +# Note: "make install" needs to be ran once before this target will work, but we +# don't want to set it as a dependency otherwise it unnecessarily slows down +# fast implement/test cycles. "make install" created an editable install of the +# package which is linked to the files you are editing so there is no need to +# re-install after each change. +unit-test: + . venv/bin/activate && pytest test/src/mock + lint: venv rm -f README.rst . venv/bin/activate && flake8 src --count --select=E9,F63,F7,F82 --show-source --statistics && flake8 src --count --exit-zero --max-complexity=10 --max-line-length=200 --statistics diff --git a/setup.py b/setup.py index c46f6a3f..79f8d274 100644 --- a/setup.py +++ b/setup.py @@ -70,7 +70,8 @@ def get_version(rel_path): 'flake8', # MIT License 'pytest', # MIT License 'pytest-mock', # MIT License - 'requests-mock' # Apache Software License + 'requests-mock', # Apache Software License + 'setuptools', # MIT License ] }, classifiers=[ diff --git a/test/src/mock/test_mas_mock.py b/test/src/mock/test_mas_mock.py new file mode 100644 index 00000000..a50057b2 --- /dev/null +++ b/test/src/mock/test_mas_mock.py @@ -0,0 +1,96 @@ +# ***************************************************************************** +# Copyright (c) 2025 IBM Corporation and other Contributors. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v10.html +# +# ***************************************************************************** + +import pytest +from unittest import mock +from unittest.mock import MagicMock +from openshift.dynamic.exceptions import NotFoundError +from kubernetes.client.rest import ApiException + +from mas.devops import mas + + +CATALOG_ID = 'v9-250101-amd64' +CATALOG_DISPLAY_NAME_VALID = f'IBM Maximo Operators {CATALOG_ID}' +CATALOG_DISPLAY_NAME_INVALID = 'invalidCatalogName' +IMAGE = 'testImage' + + +# ----------------------------------------------------------------------------- +# WARNING: All tests must be written with strictly no external dependencies. +# Mocks must be used in place of any calls to OpenShift API etc. +# ----------------------------------------------------------------------------- + + +@pytest.fixture(autouse=True) +@mock.patch('openshift.dynamic.DynamicClient') +def dynamic_client(client): + return client + + +def test_get_current_catalog_success(dynamic_client): + # 1. Create a mock catalogsource resources API + catalog_api = MagicMock() + + # 2. Create a mock kubernetes resources API and attach the mock catalogsource API + resources = MagicMock() + resources.get.side_effect = lambda **kwargs: catalog_api if kwargs['api_version'] == 'operators.coreos.com/v1alpha1' \ + and kwargs['kind'] == 'CatalogSource' else None + + # 3. Create a mock client using the mock resources API + client = dynamic_client() + client.resources = resources + + # 4. Create a mock catalogsource API response for the catalogsource mock + spec = MagicMock() + spec.displayName = CATALOG_DISPLAY_NAME_VALID + spec.image = IMAGE + catalog = MagicMock() + catalog.spec = spec + + catalog_api.get.side_effect = lambda **kwargs: catalog if kwargs['name'] == 'ibm-operator-catalog' \ + and kwargs['namespace'] == 'openshift-marketplace' else None + + # 5. Call the mock API + current_catalog = mas.getCurrentCatalog(client) + assert current_catalog['displayName'] == CATALOG_DISPLAY_NAME_VALID + assert current_catalog['catalogId'] == CATALOG_ID + assert current_catalog['image'] == IMAGE + + +def test_get_current_catalog_not_found(dynamic_client): + client = dynamic_client() + resources = MagicMock() + catalog_api = MagicMock() + resources.get.side_effect = lambda **kwargs: catalog_api if kwargs['api_version'] == 'operators.coreos.com/v1alpha1' \ + and kwargs['kind'] == 'CatalogSource' else None + client.resources = resources + catalog_api.get.side_effect = NotFoundError(ApiException(status='404')) + assert mas.getCurrentCatalog(client) is None + + +def test_get_current_catalog_invalid_id(dynamic_client): + client = dynamic_client() + resources = MagicMock() + catalog_api = MagicMock() + resources.get.side_effect = lambda **kwargs: catalog_api if kwargs['api_version'] == 'operators.coreos.com/v1alpha1' \ + and kwargs['kind'] == 'CatalogSource' else None + client.resources = resources + catalog = MagicMock() + catalog_api.get.side_effect = lambda **kwargs: catalog if kwargs['name'] == 'ibm-operator-catalog' \ + and kwargs['namespace'] == 'openshift-marketplace' else None + spec = MagicMock() + catalog.spec = spec + spec.displayName = CATALOG_DISPLAY_NAME_INVALID + spec.image = IMAGE + current_catalog = mas.getCurrentCatalog(client) + assert current_catalog['displayName'] == CATALOG_DISPLAY_NAME_INVALID + assert current_catalog['image'] == IMAGE + assert current_catalog['catalogId'] is None