ouroboros/tests/test_agent_python_env.py
Ouroboros 54ad148f28 fix(deps): complete uv migration contracts
Validate lock freshness, preserve the pip runtime capability and N-1 updater bridge, synchronize the uv root version during releases, and align install documentation and regression tests.
2026-08-11 06:34:54 +03:00

107 lines
4.3 KiB
Python

"""Regression tests for the agent interpreter handle.
The bundled app must run pytest through the same interpreter that launched
Ouroboros. Plain `pytest` or `python -m pytest` can resolve to the wrong
runtime in packaged builds.
"""
from __future__ import annotations
import os
import pathlib
import subprocess
import sys
import pytest
def test_server_py_injects_agent_python_env_var():
repo_root = pathlib.Path(__file__).resolve().parent.parent
source = (repo_root / "server.py").read_text(encoding="utf-8")
assert 'os.environ.get("OUROBOROS_AGENT_PYTHON")' in source
assert 'os.environ["OUROBOROS_AGENT_PYTHON"]' in source
assert "sys.executable" in source
assert "isinstance" in source and "_agent_python" in source
def test_preflight_test_runner_uses_sys_executable():
repo_root = pathlib.Path(__file__).resolve().parent.parent
source = (repo_root / "ouroboros" / "preflight_runner.py").read_text(encoding="utf-8")
review_helpers = (repo_root / "ouroboros" / "tools" / "review_helpers.py").read_text(encoding="utf-8")
assert '["pytest", "tests/"' not in source
assert '"-m", "pytest"' in source
assert "sys.executable" in source
assert "run_hermetic_pytest" in review_helpers
def test_git_pre_push_tests_uses_sys_executable():
repo_root = pathlib.Path(__file__).resolve().parent.parent
source = (repo_root / "ouroboros" / "preflight_runner.py").read_text(encoding="utf-8")
git_tools = (repo_root / "ouroboros" / "tools" / "git.py").read_text(encoding="utf-8")
assert '["pytest", "tests/"' not in source
assert '"-m", "pytest"' in source
assert "sys.executable" in source
assert "OUROBOROS_AGENT_PYTHON" in source
assert "run_hermetic_pytest" in git_tools
def test_shell_no_longer_launches_pytest_at_all():
"""The shell validation runner retired WITH its only consumer (D10).
`_run_validation` was `claude_code_edit`'s own `validate=True`
implementation — one caller since the initial bundle — so it left with the
tool. The interpreter-handle invariant this pin protected (a packaged app
must run pytest through the interpreter that launched it, never bare
`python`) keeps its two FIRST-CLASS homes, each pinned by its own test
above: the preflight runner and the git pre-push tests. This pin now
asserts the new truth at the old location: shell.py launches pytest in NO
form, so an ad-hoc runner with the wrong interpreter cannot sneak back in
beside process tools — a new pytest launch belongs in
preflight_runner/run_hermetic_pytest, where the sibling pins bind it.
"""
repo_root = pathlib.Path(__file__).resolve().parent.parent
source = (repo_root / "ouroboros" / "tools" / "shell.py").read_text(encoding="utf-8")
assert '["python", "-m", "pytest"' not in source
assert '"-m", "pytest"' not in source
assert '"pytest"' not in source
def test_sys_executable_minus_m_pytest_exits_zero():
result = subprocess.run(
[sys.executable, "-m", "pytest", "--version"],
capture_output=True,
text=True,
timeout=30,
)
assert result.returncode == 0, (
f"sys.executable -m pytest --version exited {result.returncode}; "
f"stdout={result.stdout!r} stderr={result.stderr!r}"
)
assert "pytest" in (result.stdout + result.stderr).lower()
def test_agent_python_env_var_points_to_usable_python():
agent_python = os.environ.get("OUROBOROS_AGENT_PYTHON")
if not agent_python:
pytest.skip("OUROBOROS_AGENT_PYTHON is not set in this unit-test run")
result = subprocess.run(
[agent_python, "-c", "print('ok')"],
capture_output=True,
text=True,
timeout=10,
)
assert result.returncode == 0
assert "ok" in result.stdout
def test_pyproject_declares_pytest_as_runtime_dependency():
repo_root = pathlib.Path(__file__).resolve().parent.parent
pyproject = (repo_root / "pyproject.toml").read_text(encoding="utf-8")
assert '"pytest>=7.0"' in pyproject
def test_pyproject_declares_pip_as_runtime_dependency():
"""uv environments must preserve the runtime's ``python -m pip`` contract."""
repo_root = pathlib.Path(__file__).resolve().parent.parent
pyproject = (repo_root / "pyproject.toml").read_text(encoding="utf-8")
assert '"pip"' in pyproject