From c3d34bc5f2cb675c7b9f0ffbc4335f89146f360f Mon Sep 17 00:00:00 2001 From: bogay Date: Tue, 9 Aug 2022 10:05:57 +0800 Subject: [PATCH 1/7] fix(init): report error when hook installation failed --- commitizen/commands/init.py | 98 +++++++++++++++++------------ commitizen/exceptions.py | 5 ++ tests/commands/test_init_command.py | 10 ++- 3 files changed, 71 insertions(+), 42 deletions(-) diff --git a/commitizen/commands/init.py b/commitizen/commands/init.py index 3b10a58230..bf917f0c14 100644 --- a/commitizen/commands/init.py +++ b/commitizen/commands/init.py @@ -1,4 +1,6 @@ import os +import shutil +from typing import Any, Dict import questionary import yaml @@ -9,7 +11,7 @@ from commitizen.config import BaseConfig, JsonConfig, TomlConfig, YAMLConfig from commitizen.cz import registry from commitizen.defaults import config_files -from commitizen.exceptions import NoAnswersError +from commitizen.exceptions import InitFailedError, NoAnswersError from commitizen.git import get_latest_tag_name, get_tag_names, smart_open @@ -19,34 +21,36 @@ def __init__(self, config: BaseConfig, *args): self.cz = factory.commiter_factory(self.config) def __call__(self): - values_to_add = {} + if self.config.path: + out.line(f"Config file {self.config.path} already exists") + return # No config for commitizen exist - if not self.config.path: - config_path = self._ask_config_path() - if "toml" in config_path: - self.config = TomlConfig(data="", path=config_path) - elif "json" in config_path: - self.config = JsonConfig(data="{}", path=config_path) - elif "yaml" in config_path: - self.config = YAMLConfig(data="", path=config_path) - - self.config.init_empty_config_content() - - values_to_add["name"] = self._ask_name() - tag = self._ask_tag() - values_to_add["version"] = Version(tag).public - values_to_add["tag_format"] = self._ask_tag_format(tag) - self._update_config_file(values_to_add) - - if questionary.confirm("Do you want to install pre-commit hook?").ask(): - self._install_pre_commit_hook() - - out.write("You can bump the version and create changelog running:\n") - out.info("cz bump --changelog") - out.success("The configuration are all set.") - else: - out.line(f"Config file {self.config.path} already exists") + config_path = self._ask_config_path() + if "toml" in config_path: + self.config = TomlConfig(data="", path=config_path) + elif "json" in config_path: + self.config = JsonConfig(data="{}", path=config_path) + elif "yaml" in config_path: + self.config = YAMLConfig(data="", path=config_path) + self.config.init_empty_config_content() + + values_to_add = {} + values_to_add["name"] = self._ask_name() + tag = self._ask_tag() + values_to_add["version"] = Version(tag).public + values_to_add["tag_format"] = self._ask_tag_format(tag) + self._update_config_file(values_to_add) + + if questionary.confirm("Do you want to install pre-commit hook?").ask(): + if not self._install_pre_commit_hook(): + raise InitFailedError( + "Installation failed. See error outputs for more information." + ) + + out.write("You can bump the version and create changelog running:\n") + out.info("cz bump --changelog") + out.success("The configuration are all set.") def _ask_config_path(self) -> str: name: str = questionary.select( @@ -109,7 +113,20 @@ def _ask_tag_format(self, latest_tag) -> str: tag_format = "$version" return tag_format - def _install_pre_commit_hook(self): + def _search_pre_commit(self): + return shutil.which("pre-commit") is not None + + def _exec_install_pre_commit_hook(self): + cmd_str = "pre-commit install --hook-type commit-msg" + c = cmd.run(cmd_str) + if c.return_code != 0: + out.error(f"Error running {cmd_str}. Outputs are attached below:") + out.error(f"stdout: {c.out}") + out.error(f"stderr: {c.err}") + return False + return True + + def _install_pre_commit_hook(self) -> bool: pre_commit_config_filename = ".pre-commit-config.yaml" cz_hook_config = { "repo": "https://github.com/commitizen-tools/commitizen", @@ -119,7 +136,7 @@ def _install_pre_commit_hook(self): config_data = {} if not os.path.isfile(pre_commit_config_filename): - # .pre-commit-config does not exist + # .pre-commit-config.yaml does not exist config_data["repos"] = [cz_hook_config] else: with open(pre_commit_config_filename) as config_file: @@ -135,23 +152,22 @@ def _install_pre_commit_hook(self): else: config_data["repos"].append(cz_hook_config) else: - # .pre-commit-config exists but there's no "repos" key + # .pre-commit-config.yaml exists but there's no "repos" key config_data["repos"] = [cz_hook_config] with smart_open(pre_commit_config_filename, "w") as config_file: yaml.safe_dump(config_data, stream=config_file) - c = cmd.run("pre-commit install --hook-type commit-msg") - if c.return_code == 127: - out.error( - "pre-commit is not installed in current environement.\n" - "Run 'pre-commit install --hook-type commit-msg' again after it's installed" - ) - elif c.return_code != 0: - out.error(c.err) - else: - out.write("commitizen pre-commit hook is now installed in your '.git'\n") + if not self._search_pre_commit(): + out.error("pre-commit is not installed in current environement.") + return False + + if not self._exec_install_pre_commit_hook(): + return False + + out.write("commitizen pre-commit hook is now installed in your '.git'\n") + return True - def _update_config_file(self, values): + def _update_config_file(self, values: Dict[str, Any]): for key, value in values.items(): self.config.set_key(key, value) diff --git a/commitizen/exceptions.py b/commitizen/exceptions.py index cc923ab988..f2615ff90e 100644 --- a/commitizen/exceptions.py +++ b/commitizen/exceptions.py @@ -29,6 +29,7 @@ class ExitCode(enum.IntEnum): UNRECOGNIZED_CHARACTERSET_ENCODING = 22 GIT_COMMAND_ERROR = 23 INVALID_MANUAL_VERSION = 24 + INIT_FAILED = 25 class CommitizenException(Exception): @@ -163,3 +164,7 @@ class GitCommandError(CommitizenException): class InvalidManualVersion(CommitizenException): exit_code = ExitCode.INVALID_MANUAL_VERSION + + +class InitFailedError(CommitizenException): + exit_code = ExitCode.INIT_FAILED diff --git a/tests/commands/test_init_command.py b/tests/commands/test_init_command.py index 4b59665b8e..2bff02a861 100644 --- a/tests/commands/test_init_command.py +++ b/tests/commands/test_init_command.py @@ -105,7 +105,15 @@ def default_choice(_, request, mocker): ) mocker.patch("questionary.confirm", return_value=FakeQuestion(True)) mocker.patch("questionary.text", return_value=FakeQuestion("$version")) - mocker.patch("questionary.confirm", return_value=FakeQuestion(True)) + # Assume the `pre-commit` is installed + mocker.patch( + "commitizen.commands.init.Init._search_pre_commit", + return_value=True, + ) + mocker.patch( + "commitizen.commands.init.Init._exec_install_pre_commit_hook", + return_value=True, + ) return request.param def test_no_existing_pre_commit_conifg(_, default_choice, tmpdir, config): From 3bf0190ce98304c42a6445a7fe12dbce64443e34 Mon Sep 17 00:00:00 2001 From: bogay Date: Tue, 9 Aug 2022 10:22:46 +0800 Subject: [PATCH 2/7] style(test): add type hints for `mocker` See more: https://pytest-mock.readthedocs.io/en/latest/remarks.html#type-annotations --- tests/commands/test_bump_command.py | 57 ++++++++++--------- tests/commands/test_changelog_command.py | 71 +++++++++++++----------- tests/commands/test_check_command.py | 43 +++++++------- tests/commands/test_commit_command.py | 23 ++++---- tests/commands/test_init_command.py | 7 ++- tests/commands/test_other_commands.py | 10 ++-- tests/test_bump_create_commit_message.py | 7 ++- tests/test_cli.py | 13 +++-- tests/test_git.py | 9 +-- 9 files changed, 133 insertions(+), 107 deletions(-) diff --git a/tests/commands/test_bump_command.py b/tests/commands/test_bump_command.py index b79c73e7d7..dcdca163b2 100644 --- a/tests/commands/test_bump_command.py +++ b/tests/commands/test_bump_command.py @@ -4,6 +4,7 @@ from unittest.mock import MagicMock import pytest +from pytest_mock import MockFixture import commitizen.commands.bump as bump from commitizen import cli, cmd, git @@ -37,7 +38,7 @@ ), ) @pytest.mark.usefixtures("tmp_commitizen_project") -def test_bump_patch_increment(commit_msg, mocker): +def test_bump_patch_increment(commit_msg, mocker: MockFixture): create_file_and_commit(commit_msg) testargs = ["cz", "bump", "--yes"] mocker.patch.object(sys, "argv", testargs) @@ -48,7 +49,7 @@ def test_bump_patch_increment(commit_msg, mocker): @pytest.mark.parametrize("commit_msg", ("feat: new file", "feat(user): new file")) @pytest.mark.usefixtures("tmp_commitizen_project") -def test_bump_minor_increment(commit_msg, mocker): +def test_bump_minor_increment(commit_msg, mocker: MockFixture): create_file_and_commit(commit_msg) testargs = ["cz", "bump", "--yes"] mocker.patch.object(sys, "argv", testargs) @@ -60,7 +61,7 @@ def test_bump_minor_increment(commit_msg, mocker): @pytest.mark.parametrize("commit_msg", ("feat: new file", "feat(user): new file")) @pytest.mark.usefixtures("tmp_commitizen_project") -def test_bump_minor_increment_annotated(commit_msg, mocker): +def test_bump_minor_increment_annotated(commit_msg, mocker: MockFixture): create_file_and_commit(commit_msg) testargs = ["cz", "bump", "--yes", "--annotated-tag"] mocker.patch.object(sys, "argv", testargs) @@ -75,7 +76,7 @@ def test_bump_minor_increment_annotated(commit_msg, mocker): @pytest.mark.parametrize("commit_msg", ("feat: new file", "feat(user): new file")) @pytest.mark.usefixtures("tmp_commitizen_project_with_gpg") -def test_bump_minor_increment_signed(commit_msg, mocker): +def test_bump_minor_increment_signed(commit_msg, mocker: MockFixture): create_file_and_commit(commit_msg) testargs = ["cz", "bump", "--yes", "--gpg-sign"] mocker.patch.object(sys, "argv", testargs) @@ -90,7 +91,7 @@ def test_bump_minor_increment_signed(commit_msg, mocker): @pytest.mark.parametrize("commit_msg", ("feat: new file", "feat(user): new file")) def test_bump_minor_increment_annotated_config_file( - commit_msg, mocker, tmp_commitizen_project + commit_msg, mocker: MockFixture, tmp_commitizen_project ): tmp_commitizen_cfg_file = tmp_commitizen_project.join("pyproject.toml") tmp_commitizen_cfg_file.write( @@ -110,7 +111,7 @@ def test_bump_minor_increment_annotated_config_file( @pytest.mark.parametrize("commit_msg", ("feat: new file", "feat(user): new file")) def test_bump_minor_increment_signed_config_file( - commit_msg, mocker, tmp_commitizen_project_with_gpg + commit_msg, mocker: MockFixture, tmp_commitizen_project_with_gpg ): tmp_commitizen_cfg_file = tmp_commitizen_project_with_gpg.join("pyproject.toml") tmp_commitizen_cfg_file.write(f"{tmp_commitizen_cfg_file.read()}\n" f"gpg_sign = 1") @@ -140,7 +141,7 @@ def test_bump_minor_increment_signed_config_file( "BREAKING-CHANGE: age is no longer supported", ), ) -def test_bump_major_increment(commit_msg, mocker): +def test_bump_major_increment(commit_msg, mocker: MockFixture): create_file_and_commit(commit_msg) testargs = ["cz", "bump", "--yes"] @@ -186,7 +187,9 @@ def test_bump_major_increment_major_version_zero(commit_msg, mocker): ("BREAKING CHANGE: age is no longer supported", "minor", "0.2.0"), ], ) -def test_bump_command_increment_option(commit_msg, increment, expected_tag, mocker): +def test_bump_command_increment_option( + commit_msg, increment, expected_tag, mocker: MockFixture +): create_file_and_commit(commit_msg) testargs = ["cz", "bump", "--increment", increment, "--yes"] @@ -198,7 +201,7 @@ def test_bump_command_increment_option(commit_msg, increment, expected_tag, mock @pytest.mark.usefixtures("tmp_commitizen_project") -def test_bump_command_prelease(mocker): +def test_bump_command_prelease(mocker: MockFixture): # PRERELEASE create_file_and_commit("feat: location") @@ -219,7 +222,7 @@ def test_bump_command_prelease(mocker): @pytest.mark.usefixtures("tmp_commitizen_project") -def test_bump_on_git_with_hooks_no_verify_disabled(mocker): +def test_bump_on_git_with_hooks_no_verify_disabled(mocker: MockFixture): """Bump commit without --no-verify""" cmd.run("mkdir .git/hooks") with open(".git/hooks/pre-commit", "w") as f: @@ -239,7 +242,7 @@ def test_bump_on_git_with_hooks_no_verify_disabled(mocker): @pytest.mark.usefixtures("tmp_commitizen_project") -def test_bump_tag_exists_raises_exception(mocker): +def test_bump_tag_exists_raises_exception(mocker: MockFixture): cmd.run("mkdir .git/hooks") with open(".git/hooks/post-commit", "w") as f: f.write("#!/usr/bin/env bash\n" "exit 9") @@ -258,7 +261,7 @@ def test_bump_tag_exists_raises_exception(mocker): @pytest.mark.usefixtures("tmp_commitizen_project") -def test_bump_on_git_with_hooks_no_verify_enabled(mocker): +def test_bump_on_git_with_hooks_no_verify_enabled(mocker: MockFixture): cmd.run("mkdir .git/hooks") with open(".git/hooks/pre-commit", "w") as f: f.write("#!/usr/bin/env bash\n" 'echo "0.1.0"') @@ -275,7 +278,7 @@ def test_bump_on_git_with_hooks_no_verify_enabled(mocker): assert tag_exists is True -def test_bump_when_bumpping_is_not_support(mocker, tmp_commitizen_project): +def test_bump_when_bumpping_is_not_support(mocker: MockFixture, tmp_commitizen_project): create_file_and_commit( "feat: new user interface\n\nBREAKING CHANGE: age is no longer supported" ) @@ -290,7 +293,7 @@ def test_bump_when_bumpping_is_not_support(mocker, tmp_commitizen_project): @pytest.mark.usefixtures("tmp_git_project") -def test_bump_when_version_is_not_specify(mocker): +def test_bump_when_version_is_not_specify(mocker: MockFixture): mocker.patch.object(sys, "argv", ["cz", "bump"]) with pytest.raises(NoVersionSpecifiedError) as excinfo: @@ -300,7 +303,7 @@ def test_bump_when_version_is_not_specify(mocker): @pytest.mark.usefixtures("tmp_commitizen_project") -def test_bump_when_no_new_commit(mocker): +def test_bump_when_no_new_commit(mocker: MockFixture): """bump without any commits since the last bump.""" # We need this first commit otherwise the revision is invalid. create_file_and_commit("feat: initial") @@ -322,7 +325,7 @@ def test_bump_when_no_new_commit(mocker): def test_bump_when_version_inconsistent_in_version_files( - tmp_commitizen_project, mocker + tmp_commitizen_project, mocker: MockFixture ): tmp_version_file = tmp_commitizen_project.join("__version__.py") tmp_version_file.write("100.999.10000") @@ -374,7 +377,7 @@ def test_bump_major_version_zero_when_major_is_not_zero(mocker, tmp_commitizen_p assert expected_error_message in str(excinfo.value) -def test_bump_files_only(mocker, tmp_commitizen_project): +def test_bump_files_only(mocker: MockFixture, tmp_commitizen_project): tmp_version_file = tmp_commitizen_project.join("__version__.py") tmp_version_file.write("0.1.0") tmp_commitizen_cfg_file = tmp_commitizen_project.join("pyproject.toml") @@ -407,7 +410,7 @@ def test_bump_files_only(mocker, tmp_commitizen_project): assert "0.3.0" in f.read() -def test_bump_local_version(mocker, tmp_commitizen_project): +def test_bump_local_version(mocker: MockFixture, tmp_commitizen_project): tmp_version_file = tmp_commitizen_project.join("__version__.py") tmp_version_file.write("4.5.1+0.1.0") tmp_commitizen_cfg_file = tmp_commitizen_project.join("pyproject.toml") @@ -429,7 +432,7 @@ def test_bump_local_version(mocker, tmp_commitizen_project): assert "4.5.1+0.2.0" in f.read() -def test_bump_dry_run(mocker, capsys, tmp_commitizen_project): +def test_bump_dry_run(mocker: MockFixture, capsys, tmp_commitizen_project): create_file_and_commit("feat: new file") testargs = ["cz", "bump", "--yes", "--dry-run"] @@ -444,7 +447,7 @@ def test_bump_dry_run(mocker, capsys, tmp_commitizen_project): assert tag_exists is False -def test_bump_in_non_git_project(tmpdir, config, mocker): +def test_bump_in_non_git_project(tmpdir, config, mocker: MockFixture): testargs = ["cz", "bump", "--yes"] mocker.patch.object(sys, "argv", testargs) @@ -472,7 +475,7 @@ def test_none_increment_exit_is_exception(): @pytest.mark.usefixtures("tmp_commitizen_project") def test_none_increment_should_not_call_git_tag_and_error_code_is_not_zero( - mocker, tmp_commitizen_project + mocker: MockFixture, tmp_commitizen_project ): create_file_and_commit("test(test_get_all_droplets): fix bad comparison test") testargs = ["cz", "bump", "--yes"] @@ -496,7 +499,7 @@ def test_none_increment_should_not_call_git_tag_and_error_code_is_not_zero( @pytest.mark.usefixtures("tmp_commitizen_project") -def test_bump_with_changelog_arg(mocker, changelog_path): +def test_bump_with_changelog_arg(mocker: MockFixture, changelog_path): create_file_and_commit("feat(user): new file") testargs = ["cz", "bump", "--yes", "--changelog"] mocker.patch.object(sys, "argv", testargs) @@ -511,7 +514,7 @@ def test_bump_with_changelog_arg(mocker, changelog_path): @pytest.mark.usefixtures("tmp_commitizen_project") -def test_bump_with_changelog_config(mocker, changelog_path, config_path): +def test_bump_with_changelog_config(mocker: MockFixture, changelog_path, config_path): create_file_and_commit("feat(user): new file") with open(config_path, "a") as fp: fp.write("update_changelog_on_bump = true\n") @@ -529,7 +532,7 @@ def test_bump_with_changelog_config(mocker, changelog_path, config_path): def test_prevent_prerelease_when_no_increment_detected( - mocker, capsys, tmp_commitizen_project + mocker: MockFixture, capsys, tmp_commitizen_project ): create_file_and_commit("feat: new file") @@ -555,7 +558,7 @@ def test_prevent_prerelease_when_no_increment_detected( @pytest.mark.usefixtures("tmp_commitizen_project") -def test_bump_with_changelog_to_stdout_arg(mocker, capsys, changelog_path): +def test_bump_with_changelog_to_stdout_arg(mocker: MockFixture, capsys, changelog_path): create_file_and_commit("feat(user): this should appear in stdout") testargs = ["cz", "bump", "--yes", "--changelog-to-stdout"] mocker.patch.object(sys, "argv", testargs) @@ -573,7 +576,9 @@ def test_bump_with_changelog_to_stdout_arg(mocker, capsys, changelog_path): @pytest.mark.usefixtures("tmp_commitizen_project") -def test_bump_with_changelog_to_stdout_dry_run_arg(mocker, capsys, changelog_path): +def test_bump_with_changelog_to_stdout_dry_run_arg( + mocker: MockFixture, capsys, changelog_path +): create_file_and_commit( "feat(user): this should appear in stdout with dry-run enabled" ) diff --git a/tests/commands/test_changelog_command.py b/tests/commands/test_changelog_command.py index c2fafd4552..f37915baa2 100644 --- a/tests/commands/test_changelog_command.py +++ b/tests/commands/test_changelog_command.py @@ -2,6 +2,7 @@ from datetime import datetime import pytest +from pytest_mock import MockFixture from commitizen import cli, git from commitizen.commands.changelog import Changelog @@ -16,7 +17,9 @@ @pytest.mark.usefixtures("tmp_commitizen_project") -def test_changelog_from_version_zero_point_two(mocker, capsys, file_regression): +def test_changelog_from_version_zero_point_two( + mocker: MockFixture, capsys, file_regression +): create_file_and_commit("feat: new file") create_file_and_commit("refactor: not in changelog") @@ -39,7 +42,7 @@ def test_changelog_from_version_zero_point_two(mocker, capsys, file_regression): @pytest.mark.usefixtures("tmp_commitizen_project") -def test_changelog_with_different_cz(mocker, capsys, file_regression): +def test_changelog_with_different_cz(mocker: MockFixture, capsys, file_regression): create_file_and_commit("JRA-34 #comment corrected indent issue") create_file_and_commit("JRA-35 #time 1w 2d 4h 30m Total work logged") @@ -53,7 +56,9 @@ def test_changelog_with_different_cz(mocker, capsys, file_regression): @pytest.mark.usefixtures("tmp_commitizen_project") -def test_changelog_from_start(mocker, capsys, changelog_path, file_regression): +def test_changelog_from_start( + mocker: MockFixture, capsys, changelog_path, file_regression +): create_file_and_commit("feat: new file") create_file_and_commit("refactor: is in changelog") create_file_and_commit("Merge into master") @@ -69,7 +74,7 @@ def test_changelog_from_start(mocker, capsys, changelog_path, file_regression): @pytest.mark.usefixtures("tmp_commitizen_project") def test_changelog_replacing_unreleased_using_incremental( - mocker, capsys, changelog_path, file_regression + mocker: MockFixture, capsys, changelog_path, file_regression ): create_file_and_commit("feat: add new output") create_file_and_commit("fix: output glitch") @@ -101,7 +106,7 @@ def test_changelog_replacing_unreleased_using_incremental( @pytest.mark.usefixtures("tmp_commitizen_project") def test_changelog_is_persisted_using_incremental( - mocker, capsys, changelog_path, file_regression + mocker: MockFixture, capsys, changelog_path, file_regression ): create_file_and_commit("feat: add new output") @@ -138,7 +143,7 @@ def test_changelog_is_persisted_using_incremental( @pytest.mark.usefixtures("tmp_commitizen_project") def test_changelog_incremental_angular_sample( - mocker, capsys, changelog_path, file_regression + mocker: MockFixture, capsys, changelog_path, file_regression ): with open(changelog_path, "w") as f: f.write( @@ -195,7 +200,7 @@ def test_changelog_incremental_angular_sample( @pytest.mark.usefixtures("tmp_commitizen_project") def test_changelog_incremental_keep_a_changelog_sample( - mocker, capsys, changelog_path, file_regression + mocker: MockFixture, capsys, changelog_path, file_regression ): with open(changelog_path, "w") as f: f.write(KEEP_A_CHANGELOG) @@ -220,7 +225,7 @@ def test_changelog_incremental_keep_a_changelog_sample( @pytest.mark.usefixtures("tmp_commitizen_project") -def test_changelog_hook(mocker, config): +def test_changelog_hook(mocker: MockFixture, config): changelog_hook_mock = mocker.Mock() changelog_hook_mock.return_value = "cool changelog hook" @@ -242,7 +247,7 @@ def test_changelog_hook(mocker, config): @pytest.mark.usefixtures("tmp_commitizen_project") -def test_changelog_hook_customize(mocker, config_customize): +def test_changelog_hook_customize(mocker: MockFixture, config_customize): changelog_hook_mock = mocker.Mock() changelog_hook_mock.return_value = "cool changelog hook" @@ -265,7 +270,7 @@ def test_changelog_hook_customize(mocker, config_customize): @pytest.mark.usefixtures("tmp_commitizen_project") def test_changelog_multiple_incremental_do_not_add_new_lines( - mocker, capsys, changelog_path, file_regression + mocker: MockFixture, capsys, changelog_path, file_regression ): """Test for bug https://github.com/commitizen-tools/commitizen/issues/192""" create_file_and_commit("feat: add new output") @@ -300,7 +305,7 @@ def test_changelog_multiple_incremental_do_not_add_new_lines( @pytest.mark.usefixtures("tmp_commitizen_project") def test_changelog_incremental_newline_separates_new_content_from_old( - mocker, changelog_path + mocker: MockFixture, changelog_path ): """Test for https://github.com/commitizen-tools/commitizen/issues/509""" with open(changelog_path, "w") as f: @@ -322,7 +327,7 @@ def test_changelog_incremental_newline_separates_new_content_from_old( ) -def test_changelog_without_revision(mocker, tmp_commitizen_project): +def test_changelog_without_revision(mocker: MockFixture, tmp_commitizen_project): changelog_file = tmp_commitizen_project.join("CHANGELOG.md") changelog_file.write( """ @@ -350,7 +355,7 @@ def test_changelog_incremental_with_revision(mocker): def test_changelog_with_different_tag_name_and_changelog_content( - mocker, tmp_commitizen_project + mocker: MockFixture, tmp_commitizen_project ): changelog_file = tmp_commitizen_project.join("CHANGELOG.md") changelog_file.write( @@ -371,7 +376,7 @@ def test_changelog_with_different_tag_name_and_changelog_content( cli.main() -def test_changelog_in_non_git_project(tmpdir, config, mocker): +def test_changelog_in_non_git_project(tmpdir, config, mocker: MockFixture): testargs = ["cz", "changelog", "--incremental"] mocker.patch.object(sys, "argv", testargs) @@ -381,7 +386,7 @@ def test_changelog_in_non_git_project(tmpdir, config, mocker): @pytest.mark.usefixtures("tmp_commitizen_project") -def test_breaking_change_content_v1_beta(mocker, capsys, file_regression): +def test_breaking_change_content_v1_beta(mocker: MockFixture, capsys, file_regression): commit_message = ( "feat(users): email pattern corrected\n\n" "BREAKING CHANGE: migrate by renaming user to users\n\n" @@ -397,7 +402,7 @@ def test_breaking_change_content_v1_beta(mocker, capsys, file_regression): @pytest.mark.usefixtures("tmp_commitizen_project") -def test_breaking_change_content_v1(mocker, capsys, file_regression): +def test_breaking_change_content_v1(mocker: MockFixture, capsys, file_regression): commit_message = ( "feat(users): email pattern corrected\n\n" "body content\n\n" @@ -414,7 +419,9 @@ def test_breaking_change_content_v1(mocker, capsys, file_regression): @pytest.mark.usefixtures("tmp_commitizen_project") -def test_breaking_change_content_v1_multiline(mocker, capsys, file_regression): +def test_breaking_change_content_v1_multiline( + mocker: MockFixture, capsys, file_regression +): commit_message = ( "feat(users): email pattern corrected\n\n" "body content\n\n" @@ -433,7 +440,7 @@ def test_breaking_change_content_v1_multiline(mocker, capsys, file_regression): @pytest.mark.usefixtures("tmp_commitizen_project") def test_changelog_config_flag_increment( - mocker, changelog_path, config_path, file_regression + mocker: MockFixture, changelog_path, config_path, file_regression ): with open(config_path, "a") as f: @@ -456,7 +463,7 @@ def test_changelog_config_flag_increment( @pytest.mark.usefixtures("tmp_commitizen_project") def test_changelog_config_start_rev_option( - mocker, capsys, config_path, file_regression + mocker: MockFixture, capsys, config_path, file_regression ): # create commit and tag @@ -483,7 +490,7 @@ def test_changelog_config_start_rev_option( @pytest.mark.usefixtures("tmp_commitizen_project") def test_changelog_incremental_keep_a_changelog_sample_with_annotated_tag( - mocker, capsys, changelog_path, file_regression + mocker: MockFixture, capsys, changelog_path, file_regression ): """Fix #378""" with open(changelog_path, "w") as f: @@ -512,7 +519,7 @@ def test_changelog_incremental_keep_a_changelog_sample_with_annotated_tag( @pytest.mark.usefixtures("tmp_commitizen_project") @pytest.mark.freeze_time("2021-06-11") def test_changelog_incremental_with_release_candidate_version( - mocker, changelog_path, file_regression, test_input + mocker: MockFixture, changelog_path, file_regression, test_input ): """Fix #357""" with open(changelog_path, "w") as f: @@ -543,7 +550,9 @@ def test_changelog_incremental_with_release_candidate_version( @pytest.mark.usefixtures("tmp_commitizen_project") -def test_changelog_with_filename_as_empty_string(mocker, changelog_path, config_path): +def test_changelog_with_filename_as_empty_string( + mocker: MockFixture, changelog_path, config_path +): with open(config_path, "a") as f: f.write("changelog_file = true\n") @@ -559,7 +568,7 @@ def test_changelog_with_filename_as_empty_string(mocker, changelog_path, config_ @pytest.mark.usefixtures("tmp_commitizen_project") @pytest.mark.freeze_time("2022-02-13") def test_changelog_from_rev_first_version_from_arg( - mocker, config_path, changelog_path, file_regression + mocker: MockFixture, config_path, changelog_path, file_regression ): mocker.patch("commitizen.git.GitTag.date", "2022-02-13") @@ -593,7 +602,7 @@ def test_changelog_from_rev_first_version_from_arg( @pytest.mark.usefixtures("tmp_commitizen_project") @pytest.mark.freeze_time("2022-02-13") def test_changelog_from_rev_latest_version_from_arg( - mocker, config_path, changelog_path, file_regression + mocker: MockFixture, config_path, changelog_path, file_regression ): mocker.patch("commitizen.git.GitTag.date", "2022-02-13") @@ -629,7 +638,7 @@ def test_changelog_from_rev_latest_version_from_arg( @pytest.mark.usefixtures("tmp_commitizen_project") @pytest.mark.freeze_time("2022-02-13") def test_changelog_from_rev_single_version_not_found( - mocker, config_path, changelog_path + mocker: MockFixture, config_path, changelog_path ): """Provides an invalid revision ID to changelog command""" with open(config_path, "a") as f: @@ -692,7 +701,7 @@ def test_changelog_from_rev_range_default_tag_format( @pytest.mark.usefixtures("tmp_commitizen_project") @pytest.mark.freeze_time("2022-02-13") -def test_changelog_from_rev_range_version_not_found(mocker, config_path): +def test_changelog_from_rev_range_version_not_found(mocker: MockFixture, config_path): """Provides an invalid end revision ID to changelog command""" with open(config_path, "a") as f: f.write('tag_format = "$version"\n') @@ -721,7 +730,7 @@ def test_changelog_from_rev_range_version_not_found(mocker, config_path): @pytest.mark.usefixtures("tmp_commitizen_project") @pytest.mark.freeze_time("2022-02-13") def test_changelog_from_rev_version_range_including_first_tag( - mocker, config_path, changelog_path, file_regression + mocker: MockFixture, config_path, changelog_path, file_regression ): mocker.patch("commitizen.git.GitTag.date", "2022-02-13") @@ -753,7 +762,7 @@ def test_changelog_from_rev_version_range_including_first_tag( @pytest.mark.usefixtures("tmp_commitizen_project") @pytest.mark.freeze_time("2022-02-13") def test_changelog_from_rev_version_range_from_arg( - mocker, config_path, changelog_path, file_regression + mocker: MockFixture, config_path, changelog_path, file_regression ): mocker.patch("commitizen.git.GitTag.date", "2022-02-13") @@ -793,7 +802,7 @@ def test_changelog_from_rev_version_range_from_arg( @pytest.mark.usefixtures("tmp_commitizen_project") @pytest.mark.freeze_time("2022-02-13") def test_changelog_from_rev_version_with_big_range_from_arg( - mocker, config_path, changelog_path, file_regression + mocker: MockFixture, config_path, changelog_path, file_regression ): mocker.patch("commitizen.git.GitTag.date", "2022-02-13") @@ -853,7 +862,7 @@ def test_changelog_from_rev_version_with_big_range_from_arg( @pytest.mark.usefixtures("tmp_commitizen_project") @pytest.mark.freeze_time("2022-02-13") def test_changelog_from_rev_latest_version_dry_run( - mocker, capsys, config_path, changelog_path, file_regression + mocker: MockFixture, capsys, config_path, changelog_path, file_regression ): mocker.patch("commitizen.git.GitTag.date", "2022-02-13") @@ -887,7 +896,7 @@ def test_changelog_from_rev_latest_version_dry_run( @pytest.mark.usefixtures("tmp_commitizen_project") -def test_invalid_subject_is_skipped(mocker, capsys): +def test_invalid_subject_is_skipped(mocker: MockFixture, capsys): """Fix #510""" non_conformant_commit_title = ( "Merge pull request #487 from manang/master\n\n" diff --git a/tests/commands/test_check_command.py b/tests/commands/test_check_command.py index 71e7aed6d2..b59d8a7ab9 100644 --- a/tests/commands/test_check_command.py +++ b/tests/commands/test_check_command.py @@ -3,6 +3,7 @@ from typing import List import pytest +from pytest_mock import MockFixture from commitizen import cli, commands, git from commitizen.exceptions import ( @@ -55,7 +56,7 @@ def _build_fake_git_commits(commit_msgs: List[str]) -> List[git.GitCommit]: return [git.GitCommit("test_rev", commit_msg) for commit_msg in commit_msgs] -def test_check_jira_fails(mocker): +def test_check_jira_fails(mocker: MockFixture): testargs = ["cz", "-n", "cz_jira", "check", "--commit-msg-file", "some_file"] mocker.patch.object(sys, "argv", testargs) mocker.patch( @@ -67,7 +68,7 @@ def test_check_jira_fails(mocker): assert "commit validation: failed!" in str(excinfo.value) -def test_check_jira_command_after_issue_one_space(mocker, capsys): +def test_check_jira_command_after_issue_one_space(mocker: MockFixture, capsys): testargs = ["cz", "-n", "cz_jira", "check", "--commit-msg-file", "some_file"] mocker.patch.object(sys, "argv", testargs) mocker.patch( @@ -79,7 +80,7 @@ def test_check_jira_command_after_issue_one_space(mocker, capsys): assert "Commit validation: successful!" in out -def test_check_jira_command_after_issue_two_spaces(mocker, capsys): +def test_check_jira_command_after_issue_two_spaces(mocker: MockFixture, capsys): testargs = ["cz", "-n", "cz_jira", "check", "--commit-msg-file", "some_file"] mocker.patch.object(sys, "argv", testargs) mocker.patch( @@ -91,7 +92,7 @@ def test_check_jira_command_after_issue_two_spaces(mocker, capsys): assert "Commit validation: successful!" in out -def test_check_jira_text_between_issue_and_command(mocker, capsys): +def test_check_jira_text_between_issue_and_command(mocker: MockFixture, capsys): testargs = ["cz", "-n", "cz_jira", "check", "--commit-msg-file", "some_file"] mocker.patch.object(sys, "argv", testargs) mocker.patch( @@ -103,7 +104,7 @@ def test_check_jira_text_between_issue_and_command(mocker, capsys): assert "Commit validation: successful!" in out -def test_check_jira_multiple_commands(mocker, capsys): +def test_check_jira_multiple_commands(mocker: MockFixture, capsys): testargs = ["cz", "-n", "cz_jira", "check", "--commit-msg-file", "some_file"] mocker.patch.object(sys, "argv", testargs) mocker.patch( @@ -115,7 +116,7 @@ def test_check_jira_multiple_commands(mocker, capsys): assert "Commit validation: successful!" in out -def test_check_conventional_commit_succeeds(mocker, capsys): +def test_check_conventional_commit_succeeds(mocker: MockFixture, capsys): testargs = ["cz", "check", "--commit-msg-file", "some_file"] mocker.patch.object(sys, "argv", testargs) mocker.patch( @@ -139,7 +140,7 @@ def test_check_conventional_commit_succeeds(mocker, capsys): ), ), ) -def test_check_no_conventional_commit(commit_msg, config, mocker, tmpdir): +def test_check_no_conventional_commit(commit_msg, config, mocker: MockFixture, tmpdir): with pytest.raises(InvalidCommitMessageError): error_mock = mocker.patch("commitizen.out.error") @@ -162,7 +163,7 @@ def test_check_no_conventional_commit(commit_msg, config, mocker, tmpdir): "bump: 0.0.1 -> 1.0.0", ), ) -def test_check_conventional_commit(commit_msg, config, mocker, tmpdir): +def test_check_conventional_commit(commit_msg, config, mocker: MockFixture, tmpdir): success_mock = mocker.patch("commitizen.out.success") tempfile = tmpdir.join("temp_commit_file") @@ -179,7 +180,7 @@ def test_check_command_when_commit_file_not_found(config): commands.Check(config=config, arguments={"commit_msg_file": "no_such_file"})() -def test_check_a_range_of_git_commits(config, mocker): +def test_check_a_range_of_git_commits(config, mocker: MockFixture): success_mock = mocker.patch("commitizen.out.success") mocker.patch( "commitizen.git.get_commits", return_value=_build_fake_git_commits(COMMIT_LOG) @@ -193,7 +194,7 @@ def test_check_a_range_of_git_commits(config, mocker): success_mock.assert_called_once() -def test_check_a_range_of_git_commits_and_failed(config, mocker): +def test_check_a_range_of_git_commits_and_failed(config, mocker: MockFixture): error_mock = mocker.patch("commitizen.out.error") mocker.patch( "commitizen.git.get_commits", @@ -221,7 +222,7 @@ def test_check_command_with_invalid_argument(config): @pytest.mark.usefixtures("tmp_commitizen_project") -def test_check_command_with_empty_range(config, mocker): +def test_check_command_with_empty_range(config, mocker: MockFixture): # must initialize git with a commit create_file_and_commit("feat: initial") @@ -233,7 +234,7 @@ def test_check_command_with_empty_range(config, mocker): assert "No commit found with range: 'master..master'" in str(excinfo) -def test_check_a_range_of_failed_git_commits(config, mocker): +def test_check_a_range_of_failed_git_commits(config, mocker: MockFixture): ill_formated_commits_msgs = [ "First commit does not follow rule", "Second commit does not follow rule", @@ -252,7 +253,7 @@ def test_check_a_range_of_failed_git_commits(config, mocker): assert all([msg in str(excinfo.value) for msg in ill_formated_commits_msgs]) -def test_check_command_with_valid_message(config, mocker): +def test_check_command_with_valid_message(config, mocker: MockFixture): success_mock = mocker.patch("commitizen.out.success") check_cmd = commands.Check( config=config, arguments={"message": "fix(scope): some commit message"} @@ -262,7 +263,7 @@ def test_check_command_with_valid_message(config, mocker): success_mock.assert_called_once() -def test_check_command_with_invalid_message(config, mocker): +def test_check_command_with_invalid_message(config, mocker: MockFixture): error_mock = mocker.patch("commitizen.out.error") check_cmd = commands.Check(config=config, arguments={"message": "bad commit"}) @@ -271,7 +272,7 @@ def test_check_command_with_invalid_message(config, mocker): error_mock.assert_called_once() -def test_check_command_with_empty_message(config, mocker): +def test_check_command_with_empty_message(config, mocker: MockFixture): error_mock = mocker.patch("commitizen.out.error") check_cmd = commands.Check(config=config, arguments={"message": ""}) @@ -280,7 +281,7 @@ def test_check_command_with_empty_message(config, mocker): error_mock.assert_called_once() -def test_check_command_with_allow_abort_arg(config, mocker): +def test_check_command_with_allow_abort_arg(config, mocker: MockFixture): success_mock = mocker.patch("commitizen.out.success") check_cmd = commands.Check( config=config, arguments={"message": "", "allow_abort": True} @@ -290,7 +291,7 @@ def test_check_command_with_allow_abort_arg(config, mocker): success_mock.assert_called_once() -def test_check_command_with_allow_abort_config(config, mocker): +def test_check_command_with_allow_abort_config(config, mocker: MockFixture): success_mock = mocker.patch("commitizen.out.success") config.settings["allow_abort"] = True check_cmd = commands.Check(config=config, arguments={"message": ""}) @@ -299,7 +300,7 @@ def test_check_command_with_allow_abort_config(config, mocker): success_mock.assert_called_once() -def test_check_command_override_allow_abort_config(config, mocker): +def test_check_command_override_allow_abort_config(config, mocker: MockFixture): error_mock = mocker.patch("commitizen.out.error") config.settings["allow_abort"] = True check_cmd = commands.Check( @@ -311,7 +312,7 @@ def test_check_command_override_allow_abort_config(config, mocker): error_mock.assert_called_once() -def test_check_command_with_pipe_message(mocker, capsys): +def test_check_command_with_pipe_message(mocker: MockFixture, capsys): testargs = ["cz", "check"] mocker.patch.object(sys, "argv", testargs) mocker.patch("sys.stdin", StringIO("fix(scope): some commit message")) @@ -321,7 +322,7 @@ def test_check_command_with_pipe_message(mocker, capsys): assert "Commit validation: successful!" in out -def test_check_command_with_pipe_message_and_failed(mocker): +def test_check_command_with_pipe_message_and_failed(mocker: MockFixture): testargs = ["cz", "check"] mocker.patch.object(sys, "argv", testargs) mocker.patch("sys.stdin", StringIO("bad commit message")) @@ -331,7 +332,7 @@ def test_check_command_with_pipe_message_and_failed(mocker): assert "commit validation: failed!" in str(excinfo.value) -def test_check_command_with_comment_in_messege_file(mocker, capsys): +def test_check_command_with_comment_in_messege_file(mocker: MockFixture, capsys): testargs = ["cz", "check", "--commit-msg-file", "some_file"] mocker.patch.object(sys, "argv", testargs) mocker.patch( diff --git a/tests/commands/test_commit_command.py b/tests/commands/test_commit_command.py index 8544833c8f..7c71665ccd 100644 --- a/tests/commands/test_commit_command.py +++ b/tests/commands/test_commit_command.py @@ -1,6 +1,7 @@ import os import pytest +from pytest_mock import MockFixture from commitizen import cmd, commands from commitizen.cz.exceptions import CzException @@ -16,13 +17,13 @@ @pytest.fixture -def staging_is_clean(mocker): +def staging_is_clean(mocker: MockFixture): is_staging_clean_mock = mocker.patch("commitizen.git.is_staging_clean") is_staging_clean_mock.return_value = False @pytest.mark.usefixtures("staging_is_clean") -def test_commit(config, mocker): +def test_commit(config, mocker: MockFixture): prompt_mock = mocker.patch("questionary.prompt") prompt_mock.return_value = { "prefix": "feat", @@ -42,7 +43,7 @@ def test_commit(config, mocker): @pytest.mark.usefixtures("staging_is_clean") -def test_commit_retry_fails_no_backup(config, mocker): +def test_commit_retry_fails_no_backup(config, mocker: MockFixture): commit_mock = mocker.patch("commitizen.git.commit") commit_mock.return_value = cmd.Command("success", "", "", "", 0) @@ -53,7 +54,7 @@ def test_commit_retry_fails_no_backup(config, mocker): @pytest.mark.usefixtures("staging_is_clean") -def test_commit_retry_works(config, mocker): +def test_commit_retry_works(config, mocker: MockFixture): prompt_mock = mocker.patch("questionary.prompt") prompt_mock.return_value = { "prefix": "feat", @@ -91,7 +92,7 @@ def test_commit_retry_works(config, mocker): @pytest.mark.usefixtures("staging_is_clean") -def test_commit_command_with_dry_run_option(config, mocker): +def test_commit_command_with_dry_run_option(config, mocker: MockFixture): prompt_mock = mocker = mocker.patch("questionary.prompt") prompt_mock.return_value = { "prefix": "feat", @@ -108,7 +109,7 @@ def test_commit_command_with_dry_run_option(config, mocker): @pytest.mark.usefixtures("staging_is_clean") -def test_commit_command_with_signoff_option(config, mocker): +def test_commit_command_with_signoff_option(config, mocker: MockFixture): prompt_mock = mocker.patch("questionary.prompt") prompt_mock.return_value = { "prefix": "feat", @@ -127,7 +128,7 @@ def test_commit_command_with_signoff_option(config, mocker): success_mock.assert_called_once() -def test_commit_when_nothing_to_commit(config, mocker): +def test_commit_when_nothing_to_commit(config, mocker: MockFixture): is_staging_clean_mock = mocker.patch("commitizen.git.is_staging_clean") is_staging_clean_mock.return_value = True @@ -139,7 +140,7 @@ def test_commit_when_nothing_to_commit(config, mocker): @pytest.mark.usefixtures("staging_is_clean") -def test_commit_when_customized_expected_raised(config, mocker, capsys): +def test_commit_when_customized_expected_raised(config, mocker: MockFixture, capsys): _err = ValueError() _err.__context__ = CzException("This is the root custom err") prompt_mock = mocker.patch("questionary.prompt") @@ -154,7 +155,9 @@ def test_commit_when_customized_expected_raised(config, mocker, capsys): @pytest.mark.usefixtures("staging_is_clean") -def test_commit_when_non_customized_expected_raised(config, mocker, capsys): +def test_commit_when_non_customized_expected_raised( + config, mocker: MockFixture, capsys +): _err = ValueError() prompt_mock = mocker.patch("questionary.prompt") prompt_mock.side_effect = _err @@ -165,7 +168,7 @@ def test_commit_when_non_customized_expected_raised(config, mocker, capsys): @pytest.mark.usefixtures("staging_is_clean") -def test_commit_when_no_user_answer(config, mocker, capsys): +def test_commit_when_no_user_answer(config, mocker: MockFixture, capsys): prompt_mock = mocker.patch("questionary.prompt") prompt_mock.return_value = None diff --git a/tests/commands/test_init_command.py b/tests/commands/test_init_command.py index 2bff02a861..d7b7855328 100644 --- a/tests/commands/test_init_command.py +++ b/tests/commands/test_init_command.py @@ -3,6 +3,7 @@ import pytest import yaml +from pytest_mock import MockFixture from commitizen import commands from commitizen.__version__ import __version__ @@ -40,7 +41,7 @@ def ask(self): } -def test_init_without_setup_pre_commit_hook(tmpdir, mocker, config): +def test_init_without_setup_pre_commit_hook(tmpdir, mocker: MockFixture, config): mocker.patch( "questionary.select", side_effect=[ @@ -72,7 +73,7 @@ def test_init_when_config_already_exists(config, capsys): assert captured.out == f"Config file {path} already exists\n" -def test_init_without_choosing_tag(config, mocker, tmpdir): +def test_init_without_choosing_tag(config, mocker: MockFixture, tmpdir): mocker.patch( "commitizen.commands.init.get_tag_names", return_value=["0.0.1", "0.0.2"] ) @@ -95,7 +96,7 @@ def test_init_without_choosing_tag(config, mocker, tmpdir): class TestPreCommitCases: @pytest.fixture(scope="function", params=["pyproject.toml", ".cz.json", ".cz.yaml"]) - def default_choice(_, request, mocker): + def default_choice(_, request, mocker: MockFixture): mocker.patch( "questionary.select", side_effect=[ diff --git a/tests/commands/test_other_commands.py b/tests/commands/test_other_commands.py index 619b6743d1..3f7f855d54 100644 --- a/tests/commands/test_other_commands.py +++ b/tests/commands/test_other_commands.py @@ -1,25 +1,27 @@ +from pytest_mock import MockFixture + from commitizen import commands -def test_example(config, mocker): +def test_example(config, mocker: MockFixture): write_mock = mocker.patch("commitizen.out.write") commands.Example(config)() write_mock.assert_called_once() -def test_info(config, mocker): +def test_info(config, mocker: MockFixture): write_mock = mocker.patch("commitizen.out.write") commands.Info(config)() write_mock.assert_called_once() -def test_schema(config, mocker): +def test_schema(config, mocker: MockFixture): write_mock = mocker.patch("commitizen.out.write") commands.Schema(config)() write_mock.assert_called_once() -def test_list_cz(config, mocker): +def test_list_cz(config, mocker: MockFixture): write_mock = mocker.patch("commitizen.out.write") commands.ListCz(config)() write_mock.assert_called_once() diff --git a/tests/test_bump_create_commit_message.py b/tests/test_bump_create_commit_message.py index b4bcf9631a..6fc31778d7 100644 --- a/tests/test_bump_create_commit_message.py +++ b/tests/test_bump_create_commit_message.py @@ -5,6 +5,7 @@ import pytest from packaging.version import Version +from pytest_mock import MockFixture from commitizen import bump, cli, cmd, exceptions @@ -28,7 +29,9 @@ def test_create_tag(test_input, expected): @pytest.mark.parametrize("retry", (True, False)) -def test_bump_pre_commit_changelog(tmp_commitizen_project, mocker, freezer, retry): +def test_bump_pre_commit_changelog( + tmp_commitizen_project, mocker: MockFixture, freezer, retry +): freezer.move_to("2022-04-01") testargs = ["cz", "bump", "--changelog", "--yes"] if retry: @@ -76,7 +79,7 @@ def test_bump_pre_commit_changelog(tmp_commitizen_project, mocker, freezer, retr @pytest.mark.parametrize("retry", (True, False)) def test_bump_pre_commit_changelog_fails_always( - tmp_commitizen_project, mocker, freezer, retry + tmp_commitizen_project, mocker: MockFixture, freezer, retry ): freezer.move_to("2022-04-01") testargs = ["cz", "bump", "--changelog", "--yes"] diff --git a/tests/test_cli.py b/tests/test_cli.py index 68a9c04a23..7efd97a887 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -3,12 +3,13 @@ import sys import pytest +from pytest_mock import MockFixture from commitizen import cli from commitizen.exceptions import ExpectedExit, NoCommandFoundError, NotAGitProjectError -def test_sysexit_no_argv(mocker, capsys): +def test_sysexit_no_argv(mocker: MockFixture, capsys): testargs = ["cz"] mocker.patch.object(sys, "argv", testargs) @@ -18,7 +19,7 @@ def test_sysexit_no_argv(mocker, capsys): assert out.startswith("usage") -def test_cz_with_arg_but_without_command(mocker): +def test_cz_with_arg_but_without_command(mocker: MockFixture): testargs = ["cz", "--name", "cz_jira"] mocker.patch.object(sys, "argv", testargs) @@ -27,7 +28,7 @@ def test_cz_with_arg_but_without_command(mocker): assert "Command is required" in str(excinfo.value) -def test_name(mocker, capsys): +def test_name(mocker: MockFixture, capsys): testargs = ["cz", "-n", "cz_jira", "example"] mocker.patch.object(sys, "argv", testargs) @@ -37,7 +38,7 @@ def test_name(mocker, capsys): @pytest.mark.usefixtures("tmp_git_project") -def test_name_default_value(mocker, capsys): +def test_name_default_value(mocker: MockFixture, capsys): testargs = ["cz", "example"] mocker.patch.object(sys, "argv", testargs) @@ -46,7 +47,7 @@ def test_name_default_value(mocker, capsys): assert out.startswith("fix: correct minor typos in code") -def test_ls(mocker, capsys): +def test_ls(mocker: MockFixture, capsys): testargs = ["cz", "-n", "cz_jira", "ls"] mocker.patch.object(sys, "argv", testargs) cli.main() @@ -56,7 +57,7 @@ def test_ls(mocker, capsys): assert isinstance(out, str) -def test_arg_debug(mocker): +def test_arg_debug(mocker: MockFixture): testargs = ["cz", "--debug", "info"] mocker.patch.object(sys, "argv", testargs) cli.main() diff --git a/tests/test_git.py b/tests/test_git.py index cc1f4cf3c9..81089f6759 100644 --- a/tests/test_git.py +++ b/tests/test_git.py @@ -4,6 +4,7 @@ from typing import List, Optional import pytest +from pytest_mock import MockFixture from commitizen import cmd, exceptions, git from tests.utils import FakeCommand, create_file_and_commit @@ -19,7 +20,7 @@ def test_git_object_eq(): assert git_commit != "sha1-code" -def test_get_tags(mocker): +def test_get_tags(mocker: MockFixture): tag_str = ( "v1.0.0---inner_delimiter---333---inner_delimiter---2020-01-20---inner_delimiter---\n" "v0.5.0---inner_delimiter---222---inner_delimiter---2020-01-17---inner_delimiter---\n" @@ -39,7 +40,7 @@ def test_get_tags(mocker): assert git.get_tags() == [] -def test_get_tag_names(mocker): +def test_get_tag_names(mocker: MockFixture): tag_str = "v1.0.0\n" "v0.5.0\n" "v0.0.1\n" mocker.patch("commitizen.cmd.run", return_value=FakeCommand(out=tag_str)) @@ -85,7 +86,7 @@ def test_get_commits_author_and_email(): assert "@" in commit.author_email -def test_get_commits_without_email(mocker): +def test_get_commits_without_email(mocker: MockFixture): raw_commit = ( "a515bb8f71c403f6f7d1c17b9d8ebf2ce3959395\n" "\n" @@ -112,7 +113,7 @@ def test_get_commits_without_email(mocker): assert commits[1].title == "feat(users): add username" -def test_get_commits_without_breakline_in_each_commit(mocker): +def test_get_commits_without_breakline_in_each_commit(mocker: MockFixture): raw_commit = ( "ae9ba6fc5526cf478f52ef901418d85505109744\n" "bump: version 2.13.0 → 2.14.0\n" From 5be07693b8a25a49ad54192bc3c87c5f7cb819b3 Mon Sep 17 00:00:00 2001 From: bogay Date: Tue, 9 Aug 2022 10:51:14 +0800 Subject: [PATCH 3/7] test: fix some incompatible types of function calls --- commitizen/commands/check.py | 4 ++-- tests/commands/test_commit_command.py | 10 +++++----- tests/test_cli.py | 5 ++++- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/commitizen/commands/check.py b/commitizen/commands/check.py index 2ce8ed4d64..f332b6f318 100644 --- a/commitizen/commands/check.py +++ b/commitizen/commands/check.py @@ -1,7 +1,7 @@ import os import re import sys -from typing import Dict, Optional +from typing import Any, Dict, Optional from commitizen import factory, git, out from commitizen.config import BaseConfig @@ -15,7 +15,7 @@ class Check: """Check if the current commit msg matches the commitizen format.""" - def __init__(self, config: BaseConfig, arguments: Dict[str, str], cwd=os.getcwd()): + def __init__(self, config: BaseConfig, arguments: Dict[str, Any], cwd=os.getcwd()): """Initial check command. Args: diff --git a/tests/commands/test_commit_command.py b/tests/commands/test_commit_command.py index 7c71665ccd..05e72cfabd 100644 --- a/tests/commands/test_commit_command.py +++ b/tests/commands/test_commit_command.py @@ -35,7 +35,7 @@ def test_commit(config, mocker: MockFixture): } commit_mock = mocker.patch("commitizen.git.commit") - commit_mock.return_value = cmd.Command("success", "", "", "", 0) + commit_mock.return_value = cmd.Command("success", "", b"", b"", 0) success_mock = mocker.patch("commitizen.out.success") commands.Commit(config, {})() @@ -45,7 +45,7 @@ def test_commit(config, mocker: MockFixture): @pytest.mark.usefixtures("staging_is_clean") def test_commit_retry_fails_no_backup(config, mocker: MockFixture): commit_mock = mocker.patch("commitizen.git.commit") - commit_mock.return_value = cmd.Command("success", "", "", "", 0) + commit_mock.return_value = cmd.Command("success", "", b"", b"", 0) with pytest.raises(NoCommitBackupError) as excinfo: commands.Commit(config, {"retry": True})() @@ -66,7 +66,7 @@ def test_commit_retry_works(config, mocker: MockFixture): } commit_mock = mocker.patch("commitizen.git.commit") - commit_mock.return_value = cmd.Command("", "error", "", "", 9) + commit_mock.return_value = cmd.Command("", "error", b"", b"", 9) error_mock = mocker.patch("commitizen.out.error") with pytest.raises(CommitError): @@ -80,7 +80,7 @@ def test_commit_retry_works(config, mocker: MockFixture): # Previous commit failed, so retry should pick up the backup commit # commit_mock = mocker.patch("commitizen.git.commit") - commit_mock.return_value = cmd.Command("success", "", "", "", 0) + commit_mock.return_value = cmd.Command("success", "", b"", b"", 0) success_mock = mocker.patch("commitizen.out.success") commands.Commit(config, {"retry": True})() @@ -121,7 +121,7 @@ def test_commit_command_with_signoff_option(config, mocker: MockFixture): } commit_mock = mocker.patch("commitizen.git.commit") - commit_mock.return_value = cmd.Command("success", "", "", "", 0) + commit_mock.return_value = cmd.Command("success", "", b"", b"", 0) success_mock = mocker.patch("commitizen.out.success") commands.Commit(config, {"signoff": True})() diff --git a/tests/test_cli.py b/tests/test_cli.py index 7efd97a887..401b81829d 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -1,6 +1,7 @@ import os import subprocess import sys +from functools import partial import pytest from pytest_mock import MockFixture @@ -61,7 +62,9 @@ def test_arg_debug(mocker: MockFixture): testargs = ["cz", "--debug", "info"] mocker.patch.object(sys, "argv", testargs) cli.main() - assert sys.excepthook.keywords.get("debug") is True + excepthook = sys.excepthook + assert isinstance(excepthook, partial) + assert excepthook.keywords.get("debug") is True def test_commitizen_excepthook(capsys): From f1b581c8e08774572374410ed92967dad10ad9ae Mon Sep 17 00:00:00 2001 From: bogay Date: Thu, 8 Sep 2022 05:03:12 +0800 Subject: [PATCH 4/7] test(init): pre-commit failed cases --- tests/commands/test_init_command.py | 83 +++++++++++++++++++++-------- tests/test_cli.py | 2 + 2 files changed, 62 insertions(+), 23 deletions(-) diff --git a/tests/commands/test_init_command.py b/tests/commands/test_init_command.py index d7b7855328..bda9ebd1d6 100644 --- a/tests/commands/test_init_command.py +++ b/tests/commands/test_init_command.py @@ -7,7 +7,7 @@ from commitizen import commands from commitizen.__version__ import __version__ -from commitizen.exceptions import NoAnswersError +from commitizen.exceptions import InitFailedError, NoAnswersError class FakeQuestion: @@ -94,29 +94,35 @@ def test_init_without_choosing_tag(config, mocker: MockFixture, tmpdir): commands.Init(config)() -class TestPreCommitCases: - @pytest.fixture(scope="function", params=["pyproject.toml", ".cz.json", ".cz.yaml"]) - def default_choice(_, request, mocker: MockFixture): - mocker.patch( - "questionary.select", - side_effect=[ - FakeQuestion(request.param), - FakeQuestion("cz_conventional_commits"), - ], - ) - mocker.patch("questionary.confirm", return_value=FakeQuestion(True)) - mocker.patch("questionary.text", return_value=FakeQuestion("$version")) - # Assume the `pre-commit` is installed - mocker.patch( - "commitizen.commands.init.Init._search_pre_commit", - return_value=True, - ) - mocker.patch( - "commitizen.commands.init.Init._exec_install_pre_commit_hook", - return_value=True, - ) - return request.param +@pytest.fixture(scope="function") +def pre_commit_installed(mocker: MockFixture): + # Assume the `pre-commit` is installed + mocker.patch( + "commitizen.commands.init.Init._search_pre_commit", + return_value=True, + ) + mocker.patch( + "commitizen.commands.init.Init._exec_install_pre_commit_hook", + return_value=True, + ) + + +@pytest.fixture(scope="function", params=["pyproject.toml", ".cz.json", ".cz.yaml"]) +def default_choice(request, mocker: MockFixture): + mocker.patch( + "questionary.select", + side_effect=[ + FakeQuestion(request.param), + FakeQuestion("cz_conventional_commits"), + ], + ) + mocker.patch("questionary.confirm", return_value=FakeQuestion(True)) + mocker.patch("questionary.text", return_value=FakeQuestion("$version")) + yield request.param + +@pytest.mark.usefixtures("pre_commit_installed") +class TestPreCommitCases: def test_no_existing_pre_commit_conifg(_, default_choice, tmpdir, config): with tmpdir.as_cwd(): commands.Init(config)() @@ -211,3 +217,34 @@ def test_cz_hook_exists_in_pre_commit_config(_, default_choice, tmpdir, config): # check that config is not duplicated assert pre_commit_config_data == {"repos": [cz_hook_config]} + + +class TestNoPreCommitInstalled: + def test_pre_commit_not_installed( + _, mocker: MockFixture, config, default_choice, tmpdir + ): + # Assume `pre-commit` is not installed + mocker.patch( + "commitizen.commands.init.Init._search_pre_commit", + return_value=False, + ) + with tmpdir.as_cwd(): + with pytest.raises(InitFailedError): + commands.Init(config)() + + def test_pre_commit_exec_failed( + _, mocker: MockFixture, config, default_choice, tmpdir + ): + # Assume `pre-commit` is installed + mocker.patch( + "commitizen.commands.init.Init._search_pre_commit", + return_value=True, + ) + # But pre-commit installation will fail + mocker.patch( + "commitizen.commands.init.Init._exec_install_pre_commit_hook", + return_value=False, + ) + with tmpdir.as_cwd(): + with pytest.raises(InitFailedError): + commands.Init(config)() diff --git a/tests/test_cli.py b/tests/test_cli.py index 401b81829d..6d53ed7ba2 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -63,6 +63,8 @@ def test_arg_debug(mocker: MockFixture): mocker.patch.object(sys, "argv", testargs) cli.main() excepthook = sys.excepthook + # `sys.excepthook` is replaced by a `partial` in `cli.main` + # it's not a normal function assert isinstance(excepthook, partial) assert excepthook.keywords.get("debug") is True From 86c4f60ed4dd138bfcf3bf454c66b0206e219894 Mon Sep 17 00:00:00 2001 From: bogay Date: Thu, 29 Sep 2022 00:21:15 +0800 Subject: [PATCH 5/7] feat(init): allow user to select which type of pre commit hooks to install See more: https://commitizen-tools.github.io/commitizen/#integrating-with-pre-commit --- commitizen/commands/init.py | 32 ++++++-- tests/commands/test_init_command.py | 110 ++++++++++++---------------- 2 files changed, 69 insertions(+), 73 deletions(-) diff --git a/commitizen/commands/init.py b/commitizen/commands/init.py index bf917f0c14..15febd821b 100644 --- a/commitizen/commands/init.py +++ b/commitizen/commands/init.py @@ -1,6 +1,6 @@ import os import shutil -from typing import Any, Dict +from typing import Any, Dict, List, Optional import questionary import yaml @@ -42,8 +42,15 @@ def __call__(self): values_to_add["tag_format"] = self._ask_tag_format(tag) self._update_config_file(values_to_add) - if questionary.confirm("Do you want to install pre-commit hook?").ask(): - if not self._install_pre_commit_hook(): + hook_types = questionary.checkbox( + "What types of pre-commit hook you want to install? (Leave blank if you don't want to install)", + choices=[ + questionary.Choice("commit-msg", checked=True), + questionary.Choice("pre-push", checked=True), + ], + ).ask() + if hook_types: + if not self._install_pre_commit_hook(hook_types): raise InitFailedError( "Installation failed. See error outputs for more information." ) @@ -116,8 +123,12 @@ def _ask_tag_format(self, latest_tag) -> str: def _search_pre_commit(self): return shutil.which("pre-commit") is not None - def _exec_install_pre_commit_hook(self): - cmd_str = "pre-commit install --hook-type commit-msg" + def _exec_install_pre_commit_hook(self, hook_types: List[str]): + if not hook_types: + raise ValueError("At least 1 hook type should be provided.") + cmd_str = "pre-commit install " + "".join( + f"--hook-type {ty}" for ty in hook_types + ) c = cmd.run(cmd_str) if c.return_code != 0: out.error(f"Error running {cmd_str}. Outputs are attached below:") @@ -126,12 +137,15 @@ def _exec_install_pre_commit_hook(self): return False return True - def _install_pre_commit_hook(self) -> bool: + def _install_pre_commit_hook(self, hook_types: Optional[List[str]] = None) -> bool: pre_commit_config_filename = ".pre-commit-config.yaml" cz_hook_config = { "repo": "https://github.com/commitizen-tools/commitizen", "rev": f"v{__version__}", - "hooks": [{"id": "commitizen"}], + "hooks": [ + {"id": "commitizen"}, + {"id": "commitizen-branch", "stages": ["push"]}, + ], } config_data = {} @@ -162,7 +176,9 @@ def _install_pre_commit_hook(self) -> bool: out.error("pre-commit is not installed in current environement.") return False - if not self._exec_install_pre_commit_hook(): + if hook_types is None: + hook_types = ["commit-msg", "pre-push"] + if not self._exec_install_pre_commit_hook(hook_types): return False out.write("commitizen pre-commit hook is now installed in your '.git'\n") diff --git a/tests/commands/test_init_command.py b/tests/commands/test_init_command.py index bda9ebd1d6..481923cb6c 100644 --- a/tests/commands/test_init_command.py +++ b/tests/commands/test_init_command.py @@ -1,5 +1,6 @@ import json import os +from typing import Any, Dict, List import pytest import yaml @@ -22,7 +23,10 @@ def ask(self): cz_hook_config = { "repo": "https://github.com/commitizen-tools/commitizen", "rev": f"v{__version__}", - "hooks": [{"id": "commitizen"}], + "hooks": [ + {"id": "commitizen"}, + {"id": "commitizen-branch", "stages": ["push"]}, + ], } expected_config = ( @@ -51,7 +55,8 @@ def test_init_without_setup_pre_commit_hook(tmpdir, mocker: MockFixture, config) ) mocker.patch("questionary.confirm", return_value=FakeQuestion(True)) mocker.patch("questionary.text", return_value=FakeQuestion("$version")) - mocker.patch("questionary.confirm", return_value=FakeQuestion(False)) + # Return None to skip hook installation + mocker.patch("questionary.checkbox", return_value=FakeQuestion(None)) with tmpdir.as_cwd(): commands.Init(config)() @@ -118,29 +123,46 @@ def default_choice(request, mocker: MockFixture): ) mocker.patch("questionary.confirm", return_value=FakeQuestion(True)) mocker.patch("questionary.text", return_value=FakeQuestion("$version")) + mocker.patch( + "questionary.checkbox", + return_value=FakeQuestion(["commit-msg", "pre-push"]), + ) yield request.param +def check_cz_config(config: str): + """ + Cehck the content of commitizen config is as expected + + Args: + config: The config path + """ + with open(config, "r") as file: + if "json" in config: + assert json.load(file) == EXPECTED_DICT_CONFIG + elif "yaml" in config: + assert yaml.load(file, Loader=yaml.FullLoader) == EXPECTED_DICT_CONFIG + else: + config_data = file.read() + assert config_data == expected_config + + +def check_pre_commit_config(expected: List[Dict[str, Any]]): + """ + Check the content of pre-commit config is as expected + """ + with open(pre_commit_config_filename, "r") as pre_commit_file: + pre_commit_config_data = yaml.safe_load(pre_commit_file.read()) + assert pre_commit_config_data == {"repos": expected} + + @pytest.mark.usefixtures("pre_commit_installed") class TestPreCommitCases: def test_no_existing_pre_commit_conifg(_, default_choice, tmpdir, config): with tmpdir.as_cwd(): commands.Init(config)() - - with open(default_choice, "r") as file: - if "json" in default_choice: - assert json.load(file) == EXPECTED_DICT_CONFIG - elif "yaml" in default_choice: - assert ( - yaml.load(file, Loader=yaml.FullLoader) == EXPECTED_DICT_CONFIG - ) - else: - config_data = file.read() - assert config_data == expected_config - - with open(pre_commit_config_filename, "r") as pre_commit_file: - pre_commit_config_data = yaml.safe_load(pre_commit_file.read()) - assert pre_commit_config_data == {"repos": [cz_hook_config]} + check_cz_config(default_choice) + check_pre_commit_config([cz_hook_config]) def test_empty_pre_commit_config(_, default_choice, tmpdir, config): with tmpdir.as_cwd(): @@ -148,21 +170,8 @@ def test_empty_pre_commit_config(_, default_choice, tmpdir, config): p.write("") commands.Init(config)() - - with open(default_choice, "r") as file: - if "json" in default_choice: - assert json.load(file) == EXPECTED_DICT_CONFIG - elif "yaml" in default_choice: - assert ( - yaml.load(file, Loader=yaml.FullLoader) == EXPECTED_DICT_CONFIG - ) - else: - config_data = file.read() - assert config_data == expected_config - - with open(pre_commit_config_filename, "r") as pre_commit_file: - pre_commit_config_data = yaml.safe_load(pre_commit_file.read()) - assert pre_commit_config_data == {"repos": [cz_hook_config]} + check_cz_config(default_choice) + check_pre_commit_config([cz_hook_config]) def test_pre_commit_config_without_cz_hook(_, default_choice, tmpdir, config): existing_hook_config = { @@ -176,23 +185,8 @@ def test_pre_commit_config_without_cz_hook(_, default_choice, tmpdir, config): p.write(yaml.safe_dump({"repos": [existing_hook_config]})) commands.Init(config)() - - with open(default_choice, "r") as file: - if "json" in default_choice: - assert json.load(file) == EXPECTED_DICT_CONFIG - elif "yaml" in default_choice: - assert ( - yaml.load(file, Loader=yaml.FullLoader) == EXPECTED_DICT_CONFIG - ) - else: - config_data = file.read() - assert config_data == expected_config - - with open(pre_commit_config_filename, "r") as pre_commit_file: - pre_commit_config_data = yaml.safe_load(pre_commit_file.read()) - assert pre_commit_config_data == { - "repos": [existing_hook_config, cz_hook_config] - } + check_cz_config(default_choice) + check_pre_commit_config([existing_hook_config, cz_hook_config]) def test_cz_hook_exists_in_pre_commit_config(_, default_choice, tmpdir, config): with tmpdir.as_cwd(): @@ -200,23 +194,9 @@ def test_cz_hook_exists_in_pre_commit_config(_, default_choice, tmpdir, config): p.write(yaml.safe_dump({"repos": [cz_hook_config]})) commands.Init(config)() - - with open(default_choice, "r") as file: - if "json" in default_choice: - assert json.load(file) == EXPECTED_DICT_CONFIG - elif "yaml" in default_choice: - assert ( - yaml.load(file, Loader=yaml.FullLoader) == EXPECTED_DICT_CONFIG - ) - else: - config_data = file.read() - assert config_data == expected_config - - with open(pre_commit_config_filename, "r") as pre_commit_file: - pre_commit_config_data = yaml.safe_load(pre_commit_file.read()) - + check_cz_config(default_choice) # check that config is not duplicated - assert pre_commit_config_data == {"repos": [cz_hook_config]} + check_pre_commit_config([cz_hook_config]) class TestNoPreCommitInstalled: From 599c10f7bc342790f302db3016d605483d1d0d1b Mon Sep 17 00:00:00 2001 From: bogay Date: Fri, 21 Oct 2022 02:41:42 +0800 Subject: [PATCH 6/7] refactor(init): `_install_pre_commit_hook` raise error when failed instead of returning a `False` value. --- commitizen/commands/init.py | 45 ++++++++++++++++------------- tests/commands/test_init_command.py | 5 ++-- 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/commitizen/commands/init.py b/commitizen/commands/init.py index 15febd821b..b718f526ef 100644 --- a/commitizen/commands/init.py +++ b/commitizen/commands/init.py @@ -50,10 +50,10 @@ def __call__(self): ], ).ask() if hook_types: - if not self._install_pre_commit_hook(hook_types): - raise InitFailedError( - "Installation failed. See error outputs for more information." - ) + try: + self._install_pre_commit_hook(hook_types) + except InitFailedError as e: + raise InitFailedError(f"Failed to install pre-commit hook.\n{e}") out.write("You can bump the version and create changelog running:\n") out.info("cz bump --changelog") @@ -120,24 +120,32 @@ def _ask_tag_format(self, latest_tag) -> str: tag_format = "$version" return tag_format - def _search_pre_commit(self): + def _search_pre_commit(self) -> bool: + """Check whether pre-commit is installed""" return shutil.which("pre-commit") is not None def _exec_install_pre_commit_hook(self, hook_types: List[str]): + cmd_str = self._gen_pre_commit_cmd(hook_types) + c = cmd.run(cmd_str) + if c.return_code != 0: + err_msg = ( + f"Error running {cmd_str}." + "Outputs are attached below:\n" + f"stdout: {c.out}\n" + f"stderr: {c.err}" + ) + raise InitFailedError(err_msg) + + def _gen_pre_commit_cmd(self, hook_types: List[str]) -> str: + """Generate pre-commit command according to given hook types""" if not hook_types: raise ValueError("At least 1 hook type should be provided.") cmd_str = "pre-commit install " + "".join( f"--hook-type {ty}" for ty in hook_types ) - c = cmd.run(cmd_str) - if c.return_code != 0: - out.error(f"Error running {cmd_str}. Outputs are attached below:") - out.error(f"stdout: {c.out}") - out.error(f"stderr: {c.err}") - return False - return True + return cmd_str - def _install_pre_commit_hook(self, hook_types: Optional[List[str]] = None) -> bool: + def _install_pre_commit_hook(self, hook_types: Optional[List[str]] = None): pre_commit_config_filename = ".pre-commit-config.yaml" cz_hook_config = { "repo": "https://github.com/commitizen-tools/commitizen", @@ -173,16 +181,13 @@ def _install_pre_commit_hook(self, hook_types: Optional[List[str]] = None) -> bo yaml.safe_dump(config_data, stream=config_file) if not self._search_pre_commit(): - out.error("pre-commit is not installed in current environement.") - return False - + raise InitFailedError( + "pre-commit is not installed in current environement." + ) if hook_types is None: hook_types = ["commit-msg", "pre-push"] - if not self._exec_install_pre_commit_hook(hook_types): - return False - + self._exec_install_pre_commit_hook(hook_types) out.write("commitizen pre-commit hook is now installed in your '.git'\n") - return True def _update_config_file(self, values: Dict[str, Any]): for key, value in values.items(): diff --git a/tests/commands/test_init_command.py b/tests/commands/test_init_command.py index 481923cb6c..a28ee83544 100644 --- a/tests/commands/test_init_command.py +++ b/tests/commands/test_init_command.py @@ -106,9 +106,10 @@ def pre_commit_installed(mocker: MockFixture): "commitizen.commands.init.Init._search_pre_commit", return_value=True, ) + # And installation success (i.e. no exception raised) mocker.patch( "commitizen.commands.init.Init._exec_install_pre_commit_hook", - return_value=True, + return_value=None, ) @@ -223,7 +224,7 @@ def test_pre_commit_exec_failed( # But pre-commit installation will fail mocker.patch( "commitizen.commands.init.Init._exec_install_pre_commit_hook", - return_value=False, + side_effect=InitFailedError("Mock init failed error."), ) with tmpdir.as_cwd(): with pytest.raises(InitFailedError): From a23ef07963c4a4bf5da1506c00dbe513f3b93bfa Mon Sep 17 00:00:00 2001 From: bogay Date: Fri, 21 Oct 2022 02:44:24 +0800 Subject: [PATCH 7/7] fix(init): space between `--hook-type` options there should be a space between `--hook-type` options in generated `pre-commit` command string. --- commitizen/commands/init.py | 2 +- tests/commands/test_init_command.py | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/commitizen/commands/init.py b/commitizen/commands/init.py index b718f526ef..91b5bd4b70 100644 --- a/commitizen/commands/init.py +++ b/commitizen/commands/init.py @@ -140,7 +140,7 @@ def _gen_pre_commit_cmd(self, hook_types: List[str]) -> str: """Generate pre-commit command according to given hook types""" if not hook_types: raise ValueError("At least 1 hook type should be provided.") - cmd_str = "pre-commit install " + "".join( + cmd_str = "pre-commit install " + " ".join( f"--hook-type {ty}" for ty in hook_types ) return cmd_str diff --git a/tests/commands/test_init_command.py b/tests/commands/test_init_command.py index a28ee83544..43b03f112d 100644 --- a/tests/commands/test_init_command.py +++ b/tests/commands/test_init_command.py @@ -99,6 +99,12 @@ def test_init_without_choosing_tag(config, mocker: MockFixture, tmpdir): commands.Init(config)() +def test_executed_pre_commit_command(config): + init = commands.Init(config) + expected_cmd = "pre-commit install --hook-type commit-msg --hook-type pre-push" + assert init._gen_pre_commit_cmd(["commit-msg", "pre-push"]) == expected_cmd + + @pytest.fixture(scope="function") def pre_commit_installed(mocker: MockFixture): # Assume the `pre-commit` is installed