1
0
Fork 0
commitizen/tests/commands/test_init_command.py
Daniel Baumann 167a3f8553
Adding upstream version 4.6.0+dfsg.
Signed-off-by: Daniel Baumann <daniel@debian.org>
2025-04-21 11:40:48 +02:00

268 lines
8.4 KiB
Python

from __future__ import annotations
import json
import os
import sys
from typing import Any
import pytest
import yaml
from pytest_mock import MockFixture
from commitizen import cli, commands
from commitizen.__version__ import __version__
from commitizen.exceptions import InitFailedError, NoAnswersError
from tests.utils import skip_below_py_3_10
class FakeQuestion:
def __init__(self, expected_return):
self.expected_return = expected_return
def ask(self):
return self.expected_return
def unsafe_ask(self):
return self.expected_return
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"},
{"id": "commitizen-branch", "stages": ["push"]},
],
}
expected_config = (
"[tool.commitizen]\n"
'name = "cz_conventional_commits"\n'
'tag_format = "$version"\n'
'version_scheme = "semver"\n'
'version = "0.0.1"\n'
"update_changelog_on_bump = true\n"
"major_version_zero = true\n"
)
EXPECTED_DICT_CONFIG = {
"commitizen": {
"name": "cz_conventional_commits",
"tag_format": "$version",
"version_scheme": "semver",
"version": "0.0.1",
"update_changelog_on_bump": True,
"major_version_zero": True,
}
}
def test_init_without_setup_pre_commit_hook(tmpdir, mocker: MockFixture, config):
mocker.patch(
"questionary.select",
side_effect=[
FakeQuestion("pyproject.toml"),
FakeQuestion("cz_conventional_commits"),
FakeQuestion("commitizen"),
FakeQuestion("semver"),
],
)
mocker.patch("questionary.confirm", return_value=FakeQuestion(True))
mocker.patch("questionary.text", return_value=FakeQuestion("$version"))
# Return None to skip hook installation
mocker.patch("questionary.checkbox", return_value=FakeQuestion(None))
with tmpdir.as_cwd():
commands.Init(config)()
with open("pyproject.toml", encoding="utf-8") as toml_file:
config_data = toml_file.read()
assert config_data == expected_config
assert not os.path.isfile(pre_commit_config_filename)
def test_init_when_config_already_exists(config, capsys):
# Set config path
path = os.sep.join(["tests", "pyproject.toml"])
config.add_path(path)
commands.Init(config)()
captured = capsys.readouterr()
assert captured.out == f"Config file {path} already exists\n"
def test_init_without_choosing_tag(config, mocker: MockFixture, tmpdir):
mocker.patch(
"commitizen.commands.init.get_tag_names", return_value=["0.0.2", "0.0.1"]
)
mocker.patch("commitizen.commands.init.get_latest_tag_name", return_value="0.0.2")
mocker.patch(
"questionary.select",
side_effect=[
FakeQuestion("pyproject.toml"),
FakeQuestion("cz_conventional_commits"),
FakeQuestion("commitizen"),
FakeQuestion(""),
],
)
mocker.patch("questionary.confirm", return_value=FakeQuestion(False))
mocker.patch("questionary.text", return_value=FakeQuestion("y"))
with tmpdir.as_cwd():
with pytest.raises(NoAnswersError):
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
mocker.patch(
"commitizen.commands.init.ProjectInfo.is_pre_commit_installed",
return_value=True,
)
# And installation success (i.e. no exception raised)
mocker.patch(
"commitizen.commands.init.Init._exec_install_pre_commit_hook",
return_value=None,
)
@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"),
FakeQuestion("commitizen"),
FakeQuestion("semver"),
],
)
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):
"""
Check the content of commitizen config is as expected
Args:
config: The config path
"""
with open(config) 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) 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)()
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():
p = tmpdir.join(pre_commit_config_filename)
p.write("")
commands.Init(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 = {
"repo": "https://github.com/pre-commit/pre-commit-hooks",
"rev": "v1.2.3",
"hooks": [{"id", "trailing-whitespace"}],
}
with tmpdir.as_cwd():
p = tmpdir.join(pre_commit_config_filename)
p.write(yaml.safe_dump({"repos": [existing_hook_config]}))
commands.Init(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():
p = tmpdir.join(pre_commit_config_filename)
p.write(yaml.safe_dump({"repos": [cz_hook_config]}))
commands.Init(config)()
check_cz_config(default_choice)
# check that config is not duplicated
check_pre_commit_config([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.ProjectInfo.is_pre_commit_installed",
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.ProjectInfo.is_pre_commit_installed",
return_value=True,
)
# But pre-commit installation will fail
mocker.patch(
"commitizen.commands.init.Init._exec_install_pre_commit_hook",
side_effect=InitFailedError("Mock init failed error."),
)
with tmpdir.as_cwd():
with pytest.raises(InitFailedError):
commands.Init(config)()
@skip_below_py_3_10
def test_init_command_shows_description_when_use_help_option(
mocker: MockFixture, capsys, file_regression
):
testargs = ["cz", "init", "--help"]
mocker.patch.object(sys, "argv", testargs)
with pytest.raises(SystemExit):
cli.main()
out, _ = capsys.readouterr()
file_regression.check(out, extension=".txt")