✅ Test the CLI
This commit is contained in:
parent
8cb0fe4b19
commit
8b12b056e4
3 changed files with 240 additions and 0 deletions
|
|
@ -12,5 +12,10 @@ dependencies = [
|
|||
[tool.uv]
|
||||
package = true
|
||||
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
"pytest>=9.1.1",
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
git-annex-duplicates = "git_annex_duplicates:main"
|
||||
|
|
|
|||
166
test/test_cli.py
Normal file
166
test/test_cli.py
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
import subprocess
|
||||
from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
from click.testing import CliRunner
|
||||
|
||||
import git_annex_duplicates
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def invoke_cli():
|
||||
runner = CliRunner()
|
||||
|
||||
def invoke(args=None):
|
||||
return runner.invoke(
|
||||
git_annex_duplicates.main,
|
||||
args,
|
||||
prog_name="git-annex-duplicates",
|
||||
)
|
||||
|
||||
return invoke
|
||||
|
||||
|
||||
def mock_git_annex(monkeypatch, output):
|
||||
run = Mock(
|
||||
return_value=subprocess.CompletedProcess(
|
||||
args=[],
|
||||
returncode=0,
|
||||
stdout=output.encode(),
|
||||
stderr=b"",
|
||||
)
|
||||
)
|
||||
monkeypatch.setattr(git_annex_duplicates.subprocess, "run", run)
|
||||
return run
|
||||
|
||||
|
||||
def test_help_lists_cli_options(invoke_cli):
|
||||
result = invoke_cli(["--help"])
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert "Usage: git-annex-duplicates [OPTIONS] [DIRECTORIES]..." in result.output
|
||||
assert "--exclude TEXT" in result.output
|
||||
assert "--across" in result.output
|
||||
|
||||
|
||||
def test_default_directory_and_duplicate_output(invoke_cli, monkeypatch, tmp_path):
|
||||
monkeypatch.chdir(tmp_path)
|
||||
run = mock_git_annex(
|
||||
monkeypatch,
|
||||
"key-a:photos/first.jpg\n"
|
||||
"key-b:only-once.txt\n"
|
||||
"key-a:backup/first.jpg\n"
|
||||
"key-c:one.txt\n"
|
||||
"key-c:two.txt\n",
|
||||
)
|
||||
|
||||
result = invoke_cli()
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert result.output == (
|
||||
"photos/first.jpg\n"
|
||||
"backup/first.jpg\n"
|
||||
"\n"
|
||||
"one.txt\n"
|
||||
"two.txt\n"
|
||||
"\n"
|
||||
)
|
||||
run.assert_called_once_with(
|
||||
[
|
||||
"git",
|
||||
"annex",
|
||||
"find",
|
||||
"--include",
|
||||
"*",
|
||||
"--format=${key}:${file}\n",
|
||||
tmp_path,
|
||||
],
|
||||
capture_output=True,
|
||||
check=True,
|
||||
)
|
||||
|
||||
|
||||
def test_directories_and_repeated_excludes_are_passed_to_git_annex(
|
||||
invoke_cli, monkeypatch, tmp_path
|
||||
):
|
||||
first = tmp_path / "first"
|
||||
second = tmp_path / "second"
|
||||
first.mkdir()
|
||||
second.mkdir()
|
||||
run = mock_git_annex(monkeypatch, "")
|
||||
|
||||
result = invoke_cli(
|
||||
[
|
||||
"--exclude",
|
||||
"*.iso",
|
||||
"--exclude",
|
||||
"archive/**",
|
||||
str(first),
|
||||
str(second),
|
||||
],
|
||||
)
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert result.output == ""
|
||||
run.assert_called_once_with(
|
||||
[
|
||||
"git",
|
||||
"annex",
|
||||
"find",
|
||||
"--include",
|
||||
"*",
|
||||
"--format=${key}:${file}\n",
|
||||
first,
|
||||
second,
|
||||
"--exclude",
|
||||
"*.iso",
|
||||
"--exclude",
|
||||
"archive/**",
|
||||
],
|
||||
capture_output=True,
|
||||
check=True,
|
||||
)
|
||||
|
||||
|
||||
def test_across_only_reports_duplicates_in_different_directories(
|
||||
invoke_cli, monkeypatch, tmp_path
|
||||
):
|
||||
first = tmp_path / "first"
|
||||
second = tmp_path / "second"
|
||||
first.mkdir()
|
||||
second.mkdir()
|
||||
monkeypatch.chdir(tmp_path)
|
||||
mock_git_annex(
|
||||
monkeypatch,
|
||||
"same-first:first/one.txt\n"
|
||||
"same-first:first/two.txt\n"
|
||||
"across:first/shared.txt\n"
|
||||
"across:second/shared.txt\n"
|
||||
"same-second:second/one.txt\n"
|
||||
"same-second:second/two.txt\n",
|
||||
)
|
||||
|
||||
result = invoke_cli(["--across", str(first), str(second)])
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert result.output == "first/shared.txt\nsecond/shared.txt\n\n"
|
||||
|
||||
|
||||
def test_across_requires_two_directories(invoke_cli, tmp_path):
|
||||
directory = tmp_path / "only"
|
||||
directory.mkdir()
|
||||
|
||||
result = invoke_cli(["--across", str(directory)])
|
||||
|
||||
assert result.exit_code == 2
|
||||
assert "Error: --across requires at least two directories" in result.output
|
||||
|
||||
|
||||
def test_rejects_a_missing_directory(invoke_cli, tmp_path):
|
||||
missing = tmp_path / "missing"
|
||||
|
||||
result = invoke_cli([str(missing)])
|
||||
|
||||
assert result.exit_code == 2
|
||||
assert "Directory" in result.output
|
||||
assert "does not exist" in result.output
|
||||
69
uv.lock
generated
69
uv.lock
generated
|
|
@ -11,6 +11,15 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/58/50/6c0d534c5f134586a8e1ba4e330569e32f057e33372ae556463212fb4cd3/click-8.5.0-py3-none-any.whl", hash = "sha256:255bc9599cf7748b4b1a446ccc735421bd08a2ae529a8b88597d3de5664ee360", size = 125251, upload-time = "2026-08-26T13:33:12.928Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colorama"
|
||||
version = "0.4.6"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "git-annex-duplicates"
|
||||
version = "0.1.0"
|
||||
|
|
@ -19,5 +28,65 @@ dependencies = [
|
|||
{ name = "click" },
|
||||
]
|
||||
|
||||
[package.dev-dependencies]
|
||||
dev = [
|
||||
{ name = "pytest" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [{ name = "click", specifier = ">=8.5.0" }]
|
||||
|
||||
[package.metadata.requires-dev]
|
||||
dev = [{ name = "pytest", specifier = ">=9.1.1" }]
|
||||
|
||||
[[package]]
|
||||
name = "iniconfig"
|
||||
version = "2.3.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "26.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/7d/fa/3944b40b07da9ce895c0e6303a5ab7d53da063554f534556b134a54d6093/packaging-26.3.tar.gz", hash = "sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79", size = 313412, upload-time = "2026-08-04T18:15:28.737Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl", hash = "sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c", size = 129956, upload-time = "2026-08-04T18:15:27.159Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pluggy"
|
||||
version = "1.6.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pygments"
|
||||
version = "2.21.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/49/2e/ced460408999b33da6b31b0021b0f37d329e202d4169aeb164493778f25b/pygments-2.21.0.tar.gz", hash = "sha256:610ca751c9bc2492b38eb9a38a7fbc93edbbb2d7182edaf34e66ae493dee5c8c", size = 5005329, upload-time = "2026-08-17T08:02:48.824Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/71/46/17f022dd3e953bf20a04a028a21ec746d942f8d2af30fa0f124fa0e6a684/pygments-2.21.0-py3-none-any.whl", hash = "sha256:2363c69b61c4a97c838da3b130dcd6468f4848992b21a82f2a63ec34377137d9", size = 1250147, upload-time = "2026-08-17T08:02:44.912Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pytest"
|
||||
version = "9.1.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
||||
{ name = "iniconfig" },
|
||||
{ name = "packaging" },
|
||||
{ name = "pluggy" },
|
||||
{ name = "pygments" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e4/47/b9efed96c114afcfa3c9d3fe98a76a1d14c74a9e266d397cf6eb64be5e01/pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313", size = 1636369, upload-time = "2026-06-19T10:58:32.857Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c", size = 386536, upload-time = "2026-06-19T10:58:31.347Z" },
|
||||
]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue