* feat(src): migrate second-brain RAG CLI + tests * fix(embedder): strip hardcoded ai.slaid098.dev endpoint * refactor: rename slaid098/opencode to opencode-config * docs(handoff): add pr-5 handoff + ADR-001 * fix(docs): rebase handoff/ADR naming to PR number + drop dangling ADR-009 refs * fix(tests): update script paths + assertions for opencode-config migration * fix(pyproject): update cov + ruff paths config/scripts -> .opencode/scripts * docs: update project map + handoff + ADR --------- Co-authored-by: opencode-agent <agent@slaid098.dev>
157 lines
5.9 KiB
Python
157 lines
5.9 KiB
Python
"""Tests for .opencode/scripts/check-adr-refs.py — dangling ADR reference guard.
|
|
|
|
Covers the deterministic ADR-ref guard: clean repo passes, a dangling
|
|
``ADR-999`` in a temp handoff is detected, self-reference inside an
|
|
``NNN-*.md`` file is excluded, a valid cross-ref (``ADR-002``) passes,
|
|
a file with no ADR refs passes, and ``node_modules/`` is excluded.
|
|
|
|
Strategy mirrors ``tests/test_check_permissions.py``:
|
|
- ``test_clean_repo_passes`` runs the script as a subprocess on the
|
|
real (clean) repo — black-box, returncode 0, OK message in stdout.
|
|
- The remaining tests load the script in-process via importlib and
|
|
monkeypatch ``REPO_ROOT`` / ``ADR_DIR`` to point at a tmp_path tree,
|
|
then call ``main()`` and assert on captured stdout / SystemExit code.
|
|
"""
|
|
|
|
import importlib.util
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parent.parent
|
|
SCRIPT_PATH = REPO_ROOT / ".opencode" / "scripts" / "check-adr-refs.py"
|
|
|
|
|
|
def _load_script():
|
|
spec = importlib.util.spec_from_file_location("check_adr_refs", SCRIPT_PATH)
|
|
module = importlib.util.module_from_spec(spec)
|
|
sys.modules["check_adr_refs"] = module
|
|
spec.loader.exec_module(module)
|
|
return module
|
|
|
|
|
|
car = _load_script()
|
|
|
|
|
|
# ── clean repo (subprocess black-box) ───────────────────────────────────────
|
|
|
|
|
|
def test_clean_repo_passes():
|
|
"""On the current (clean) repo the script exits 0 with the OK message."""
|
|
result = subprocess.run(
|
|
["python3", str(SCRIPT_PATH)],
|
|
capture_output=True,
|
|
text=True,
|
|
check=False,
|
|
)
|
|
assert result.returncode == 0, result.stderr
|
|
assert "OK: No dangling ADR references." in result.stdout
|
|
|
|
|
|
# ── helpers for in-process tests ────────────────────────────────────────────
|
|
|
|
|
|
def _setup_tmp_repo(tmp_path: Path, monkeypatch) -> tuple[Path, Path]:
|
|
"""Point the script's REPO_ROOT and ADR_DIR at a tmp_path tree.
|
|
|
|
Creates the ``docs/decisions/`` dir and returns (repo_root, adr_dir).
|
|
"""
|
|
repo = tmp_path / "repo"
|
|
adr_dir = repo / "docs" / "decisions"
|
|
adr_dir.mkdir(parents=True)
|
|
handoff_dir = repo / "docs" / "handoff"
|
|
handoff_dir.mkdir(parents=True)
|
|
monkeypatch.setattr(car, "REPO_ROOT", repo)
|
|
monkeypatch.setattr(car, "ADR_DIR", adr_dir)
|
|
return repo, adr_dir
|
|
|
|
|
|
# ── dangling reference detected ─────────────────────────────────────────────
|
|
|
|
|
|
def test_dangling_ref_detected(tmp_path, monkeypatch, capsys):
|
|
"""A handoff referencing ADR-999 (no 999-*.md) → exit 1 + ADR-999 in output."""
|
|
repo, _adr_dir = _setup_tmp_repo(tmp_path, monkeypatch)
|
|
(repo / "docs" / "handoff" / "pr-1-test.md").write_text("See ADR-999 for context.\n")
|
|
|
|
with pytest.raises(SystemExit) as exc_info:
|
|
car.main()
|
|
|
|
assert exc_info.value.code == 1
|
|
captured = capsys.readouterr()
|
|
assert "ADR-999" in captured.out
|
|
assert "FAIL" in captured.out
|
|
|
|
|
|
# ── self-reference excluded ─────────────────────────────────────────────────
|
|
|
|
|
|
def test_self_reference_excluded(tmp_path, monkeypatch, capsys):
|
|
"""An ADR file ``019-pr-94-test.md`` mentioning ADR-019 → passes (self-ref)."""
|
|
_repo, adr_dir = _setup_tmp_repo(tmp_path, monkeypatch)
|
|
(adr_dir / "019-pr-94-test.md").write_text("# ADR-019: self-reference is OK.\n")
|
|
|
|
with pytest.raises(SystemExit) as exc_info:
|
|
car.main()
|
|
|
|
assert exc_info.value.code == 0
|
|
captured = capsys.readouterr()
|
|
assert "OK: No dangling ADR references." in captured.out
|
|
|
|
|
|
# ── valid cross-reference passes ────────────────────────────────────────────
|
|
|
|
|
|
def test_valid_cross_ref_passes(tmp_path, monkeypatch, capsys):
|
|
"""A handoff referencing ADR-002 when 002-*.md exists → passes."""
|
|
repo, adr_dir = _setup_tmp_repo(tmp_path, monkeypatch)
|
|
(adr_dir / "002-pr-56-pipeline-mandatory-adr.md").write_text("# ADR-002\n")
|
|
(repo / "docs" / "handoff" / "pr-3-test.md").write_text("Per ADR-002, ADR is mandatory.\n")
|
|
|
|
with pytest.raises(SystemExit) as exc_info:
|
|
car.main()
|
|
|
|
assert exc_info.value.code == 0
|
|
captured = capsys.readouterr()
|
|
assert "OK: No dangling ADR references." in captured.out
|
|
|
|
|
|
# ── no ADR refs in a file passes ────────────────────────────────────────────
|
|
|
|
|
|
def test_no_adr_refs_passes(tmp_path, monkeypatch, capsys):
|
|
"""A handoff with no ADR-NNN references → passes."""
|
|
repo, _adr_dir = _setup_tmp_repo(tmp_path, monkeypatch)
|
|
(repo / "docs" / "handoff" / "pr-4-test.md").write_text("No ADR references here.\n")
|
|
|
|
with pytest.raises(SystemExit) as exc_info:
|
|
car.main()
|
|
|
|
assert exc_info.value.code == 0
|
|
captured = capsys.readouterr()
|
|
assert "OK: No dangling ADR references." in captured.out
|
|
|
|
|
|
# ── node_modules excluded ───────────────────────────────────────────────────
|
|
|
|
|
|
def test_exclude_node_modules(tmp_path, monkeypatch, capsys):
|
|
"""A ``.md`` file under ``node_modules/`` with ADR-999 is NOT scanned."""
|
|
repo, _adr_dir = _setup_tmp_repo(tmp_path, monkeypatch)
|
|
nm = repo / "node_modules" / "some-pkg"
|
|
nm.mkdir(parents=True)
|
|
(nm / "README.md").write_text("Dangling ADR-999 should be ignored.\n")
|
|
|
|
with pytest.raises(SystemExit) as exc_info:
|
|
car.main()
|
|
|
|
assert exc_info.value.code == 0
|
|
captured = capsys.readouterr()
|
|
assert "OK: No dangling ADR references." in captured.out
|
|
assert "ADR-999" not in captured.out
|
|
|
|
|
|
if __name__ == "__main__":
|
|
pytest.main([__file__, "-v"])
|