mirror of
https://github.com/kuhyx/utils.git
synced 2026-11-10 21:07:15 +01:00
115 lines
3.6 KiB
Python
115 lines
3.6 KiB
Python
"""dep_freshness_changed.sh: the pre-commit hook only gates manifests this branch changed.
|
|
|
|
Each test builds a throwaway git repo with a `main` branch and points the
|
|
wrapper at a stub gate (DEP_FRESHNESS_GATE) that records its arguments, so
|
|
nothing touches the network and the only thing under test is the filtering.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import subprocess
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
SCRIPT = Path(__file__).resolve().parent.parent / "dep_freshness_changed.sh"
|
|
|
|
STUB = """\
|
|
#!/bin/bash
|
|
printf '%s\\n' "$@" > "$STUB_LOG"
|
|
exit "${STUB_EXIT:-0}"
|
|
"""
|
|
|
|
|
|
def _git(repo: Path, *args: str) -> None:
|
|
subprocess.run(["git", *args], cwd=repo, check=True, capture_output=True)
|
|
|
|
|
|
@pytest.fixture
|
|
def repo(tmp_path: Path) -> Path:
|
|
"""Repo on `main` with two committed manifests, then a feature branch."""
|
|
root = tmp_path / "repo"
|
|
root.mkdir()
|
|
_git(root, "init", "-q", "-b", "main")
|
|
_git(root, "config", "user.email", "t@example.invalid")
|
|
_git(root, "config", "user.name", "t")
|
|
for sub in ("touched", "untouched"):
|
|
(root / sub).mkdir()
|
|
(root / sub / "requirements.txt").write_text("ruff==0.1.0\n")
|
|
_git(root, "add", ".")
|
|
_git(root, "commit", "-q", "-m", "init")
|
|
_git(root, "checkout", "-q", "-b", "feature")
|
|
return root
|
|
|
|
|
|
def _run(
|
|
repo: Path, tmp_path: Path, *files: str, **env: str
|
|
) -> tuple[int, list[str] | None]:
|
|
stub = tmp_path / "stub.sh"
|
|
stub.write_text(STUB)
|
|
stub.chmod(0o755)
|
|
log = tmp_path / "stub.log"
|
|
log.unlink(missing_ok=True)
|
|
full_env = {
|
|
**os.environ,
|
|
"DEP_FRESHNESS_GATE": str(stub),
|
|
"STUB_LOG": str(log),
|
|
**env,
|
|
}
|
|
proc = subprocess.run(
|
|
["bash", str(SCRIPT), *files],
|
|
check=False,
|
|
cwd=repo,
|
|
env=full_env,
|
|
capture_output=True,
|
|
text=True,
|
|
)
|
|
forwarded = log.read_text().split() if log.exists() else None
|
|
return proc.returncode, forwarded
|
|
|
|
|
|
ALL = ("touched/requirements.txt", "untouched/requirements.txt")
|
|
|
|
|
|
def test_forwards_only_uncommitted_change(repo: Path, tmp_path: Path) -> None:
|
|
(repo / "touched" / "requirements.txt").write_text("ruff==0.2.0\n")
|
|
assert _run(repo, tmp_path, *ALL) == (0, ["touched/requirements.txt"])
|
|
|
|
|
|
def test_forwards_committed_branch_change(repo: Path, tmp_path: Path) -> None:
|
|
(repo / "touched" / "requirements.txt").write_text("ruff==0.2.0\n")
|
|
_git(repo, "commit", "-q", "-am", "bump")
|
|
assert _run(repo, tmp_path, *ALL) == (0, ["touched/requirements.txt"])
|
|
|
|
|
|
def test_forwards_untracked_manifest(repo: Path, tmp_path: Path) -> None:
|
|
(repo / "new").mkdir()
|
|
(repo / "new" / "requirements.txt").write_text("ruff==0.1.0\n")
|
|
assert _run(repo, tmp_path, *ALL, "new/requirements.txt") == (
|
|
0,
|
|
["new/requirements.txt"],
|
|
)
|
|
|
|
|
|
def test_no_branch_change_skips_gate(repo: Path, tmp_path: Path) -> None:
|
|
assert _run(repo, tmp_path, *ALL) == (0, None)
|
|
|
|
|
|
def test_gate_failure_propagates(repo: Path, tmp_path: Path) -> None:
|
|
(repo / "touched" / "requirements.txt").write_text("ruff==0.2.0\n")
|
|
assert _run(repo, tmp_path, *ALL, STUB_EXIT="1") == (
|
|
1,
|
|
["touched/requirements.txt"],
|
|
)
|
|
|
|
|
|
def test_unresolvable_base_fails_closed(repo: Path, tmp_path: Path) -> None:
|
|
code, forwarded = _run(repo, tmp_path, *ALL, DEP_FRESHNESS_BASE="no-such-ref")
|
|
assert (code, forwarded) == (0, list(ALL))
|
|
|
|
|
|
def test_subdirectory_cwd_fails_closed(repo: Path, tmp_path: Path) -> None:
|
|
code, forwarded = _run(repo / "touched", tmp_path, "requirements.txt")
|
|
assert (code, forwarded) == (0, ["requirements.txt"])
|