Skip to content

Commit 87c4c7a

Browse files
committed
test: Add golden test infrastructure
Golden test fixtures for the new dependabot-grpc-fixer.py script will be stored in tests/cookiecutter/scripts/fixtures/fixer_cases with one sub-directory per test case, where each test case is composed of the expected inputs and outputs. Signed-off-by: Leandro Lucarella <luca-frequenz@llucax.com>
1 parent 3f0771b commit 87c4c7a

1 file changed

Lines changed: 114 additions & 0 deletions

File tree

tests/cookiecutter/scripts/test_dependabot-grpc-fixer.py

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import re
66
import shutil
77
import sys
8+
from dataclasses import dataclass
89
from pathlib import Path
910
from typing import Protocol, cast
1011

@@ -43,6 +44,119 @@ def _load_fixer() -> FixerModule:
4344

4445
fixer = _load_fixer()
4546

47+
CASE_ROOT = Path(__file__).with_name("fixtures") / "dependabot-grpc-fixer"
48+
49+
50+
@dataclass(frozen=True)
51+
class Case:
52+
"""A single file-based test case."""
53+
54+
path: Path
55+
stdout_exact: str | None
56+
stdout_contains: str | None
57+
stderr_exact: str | None
58+
stderr_contains: str | None
59+
60+
@property
61+
def expected_pyproject(self) -> Path:
62+
"""Return the expected `pyproject.toml` path for the case."""
63+
return self.path / "expected" / "pyproject.toml"
64+
65+
66+
def _case_dirs() -> list[Path]:
67+
"""Return all fixture case directories."""
68+
case_dirs = sorted(path for path in CASE_ROOT.iterdir() if path.is_dir())
69+
if not case_dirs:
70+
raise AssertionError(f"no fixture cases found in {CASE_ROOT}")
71+
return case_dirs
72+
73+
74+
def _read_optional(path: Path) -> str | None:
75+
"""Read a file if it exists."""
76+
if path.exists():
77+
return path.read_text(encoding="utf-8")
78+
return None
79+
80+
81+
def _read_contains(path: Path) -> str | None:
82+
"""Read a partial-match expectation if it exists."""
83+
if path.exists():
84+
return path.read_text(encoding="utf-8").rstrip("\n")
85+
return None
86+
87+
88+
def _load_case(case_dir: Path) -> Case:
89+
"""Load a case and its optional output expectations."""
90+
expected_dir = case_dir / "expected"
91+
return Case(
92+
path=case_dir,
93+
stdout_exact=_read_optional(expected_dir / "stdout.txt"),
94+
stdout_contains=_read_contains(expected_dir / "stdout.contains.txt"),
95+
stderr_exact=_read_optional(expected_dir / "stderr.txt"),
96+
stderr_contains=_read_contains(expected_dir / "stderr.contains.txt"),
97+
)
98+
99+
100+
def _prepare_workspace(
101+
case: Case,
102+
tmp_path: Path,
103+
monkeypatch: pytest.MonkeyPatch,
104+
) -> Path:
105+
"""Copy fixture inputs into a temp workspace and set env vars."""
106+
input_dir = case.path / "input"
107+
input_pyproject = input_dir / "pyproject.toml"
108+
if input_pyproject.exists():
109+
shutil.copyfile(input_pyproject, tmp_path / fixer.PYPROJECT.name)
110+
monkeypatch.chdir(tmp_path)
111+
monkeypatch.setattr(sys, "argv", [str(FIXER_PATH)])
112+
input_metadata = input_dir / "dependabot-metadata.json"
113+
monkeypatch.setenv(
114+
"UPDATED_DEPENDENCIES_JSON",
115+
input_metadata.read_text(encoding="utf-8") if input_metadata.exists() else "",
116+
)
117+
return tmp_path / fixer.PYPROJECT.name
118+
119+
120+
@pytest.mark.parametrize(
121+
"case", map(_load_case, _case_dirs()), ids=lambda case: case.path.name
122+
)
123+
def test_fixer_cases(
124+
case: Case,
125+
tmp_path: Path,
126+
monkeypatch: pytest.MonkeyPatch,
127+
capsys: pytest.CaptureFixture[str],
128+
) -> None:
129+
"""Apply each case and compare the rewritten files and output."""
130+
workspace = _prepare_workspace(case, tmp_path, monkeypatch)
131+
if case.stderr_exact is not None or case.stderr_contains is not None:
132+
with pytest.raises(SystemExit) as excinfo:
133+
fixer.main()
134+
assert excinfo.value.code == 1
135+
else:
136+
fixer.main()
137+
138+
captured = capsys.readouterr()
139+
140+
if case.stdout_exact is not None:
141+
assert captured.out == case.stdout_exact
142+
elif case.stdout_contains is not None:
143+
assert case.stdout_contains in captured.out
144+
else:
145+
assert captured.out == ""
146+
147+
if case.stderr_exact is not None:
148+
assert captured.err == case.stderr_exact
149+
elif case.stderr_contains is not None:
150+
assert case.stderr_contains in captured.err
151+
else:
152+
assert captured.err == ""
153+
154+
expected_pyproject = case.expected_pyproject
155+
if expected_pyproject.exists():
156+
assert workspace.read_text(encoding="utf-8") == expected_pyproject.read_text(
157+
encoding="utf-8"
158+
)
159+
46160

47161
@pytest.mark.parametrize(
48162
("dependency", "version", "original", "expected"),

0 commit comments

Comments
 (0)