Files
tzst/tests/cli/test_cli_coverage_gaps.py

247 lines
8.4 KiB
Python

"""Targeted tests for remaining uncovered CLI branches."""
import json
import runpy
import sys
from pathlib import Path
from types import SimpleNamespace
import pytest
from tzst import create_archive
from tzst.cli import (
_interactive_conflict_callback,
_is_extreme_compression_level_in_argv,
_normalize_archive_path,
_validate_command_in_argv,
_validate_compression_level_in_argv,
_validate_filter_in_argv,
cmd_extract_flat,
cmd_extract_full,
cmd_version,
main,
)
class TestCliCoverageGaps:
"""Exercise the remaining CLI coverage hotspots."""
def test_normalize_archive_path_handles_tar_and_custom_extensions(self):
assert _normalize_archive_path(Path("bundle.tar")) == Path("bundle.tar.zst")
assert _normalize_archive_path(Path("bundle.backup")) == Path(
"bundle.backup.tzst"
)
@pytest.mark.parametrize(
("choice", "expected"),
[
("A", "replace_all"),
("S", "skip_all"),
("U", "auto_rename_all"),
],
)
def test_interactive_conflict_callback_covers_remaining_choices(
self, monkeypatch, temp_dir, choice, expected
):
monkeypatch.setattr("builtins.input", lambda _prompt: choice)
result = _interactive_conflict_callback(temp_dir / "existing.txt")
assert result.value == expected
def test_extract_full_rejects_json_interactive_conflicts(self, temp_dir):
archive_path = temp_dir / "archive.tzst"
source_file = temp_dir / "source.txt"
source_file.write_text("content")
create_archive(archive_path, [source_file], use_temp_file=False)
args = SimpleNamespace(
archive=str(archive_path),
output=None,
files=[],
streaming=False,
filter="data",
conflict_resolution="replace",
interactive=True,
json_output=True,
no_banner=True,
)
assert cmd_extract_full(args) == 1
def test_extract_full_json_success_payload(self, temp_dir, capsys):
archive_path = temp_dir / "archive.tzst"
source_file = temp_dir / "source.txt"
source_file.write_text("content")
create_archive(archive_path, [source_file], use_temp_file=False)
output_dir = temp_dir / "extract"
result = main(
[
"--json",
"x",
str(archive_path),
"-o",
str(output_dir),
"--conflict-resolution",
"replace",
]
)
assert result == 0
payload = json.loads(capsys.readouterr().out)
assert payload["command"] == "extract"
assert payload["flatten"] is False
def test_extract_full_handles_file_not_found_from_backend(
self, temp_dir, monkeypatch
):
archive_path = temp_dir / "archive.tzst"
source_file = temp_dir / "source.txt"
source_file.write_text("content")
create_archive(archive_path, [source_file], use_temp_file=False)
def fail_extract(*args, **kwargs):
raise FileNotFoundError("backend missing file")
monkeypatch.setattr("tzst.cli.extract_archive", fail_extract)
assert main(["x", str(archive_path)]) == 1
def test_extract_flat_rejects_json_interactive_conflicts(self, temp_dir):
archive_path = temp_dir / "archive.tzst"
source_file = temp_dir / "source.txt"
source_file.write_text("content")
create_archive(archive_path, [source_file], use_temp_file=False)
args = SimpleNamespace(
archive=str(archive_path),
output=None,
files=[],
streaming=False,
filter="data",
conflict_resolution="replace",
interactive=True,
json_output=True,
no_banner=True,
)
assert cmd_extract_flat(args) == 1
def test_extract_flat_json_success_payload(self, temp_dir, capsys):
archive_path = temp_dir / "archive.tzst"
source_file = temp_dir / "source.txt"
source_file.write_text("content")
create_archive(archive_path, [source_file], use_temp_file=False)
output_dir = temp_dir / "extract"
result = main(
[
"--json",
"e",
str(archive_path),
"-o",
str(output_dir),
"--conflict-resolution",
"replace",
]
)
assert result == 0
payload = json.loads(capsys.readouterr().out)
assert payload["command"] == "extract-flat"
assert payload["flatten"] is True
def test_extract_flat_handles_file_not_found_from_backend(
self, temp_dir, monkeypatch
):
archive_path = temp_dir / "archive.tzst"
source_file = temp_dir / "source.txt"
source_file.write_text("content")
create_archive(archive_path, [source_file], use_temp_file=False)
def fail_extract(*args, **kwargs):
raise FileNotFoundError("backend missing file")
monkeypatch.setattr("tzst.cli.extract_archive", fail_extract)
assert main(["e", str(archive_path)]) == 1
def test_list_handles_file_not_found_from_backend(self, temp_dir, monkeypatch):
archive_path = temp_dir / "archive.tzst"
source_file = temp_dir / "source.txt"
source_file.write_text("content")
create_archive(archive_path, [source_file], use_temp_file=False)
def fail_list(*args, **kwargs):
raise FileNotFoundError("backend missing file")
monkeypatch.setattr("tzst.cli.list_archive", fail_list)
assert main(["l", str(archive_path)]) == 1
def test_test_command_json_success_and_file_not_found_backend(
self, temp_dir, capsys, monkeypatch
):
archive_path = temp_dir / "archive.tzst"
source_file = temp_dir / "source.txt"
source_file.write_text("content")
create_archive(archive_path, [source_file], use_temp_file=False)
result = main(["--json", "t", str(archive_path)])
assert result == 0
payload = json.loads(capsys.readouterr().out)
assert payload["command"] == "test"
assert payload["healthy"] is True
def fail_test(*args, **kwargs):
raise FileNotFoundError("backend missing file")
monkeypatch.setattr("tzst.cli.test_archive", fail_test)
assert main(["t", str(archive_path)]) == 1
def test_cmd_version_prints_when_no_banner_requested(self, capsys):
args = SimpleNamespace(json_output=False, no_banner=True)
assert cmd_version(args) == 0
assert capsys.readouterr().out.startswith("tzst ")
def test_validate_compression_level_in_argv_covers_short_flag_and_index_error(
self, capsys
):
assert _validate_compression_level_in_argv(["-c", "0"]) is True
assert "Invalid compression level: 0" in capsys.readouterr().err
assert _validate_compression_level_in_argv(["-c"]) is False
def test_validate_filter_in_argv_handles_missing_value(self):
assert _validate_filter_in_argv(["--filter"]) is False
def test_validate_command_in_argv_handles_empty_and_invalid_input(self, capsys):
assert _validate_command_in_argv([]) is False
assert _validate_command_in_argv(["bogus"]) is True
assert "Invalid command: 'bogus'" in capsys.readouterr().err
def test_is_extreme_compression_level_in_argv_covers_all_remaining_paths(self):
assert _is_extreme_compression_level_in_argv([]) is False
assert _is_extreme_compression_level_in_argv(["-c", "1000"]) is True
assert _is_extreme_compression_level_in_argv(["-l", "abc"]) is False
assert _is_extreme_compression_level_in_argv(["--level"]) is False
def test_cli_module_main_guard_executes(self, monkeypatch):
monkeypatch.delitem(sys.modules, "tzst.cli", raising=False)
monkeypatch.setattr(sys, "argv", ["tzst", "--version"])
with pytest.raises(SystemExit) as exc_info:
runpy.run_module("tzst.cli", run_name="__main__")
assert exc_info.value.code == 0
def test_package_main_module_executes(self, monkeypatch):
monkeypatch.delitem(sys.modules, "tzst.__main__", raising=False)
monkeypatch.setattr(sys, "argv", ["python", "--version"])
with pytest.raises(SystemExit) as exc_info:
runpy.run_module("tzst.__main__", run_name="__main__")
assert exc_info.value.code == 0