GRM-35: fix: enforce tests pass before tagging a release
The release script now runs lint and tests before committing or tagging. If either fails, the release is aborted. Also fixes test_cli_version to use __version__ dynamically. Closes GRM-35
This commit is contained in:
@@ -4,6 +4,7 @@ from unittest.mock import MagicMock, patch
|
||||
|
||||
from click.testing import CliRunner
|
||||
|
||||
from gitea_runner_manager import __version__
|
||||
from gitea_runner_manager.cli import cli
|
||||
|
||||
|
||||
@@ -12,7 +13,7 @@ class TestCLI:
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(cli, ["--version"])
|
||||
assert result.exit_code == 0
|
||||
assert "0.2.0" in result.output
|
||||
assert __version__ in result.output
|
||||
|
||||
@patch("gitea_runner_manager.cli.RunnerManager")
|
||||
def test_install(self, mock_manager_class: MagicMock) -> None:
|
||||
|
||||
@@ -15,6 +15,7 @@ from scripts.release import (
|
||||
has_unreleased_changes,
|
||||
main,
|
||||
run_cmd,
|
||||
run_tests,
|
||||
tag_exists,
|
||||
update_changelog,
|
||||
update_init_version,
|
||||
@@ -269,6 +270,28 @@ class TestCreateAndPushTag:
|
||||
mock_run_cmd.assert_not_called()
|
||||
|
||||
|
||||
class TestRunTests:
|
||||
@patch("scripts.release.run_cmd")
|
||||
def test_lint_and_tests_pass(self, mock_run_cmd: MagicMock) -> None:
|
||||
mock_run_cmd.return_value = MagicMock(returncode=0, stdout="", stderr="")
|
||||
run_tests() # should not raise
|
||||
|
||||
@patch("scripts.release.run_cmd")
|
||||
def test_lint_fails_raises(self, mock_run_cmd: MagicMock) -> None:
|
||||
mock_run_cmd.return_value = MagicMock(returncode=1, stdout="", stderr="lint error")
|
||||
with pytest.raises(click.ClickException, match="Lint failed"):
|
||||
run_tests()
|
||||
|
||||
@patch("scripts.release.run_cmd")
|
||||
def test_tests_fail_raises(self, mock_run_cmd: MagicMock) -> None:
|
||||
mock_run_cmd.side_effect = [
|
||||
MagicMock(returncode=0, stdout="", stderr=""), # lint passes
|
||||
MagicMock(returncode=1, stdout="", stderr="test failure"), # tests fail
|
||||
]
|
||||
with pytest.raises(click.ClickException, match="Tests failed"):
|
||||
run_tests()
|
||||
|
||||
|
||||
class TestMain:
|
||||
@patch.dict("os.environ", {})
|
||||
@patch("scripts.release.run_cmd")
|
||||
@@ -351,6 +374,7 @@ class TestMain:
|
||||
mock_tag.assert_not_called()
|
||||
|
||||
@patch.dict("os.environ", {})
|
||||
@patch("scripts.release.run_tests")
|
||||
@patch("scripts.release.create_and_push_tag", return_value=True)
|
||||
@patch("scripts.release.commit_release_changes", return_value=True)
|
||||
@patch("scripts.release.update_changelog")
|
||||
@@ -371,6 +395,7 @@ class TestMain:
|
||||
mock_update_changelog: MagicMock,
|
||||
mock_commit: MagicMock,
|
||||
mock_tag: MagicMock,
|
||||
mock_run_tests: MagicMock,
|
||||
) -> None:
|
||||
mock_run_cmd.return_value = MagicMock(returncode=0, stdout="master\n", stderr="")
|
||||
runner = CliRunner()
|
||||
@@ -379,10 +404,12 @@ class TestMain:
|
||||
assert "Bumping version" in result.output
|
||||
mock_update_init.assert_called_once_with("0.2.0")
|
||||
mock_update_changelog.assert_called_once_with("changelog")
|
||||
mock_run_tests.assert_called_once()
|
||||
mock_commit.assert_called_once_with("0.2.0")
|
||||
mock_tag.assert_called_once_with("0.2.0", "changelog", False)
|
||||
|
||||
@patch.dict("os.environ", {})
|
||||
@patch("scripts.release.run_tests")
|
||||
@patch("scripts.release.create_and_push_tag", return_value=False)
|
||||
@patch("scripts.release.commit_release_changes", return_value=False)
|
||||
@patch("scripts.release.update_changelog")
|
||||
@@ -403,6 +430,7 @@ class TestMain:
|
||||
mock_update_changelog: MagicMock,
|
||||
mock_commit: MagicMock,
|
||||
mock_tag: MagicMock,
|
||||
mock_run_tests: MagicMock,
|
||||
) -> None:
|
||||
"""When tag already exists, still update files but report existing tag."""
|
||||
mock_run_cmd.return_value = MagicMock(returncode=0, stdout="master\n", stderr="")
|
||||
@@ -411,3 +439,106 @@ class TestMain:
|
||||
assert result.exit_code == 0
|
||||
assert "already existed" in result.output
|
||||
mock_tag.assert_called_once_with("0.1.0", "changelog", False)
|
||||
|
||||
@patch.dict("os.environ", {})
|
||||
@patch("scripts.release.create_and_push_tag", return_value=True)
|
||||
@patch("scripts.release.commit_release_changes", return_value=True)
|
||||
@patch("scripts.release.update_changelog")
|
||||
@patch("scripts.release.update_init_version")
|
||||
@patch("scripts.release.get_changelog", return_value="changelog")
|
||||
@patch("scripts.release.get_latest_tag", return_value="v0.1.0")
|
||||
@patch("scripts.release.get_bumped_version", return_value="0.2.0")
|
||||
@patch("scripts.release.has_unreleased_changes", return_value=True)
|
||||
@patch("scripts.release.run_cmd")
|
||||
def test_full_flow_skip_tests(
|
||||
self,
|
||||
mock_run_cmd: MagicMock,
|
||||
mock_has: MagicMock,
|
||||
mock_bumped: MagicMock,
|
||||
mock_latest: MagicMock,
|
||||
mock_changelog: MagicMock,
|
||||
mock_update_init: MagicMock,
|
||||
mock_update_changelog: MagicMock,
|
||||
mock_commit: MagicMock,
|
||||
mock_tag: MagicMock,
|
||||
) -> None:
|
||||
"""--skip-tests bypasses test verification."""
|
||||
mock_run_cmd.return_value = MagicMock(returncode=0, stdout="master\n", stderr="")
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(main, ["--skip-tests"])
|
||||
assert result.exit_code == 0
|
||||
assert "WARNING: --skip-tests" in result.output
|
||||
# run_tests should NOT be called — verify no "make lint-ruff" or "make pytest-cov" calls
|
||||
make_calls = [c.args[0] for c in mock_run_cmd.call_args_list if c.args[0][:1] == ["make"]]
|
||||
assert make_calls == []
|
||||
|
||||
@patch.dict("os.environ", {})
|
||||
@patch("scripts.release.create_and_push_tag")
|
||||
@patch("scripts.release.commit_release_changes")
|
||||
@patch("scripts.release.update_changelog")
|
||||
@patch("scripts.release.update_init_version")
|
||||
@patch("scripts.release.get_changelog", return_value="changelog")
|
||||
@patch("scripts.release.get_latest_tag", return_value="v0.1.0")
|
||||
@patch("scripts.release.get_bumped_version", return_value="0.2.0")
|
||||
@patch("scripts.release.has_unreleased_changes", return_value=True)
|
||||
@patch("scripts.release.run_cmd")
|
||||
def test_tests_fail_aborts_before_tag(
|
||||
self,
|
||||
mock_run_cmd: MagicMock,
|
||||
mock_has: MagicMock,
|
||||
mock_bumped: MagicMock,
|
||||
mock_latest: MagicMock,
|
||||
mock_changelog: MagicMock,
|
||||
mock_update_init: MagicMock,
|
||||
mock_update_changelog: MagicMock,
|
||||
mock_commit: MagicMock,
|
||||
mock_tag: MagicMock,
|
||||
) -> None:
|
||||
"""If tests fail, release aborts — no commit, no tag."""
|
||||
# First call: git rev-parse (master), then make lint-ruff (success),
|
||||
# then make pytest-cov (failure)
|
||||
mock_run_cmd.side_effect = [
|
||||
MagicMock(returncode=0, stdout="master\n", stderr=""),
|
||||
MagicMock(returncode=0, stdout="", stderr=""),
|
||||
MagicMock(returncode=1, stdout="", stderr="test failure"),
|
||||
]
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(main, [])
|
||||
assert result.exit_code != 0
|
||||
assert "Tests failed" in result.output
|
||||
mock_commit.assert_not_called()
|
||||
mock_tag.assert_not_called()
|
||||
|
||||
@patch.dict("os.environ", {})
|
||||
@patch("scripts.release.create_and_push_tag")
|
||||
@patch("scripts.release.commit_release_changes")
|
||||
@patch("scripts.release.update_changelog")
|
||||
@patch("scripts.release.update_init_version")
|
||||
@patch("scripts.release.get_changelog", return_value="changelog")
|
||||
@patch("scripts.release.get_latest_tag", return_value="v0.1.0")
|
||||
@patch("scripts.release.get_bumped_version", return_value="0.2.0")
|
||||
@patch("scripts.release.has_unreleased_changes", return_value=True)
|
||||
@patch("scripts.release.run_cmd")
|
||||
def test_lint_fail_aborts_before_tag(
|
||||
self,
|
||||
mock_run_cmd: MagicMock,
|
||||
mock_has: MagicMock,
|
||||
mock_bumped: MagicMock,
|
||||
mock_latest: MagicMock,
|
||||
mock_changelog: MagicMock,
|
||||
mock_update_init: MagicMock,
|
||||
mock_update_changelog: MagicMock,
|
||||
mock_commit: MagicMock,
|
||||
mock_tag: MagicMock,
|
||||
) -> None:
|
||||
"""If lint fails, release aborts — no commit, no tag."""
|
||||
mock_run_cmd.side_effect = [
|
||||
MagicMock(returncode=0, stdout="master\n", stderr=""),
|
||||
MagicMock(returncode=1, stdout="", stderr="lint error"),
|
||||
]
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(main, [])
|
||||
assert result.exit_code != 0
|
||||
assert "Lint failed" in result.output
|
||||
mock_commit.assert_not_called()
|
||||
mock_tag.assert_not_called()
|
||||
|
||||
Reference in New Issue
Block a user