diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..5951051 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,4 @@ +[report] + +exclude_lines = + if __name__ == .__main__.: \ No newline at end of file diff --git a/.gitignore b/.gitignore index 7de892e..904c4c4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ .idea main_version -branch_version \ No newline at end of file +branch_version +src/__pycache__/* +tests/__pycache__/* \ No newline at end of file diff --git a/.pylintrc b/.pylintrc index 25749b5..fad2aa6 100644 --- a/.pylintrc +++ b/.pylintrc @@ -6,7 +6,8 @@ max-line-length=120 # Disable various warnings: # W0237 Disabled as it makes the code more readable -disable=W0237 +# R0801 Disabled as it's a small amount of duplication +disable=W0237, R0801 [MASTER] init-hook='import sys; sys.path.append("src")' \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 95d571b..200a798 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,3 +1,3 @@ services: self-test: - image: some/test:0.4.7 + image: some/test:0.4.8 diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000..3b93724 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,5 @@ +[pytest] +pythonpath = src +testpaths = tests +python_files = *.py +python_functions = test_* diff --git a/src/base.py b/src/base.py index b2e6a20..84a79f1 100644 --- a/src/base.py +++ b/src/base.py @@ -1,4 +1,5 @@ """This is the base module including helper/abstract classes and errors.""" + from abc import ABC, abstractmethod from pathlib import Path from typing import Union @@ -31,5 +32,5 @@ def get_version(content: str) -> Version: @staticmethod @abstractmethod - def compare(version1: Version, version2: Version) -> Union[True, VersionNotUpdated]: + def compare(version1: Version, version2: Version) -> Union[bool, VersionNotUpdated]: """This method should compare the versions and return a bool status""" diff --git a/src/comparison.py b/src/comparison.py index 9e279dc..6cc1c3c 100644 --- a/src/comparison.py +++ b/src/comparison.py @@ -1,6 +1,7 @@ """The comparison module which is where the main check classes are.""" + from pathlib import Path -from typing import Union, List +from typing import Union, List, Type from packaging.version import Version from base import Base, VersionNotUpdated @@ -51,7 +52,7 @@ def get_version(content: str) -> Version: return Version(content) @staticmethod - def compare(main: Version, branch: Version) -> Union[True, VersionNotUpdated]: + def compare(main: Version, branch: Version) -> Union[bool, Type[VersionNotUpdated]]: """ Returns if the branch version is larger than the main version :param main: Version on main @@ -99,7 +100,7 @@ def read_files(app: Path, compose: Path) -> (str, List): return content1, content2 @staticmethod - def get_version(content: str) -> Version: + def get_version(content: List[str]) -> Version: """ This method returns the version from the file as an object For compose versions we have to do some data handling. @@ -114,7 +115,7 @@ def get_version(content: str) -> Version: return Version(version_str) @staticmethod - def compare(app: Version, compose: Version) -> Union[True, VersionNotUpdated]: + def compare(app: Version, compose: Version) -> Union[bool, Type[VersionNotUpdated]]: """ Returns if the application version and docker compose version are equal. :param app: App version diff --git a/src/main.py b/src/main.py index d830360..8286bb5 100644 --- a/src/main.py +++ b/src/main.py @@ -15,8 +15,8 @@ def main(): root_path = Path(os.environ.get("GITHUB_WORKSPACE")) main_path = root_path / "main" branch_path = root_path / "branch" - with open(branch_path / app_path, "r", encoding='utf-8') as release_file: - release_version = release_file.read().strip('\n') + with open(branch_path / app_path, "r", encoding="utf-8") as release_file: + release_version = release_file.read().strip("\n") CompareAppVersion().run(main_path / app_path, branch_path / app_path) if compose_path: @@ -24,7 +24,7 @@ def main(): CompareComposeVersion().run(branch_path / app_path, branch_path / compose_path) github_env = os.getenv("GITHUB_ENV") - with open(github_env, "a", encoding='utf-8') as env: + with open(github_env, "a", encoding="utf-8") as env: # We can assume either/both of these values returned true otherwise they would have errored env.write("app_updated=true\n") if compose_path: diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_compare_app_version.py b/tests/test_compare_app_version.py new file mode 100644 index 0000000..37033db --- /dev/null +++ b/tests/test_compare_app_version.py @@ -0,0 +1,66 @@ +"""Tests for comparison.CompareAppVersion""" +from unittest.mock import patch, mock_open +from pathlib import Path +from packaging.version import Version +import pytest +from comparison import CompareAppVersion, VersionNotUpdated + + +@pytest.fixture(name="instance", scope="function") +def instance_fixture(): + """Provide a fixture instance for the tests""" + return CompareAppVersion() + + +@patch("comparison.CompareAppVersion.compare") +@patch("comparison.CompareAppVersion.get_version") +@patch("comparison.CompareAppVersion.read_files") +def test_run(mock_read, mock_get_version, mock_compare, instance): + """Test the run method makes correct calls.""" + mock_path1 = Path("mock1") + mock_path2 = Path("mock2") + mock_read.return_value = ("1.0.0", "1.0.1") + mock_get_version.side_effect = [Version("1.0.0"), Version("1.0.0")] + mock_compare.return_value = True + res = instance.run(mock_path1, mock_path2) + mock_read.assert_called_once_with(mock_path1, mock_path2) + mock_get_version.assert_any_call("1.0.0") + mock_get_version.assert_any_call("1.0.1") + mock_compare.assert_called_once_with(Version("1.0.0"), Version("1.0.0")) + assert res + + +@patch("comparison.CompareAppVersion.compare") +@patch("comparison.CompareAppVersion.get_version") +@patch("comparison.CompareAppVersion.read_files") +def test_run_fails(mock_read, _, mock_compare, instance): + """Test the run method fails.""" + mock_read.return_value = ("mock1", "mock2") + mock_compare.side_effect = VersionNotUpdated() + with pytest.raises(VersionNotUpdated): + instance.run(Path("mock1"), Path("mock2")) + + +def test_read_files(instance): + """Test the read files method returns a tuple""" + with patch("builtins.open", mock_open(read_data="1.0.0")): + res = instance.read_files(Path("mock1"), Path("mock2")) + assert res == ("1.0.0", "1.0.0") + + +def test_get_version(instance): + """Test a version object is returned""" + res = instance.get_version("1.0.0") + assert isinstance(res, Version) + + +def test_compare_pass(instance): + """Test that the compare returns true for a valid comparison""" + res = instance.compare(Version("1.0.0"), Version("1.0.1")) + assert res != VersionNotUpdated + + +def test_compare_fails(instance): + """Test that the compare returns an error for an invalid comparison""" + res = instance.compare(Version("1.0.1"), Version("1.0.0")) + assert res == VersionNotUpdated diff --git a/tests/test_compare_compose_version.py b/tests/test_compare_compose_version.py new file mode 100644 index 0000000..8356b67 --- /dev/null +++ b/tests/test_compare_compose_version.py @@ -0,0 +1,65 @@ +"""Tests for comparison.CompareComposeVersion""" +from unittest.mock import patch, mock_open +from pathlib import Path +import pytest +from packaging.version import Version +from comparison import CompareComposeVersion, VersionNotUpdated + + +@pytest.fixture(name="instance", scope="function") +def instance_fixture(): + """Provide a fixture instance for the tests""" + return CompareComposeVersion() + + +@patch("comparison.CompareComposeVersion.compare") +@patch("comparison.CompareComposeVersion.get_version") +@patch("comparison.CompareComposeVersion.read_files") +def test_run(mock_read, mock_get_version, mock_compare, instance): + """Test the run method makes correct calls.""" + mock_path1 = Path("mock1") + mock_path2 = Path("mock2") + mock_read.return_value = ("1.0.0", "1.0.0") + mock_get_version.side_effect = [Version("1.0.0"), Version("1.0.0")] + mock_compare.return_value = True + res = instance.run(mock_path1, mock_path2) + mock_read.assert_called_once_with(mock_path1, mock_path2) + mock_get_version.assert_any_call("1.0.0") + mock_compare.assert_called_once_with(Version("1.0.0"), Version("1.0.0")) + assert res + + +@patch("comparison.CompareComposeVersion.compare") +@patch("comparison.CompareComposeVersion.get_version") +@patch("comparison.CompareComposeVersion.read_files") +def test_run_fails(mock_read, _, mock_compare, instance): + """Test the run method fails.""" + mock_read.return_value = ("1.0.0", "1.0.1") + mock_compare.side_effect = VersionNotUpdated() + with pytest.raises(VersionNotUpdated): + instance.run(Path("mock1"), Path("mock2")) + + +def test_read_files(instance): + """Test the read files method returns a tuple""" + with patch("builtins.open", mock_open(read_data="1.0.0")): + res = instance.read_files(Path("mock1"), Path("mock2")) + assert res == ("1.0.0", ["1.0.0"]) + + +def test_get_version(instance): + """Test a version object is returned""" + res = instance.get_version(["image: some/image:1.0.0\n"]) + assert isinstance(res, Version) + + +def test_compare_pass(instance): + """Test that the compare returns true for a valid comparison""" + res = instance.compare(Version("1.0.0"), Version("1.0.0")) + assert res != VersionNotUpdated + + +def test_compare_fails(instance): + """Test that the compare returns an error for an invalid comparison""" + res = instance.compare(Version("1.0.1"), Version("1.0.0")) + assert res == VersionNotUpdated diff --git a/tests/test_main.py b/tests/test_main.py new file mode 100644 index 0000000..6a977aa --- /dev/null +++ b/tests/test_main.py @@ -0,0 +1,25 @@ +"""Tests for main""" +from unittest.mock import patch, mock_open +from pathlib import Path +from main import main + + +@patch("main.CompareComposeVersion") +@patch("main.CompareAppVersion") +@patch("main.os") +def test_main(mock_os, mock_compare_app, mock_compare_compose): + """Test the main method runs correctly.""" + mock_os.environ.get.side_effect = [Path("app"), Path("compose"), Path("workspace")] + with patch("builtins.open", mock_open(read_data="1.0.0")): + main() + mock_os.environ.get.assert_any_call("INPUT_APP_VERSION_PATH") + mock_os.environ.get.assert_any_call("INPUT_DOCKER_COMPOSE_PATH") + mock_os.environ.get.assert_any_call("GITHUB_WORKSPACE") + mock_branch_path = Path("workspace") / "branch" + mock_main_path = Path("workspace") / "main" + mock_compare_app.return_value.run.assert_called_once_with( + mock_main_path / "app", mock_branch_path / "app" + ) + mock_compare_compose.return_value.run.assert_called_once_with( + mock_branch_path / "app", mock_branch_path / "compose" + ) diff --git a/version.txt b/version.txt index 5546bd2..c650d5a 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.4.7 \ No newline at end of file +0.4.8 \ No newline at end of file