from __future__ import annotations import json import os from pathlib import Path from typing import Any import pytest import yaml from commitizen import config, defaults, git from commitizen.config.json_config import JsonConfig from commitizen.config.toml_config import TomlConfig from commitizen.config.yaml_config import YAMLConfig from commitizen.exceptions import ConfigFileIsEmpty, InvalidConfigurationError PYPROJECT = """ [tool.commitizen] name = "cz_jira" version = "1.0.0" version_files = [ "commitizen/__version__.py", "pyproject.toml" ] style = [ ["pointer", "reverse"], ["question", "underline"] ] pre_bump_hooks = [ "scripts/generate_documentation.sh" ] post_bump_hooks = ["scripts/slack_notification.sh"] [tool.black] line-length = 88 target-version = ['py36', 'py37', 'py38'] """ DICT_CONFIG = { "commitizen": { "name": "cz_jira", "version": "1.0.0", "version_files": ["commitizen/__version__.py", "pyproject.toml"], "style": [["pointer", "reverse"], ["question", "underline"]], "pre_bump_hooks": ["scripts/generate_documentation.sh"], "post_bump_hooks": ["scripts/slack_notification.sh"], } } JSON_STR = r""" { "commitizen": { "name": "cz_jira", "version": "1.0.0", "version_files": [ "commitizen/__version__.py", "pyproject.toml" ] } } """ YAML_STR = """ commitizen: name: cz_jira version: 1.0.0 version_files: - commitizen/__version__.py - pyproject.toml """ _settings: dict[str, Any] = { "name": "cz_jira", "version": "1.0.0", "version_provider": "commitizen", "version_scheme": None, "tag_format": "$version", "legacy_tag_formats": [], "ignored_tag_formats": [], "bump_message": None, "retry_after_failure": False, "allow_abort": False, "allowed_prefixes": [ "Merge", "Revert", "Pull request", "fixup!", "squash!", "amend!", ], "version_files": ["commitizen/__version__.py", "pyproject.toml"], "style": [["pointer", "reverse"], ["question", "underline"]], "changelog_file": "CHANGELOG.md", "changelog_format": None, "changelog_incremental": False, "changelog_start_rev": None, "changelog_merge_prerelease": False, "update_changelog_on_bump": False, "use_shortcuts": False, "major_version_zero": False, "pre_bump_hooks": ["scripts/generate_documentation.sh"], "post_bump_hooks": ["scripts/slack_notification.sh"], "prerelease_offset": 0, "encoding": "utf-8", "always_signoff": False, "template": None, "extras": {}, "breaking_change_exclamation_in_title": False, "message_length_limit": None, } _new_settings: dict[str, Any] = { "name": "cz_jira", "version": "2.0.0", "version_provider": "commitizen", "version_scheme": None, "tag_format": "$version", "legacy_tag_formats": [], "ignored_tag_formats": [], "bump_message": None, "retry_after_failure": False, "allow_abort": False, "allowed_prefixes": [ "Merge", "Revert", "Pull request", "fixup!", "squash!", "amend!", ], "version_files": ["commitizen/__version__.py", "pyproject.toml"], "style": [["pointer", "reverse"], ["question", "underline"]], "changelog_file": "CHANGELOG.md", "changelog_format": None, "changelog_incremental": False, "changelog_start_rev": None, "changelog_merge_prerelease": False, "update_changelog_on_bump": False, "use_shortcuts": False, "major_version_zero": False, "pre_bump_hooks": ["scripts/generate_documentation.sh"], "post_bump_hooks": ["scripts/slack_notification.sh"], "prerelease_offset": 0, "encoding": "utf-8", "always_signoff": False, "template": None, "extras": {}, "breaking_change_exclamation_in_title": False, "message_length_limit": None, } @pytest.fixture def config_files_manager(request, tmpdir): with tmpdir.as_cwd(): filename = request.param with open(filename, "w", encoding="utf-8") as f: if "toml" in filename: f.write(PYPROJECT) elif "json" in filename: json.dump(DICT_CONFIG, f) elif "yaml" in filename: yaml.dump(DICT_CONFIG, f) yield @pytest.mark.usefixtures("in_repo_root") def test_find_git_project_root(tmpdir): assert git.find_git_project_root() == Path(os.getcwd()) with tmpdir.as_cwd() as _: assert git.find_git_project_root() is None @pytest.mark.parametrize( "config_files_manager", defaults.CONFIG_FILES.copy(), indirect=True ) def test_set_key(config_files_manager): _conf = config.read_cfg() _conf.set_key("version", "2.0.0") cfg = config.read_cfg() assert cfg.settings == _new_settings class TestReadCfg: @pytest.mark.parametrize( "config_files_manager", defaults.CONFIG_FILES.copy(), indirect=True ) def test_load_conf(_, config_files_manager): cfg = config.read_cfg() assert cfg.settings == _settings def test_conf_returns_default_when_no_files(_, tmpdir): with tmpdir.as_cwd(): cfg = config.read_cfg() assert cfg.settings == defaults.DEFAULT_SETTINGS def test_load_empty_pyproject_toml_and_cz_toml_with_config(_, tmpdir): with tmpdir.as_cwd(): p = tmpdir.join("pyproject.toml") p.write("") p = tmpdir.join(".cz.toml") p.write(PYPROJECT) cfg = config.read_cfg() assert cfg.settings == _settings def test_load_pyproject_toml_from_config_argument(_, tmpdir): with tmpdir.as_cwd(): _not_root_path = tmpdir.mkdir("not_in_root").join("pyproject.toml") _not_root_path.write(PYPROJECT) cfg = config.read_cfg(filepath="./not_in_root/pyproject.toml") assert cfg.settings == _settings def test_load_cz_json_not_from_config_argument(_, tmpdir): with tmpdir.as_cwd(): _not_root_path = tmpdir.mkdir("not_in_root").join(".cz.json") _not_root_path.write(JSON_STR) cfg = config.read_cfg(filepath="./not_in_root/.cz.json") json_cfg_by_class = JsonConfig(data=JSON_STR, path=_not_root_path) assert cfg.settings == json_cfg_by_class.settings def test_load_cz_yaml_not_from_config_argument(_, tmpdir): with tmpdir.as_cwd(): _not_root_path = tmpdir.mkdir("not_in_root").join(".cz.yaml") _not_root_path.write(YAML_STR) cfg = config.read_cfg(filepath="./not_in_root/.cz.yaml") yaml_cfg_by_class = YAMLConfig(data=YAML_STR, path=_not_root_path) assert cfg.settings == yaml_cfg_by_class._settings def test_load_empty_pyproject_toml_from_config_argument(_, tmpdir): with tmpdir.as_cwd(): _not_root_path = tmpdir.mkdir("not_in_root").join("pyproject.toml") _not_root_path.write("") with pytest.raises(ConfigFileIsEmpty): config.read_cfg(filepath="./not_in_root/pyproject.toml") @pytest.mark.parametrize( "config_file, exception_string", [ (".cz.toml", r"\.cz\.toml"), ("cz.toml", r"cz\.toml"), ("pyproject.toml", r"pyproject\.toml"), ], ids=[".cz.toml", "cz.toml", "pyproject.toml"], ) class TestTomlConfig: def test_init_empty_config_content(self, tmpdir, config_file, exception_string): path = tmpdir.mkdir("commitizen").join(config_file) toml_config = TomlConfig(data="", path=path) toml_config.init_empty_config_content() with open(path, encoding="utf-8") as toml_file: assert toml_file.read() == "[tool.commitizen]\n" def test_init_empty_config_content_with_existing_content( self, tmpdir, config_file, exception_string ): existing_content = "[tool.black]\nline-length = 88\n" path = tmpdir.mkdir("commitizen").join(config_file) path.write(existing_content) toml_config = TomlConfig(data="", path=path) toml_config.init_empty_config_content() with open(path, encoding="utf-8") as toml_file: assert toml_file.read() == existing_content + "\n[tool.commitizen]\n" def test_init_with_invalid_config_content( self, tmpdir, config_file, exception_string ): existing_content = "invalid toml content" path = tmpdir.mkdir("commitizen").join(config_file) with pytest.raises(InvalidConfigurationError, match=exception_string): TomlConfig(data=existing_content, path=path) @pytest.mark.parametrize( "config_file, exception_string", [ (".cz.json", r"\.cz\.json"), ("cz.json", r"cz\.json"), ], ids=[".cz.json", "cz.json"], ) class TestJsonConfig: def test_init_empty_config_content(self, tmpdir, config_file, exception_string): path = tmpdir.mkdir("commitizen").join(config_file) json_config = JsonConfig(data="{}", path=path) json_config.init_empty_config_content() with open(path, encoding="utf-8") as json_file: assert json.load(json_file) == {"commitizen": {}} def test_init_with_invalid_config_content( self, tmpdir, config_file, exception_string ): existing_content = "invalid json content" path = tmpdir.mkdir("commitizen").join(config_file) with pytest.raises(InvalidConfigurationError, match=exception_string): JsonConfig(data=existing_content, path=path) @pytest.mark.parametrize( "config_file, exception_string", [ (".cz.yaml", r"\.cz\.yaml"), ("cz.yaml", r"cz\.yaml"), ], ids=[".cz.yaml", "cz.yaml"], ) class TestYamlConfig: def test_init_empty_config_content(self, tmpdir, config_file, exception_string): path = tmpdir.mkdir("commitizen").join(config_file) yaml_config = YAMLConfig(data="{}", path=path) yaml_config.init_empty_config_content() with open(path) as yaml_file: assert yaml.safe_load(yaml_file) == {"commitizen": {}} def test_init_with_invalid_content(self, tmpdir, config_file, exception_string): existing_content = "invalid: .cz.yaml: content: maybe?" path = tmpdir.mkdir("commitizen").join(config_file) with pytest.raises(InvalidConfigurationError, match=exception_string): YAMLConfig(data=existing_content, path=path)