feat(tests): add tests to cover external-venv PYTHONPATH isolation

This commit is contained in:
Elisa Martinez Abad
2026-08-06 09:03:17 -07:00
committed by kshitij
parent 76961b61bd
commit ec884fc655
+161 -1
View File
@@ -14,9 +14,11 @@ there is no env-var override. Tests patch ``_load_config`` directly.
import json
import os
import subprocess
import sys
import unittest
from contextlib import contextmanager
import unittest.mock
from contextlib import contextmanager, ExitStack
from unittest.mock import patch
import pytest
@@ -36,8 +38,10 @@ from tools.code_execution_tool import (
EXECUTION_MODES,
_get_execution_mode,
_is_usable_python,
_python_environment_prefix,
_resolve_child_cwd,
_resolve_child_python,
_uses_hermes_python_environment,
build_execute_code_schema,
execute_code,
)
@@ -373,5 +377,161 @@ class TestSecurityInvariantsAcrossModes(unittest.TestCase):
self.assertIn("delegate_task_available: False", result["output"])
# ---------------------------------------------------------------------------
# _python_environment_prefix / _uses_hermes_python_environment
# ---------------------------------------------------------------------------
class TestPythonEnvironmentPrefix(unittest.TestCase):
"""Unit tests for the helper that queries sys.prefix of an interpreter."""
def setUp(self):
_python_environment_prefix.cache_clear()
def tearDown(self):
_python_environment_prefix.cache_clear()
def test_returns_realpath_of_current_interpreter_prefix(self):
"""Happy path: sys.executable reports its own prefix."""
prefix = _python_environment_prefix(sys.executable)
self.assertEqual(prefix, os.path.realpath(sys.prefix))
def test_returns_empty_string_for_nonexistent_path(self):
"""A path that doesn't exist → OSError → empty string."""
result = _python_environment_prefix("/nonexistent/python-does-not-exist")
self.assertEqual(result, "")
def test_returns_empty_string_when_subprocess_times_out(self):
with patch("subprocess.run", side_effect=subprocess.TimeoutExpired(cmd=[], timeout=5)):
result = _python_environment_prefix("/some/python")
self.assertEqual(result, "")
def test_returns_empty_string_on_nonzero_exit(self):
mock_result = unittest.mock.MagicMock()
mock_result.returncode = 1
mock_result.stdout = ""
with patch("subprocess.run", return_value=mock_result):
result = _python_environment_prefix("/bad/python")
self.assertEqual(result, "")
def test_returns_empty_string_when_stdout_is_blank(self):
mock_result = unittest.mock.MagicMock()
mock_result.returncode = 0
mock_result.stdout = " \n"
with patch("subprocess.run", return_value=mock_result):
result = _python_environment_prefix("/blank/python")
self.assertEqual(result, "")
def test_result_is_cached(self):
"""Second call returns cached value without spawning another process."""
with patch("subprocess.run") as mock_run:
mock_run.return_value = unittest.mock.MagicMock(
returncode=0, stdout="/fake/prefix\n"
)
_python_environment_prefix("/cached/python")
_python_environment_prefix("/cached/python")
self.assertEqual(mock_run.call_count, 1)
class TestUsesHermesPythonEnvironment(unittest.TestCase):
"""Unit tests for _uses_hermes_python_environment."""
def setUp(self):
_python_environment_prefix.cache_clear()
def tearDown(self):
_python_environment_prefix.cache_clear()
def test_true_for_current_interpreter(self):
"""sys.executable always belongs to the current environment."""
self.assertTrue(_uses_hermes_python_environment(sys.executable))
def test_false_for_different_prefix(self):
"""An interpreter reporting a different prefix is external."""
with patch("tools.code_execution_tool._python_environment_prefix",
return_value="/some/other/venv"):
self.assertFalse(_uses_hermes_python_environment("/other/python"))
def test_false_when_prefix_is_empty(self):
"""If prefix cannot be determined (error path), treat as external."""
with patch("tools.code_execution_tool._python_environment_prefix",
return_value=""):
self.assertFalse(_uses_hermes_python_environment("/bad/python"))
def test_true_when_prefix_matches_sys_prefix(self):
hermes_prefix = os.path.realpath(sys.prefix)
with patch("tools.code_execution_tool._python_environment_prefix",
return_value=hermes_prefix):
self.assertTrue(_uses_hermes_python_environment("/same/env/python"))
# ---------------------------------------------------------------------------
# PYTHONPATH composition — hermes root included only for same-env interpreters
# ---------------------------------------------------------------------------
class TestPythonPathComposition(unittest.TestCase):
"""Verify hermes root inclusion in PYTHONPATH depends on env match.
Patches ``_uses_hermes_python_environment`` directly so these tests are
independent of subprocess availability — the unit tests above already
cover the detection logic end-to-end.
"""
def _capture_pythonpath(self, same_env: bool) -> str:
"""Return the PYTHONPATH that execute_code would pass to the child."""
captured = {}
def _fake_popen(cmd, **kwargs):
env = kwargs.get("env") or {}
captured["PYTHONPATH"] = env.get("PYTHONPATH", "")
mock_proc = unittest.mock.MagicMock()
mock_proc.stdout.read.return_value = b""
mock_proc.stderr.read.return_value = b""
mock_proc.wait.return_value = 0
mock_proc.returncode = 0
mock_proc.poll.return_value = 0
return mock_proc
with patch("tools.code_execution_tool._load_config", return_value={"mode": "strict"}), \
patch("model_tools.handle_function_call", side_effect=_mock_handle_function_call), \
patch("tools.code_execution_tool._uses_hermes_python_environment",
return_value=same_env), \
patch("subprocess.Popen", side_effect=_fake_popen):
try:
execute_code(code="pass", task_id="test-pp", enabled_tools=[])
except Exception:
pass
return captured.get("PYTHONPATH", "")
def _hermes_root(self) -> str:
tools_dir = os.path.dirname(os.path.abspath(
__import__("tools.code_execution_tool", fromlist=["__file__"]).__file__
))
return os.path.dirname(tools_dir)
def test_hermes_root_included_when_same_env(self):
"""When interpreter is in the Hermes env, hermes root is in PYTHONPATH."""
pythonpath = self._capture_pythonpath(same_env=True)
parts = pythonpath.split(os.pathsep)
self.assertIn(self._hermes_root(), parts,
"hermes root must be in PYTHONPATH for same-env interpreters")
def test_hermes_root_excluded_when_external_env(self):
"""When interpreter is external, hermes root must NOT be in PYTHONPATH."""
pythonpath = self._capture_pythonpath(same_env=False)
parts = pythonpath.split(os.pathsep)
self.assertNotIn(self._hermes_root(), parts,
"hermes root must not leak into an external interpreter's PYTHONPATH")
def test_staging_dir_always_present(self):
"""The staging tmpdir must always be the first PYTHONPATH entry."""
for same_env in (True, False):
with self.subTest(same_env=same_env):
pythonpath = self._capture_pythonpath(same_env=same_env)
parts = pythonpath.split(os.pathsep)
self.assertTrue(parts and parts[0],
"PYTHONPATH must start with the staging tmpdir")
if __name__ == "__main__":
unittest.main()