|
1 | 1 | """Tests for `bumpversion` package."""
|
| 2 | +import shutil |
| 3 | +import subprocess |
| 4 | +from pathlib import Path |
2 | 5 |
|
| 6 | +import pytest |
3 | 7 | from click.testing import CliRunner, Result
|
| 8 | +from pytest import param |
4 | 9 |
|
5 | 10 | from bumpversion import __version__, cli
|
6 | 11 | from tests.conftest import inside_dir
|
7 | 12 |
|
8 |
| -# To learn more about testing Click applications, visit the link below. |
9 |
| -# https://click.palletsprojects.com/en/8.1.x/testing/ |
10 |
| - |
11 | 13 |
|
12 | 14 | def test_version_displays_library_version():
|
13 | 15 | """
|
@@ -56,3 +58,154 @@ def test_no_configured_files_still_file_args_work(mocker, tmp_path):
|
56 | 58 | call_args = mocked_do_bump.call_args[0]
|
57 | 59 | assert len(call_args[2].files) == 1
|
58 | 60 | assert call_args[2].files[0].filename == "do-this-file.txt"
|
| 61 | + |
| 62 | + |
| 63 | +def test_missing_explicit_config_file(tmp_path: Path): |
| 64 | + """The command-line processor should raise an exception if the config file is missing.""" |
| 65 | + with inside_dir(tmp_path): |
| 66 | + runner: CliRunner = CliRunner() |
| 67 | + with inside_dir(tmp_path): |
| 68 | + result: Result = runner.invoke(cli.cli, ["--config-file", "missing-file.cfg"]) |
| 69 | + assert result.exit_code != 0 |
| 70 | + assert "'missing-file.cfg' does not exist." in result.output |
| 71 | + |
| 72 | + |
| 73 | +def test_cli_options_override_config(tmp_path: Path, fixtures_path: Path, mocker): |
| 74 | + """The command-line processor should override the config file.""" |
| 75 | + # Arrange |
| 76 | + config_path = tmp_path / "this_config.toml" |
| 77 | + fixture_toml = fixtures_path / "basic_cfg.toml" |
| 78 | + shutil.copy(fixture_toml, config_path) |
| 79 | + runner: CliRunner = CliRunner() |
| 80 | + mocked_do_bump = mocker.patch("bumpversion.cli.do_bump") |
| 81 | + |
| 82 | + # Act |
| 83 | + with inside_dir(tmp_path): |
| 84 | + result: Result = runner.invoke( |
| 85 | + cli.cli, |
| 86 | + [ |
| 87 | + "--config-file", |
| 88 | + str(config_path), |
| 89 | + "--current-version", |
| 90 | + "1.1.0", |
| 91 | + "--new-version", |
| 92 | + "1.2.0", |
| 93 | + "--allow-dirty", |
| 94 | + "--parse", |
| 95 | + r"XXX(?P<spam>\d+);(?P<blob>\d+);(?P<slurp>\d+)", |
| 96 | + "--serialize", |
| 97 | + "XXX{spam};{blob};{slurp}", |
| 98 | + "--search", |
| 99 | + "my-search", |
| 100 | + "--replace", |
| 101 | + "my-replace", |
| 102 | + "--no-commit", |
| 103 | + "--no-tag", |
| 104 | + "patch", |
| 105 | + "do-this-file.txt", |
| 106 | + ], |
| 107 | + ) |
| 108 | + |
| 109 | + # Assert |
| 110 | + assert result.exit_code == 0 |
| 111 | + assert mocked_do_bump.call_count == 1 |
| 112 | + assert mocked_do_bump.call_args[0][0] == "patch" |
| 113 | + assert mocked_do_bump.call_args[0][1] == "1.2.0" |
| 114 | + the_config = mocked_do_bump.call_args[0][2] |
| 115 | + assert the_config.current_version == "1.1.0" |
| 116 | + assert the_config.allow_dirty |
| 117 | + assert the_config.parse == r"XXX(?P<spam>\d+);(?P<blob>\d+);(?P<slurp>\d+)" |
| 118 | + assert the_config.serialize == ["XXX{spam};{blob};{slurp}"] |
| 119 | + assert the_config.search == "my-search" |
| 120 | + assert the_config.replace == "my-replace" |
| 121 | + assert the_config.commit is False |
| 122 | + assert the_config.tag is False |
| 123 | + assert len(the_config.files) == 4 |
| 124 | + assert {f.filename for f in the_config.files} == { |
| 125 | + "setup.py", |
| 126 | + "bumpversion/__init__.py", |
| 127 | + "CHANGELOG.md", |
| 128 | + "do-this-file.txt", |
| 129 | + } |
| 130 | + assert mocked_do_bump.call_args[0][3] == config_path |
| 131 | + |
| 132 | + |
| 133 | +@pytest.mark.parametrize( |
| 134 | + ["repo", "scm_command"], |
| 135 | + [ |
| 136 | + param("git_repo", "git", id="git"), |
| 137 | + param("hg_repo", "hg", id="hg"), |
| 138 | + ], |
| 139 | +) |
| 140 | +def test_dirty_work_dir_raises_error(repo: str, scm_command: str, request): |
| 141 | + repo_path: Path = request.getfixturevalue(repo) |
| 142 | + with inside_dir(repo_path): |
| 143 | + # Arrange |
| 144 | + repo_path.joinpath("dirty2").write_text("i'm dirty! 1.1.1") |
| 145 | + subprocess.run([scm_command, "add", "dirty2"], check=True) |
| 146 | + runner: CliRunner = CliRunner() |
| 147 | + |
| 148 | + # Act |
| 149 | + result: Result = runner.invoke(cli.cli, ["patch", "--current-version", "1.1.1", "--no-allow-dirty", "dirty2"]) |
| 150 | + |
| 151 | + # Assert |
| 152 | + assert result.exit_code != 0 |
| 153 | + assert "working directory is not clean" in result.output |
| 154 | + |
| 155 | + |
| 156 | +def test_listing_outputs_correctly_and_stops(tmp_path: Path, fixtures_path: Path): |
| 157 | + """The --list option should list the configuration and nothing else.""" |
| 158 | + # Arrange |
| 159 | + config_path = tmp_path / "pyproject.toml" |
| 160 | + toml_path = fixtures_path / "basic_cfg.toml" |
| 161 | + shutil.copy(toml_path, config_path) |
| 162 | + runner: CliRunner = CliRunner() |
| 163 | + |
| 164 | + with inside_dir(tmp_path): |
| 165 | + result: Result = runner.invoke(cli.cli, ["--list", "patch"]) |
| 166 | + |
| 167 | + if result.exit_code != 0: |
| 168 | + print(result.output) |
| 169 | + print(result.exception) |
| 170 | + |
| 171 | + assert result.exit_code == 0 |
| 172 | + assert set(result.output.splitlines(keepends=False)) == { |
| 173 | + "new_version=1.0.1-dev", |
| 174 | + "current_version=1.0.0", |
| 175 | + "parse=(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)(\\-(?P<release>[a-z]+))?", |
| 176 | + "serialize=['{major}.{minor}.{patch}-{release}', '{major}.{minor}.{patch}']", |
| 177 | + "search={current_version}", |
| 178 | + "replace={new_version}", |
| 179 | + "tag=True", |
| 180 | + "sign_tags=False", |
| 181 | + "tag_name=v{new_version}", |
| 182 | + "tag_message=Bump version: {current_version} → {new_version}", |
| 183 | + "allow_dirty=False", |
| 184 | + "commit=True", |
| 185 | + "message=Bump version: {current_version} → {new_version}", |
| 186 | + "commit_args=None", |
| 187 | + "files=[{'filename': 'setup.py', 'glob': None, 'parse': None, 'serialize': " |
| 188 | + "None, 'search': None, 'replace': None}, {'filename': " |
| 189 | + "'bumpversion/__init__.py', 'glob': None, 'parse': None, 'serialize': None, " |
| 190 | + "'search': None, 'replace': None}, {'filename': 'CHANGELOG.md', 'glob': None, " |
| 191 | + "'parse': None, 'serialize': None, 'search': '**unreleased**', 'replace': " |
| 192 | + "'**unreleased**\\n**v{new_version}**'}]", |
| 193 | + } |
| 194 | + |
| 195 | + |
| 196 | +def test_non_scm_operations_if_scm_not_installed(tmp_path: Path, monkeypatch): |
| 197 | + """Everything works except SCM commands if the SCM is unusable.""" |
| 198 | + # Arrange |
| 199 | + monkeypatch.setenv("PATH", "") |
| 200 | + |
| 201 | + with inside_dir(tmp_path): |
| 202 | + version_path = tmp_path / "VERSION" |
| 203 | + version_path.write_text("31.0.3") |
| 204 | + |
| 205 | + runner: CliRunner = CliRunner() |
| 206 | + |
| 207 | + # Act |
| 208 | + runner.invoke(cli.cli, ["major", "--current-version", "31.0.3", "VERSION"]) |
| 209 | + |
| 210 | + # Assert |
| 211 | + assert version_path.read_text() == "32.0.0" |
0 commit comments