"""Contract checks for the root-level `run-tests.sh` convenience runner (#473). `run-tests.sh` is the canonical full-validation entry point. It must invoke the project virtualenv interpreter, forward extra args to pytest, fail closed when the venv Python is missing, and stay repo-local (no network, no lock files). These checks pin that behavior so it cannot silently regress, and confirm the developer testing guide names the runner. """ import stat from pathlib import Path REPO_ROOT = Path(__file__).resolve().parent.parent SCRIPT = REPO_ROOT / "run-tests.sh" GUIDE = REPO_ROOT / "docs" / "developer-testing-guidelines.md" def _script_text() -> str: return SCRIPT.read_text(encoding="utf-8") def test_run_tests_script_exists(): assert SCRIPT.is_file(), "run-tests.sh must exist at the repository root" def test_run_tests_script_is_executable(): mode = SCRIPT.stat().st_mode assert mode & stat.S_IXUSR, "run-tests.sh must be executable (chmod +x)" def test_run_tests_script_has_strict_bash_flags(): text = _script_text() assert text.startswith("#!/usr/bin/env bash"), "must use the bash shebang" assert "set -euo pipefail" in text, "must set -euo pipefail" def test_run_tests_script_uses_venv_pytest_and_forwards_args(): text = _script_text() # Resolves the venv interpreter relative to the script's own directory. assert "venv/bin/python" in text, "must use the project virtualenv Python" assert "-m pytest" in text, "must run pytest via the module" assert '"$@"' in text, "must forward extra CLI args to pytest" def test_run_tests_script_fails_closed_without_venv(): text = _script_text() # Missing venv must be an explicit, non-zero-exit error, not a silent # fallback to the wrong interpreter. assert "if [[ ! -x" in text, "must guard on an executable venv Python" assert "exit 1" in text, "must exit non-zero when the venv is missing" assert "ERROR" in text, "must print a clear error message" def test_run_tests_script_stays_repo_local(): text = _script_text() # No network calls, no lock-file writes from the runner itself. for forbidden in ("curl", "wget", "gitea_issue_lock.json"): assert forbidden not in text, f"runner must not reference {forbidden!r}" def test_guide_names_canonical_runner(): text = " ".join(GUIDE.read_text(encoding="utf-8").split()) assert "./run-tests.sh" in text, "testing guide must name ./run-tests.sh" assert "./run-tests.sh tests/test_mcp_server.py -q" in text, ( "testing guide must show the focused-validation example" )