diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 0000000..7615b86 --- /dev/null +++ b/.pylintrc @@ -0,0 +1,2 @@ +[FORMAT] +max-line-length=120 diff --git a/tests/logging.config.yaml b/tests/resources/logging.config.yaml similarity index 100% rename from tests/logging.config.yaml rename to tests/resources/logging.config.yaml diff --git a/tests/test_hotfixes.py b/tests/test_hotfixes.py new file mode 100644 index 0000000..3c2066a --- /dev/null +++ b/tests/test_hotfixes.py @@ -0,0 +1,179 @@ +"""Test all scenarios where a hotfix / cherrypick is made.""" +# pylint: disable=too-many-locals +import unittest +from unittest import TestCase + +from utils import ActionInputs, ActionOutputs, CommitMessages, TestRepo, run_action, setup_logging + + +class HotfixTestCase(TestCase): + """Test all scenarios where a hotfix / cherrypick is made.""" + repo: TestRepo + + @classmethod + def setUpClass(cls): + setup_logging() + + def setUp(self): + self.repo = TestRepo() + + def tearDown(self): + self.repo.close() + + def test_feature_then_hotfix(self): + """Test Case: Run the action after a ``feat:`` commit and after a hotfix.""" + # Arrange + # Feature + args_feat_release = ActionInputs( + prefix='v', + suffix='pre', + previous_version_suffix='pre', + create_tag=True + ) + + expected_output_feat_release = ActionOutputs( + version='0.1.0-pre', + version_name='v0.1.0-pre', + previous_version='', + previous_version_name='', + has_changes=True + ) + + args_feat_beta = ActionInputs( + prefix='v', + suffix='beta', + previous_version_suffix='pre', + create_tag=True + ) + + expected_output_feat_beta = ActionOutputs( + version='0.1.0-beta', + version_name='v0.1.0-beta', + previous_version='', + previous_version_name='', + has_changes=True + ) + + args_feat_prod = ActionInputs( + prefix='v', + suffix='', + previous_version_suffix='beta', + create_tag=True + ) + + expected_output_feat_prod = ActionOutputs( + version='0.1.0', + version_name='v0.1.0', + previous_version='', + previous_version_name='', + has_changes=True + ) + + # Hotfix + args_hotfix_release = ActionInputs( + prefix='v', + suffix='pre', + previous_version_suffix='pre', + bumping_suffix='hotfix', + only_bump_suffix=True, + create_tag=True + ) + + expected_output_hotfix_release = ActionOutputs( + version='0.1.0-pre-hotfix.1', + version_name='v0.1.0-pre-hotfix.1', + previous_version='0.1.0-pre', + previous_version_name='v0.1.0-pre', + has_changes=True + ) + + args_hotfix_beta = ActionInputs( + prefix='v', + suffix='beta', + previous_version_suffix='pre', + bumping_suffix='hotfix', + only_bump_suffix=True, + create_tag=True + ) + + expected_output_hotfix_beta = ActionOutputs( + version='0.1.0-beta-hotfix.1', + version_name='v0.1.0-beta-hotfix.1', + previous_version='0.1.0-beta', + previous_version_name='v0.1.0-beta', + has_changes=True + ) + + args_hotfix_prod = ActionInputs( + prefix='v', + suffix='', + previous_version_suffix='beta', + bumping_suffix='hotfix', + only_bump_suffix=True, + create_tag=True + ) + + expected_output_hotfix_prod = ActionOutputs( + version='0.1.0-hotfix.1', + version_name='v0.1.0-hotfix.1', + previous_version='0.1.0', + previous_version_name='v0.1.0', + has_changes=True + ) + + # Act + # Feature + self.repo.commit(CommitMessages.FEATURE) + + self.repo.merge('main', 'release') + actual_output_feat_release = run_action(args_feat_release) + tag_feat_release = self.repo.get_latest_tag_name() + + self.repo.merge('release', 'release-beta') + actual_output_feat_beta = run_action(args_feat_beta) + tag_feat_beta = self.repo.get_latest_tag_name() + + self.repo.merge('release-beta', 'release-prod') + actual_output_feat_prod = run_action(args_feat_prod) + tag_feat_prod = self.repo.get_latest_tag_name() + + # Hotfix + self.repo.checkout('main') + commit = self.repo.commit(CommitMessages.FIX) + + self.repo.cherrypick(commit, 'release') + actual_output_hotfix_release = run_action(args_hotfix_release) + tag_hotfix_release = self.repo.get_latest_tag_name() + + self.repo.cherrypick(commit, 'release-beta') + actual_output_hotfix_beta = run_action(args_hotfix_beta) + tag_hotfix_beta = self.repo.get_latest_tag_name() + + self.repo.cherrypick(commit, 'release-prod') + actual_output_hotfix_prod = run_action(args_hotfix_prod) + tag_hotfix_prod = self.repo.get_latest_tag_name() + + # Assert + # Feature + self.assertEqual(expected_output_feat_release, actual_output_feat_release) + self.assertEqual(expected_output_feat_release.version_name, tag_feat_release) + + self.assertEqual(expected_output_feat_beta, actual_output_feat_beta) + self.assertEqual(expected_output_feat_beta.version_name, tag_feat_beta) + + self.assertEqual(expected_output_feat_prod, actual_output_feat_prod) + self.assertEqual(expected_output_feat_prod.version_name, tag_feat_prod) + + # Hotfix + self.assertEqual(expected_output_hotfix_release, actual_output_hotfix_release) + self.assertEqual(expected_output_hotfix_release.version_name, tag_hotfix_release) + + self.assertEqual(expected_output_hotfix_beta, actual_output_hotfix_beta) + self.assertEqual(expected_output_hotfix_beta.version_name, tag_hotfix_beta) + + self.assertEqual(expected_output_hotfix_prod, actual_output_hotfix_prod) + self.assertEqual(expected_output_hotfix_prod.version_name, tag_hotfix_prod) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_action.py b/tests/test_one_version.py similarity index 56% rename from tests/test_action.py rename to tests/test_one_version.py index 8a02015..5b12460 100644 --- a/tests/test_action.py +++ b/tests/test_one_version.py @@ -1,25 +1,13 @@ -import logging -import logging.config +"""Test all scenarios where one or zero commits (beside the initial commit) are made.""" +# pylint: disable=too-many-locals import unittest -from pathlib import Path +from unittest import TestCase -import yaml +from utils import ActionInputs, ActionOutputs, CommitMessages, TestRepo, run_action, setup_logging -from utils import * - -def setup_logging() -> None: - """Setup logging""" - config_file = Path(__file__).resolve().parent / 'logging.config.yaml' - - with config_file.open('r') as config_stream: - config = yaml.load(config_stream, yaml.SafeLoader) - - logging.config.dictConfig(config) - logging.getLogger().setLevel(logging.DEBUG) - - -class ActionTestCase(unittest.TestCase): +class OneVersionTestCase(TestCase): + """Test all scenarios where one or zero commits (beside the initial commit) are made.""" repo: TestRepo @classmethod @@ -33,8 +21,9 @@ def tearDown(self): self.repo.close() def test_initial(self): + """Test Case: Run the action directly after the initial commit.""" # Arrange - args_release = ActionArguments( + args_release = ActionInputs( prefix='v', suffix='pre', previous_version_suffix='pre', @@ -46,11 +35,10 @@ def test_initial(self): version_name='v0.0.0-pre', previous_version='', previous_version_name='', - has_changes=False, - latest_tag_name=None + has_changes=False ) - args_beta = ActionArguments( + args_beta = ActionInputs( prefix='v', suffix='beta', previous_version_suffix='pre', @@ -62,11 +50,10 @@ def test_initial(self): version_name='v0.0.0-beta', previous_version='', previous_version_name='', - has_changes=False, - latest_tag_name=None + has_changes=False ) - args_prod = ActionArguments( + args_prod = ActionInputs( prefix='v', suffix='', previous_version_suffix='beta', @@ -78,28 +65,109 @@ def test_initial(self): version_name='v0.0.0', previous_version='', previous_version_name='', - has_changes=False, - latest_tag_name=None + has_changes=False ) # Act self.repo.checkout('release') actual_output_release = run_action(args_release) + tag_release = self.repo.get_latest_tag_name() self.repo.checkout('release-beta') actual_output_beta = run_action(args_beta) + tag_beta = self.repo.get_latest_tag_name() self.repo.checkout('release-prod') actual_output_prod = run_action(args_prod) + tag_prod = self.repo.get_latest_tag_name() # Assert self.assertEqual(expected_output_release, actual_output_release) + self.assertEqual(expected_output_release.version_name, tag_release) + self.assertEqual(expected_output_beta, actual_output_beta) + self.assertEqual(expected_output_beta.version_name, tag_beta) + self.assertEqual(expected_output_prod, actual_output_prod) + self.assertEqual(expected_output_prod.version_name, tag_prod) + + def test_chore(self): + """Test Case: Run the action after a ``chore:`` commit.""" + # Arrange + args_release = ActionInputs( + prefix='v', + suffix='pre', + previous_version_suffix='pre', + create_tag=True + ) + + expected_output_release = ActionOutputs( + version='0.0.0-pre', + version_name='v0.0.0-pre', + previous_version='', + previous_version_name='', + has_changes=False + ) + + args_beta = ActionInputs( + prefix='v', + suffix='beta', + previous_version_suffix='pre', + create_tag=True + ) + + expected_output_beta = ActionOutputs( + version='0.0.0-beta', + version_name='v0.0.0-beta', + previous_version='', + previous_version_name='', + has_changes=False + ) + + args_prod = ActionInputs( + prefix='v', + suffix='', + previous_version_suffix='beta', + create_tag=True + ) + + expected_output_prod = ActionOutputs( + version='0.0.0', + version_name='v0.0.0', + previous_version='', + previous_version_name='', + has_changes=False + ) + + # Act + self.repo.commit(CommitMessages.CHORE) + + self.repo.merge('main', 'release') + actual_output_release = run_action(args_release) + tag_release = self.repo.get_latest_tag_name() + + self.repo.merge('release', 'release-beta') + actual_output_beta = run_action(args_beta) + tag_beta = self.repo.get_latest_tag_name() + + self.repo.merge('release-beta', 'release-prod') + actual_output_prod = run_action(args_prod) + tag_prod = self.repo.get_latest_tag_name() + + # Assert + self.assertEqual(expected_output_release, actual_output_release) + self.assertEqual(expected_output_release.version_name, tag_release) + + self.assertEqual(expected_output_beta, actual_output_beta) + self.assertEqual(expected_output_beta.version_name, tag_beta) + + self.assertEqual(expected_output_prod, actual_output_prod) + self.assertEqual(expected_output_prod.version_name, tag_prod) def test_first_fix(self): + """Test Case: Run the action after a ``fix:`` commit.""" # Arrange - args_release = ActionArguments( + args_release = ActionInputs( prefix='v', suffix='pre', previous_version_suffix='pre', @@ -111,11 +179,10 @@ def test_first_fix(self): version_name='v0.0.1-pre', previous_version='', previous_version_name='', - has_changes=True, - latest_tag_name='v0.0.1-pre' + has_changes=True ) - args_beta = ActionArguments( + args_beta = ActionInputs( prefix='v', suffix='beta', previous_version_suffix='pre', @@ -127,11 +194,10 @@ def test_first_fix(self): version_name='v0.0.1-beta', previous_version='', previous_version_name='', - has_changes=False, - latest_tag_name='v0.0.1-beta' + has_changes=True ) - args_prod = ActionArguments( + args_prod = ActionInputs( prefix='v', suffix='', previous_version_suffix='beta', @@ -143,8 +209,7 @@ def test_first_fix(self): version_name='v0.0.1', previous_version='', previous_version_name='', - has_changes=False, - latest_tag_name='v0.0.1' + has_changes=True ) # Act @@ -152,21 +217,30 @@ def test_first_fix(self): self.repo.merge('main', 'release') actual_output_release = run_action(args_release) + tag_release = self.repo.get_latest_tag_name() self.repo.merge('release', 'release-beta') actual_output_beta = run_action(args_beta) + tag_beta = self.repo.get_latest_tag_name() self.repo.merge('release-beta', 'release-prod') actual_output_prod = run_action(args_prod) + tag_prod = self.repo.get_latest_tag_name() # Assert self.assertEqual(expected_output_release, actual_output_release) + self.assertEqual(expected_output_release.version_name, tag_release) + self.assertEqual(expected_output_beta, actual_output_beta) + self.assertEqual(expected_output_beta.version_name, tag_beta) + self.assertEqual(expected_output_prod, actual_output_prod) + self.assertEqual(expected_output_prod.version_name, tag_prod) - def test_first_feature(self): + def test_first_feat(self): + """Test Case: Run the action after a ``feat:`` commit.""" # Arrange - args_release = ActionArguments( + args_release = ActionInputs( prefix='v', suffix='pre', previous_version_suffix='pre', @@ -178,11 +252,10 @@ def test_first_feature(self): version_name='v0.1.0-pre', previous_version='', previous_version_name='', - has_changes=True, - latest_tag_name='v0.1.0-pre' + has_changes=True ) - args_beta = ActionArguments( + args_beta = ActionInputs( prefix='v', suffix='beta', previous_version_suffix='pre', @@ -194,11 +267,10 @@ def test_first_feature(self): version_name='v0.1.0-beta', previous_version='', previous_version_name='', - has_changes=False, - latest_tag_name='v0.1.0-beta' + has_changes=True ) - args_prod = ActionArguments( + args_prod = ActionInputs( prefix='v', suffix='', previous_version_suffix='beta', @@ -210,8 +282,7 @@ def test_first_feature(self): version_name='v0.1.0', previous_version='', previous_version_name='', - has_changes=False, - latest_tag_name='v0.1.0' + has_changes=True ) # Act @@ -219,21 +290,30 @@ def test_first_feature(self): self.repo.merge('main', 'release') actual_output_release = run_action(args_release) + tag_release = self.repo.get_latest_tag_name() self.repo.merge('release', 'release-beta') actual_output_beta = run_action(args_beta) + tag_beta = self.repo.get_latest_tag_name() self.repo.merge('release-beta', 'release-prod') actual_output_prod = run_action(args_prod) + tag_prod = self.repo.get_latest_tag_name() # Assert self.assertEqual(expected_output_release, actual_output_release) + self.assertEqual(expected_output_release.version_name, tag_release) + self.assertEqual(expected_output_beta, actual_output_beta) + self.assertEqual(expected_output_beta.version_name, tag_beta) + self.assertEqual(expected_output_prod, actual_output_prod) + self.assertEqual(expected_output_prod.version_name, tag_prod) def test_first_breaking(self): + """Test Case: Run the action after a ``feat!:`` commit.""" # Arrange - args_release = ActionArguments( + args_release = ActionInputs( prefix='v', suffix='pre', previous_version_suffix='pre', @@ -245,11 +325,10 @@ def test_first_breaking(self): version_name='v1.0.0-pre', previous_version='', previous_version_name='', - has_changes=True, - latest_tag_name='v1.0.0-pre' + has_changes=True ) - args_beta = ActionArguments( + args_beta = ActionInputs( prefix='v', suffix='beta', previous_version_suffix='pre', @@ -261,11 +340,10 @@ def test_first_breaking(self): version_name='v1.0.0-beta', previous_version='', previous_version_name='', - has_changes=False, - latest_tag_name='v1.0.0-beta' + has_changes=True ) - args_prod = ActionArguments( + args_prod = ActionInputs( prefix='v', suffix='', previous_version_suffix='beta', @@ -277,8 +355,7 @@ def test_first_breaking(self): version_name='v1.0.0', previous_version='', previous_version_name='', - has_changes=False, - latest_tag_name='v1.0.0' + has_changes=True ) # Act @@ -286,17 +363,25 @@ def test_first_breaking(self): self.repo.merge('main', 'release') actual_output_release = run_action(args_release) + tag_release = self.repo.get_latest_tag_name() self.repo.merge('release', 'release-beta') actual_output_beta = run_action(args_beta) + tag_beta = self.repo.get_latest_tag_name() self.repo.merge('release-beta', 'release-prod') actual_output_prod = run_action(args_prod) + tag_prod = self.repo.get_latest_tag_name() # Assert self.assertEqual(expected_output_release, actual_output_release) + self.assertEqual(expected_output_release.version_name, tag_release) + self.assertEqual(expected_output_beta, actual_output_beta) + self.assertEqual(expected_output_beta.version_name, tag_beta) + self.assertEqual(expected_output_prod, actual_output_prod) + self.assertEqual(expected_output_prod.version_name, tag_prod) if __name__ == '__main__': diff --git a/tests/test_two_versions.py b/tests/test_two_versions.py new file mode 100644 index 0000000..51f6433 --- /dev/null +++ b/tests/test_two_versions.py @@ -0,0 +1,1348 @@ +"""Test all scenarios where two commits with a release after each are made.""" +# pylint: disable=too-many-locals +import unittest +from unittest import TestCase + +from utils import ActionInputs, ActionOutputs, CommitMessages, TestRepo, run_action, setup_logging + + +class TwoVersionTestCase(TestCase): + """Test all scenarios where two commits with a release after each are made.""" + repo: TestRepo + + @classmethod + def setUpClass(cls): + setup_logging() + + def setUp(self): + self.repo = TestRepo() + + def tearDown(self): + self.repo.close() + + def test_fix_then_fix(self): + """Test Case: Run the action after a ``fix:`` and another ``fix:`` commit.""" + # Arrange + # Fix 1 + args_fix1_release = ActionInputs( + prefix='v', + suffix='pre', + previous_version_suffix='pre', + create_tag=True + ) + + expected_output_fix1_release = ActionOutputs( + version='0.0.1-pre', + version_name='v0.0.1-pre', + previous_version='', + previous_version_name='', + has_changes=True + ) + + args_fix1_beta = ActionInputs( + prefix='v', + suffix='beta', + previous_version_suffix='pre', + create_tag=True + ) + + expected_output_fix1_beta = ActionOutputs( + version='0.0.1-beta', + version_name='v0.0.1-beta', + previous_version='', + previous_version_name='', + has_changes=True + ) + + args_fix1_prod = ActionInputs( + prefix='v', + suffix='', + previous_version_suffix='beta', + create_tag=True + ) + + expected_output_fix1_prod = ActionOutputs( + version='0.0.1', + version_name='v0.0.1', + previous_version='', + previous_version_name='', + has_changes=True + ) + + # Fix 2 + args_fix2_release = ActionInputs( + prefix='v', + suffix='pre', + previous_version_suffix='pre', + create_tag=True + ) + + expected_output_fix2_release = ActionOutputs( + version='0.0.2-pre', + version_name='v0.0.2-pre', + previous_version='0.0.1-pre', + previous_version_name='v0.0.1-pre', + has_changes=True + ) + + args_fix2_beta = ActionInputs( + prefix='v', + suffix='beta', + previous_version_suffix='pre', + create_tag=True + ) + + expected_output_fix2_beta = ActionOutputs( + version='0.0.2-beta', + version_name='v0.0.2-beta', + previous_version='0.0.1-beta', + previous_version_name='v0.0.1-beta', + has_changes=True + ) + + args_fix2_prod = ActionInputs( + prefix='v', + suffix='', + previous_version_suffix='beta', + create_tag=True + ) + + expected_output_fix2_prod = ActionOutputs( + version='0.0.2', + version_name='v0.0.2', + previous_version='0.0.1', + previous_version_name='v0.0.1', + has_changes=True + ) + + # Act + # Fix 1 + self.repo.commit(CommitMessages.FIX) + + self.repo.merge('main', 'release') + actual_output_fix1_release = run_action(args_fix1_release) + tag_fix1_release = self.repo.get_latest_tag_name() + + self.repo.merge('release', 'release-beta') + actual_output_fix1_beta = run_action(args_fix1_beta) + tag_fix1_beta = self.repo.get_latest_tag_name() + + self.repo.merge('release-beta', 'release-prod') + actual_output_fix1_prod = run_action(args_fix1_prod) + tag_fix1_prod = self.repo.get_latest_tag_name() + + # Fix 2 + self.repo.commit(CommitMessages.FIX) + + self.repo.merge('main', 'release') + actual_output_fix2_release = run_action(args_fix2_release) + tag_fix2_release = self.repo.get_latest_tag_name() + + self.repo.merge('release', 'release-beta') + actual_output_fix2_beta = run_action(args_fix2_beta) + tag_fix2_beta = self.repo.get_latest_tag_name() + + self.repo.merge('release-beta', 'release-prod') + actual_output_fix2_prod = run_action(args_fix2_prod) + tag_fix2_prod = self.repo.get_latest_tag_name() + + # Assert + # Fix 1 + self.assertEqual(expected_output_fix1_release, actual_output_fix1_release) + self.assertEqual(expected_output_fix1_release.version_name, tag_fix1_release) + + self.assertEqual(expected_output_fix1_beta, actual_output_fix1_beta) + self.assertEqual(expected_output_fix1_beta.version_name, tag_fix1_beta) + + self.assertEqual(expected_output_fix1_prod, actual_output_fix1_prod) + self.assertEqual(expected_output_fix1_prod.version_name, tag_fix1_prod) + + # Fix 2 + self.assertEqual(expected_output_fix2_release, actual_output_fix2_release) + self.assertEqual(expected_output_fix2_release.version_name, tag_fix2_release) + + self.assertEqual(expected_output_fix2_beta, actual_output_fix2_beta) + self.assertEqual(expected_output_fix2_beta.version_name, tag_fix2_beta) + + self.assertEqual(expected_output_fix2_prod, actual_output_fix2_prod) + self.assertEqual(expected_output_fix2_prod.version_name, tag_fix2_prod) + + def test_fix_then_feat(self): + """Test Case: Run the action after a ``fix:`` and a ``feat:`` commit.""" + # Arrange + # Fix + args_fix_release = ActionInputs( + prefix='v', + suffix='pre', + previous_version_suffix='pre', + create_tag=True + ) + + expected_output_fix_release = ActionOutputs( + version='0.0.1-pre', + version_name='v0.0.1-pre', + previous_version='', + previous_version_name='', + has_changes=True + ) + + args_fix_beta = ActionInputs( + prefix='v', + suffix='beta', + previous_version_suffix='pre', + create_tag=True + ) + + expected_output_fix_beta = ActionOutputs( + version='0.0.1-beta', + version_name='v0.0.1-beta', + previous_version='', + previous_version_name='', + has_changes=True + ) + + args_fix_prod = ActionInputs( + prefix='v', + suffix='', + previous_version_suffix='beta', + create_tag=True + ) + + expected_output_fix_prod = ActionOutputs( + version='0.0.1', + version_name='v0.0.1', + previous_version='', + previous_version_name='', + has_changes=True + ) + + # Feature + args_feat_release = ActionInputs( + prefix='v', + suffix='pre', + previous_version_suffix='pre', + create_tag=True + ) + + expected_output_feat_release = ActionOutputs( + version='0.1.0-pre', + version_name='v0.1.0-pre', + previous_version='0.0.1-pre', + previous_version_name='v0.0.1-pre', + has_changes=True + ) + + args_feat_beta = ActionInputs( + prefix='v', + suffix='beta', + previous_version_suffix='pre', + create_tag=True + ) + + expected_output_feat_beta = ActionOutputs( + version='0.1.0-beta', + version_name='v0.1.0-beta', + previous_version='0.0.1-beta', + previous_version_name='v0.0.1-beta', + has_changes=True + ) + + args_feat_prod = ActionInputs( + prefix='v', + suffix='', + previous_version_suffix='beta', + create_tag=True + ) + + expected_output_feat_prod = ActionOutputs( + version='0.1.0', + version_name='v0.1.0', + previous_version='0.0.1', + previous_version_name='v0.0.1', + has_changes=True + ) + + # Act + # Fix + self.repo.commit(CommitMessages.FIX) + + self.repo.merge('main', 'release') + actual_output_fix_release = run_action(args_fix_release) + tag_fix_release = self.repo.get_latest_tag_name() + + self.repo.merge('release', 'release-beta') + actual_output_fix_beta = run_action(args_fix_beta) + tag_fix_beta = self.repo.get_latest_tag_name() + + self.repo.merge('release-beta', 'release-prod') + actual_output_fix_prod = run_action(args_fix_prod) + tag_fix_prod = self.repo.get_latest_tag_name() + + # Feature + self.repo.commit(CommitMessages.FEATURE) + + self.repo.merge('main', 'release') + actual_output_feat_release = run_action(args_feat_release) + tag_feat_release = self.repo.get_latest_tag_name() + + self.repo.merge('release', 'release-beta') + actual_output_feat_beta = run_action(args_feat_beta) + tag_feat_beta = self.repo.get_latest_tag_name() + + self.repo.merge('release-beta', 'release-prod') + actual_output_feat_prod = run_action(args_feat_prod) + tag_feat_prod = self.repo.get_latest_tag_name() + + # Assert + # Fix + self.assertEqual(expected_output_fix_release, actual_output_fix_release) + self.assertEqual(expected_output_fix_release.version_name, tag_fix_release) + + self.assertEqual(expected_output_fix_beta, actual_output_fix_beta) + self.assertEqual(expected_output_fix_beta.version_name, tag_fix_beta) + + self.assertEqual(expected_output_fix_prod, actual_output_fix_prod) + self.assertEqual(expected_output_fix_prod.version_name, tag_fix_prod) + + # Feature + self.assertEqual(expected_output_feat_release, actual_output_feat_release) + self.assertEqual(expected_output_feat_release.version_name, tag_feat_release) + + self.assertEqual(expected_output_feat_beta, actual_output_feat_beta) + self.assertEqual(expected_output_feat_beta.version_name, tag_feat_beta) + + self.assertEqual(expected_output_feat_prod, actual_output_feat_prod) + self.assertEqual(expected_output_feat_prod.version_name, tag_feat_prod) + + def test_fix_then_breaking(self): + """Test Case: Run the action after a ``fix:`` and a ``feat!:`` commit.""" + # Arrange + # Fix + args_fix_release = ActionInputs( + prefix='v', + suffix='pre', + previous_version_suffix='pre', + create_tag=True + ) + + expected_output_fix_release = ActionOutputs( + version='0.0.1-pre', + version_name='v0.0.1-pre', + previous_version='', + previous_version_name='', + has_changes=True + ) + + args_fix_beta = ActionInputs( + prefix='v', + suffix='beta', + previous_version_suffix='pre', + create_tag=True + ) + + expected_output_fix_beta = ActionOutputs( + version='0.0.1-beta', + version_name='v0.0.1-beta', + previous_version='', + previous_version_name='', + has_changes=True + ) + + args_fix_prod = ActionInputs( + prefix='v', + suffix='', + previous_version_suffix='beta', + create_tag=True + ) + + expected_output_fix_prod = ActionOutputs( + version='0.0.1', + version_name='v0.0.1', + previous_version='', + previous_version_name='', + has_changes=True + ) + + # Breaking + args_breaking_release = ActionInputs( + prefix='v', + suffix='pre', + previous_version_suffix='pre', + create_tag=True + ) + + expected_output_breaking_release = ActionOutputs( + version='1.0.0-pre', + version_name='v1.0.0-pre', + previous_version='0.0.1-pre', + previous_version_name='v0.0.1-pre', + has_changes=True + ) + + args_breaking_beta = ActionInputs( + prefix='v', + suffix='beta', + previous_version_suffix='pre', + create_tag=True + ) + + expected_output_breaking_beta = ActionOutputs( + version='1.0.0-beta', + version_name='v1.0.0-beta', + previous_version='0.0.1-beta', + previous_version_name='v0.0.1-beta', + has_changes=True + ) + + args_breaking_prod = ActionInputs( + prefix='v', + suffix='', + previous_version_suffix='beta', + create_tag=True + ) + + expected_output_breaking_prod = ActionOutputs( + version='1.0.0', + version_name='v1.0.0', + previous_version='0.0.1', + previous_version_name='v0.0.1', + has_changes=True + ) + + # Act + # Fix + self.repo.commit(CommitMessages.FIX) + + self.repo.merge('main', 'release') + actual_output_fix_release = run_action(args_fix_release) + tag_fix_release = self.repo.get_latest_tag_name() + + self.repo.merge('release', 'release-beta') + actual_output_fix_beta = run_action(args_fix_beta) + tag_fix_beta = self.repo.get_latest_tag_name() + + self.repo.merge('release-beta', 'release-prod') + actual_output_fix_prod = run_action(args_fix_prod) + tag_fix_prod = self.repo.get_latest_tag_name() + + # Breaking + self.repo.commit(CommitMessages.BREAKING_FEATURE) + + self.repo.merge('main', 'release') + actual_output_breaking_release = run_action(args_breaking_release) + tag_breaking_release = self.repo.get_latest_tag_name() + + self.repo.merge('release', 'release-beta') + actual_output_breaking_beta = run_action(args_breaking_beta) + tag_breaking_beta = self.repo.get_latest_tag_name() + + self.repo.merge('release-beta', 'release-prod') + actual_output_breaking_prod = run_action(args_breaking_prod) + tag_breaking_prod = self.repo.get_latest_tag_name() + + # Assert + # Fix + self.assertEqual(expected_output_fix_release, actual_output_fix_release) + self.assertEqual(expected_output_fix_release.version_name, tag_fix_release) + + self.assertEqual(expected_output_fix_beta, actual_output_fix_beta) + self.assertEqual(expected_output_fix_beta.version_name, tag_fix_beta) + + self.assertEqual(expected_output_fix_prod, actual_output_fix_prod) + self.assertEqual(expected_output_fix_prod.version_name, tag_fix_prod) + + # Breaking + self.assertEqual(expected_output_breaking_release, actual_output_breaking_release) + self.assertEqual(expected_output_breaking_release.version_name, tag_breaking_release) + + self.assertEqual(expected_output_breaking_beta, actual_output_breaking_beta) + self.assertEqual(expected_output_breaking_beta.version_name, tag_breaking_beta) + + self.assertEqual(expected_output_breaking_prod, actual_output_breaking_prod) + self.assertEqual(expected_output_breaking_prod.version_name, tag_breaking_prod) + + def test_feat_then_fix(self): + """Test Case: Run the action after a ``feat:`` and a ``fix:`` commit.""" + # Arrange + # Feature + args_feat_release = ActionInputs( + prefix='v', + suffix='pre', + previous_version_suffix='pre', + create_tag=True + ) + + expected_output_feat_release = ActionOutputs( + version='0.1.0-pre', + version_name='v0.1.0-pre', + previous_version='', + previous_version_name='', + has_changes=True + ) + + args_feat_beta = ActionInputs( + prefix='v', + suffix='beta', + previous_version_suffix='pre', + create_tag=True + ) + + expected_output_feat_beta = ActionOutputs( + version='0.1.0-beta', + version_name='v0.1.0-beta', + previous_version='', + previous_version_name='', + has_changes=True + ) + + args_feat_prod = ActionInputs( + prefix='v', + suffix='', + previous_version_suffix='beta', + create_tag=True + ) + + expected_output_feat_prod = ActionOutputs( + version='0.1.0', + version_name='v0.1.0', + previous_version='', + previous_version_name='', + has_changes=True + ) + + # Fix + args_fix_release = ActionInputs( + prefix='v', + suffix='pre', + previous_version_suffix='pre', + create_tag=True + ) + + expected_output_fix_release = ActionOutputs( + version='0.1.1-pre', + version_name='v0.1.1-pre', + previous_version='0.1.0-pre', + previous_version_name='v0.1.0-pre', + has_changes=True + ) + + args_fix_beta = ActionInputs( + prefix='v', + suffix='beta', + previous_version_suffix='pre', + create_tag=True + ) + + expected_output_fix_beta = ActionOutputs( + version='0.1.1-beta', + version_name='v0.1.1-beta', + previous_version='0.1.0-beta', + previous_version_name='v0.1.0-beta', + has_changes=True + ) + + args_fix_prod = ActionInputs( + prefix='v', + suffix='', + previous_version_suffix='beta', + create_tag=True + ) + + expected_output_fix_prod = ActionOutputs( + version='0.1.1', + version_name='v0.1.1', + previous_version='0.1.0', + previous_version_name='v0.1.0', + has_changes=True + ) + + # Act + # Feature + self.repo.commit(CommitMessages.FEATURE) + + self.repo.merge('main', 'release') + actual_output_feat_release = run_action(args_feat_release) + tag_feat_release = self.repo.get_latest_tag_name() + + self.repo.merge('release', 'release-beta') + actual_output_feat_beta = run_action(args_feat_beta) + tag_feat_beta = self.repo.get_latest_tag_name() + + self.repo.merge('release-beta', 'release-prod') + actual_output_feat_prod = run_action(args_feat_prod) + tag_feat_prod = self.repo.get_latest_tag_name() + + # Fix + self.repo.commit(CommitMessages.FIX) + + self.repo.merge('main', 'release') + actual_output_fix_release = run_action(args_fix_release) + tag_fix_release = self.repo.get_latest_tag_name() + + self.repo.merge('release', 'release-beta') + actual_output_fix_beta = run_action(args_fix_beta) + tag_fix_beta = self.repo.get_latest_tag_name() + + self.repo.merge('release-beta', 'release-prod') + actual_output_fix_prod = run_action(args_fix_prod) + tag_fix_prod = self.repo.get_latest_tag_name() + + # Assert + # Feature + self.assertEqual(expected_output_feat_release, actual_output_feat_release) + self.assertEqual(expected_output_feat_release.version_name, tag_feat_release) + + self.assertEqual(expected_output_feat_beta, actual_output_feat_beta) + self.assertEqual(expected_output_feat_beta.version_name, tag_feat_beta) + + self.assertEqual(expected_output_feat_prod, actual_output_feat_prod) + self.assertEqual(expected_output_feat_prod.version_name, tag_feat_prod) + + # Fix + self.assertEqual(expected_output_fix_release, actual_output_fix_release) + self.assertEqual(expected_output_fix_release.version_name, tag_fix_release) + + self.assertEqual(expected_output_fix_beta, actual_output_fix_beta) + self.assertEqual(expected_output_fix_beta.version_name, tag_fix_beta) + + self.assertEqual(expected_output_fix_prod, actual_output_fix_prod) + self.assertEqual(expected_output_fix_prod.version_name, tag_fix_prod) + + def test_feat_then_feat(self): + """Test Case: Run the action after a ``feat:`` and another ``feat:`` commit.""" + # Arrange + # Feature 1 + args_feat1_release = ActionInputs( + prefix='v', + suffix='pre', + previous_version_suffix='pre', + create_tag=True + ) + + expected_output_feat1_release = ActionOutputs( + version='0.1.0-pre', + version_name='v0.1.0-pre', + previous_version='', + previous_version_name='', + has_changes=True + ) + + args_feat1_beta = ActionInputs( + prefix='v', + suffix='beta', + previous_version_suffix='pre', + create_tag=True + ) + + expected_output_feat1_beta = ActionOutputs( + version='0.1.0-beta', + version_name='v0.1.0-beta', + previous_version='', + previous_version_name='', + has_changes=True + ) + + args_feat1_prod = ActionInputs( + prefix='v', + suffix='', + previous_version_suffix='beta', + create_tag=True + ) + + expected_output_feat1_prod = ActionOutputs( + version='0.1.0', + version_name='v0.1.0', + previous_version='', + previous_version_name='', + has_changes=True + ) + + # Feature 2 + args_feat2_release = ActionInputs( + prefix='v', + suffix='pre', + previous_version_suffix='pre', + create_tag=True + ) + + expected_output_feat2_release = ActionOutputs( + version='0.2.0-pre', + version_name='v0.2.0-pre', + previous_version='0.1.0-pre', + previous_version_name='v0.1.0-pre', + has_changes=True + ) + + args_feat2_beta = ActionInputs( + prefix='v', + suffix='beta', + previous_version_suffix='pre', + create_tag=True + ) + + expected_output_feat2_beta = ActionOutputs( + version='0.2.0-beta', + version_name='v0.2.0-beta', + previous_version='0.1.0-beta', + previous_version_name='v0.1.0-beta', + has_changes=True + ) + + args_feat2_prod = ActionInputs( + prefix='v', + suffix='', + previous_version_suffix='beta', + create_tag=True, + ) + + expected_output_feat2_prod = ActionOutputs( + version='0.2.0', + version_name='v0.2.0', + previous_version='0.1.0', + previous_version_name='v0.1.0', + has_changes=True + ) + + # Act + # Feature 1 + self.repo.commit(CommitMessages.FEATURE) + + self.repo.merge('main', 'release') + actual_output_feat1_release = run_action(args_feat1_release) + tag_feat1_release = self.repo.get_latest_tag_name() + + self.repo.merge('release', 'release-beta') + actual_output_feat1_beta = run_action(args_feat1_beta) + tag_feat1_beta = self.repo.get_latest_tag_name() + + self.repo.merge('release-beta', 'release-prod') + actual_output_feat1_prod = run_action(args_feat1_prod) + tag_feat1_prod = self.repo.get_latest_tag_name() + + # Feature 2 + self.repo.commit(CommitMessages.FEATURE) + + self.repo.merge('main', 'release') + actual_output_feat2_release = run_action(args_feat2_release) + tag_feat2_release = self.repo.get_latest_tag_name() + + self.repo.merge('release', 'release-beta') + actual_output_feat2_beta = run_action(args_feat2_beta) + tag_feat2_beta = self.repo.get_latest_tag_name() + + self.repo.merge('release-beta', 'release-prod') + actual_output_feat2_prod = run_action(args_feat2_prod) + tag_feat2_prod = self.repo.get_latest_tag_name() + + # Assert + # Feature 1 + self.assertEqual(expected_output_feat1_release, actual_output_feat1_release) + self.assertEqual(expected_output_feat1_release.version_name, tag_feat1_release) + + self.assertEqual(expected_output_feat1_beta, actual_output_feat1_beta) + self.assertEqual(expected_output_feat1_beta.version_name, tag_feat1_beta) + + self.assertEqual(expected_output_feat1_prod, actual_output_feat1_prod) + self.assertEqual(expected_output_feat1_prod.version_name, tag_feat1_prod) + + # Feature 2 + self.assertEqual(expected_output_feat2_release, actual_output_feat2_release) + self.assertEqual(expected_output_feat2_release.version_name, tag_feat2_release) + + self.assertEqual(expected_output_feat2_beta, actual_output_feat2_beta) + self.assertEqual(expected_output_feat2_beta.version_name, tag_feat2_beta) + + self.assertEqual(expected_output_feat2_prod, actual_output_feat2_prod) + self.assertEqual(expected_output_feat2_prod.version_name, tag_feat2_prod) + + def test_feat_then_breaking(self): + """Test Case: Run the action after a ``feat:`` and a ``feat!:`` commit.""" + # Arrange + # Feature + args_feat_release = ActionInputs( + prefix='v', + suffix='pre', + previous_version_suffix='pre', + create_tag=True + ) + + expected_output_feat_release = ActionOutputs( + version='0.1.0-pre', + version_name='v0.1.0-pre', + previous_version='', + previous_version_name='', + has_changes=True + ) + + args_feat_beta = ActionInputs( + prefix='v', + suffix='beta', + previous_version_suffix='pre', + create_tag=True + ) + + expected_output_feat_beta = ActionOutputs( + version='0.1.0-beta', + version_name='v0.1.0-beta', + previous_version='', + previous_version_name='', + has_changes=True + ) + + args_feat_prod = ActionInputs( + prefix='v', + suffix='', + previous_version_suffix='beta', + create_tag=True + ) + + expected_output_feat_prod = ActionOutputs( + version='0.1.0', + version_name='v0.1.0', + previous_version='', + previous_version_name='', + has_changes=True + ) + + # Breaking + args_breaking_release = ActionInputs( + prefix='v', + suffix='pre', + previous_version_suffix='pre', + create_tag=True + ) + + expected_output_breaking_release = ActionOutputs( + version='1.0.0-pre', + version_name='v1.0.0-pre', + previous_version='0.1.0-pre', + previous_version_name='v0.1.0-pre', + has_changes=True + ) + + args_breaking_beta = ActionInputs( + prefix='v', + suffix='beta', + previous_version_suffix='pre', + create_tag=True + ) + + expected_output_breaking_beta = ActionOutputs( + version='1.0.0-beta', + version_name='v1.0.0-beta', + previous_version='0.1.0-beta', + previous_version_name='v0.1.0-beta', + has_changes=True + ) + + args_breaking_prod = ActionInputs( + prefix='v', + suffix='', + previous_version_suffix='beta', + create_tag=True + ) + + expected_output_breaking_prod = ActionOutputs( + version='1.0.0', + version_name='v1.0.0', + previous_version='0.1.0', + previous_version_name='v0.1.0', + has_changes=True + ) + + # Act + # Feature + self.repo.commit(CommitMessages.FEATURE) + + self.repo.merge('main', 'release') + actual_output_feat_release = run_action(args_feat_release) + tag_feat_release = self.repo.get_latest_tag_name() + + self.repo.merge('release', 'release-beta') + actual_output_feat_beta = run_action(args_feat_beta) + tag_feat_beta = self.repo.get_latest_tag_name() + + self.repo.merge('release-beta', 'release-prod') + actual_output_feat_prod = run_action(args_feat_prod) + tag_feat_prod = self.repo.get_latest_tag_name() + + # Breaking + self.repo.commit(CommitMessages.BREAKING_FEATURE) + + self.repo.merge('main', 'release') + actual_output_breaking_release = run_action(args_breaking_release) + tag_breaking_release = self.repo.get_latest_tag_name() + + self.repo.merge('release', 'release-beta') + actual_output_breaking_beta = run_action(args_breaking_beta) + tag_breaking_beta = self.repo.get_latest_tag_name() + + self.repo.merge('release-beta', 'release-prod') + actual_output_breaking_prod = run_action(args_breaking_prod) + tag_breaking_prod = self.repo.get_latest_tag_name() + + # Assert + # Feature + self.assertEqual(expected_output_feat_release, actual_output_feat_release) + self.assertEqual(expected_output_feat_release.version_name, tag_feat_release) + + self.assertEqual(expected_output_feat_beta, actual_output_feat_beta) + self.assertEqual(expected_output_feat_beta.version_name, tag_feat_beta) + + self.assertEqual(expected_output_feat_prod, actual_output_feat_prod) + self.assertEqual(expected_output_feat_prod.version_name, tag_feat_prod) + + # Breaking + self.assertEqual(expected_output_breaking_release, actual_output_breaking_release) + self.assertEqual(expected_output_breaking_release.version_name, tag_breaking_release) + + self.assertEqual(expected_output_breaking_beta, actual_output_breaking_beta) + self.assertEqual(expected_output_breaking_beta.version_name, tag_breaking_beta) + + self.assertEqual(expected_output_breaking_prod, actual_output_breaking_prod) + self.assertEqual(expected_output_breaking_prod.version_name, tag_breaking_prod) + + def test_breaking_then_fix(self): + """Test Case: Run the action after a ``feat!:`` and a ``fix:`` commit.""" + # Arrange + # Breaking + args_breaking_release = ActionInputs( + prefix='v', + suffix='pre', + previous_version_suffix='pre', + create_tag=True + ) + + expected_output_breaking_release = ActionOutputs( + version='1.0.0-pre', + version_name='v1.0.0-pre', + previous_version='', + previous_version_name='', + has_changes=True + ) + + args_breaking_beta = ActionInputs( + prefix='v', + suffix='beta', + previous_version_suffix='pre', + create_tag=True + ) + + expected_output_breaking_beta = ActionOutputs( + version='1.0.0-beta', + version_name='v1.0.0-beta', + previous_version='', + previous_version_name='', + has_changes=True + ) + + args_breaking_prod = ActionInputs( + prefix='v', + suffix='', + previous_version_suffix='beta', + create_tag=True + ) + + expected_output_breaking_prod = ActionOutputs( + version='1.0.0', + version_name='v1.0.0', + previous_version='', + previous_version_name='', + has_changes=True + ) + + # Fix + args_fix_release = ActionInputs( + prefix='v', + suffix='pre', + previous_version_suffix='pre', + create_tag=True + ) + + expected_output_fix_release = ActionOutputs( + version='1.0.1-pre', + version_name='v1.0.1-pre', + previous_version='1.0.0-pre', + previous_version_name='v1.0.0-pre', + has_changes=True + ) + + args_fix_beta = ActionInputs( + prefix='v', + suffix='beta', + previous_version_suffix='pre', + create_tag=True + ) + + expected_output_fix_beta = ActionOutputs( + version='1.0.1-beta', + version_name='v1.0.1-beta', + previous_version='1.0.0-beta', + previous_version_name='v1.0.0-beta', + has_changes=True + ) + + args_fix_prod = ActionInputs( + prefix='v', + suffix='', + previous_version_suffix='beta', + create_tag=True + ) + + expected_output_fix_prod = ActionOutputs( + version='1.0.1', + version_name='v1.0.1', + previous_version='1.0.0', + previous_version_name='v1.0.0', + has_changes=True + ) + + # Act + # Breaking + self.repo.commit(CommitMessages.BREAKING_FEATURE) + + self.repo.merge('main', 'release') + actual_output_breaking_release = run_action(args_breaking_release) + tag_breaking_release = self.repo.get_latest_tag_name() + + self.repo.merge('release', 'release-beta') + actual_output_breaking_beta = run_action(args_breaking_beta) + tag_breaking_beta = self.repo.get_latest_tag_name() + + self.repo.merge('release-beta', 'release-prod') + actual_output_breaking_prod = run_action(args_breaking_prod) + tag_breaking_prod = self.repo.get_latest_tag_name() + + # Fix + self.repo.commit(CommitMessages.FIX) + + self.repo.merge('main', 'release') + actual_output_fix_release = run_action(args_fix_release) + tag_fix_release = self.repo.get_latest_tag_name() + + self.repo.merge('release', 'release-beta') + actual_output_fix_beta = run_action(args_fix_beta) + tag_fix_beta = self.repo.get_latest_tag_name() + + self.repo.merge('release-beta', 'release-prod') + actual_output_fix_prod = run_action(args_fix_prod) + tag_fix_prod = self.repo.get_latest_tag_name() + + # Assert + # Breaking + self.assertEqual(expected_output_breaking_release, actual_output_breaking_release) + self.assertEqual(expected_output_breaking_release.version_name, tag_breaking_release) + + self.assertEqual(expected_output_breaking_beta, actual_output_breaking_beta) + self.assertEqual(expected_output_breaking_beta.version_name, tag_breaking_beta) + + self.assertEqual(expected_output_breaking_prod, actual_output_breaking_prod) + self.assertEqual(expected_output_breaking_prod.version_name, tag_breaking_prod) + + # Fix + self.assertEqual(expected_output_fix_release, actual_output_fix_release) + self.assertEqual(expected_output_fix_release.version_name, tag_fix_release) + + self.assertEqual(expected_output_fix_beta, actual_output_fix_beta) + self.assertEqual(expected_output_fix_beta.version_name, tag_fix_beta) + + self.assertEqual(expected_output_fix_prod, actual_output_fix_prod) + self.assertEqual(expected_output_fix_prod.version_name, tag_fix_prod) + + def test_breaking_then_feat(self): + """Test Case: Run the action after a ``feat!:`` and a ``feat:`` commit.""" + # Arrange + # Breaking + args_breaking_release = ActionInputs( + prefix='v', + suffix='pre', + previous_version_suffix='pre', + create_tag=True + ) + + expected_output_breaking_release = ActionOutputs( + version='1.0.0-pre', + version_name='v1.0.0-pre', + previous_version='', + previous_version_name='', + has_changes=True + ) + + args_breaking_beta = ActionInputs( + prefix='v', + suffix='beta', + previous_version_suffix='pre', + create_tag=True + ) + + expected_output_breaking_beta = ActionOutputs( + version='1.0.0-beta', + version_name='v1.0.0-beta', + previous_version='', + previous_version_name='', + has_changes=True + ) + + args_breaking_prod = ActionInputs( + prefix='v', + suffix='', + previous_version_suffix='beta', + create_tag=True + ) + + expected_output_breaking_prod = ActionOutputs( + version='1.0.0', + version_name='v1.0.0', + previous_version='', + previous_version_name='', + has_changes=True + ) + + # Feature + args_feat_release = ActionInputs( + prefix='v', + suffix='pre', + previous_version_suffix='pre', + create_tag=True + ) + + expected_output_feat_release = ActionOutputs( + version='1.1.0-pre', + version_name='v1.1.0-pre', + previous_version='1.0.0-pre', + previous_version_name='v1.0.0-pre', + has_changes=True + ) + + args_feat_beta = ActionInputs( + prefix='v', + suffix='beta', + previous_version_suffix='pre', + create_tag=True + ) + + expected_output_feat_beta = ActionOutputs( + version='1.1.0-beta', + version_name='v1.1.0-beta', + previous_version='1.0.0-beta', + previous_version_name='v1.0.0-beta', + has_changes=True + ) + + args_feat_prod = ActionInputs( + prefix='v', + suffix='', + previous_version_suffix='beta', + create_tag=True + ) + + expected_output_feat_prod = ActionOutputs( + version='1.1.0', + version_name='v1.1.0', + previous_version='1.0.0', + previous_version_name='v1.0.0', + has_changes=True + ) + + # Act + # Breaking + self.repo.commit(CommitMessages.BREAKING_FEATURE) + + self.repo.merge('main', 'release') + actual_output_breaking_release = run_action(args_breaking_release) + tag_breaking_release = self.repo.get_latest_tag_name() + + self.repo.merge('release', 'release-beta') + actual_output_breaking_beta = run_action(args_breaking_beta) + tag_breaking_beta = self.repo.get_latest_tag_name() + + self.repo.merge('release-beta', 'release-prod') + actual_output_breaking_prod = run_action(args_breaking_prod) + tag_breaking_prod = self.repo.get_latest_tag_name() + + # Feature + self.repo.commit(CommitMessages.FEATURE) + + self.repo.merge('main', 'release') + actual_output_feat_release = run_action(args_feat_release) + tag_feat_release = self.repo.get_latest_tag_name() + + self.repo.merge('release', 'release-beta') + actual_output_feat_beta = run_action(args_feat_beta) + tag_feat_beta = self.repo.get_latest_tag_name() + + self.repo.merge('release-beta', 'release-prod') + actual_output_feat_prod = run_action(args_feat_prod) + tag_feat_prod = self.repo.get_latest_tag_name() + + # Assert + # Breaking + self.assertEqual(expected_output_breaking_release, actual_output_breaking_release) + self.assertEqual(expected_output_breaking_release.version_name, tag_breaking_release) + + self.assertEqual(expected_output_breaking_beta, actual_output_breaking_beta) + self.assertEqual(expected_output_breaking_beta.version_name, tag_breaking_beta) + + self.assertEqual(expected_output_breaking_prod, actual_output_breaking_prod) + self.assertEqual(expected_output_breaking_prod.version_name, tag_breaking_prod) + + # Feature + self.assertEqual(expected_output_feat_release, actual_output_feat_release) + self.assertEqual(expected_output_feat_release.version_name, tag_feat_release) + + self.assertEqual(expected_output_feat_beta, actual_output_feat_beta) + self.assertEqual(expected_output_feat_beta.version_name, tag_feat_beta) + + self.assertEqual(expected_output_feat_prod, actual_output_feat_prod) + self.assertEqual(expected_output_feat_prod.version_name, tag_feat_prod) + + def test_breaking_then_breaking(self): + """Test Case: Run the action after a ``feat!:`` and another ``feat!:`` commit.""" + # Arrange + # Breaking 1 + args_breaking1_release = ActionInputs( + prefix='v', + suffix='pre', + previous_version_suffix='pre', + create_tag=True + ) + + expected_output_breaking1_release = ActionOutputs( + version='1.0.0-pre', + version_name='v1.0.0-pre', + previous_version='', + previous_version_name='', + has_changes=True + ) + + args_breaking1_beta = ActionInputs( + prefix='v', + suffix='beta', + previous_version_suffix='pre', + create_tag=True + ) + + expected_output_breaking1_beta = ActionOutputs( + version='1.0.0-beta', + version_name='v1.0.0-beta', + previous_version='', + previous_version_name='', + has_changes=True + ) + + args_breaking1_prod = ActionInputs( + prefix='v', + suffix='', + previous_version_suffix='beta', + create_tag=True + ) + + expected_output_breaking1_prod = ActionOutputs( + version='1.0.0', + version_name='v1.0.0', + previous_version='', + previous_version_name='', + has_changes=True + ) + + # Breaking 2 + args_breaking2_release = ActionInputs( + prefix='v', + suffix='pre', + previous_version_suffix='pre', + create_tag=True + ) + + expected_output_breaking2_release = ActionOutputs( + version='2.0.0-pre', + version_name='v2.0.0-pre', + previous_version='1.0.0-pre', + previous_version_name='v1.0.0-pre', + has_changes=True + ) + + args_breaking2_beta = ActionInputs( + prefix='v', + suffix='beta', + previous_version_suffix='pre', + create_tag=True + ) + + expected_output_breaking2_beta = ActionOutputs( + version='2.0.0-beta', + version_name='v2.0.0-beta', + previous_version='1.0.0-beta', + previous_version_name='v1.0.0-beta', + has_changes=True + ) + + args_breaking2_prod = ActionInputs( + prefix='v', + suffix='', + previous_version_suffix='beta', + create_tag=True + ) + + expected_output_breaking2_prod = ActionOutputs( + version='2.0.0', + version_name='v2.0.0', + previous_version='1.0.0', + previous_version_name='v1.0.0', + has_changes=True + ) + + # Act + # Breaking 1 + self.repo.commit(CommitMessages.BREAKING_FEATURE) + + self.repo.merge('main', 'release') + actual_output_breaking1_release = run_action(args_breaking1_release) + tag_breaking1_release = self.repo.get_latest_tag_name() + + self.repo.merge('release', 'release-beta') + actual_output_breaking1_beta = run_action(args_breaking1_beta) + tag_breaking1_beta = self.repo.get_latest_tag_name() + + self.repo.merge('release-beta', 'release-prod') + actual_output_breaking1_prod = run_action(args_breaking1_prod) + tag_breaking1_prod = self.repo.get_latest_tag_name() + + # Breaking 2 + self.repo.commit(CommitMessages.BREAKING_FEATURE) + + self.repo.merge('main', 'release') + actual_output_breaking2_release = run_action(args_breaking2_release) + tag_breaking2_release = self.repo.get_latest_tag_name() + + self.repo.merge('release', 'release-beta') + actual_output_breaking2_beta = run_action(args_breaking2_beta) + tag_breaking2_beta = self.repo.get_latest_tag_name() + + self.repo.merge('release-beta', 'release-prod') + actual_output_breaking2_prod = run_action(args_breaking2_prod) + tag_breaking2_prod = self.repo.get_latest_tag_name() + + # Assert + # Breaking 1 + self.assertEqual(expected_output_breaking1_release, actual_output_breaking1_release) + self.assertEqual(expected_output_breaking1_release.version_name, tag_breaking1_release) + + self.assertEqual(expected_output_breaking1_beta, actual_output_breaking1_beta) + self.assertEqual(expected_output_breaking1_beta.version_name, tag_breaking1_beta) + + self.assertEqual(expected_output_breaking1_prod, actual_output_breaking1_prod) + self.assertEqual(expected_output_breaking1_prod.version_name, tag_breaking1_prod) + + # Breaking 2 + self.assertEqual(expected_output_breaking2_release, actual_output_breaking2_release) + self.assertEqual(expected_output_breaking2_release.version_name, tag_breaking2_release) + + self.assertEqual(expected_output_breaking2_beta, actual_output_breaking2_beta) + self.assertEqual(expected_output_breaking2_beta.version_name, tag_breaking2_beta) + + self.assertEqual(expected_output_breaking2_prod, actual_output_breaking2_prod) + self.assertEqual(expected_output_breaking2_prod.version_name, tag_breaking2_prod) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/utils/__init__.py b/tests/utils/__init__.py index 898243c..e66a7a5 100644 --- a/tests/utils/__init__.py +++ b/tests/utils/__init__.py @@ -1,2 +1,3 @@ -from .test_repo import TestRepo, GitBranchNotFoundError, CommitMessages -from .action_wrapper import ActionOutputs, ActionArguments, run_action +from .action_wrapper import ActionInputs, ActionOutputs, run_action +from .test_repo import CommitMessages, GitBranchNotFoundError, TestRepo +from .logger import setup_logging diff --git a/tests/utils/action_wrapper.py b/tests/utils/action_wrapper.py index ad8b8d7..bc8b443 100644 --- a/tests/utils/action_wrapper.py +++ b/tests/utils/action_wrapper.py @@ -1,19 +1,31 @@ +"""Wrapper for the get-release-version-action script.""" +from __future__ import annotations + +import dataclasses +import logging import os import subprocess -import logging -import dataclasses import sys +from collections.abc import Iterable from dataclasses import dataclass +from inspect import get_annotations from pathlib import Path +from subprocess import CalledProcessError, CompletedProcess from typing import Any -from git import Repo - logger = logging.getLogger('wemogy.get-release-version-action.tests.wrapper') +__all__ = [ + 'ActionInputs', + 'ActionOutputs', + 'run_action' +] + @dataclass(frozen=True, kw_only=True) -class ActionArguments: +class ActionInputs: + """Inputs of the get-release-version-action.""" + prefix: str = 'v' """The prefix that should be prepended to the version.""" @@ -33,6 +45,7 @@ class ActionArguments: """Create a git tag for the version and push it if a remote is configured.""" def to_arg_list(self) -> list[str]: + """Convert the arguments object into a list of command line arguments.""" arg_list: list[str] = [] for name, value in dataclasses.asdict(self).items(): @@ -44,6 +57,7 @@ def to_arg_list(self) -> list[str]: @dataclass(frozen=True, kw_only=True) class ActionOutputs: + """Outputs of the get-release-version-action.""" version: str """The next version, without the prefix.""" @@ -59,11 +73,14 @@ class ActionOutputs: has_changes: bool """If any relevant changes got detected.""" - latest_tag_name: str | None - """The name of the newest git tag or None, if no tags exist.""" - @classmethod - def from_github_output(cls, github_output_file: Path): + def from_github_output(cls, github_output_file: Path) -> ActionOutputs: + """ + Parse the GitHub actions output into an outputs object. + + :param github_output_file: The path to the file provided to the script + via the GITHUB_OUTPUT environment variable. + """ lines = github_output_file.read_text().splitlines() ctor_args: dict[str, Any] = {} @@ -71,7 +88,7 @@ def from_github_output(cls, github_output_file: Path): raw_name, raw_value = line.strip().split('=', 1) name = raw_name.replace('-', '_') - field_type = cls.__annotations__[name] + field_type = get_annotations(cls, eval_str=True)[name] if field_type is bool: value = raw_value.lower() == 'true' @@ -82,23 +99,47 @@ def from_github_output(cls, github_output_file: Path): ctor_args[name] = value - try: - latest_tag_name = sorted(Repo(os.getcwd()).tags, key=lambda t: t.commit.committed_datetime, reverse=True)[0].name - except IndexError: - latest_tag_name = None + return cls(**ctor_args) # pylint: disable=missing-kwoa - return cls(**ctor_args, latest_tag_name=latest_tag_name) +def log_command(command: Iterable[str], env: dict[str, str], process: CompletedProcess | CalledProcessError) -> None: + """Log the command, environment and process result.""" + return_code = process.returncode + command_str = ' '.join([f'"{x}"' if (' ' in str(x) or str(x) == '') else str(x) for x in command]) + env_str = '; '.join([f'${k}="{v}"' for k, v in env.items()]) + output = '\n' + process.stdout if process.stdout.strip() else '' -def run_action(args: ActionArguments, python_executable: Path = None, python_path: Path = None, script_path: Path = None) -> ActionOutputs: - python_executable = python_executable or Path(sys.executable) - python_path = python_path or python_executable.parent - script_path = script_path or Path(__file__).parent.parent.parent / 'src' / 'app.py' + if isinstance(process, CalledProcessError): + logger.error( + 'Process exited unsuccessful with exit code %s:\nCommand: %s\nEnvironment: %s%s', + return_code, command_str, env_str, output + ) + else: + logger.info( + 'Process exited successful with exit code %s:\nCommand: %s\nEnvironment: %s%s', + return_code, command_str, env_str, output + ) + + +def run_action( + inputs: ActionInputs, + script_path: Path = None +) -> ActionOutputs: + """ + Run the get-release-version-action script with the current interpreter. + + :param inputs: The action inputs. + :param script_path: The path to the get-release-version-action script. Defaults to ``__file__/../../../src/app.py``. + :returns: The parsed action output. + :raises CalledProcessError: + """ + script_path = script_path or Path(__file__).resolve().parent.parent.parent / 'src' / 'app.py' github_output_file = Path('github-output.txt') - command = (python_executable, script_path, *args.to_arg_list(), '--verbose') + + command = (sys.executable, script_path, *inputs.to_arg_list(), '--verbose') env = { 'GITHUB_OUTPUT': github_output_file, - 'PATH': os.getenv('PATH') + os.pathsep + str(python_path) + 'PATH': os.getenv('PATH') } try: @@ -110,16 +151,9 @@ def run_action(args: ActionArguments, python_executable: Path = None, python_pat text=True, env=env ) - except subprocess.CalledProcessError as e: - logger.error( - 'Process exited unsuccessful with exit code %s:\nCommand: %s\nEnv: %s\n%s', - e.returncode, ' '.join([str(x) for x in command]), env, e.stdout - ) + except CalledProcessError as e: + log_command(command, env, e) raise e - logger.debug( - 'Process exited successful with exit code %s:\nCommand: %s\nEnv: %s\n%s', - process.returncode, ' '.join([str(x) for x in command]), env, process.stdout - ) - + log_command(command, env, process) return ActionOutputs.from_github_output(github_output_file) diff --git a/tests/utils/logger.py b/tests/utils/logger.py new file mode 100644 index 0000000..8911d46 --- /dev/null +++ b/tests/utils/logger.py @@ -0,0 +1,20 @@ +import logging +import logging.config +from pathlib import Path + +import yaml + +__all__ = [ + 'setup_logging' +] + + +def setup_logging() -> None: + """Setup logging""" + config_file = Path(__file__).resolve().parent.parent / 'resources' / 'logging.config.yaml' + + with config_file.open('r') as config_stream: + config = yaml.load(config_stream, yaml.SafeLoader) + + logging.config.dictConfig(config) + logging.getLogger().setLevel(logging.DEBUG) diff --git a/tests/utils/test_repo.py b/tests/utils/test_repo.py index 82061f8..920173e 100644 --- a/tests/utils/test_repo.py +++ b/tests/utils/test_repo.py @@ -1,4 +1,4 @@ -"""Wrapper around the git.Repo class that implements concrete methods for unit testing.""" +"""Wrapper around the ``git.Repo`` class that implements specific methods for unit testing.""" import logging import os import shutil @@ -11,9 +11,15 @@ logger = logging.getLogger('wemogy.get-release-version-action.tests.repo') +__all__ = [ + 'GitBranchNotFoundError', + 'CommitMessages', + 'TestRepo' +] + class GitBranchNotFoundError(Exception): - """Exception for when a git branch is not found.""" + """Raised when a git branch is not found.""" class CommitMessages(StrEnum): @@ -26,19 +32,22 @@ class CommitMessages(StrEnum): class TestRepo: - """Wrapper around the git.Repo class that implements concrete methods for unit testing.""" + """Wrapper around the ``git.Repo`` class that implements specific methods for unit testing.""" path: Path repo: Repo - keep_repository_dir: bool - def __init__(self, keep_repository_dir: bool = False) -> None: - """Wrapper around the git.Repo class that implements concrete methods for unit testing.""" - self.keep_repository_dir = keep_repository_dir + def __init__(self): + """ + Wrapper around the ``git.Repo`` class that implements specific methods for unit testing. + + This creates a new git repo in a temporary directory, makes an initial README commit to the ``main`` branch + and creates 3 branches called ``release``, ``release-beta`` and ``release-prod``. + """ self.path = Path(mkdtemp(prefix='wemogy.get-release-version-action.tests')) - logger.info('Created git repository in directory %s', self.path) + logger.info('Creating git repository in directory %s', self.path) os.chdir(self.path) - # Initialize repository and make initial commit + # Initialize repository and make initial commit. self.repo = Repo.init(self.path, initial_branch='main') self.commit('Initial commit', 'README.md') @@ -48,26 +57,31 @@ def __init__(self, keep_repository_dir: bool = False) -> None: self.create_branch('release-prod', 'release-beta') self.checkout('main') + logger.info('Initializing done') + def __enter__(self): return self - def __exit__(self, exc_type, exc_val, exc_tb) -> None: + def __exit__(self, exc_type, exc_val, exc_tb): self.close() - def close(self) -> None: - """Remove the test repo.""" + def close(self, keep_repository_dir: bool = False) -> None: + """Close the ``git.Repo`` object and delete the temporary folder unless specified otherwise.""" self.repo.close() - if not self.keep_repository_dir: + if not keep_repository_dir: logger.info('Removing repository %s', self.path) shutil.rmtree(self.path) else: - logger.info('Keeping repository %s', self.path) + logger.warning('Keeping repository %s', self.path) def create_branch(self, name: str, base_name: str | None) -> None: - """Create a branch named `name` that is based on the branch with `base_name` and check it out.""" + """Create a branch named ``name`` that is based on the branch with ``base_name`` and check it out.""" + logger.info('Creating branch %s from %s', name, base_name) + if base_name is not None: self.checkout(base_name) + self.repo.create_head(name) self.checkout(name) @@ -77,25 +91,28 @@ def checkout(self, branch_name: str) -> None: :raises GitBranchNotFoundError: If the branch was not found. """ + logger.info('Checking out branch %s', branch_name) + try: self.repo.heads[branch_name].checkout() except IndexError as exc: raise GitBranchNotFoundError(f'Branch {branch_name} was not found') from exc - def commit(self, commit_message: CommitMessages | str, file_name: str | None = None) -> Commit: + def commit(self, message: CommitMessages | str, file_name: str | None = None) -> Commit: """ Create a file and make a commit with the given level as commit message. - :param commit_message: A conventional commit message. - :param file_name: An optional file name, if None a random file name is chosen. + :param message: A conventional commit message. + :param file_name: An optional file name. Defaults to ``file_{uuid4()}``. :returns: The created commit. """ file_name = file_name or f'file_{uuid4()}' file_path = self.path / file_name file_path.write_text('test') + logger.info('Committing file %s with message %s', file_name, message) self.repo.index.add(file_name) - return self.repo.index.commit(commit_message) + return self.repo.index.commit(message) def merge(self, source_branch_name: str, dest_branch_name: str) -> None: """ @@ -103,8 +120,9 @@ def merge(self, source_branch_name: str, dest_branch_name: str) -> None: :param source_branch_name: The branch name to merge from. :param dest_branch_name: The branch name to merge into. - :raises GitBranchNotFoundError: If the branch was not found. + :raises GitBranchNotFoundError: If the destination branch was not found. """ + logger.info('Merging branch %s into branch %s', source_branch_name, dest_branch_name) self.checkout(dest_branch_name) self.repo.git.merge(source_branch_name) @@ -116,12 +134,14 @@ def cherrypick(self, commit: Commit, dest_branch_name: str) -> None: :param dest_branch_name: The branch name to cherrypick onto. :raises GitBranchNotFoundError: If the branch was not found. """ + logger.info('Cherrypicking commit %s (%s) into branch %s', + commit.message, commit.hexsha, dest_branch_name) self.checkout(dest_branch_name) self.repo.git.cherry_pick(commit.hexsha) - -__all__ = [ - TestRepo, - CommitMessages, - GitBranchNotFoundError -] + def get_latest_tag_name(self) -> str | None: + """Return the newest tag name or ``None``, if no tags exist.""" + try: + return sorted(self.repo.tags, key=lambda t: t.commit.committed_datetime, reverse=True)[0].name + except IndexError: + return None