test(managed-uv): use native Windows fixtures and isolated stamps

This commit is contained in:
ethernet
2026-09-05 18:23:03 -04:00
parent c5ce06b87b
commit a3eef50d0d
+111 -68
View File
@@ -16,6 +16,12 @@ import pytest
# Helpers
# ---------------------------------------------------------------------------
# Host-native managed-uv binary name: managed_uv_path() installs `uv` on
# POSIX and `uv.exe` on Windows. Fixtures must build what the real host
# resolves — no platform fake.
_UV_BINARY_NAME = "uv.exe" if sys.platform == "win32" else "uv"
def _make_executable(path: Path) -> None:
"""Create a minimal fake uv binary at *path*."""
path.parent.mkdir(parents=True, exist_ok=True)
@@ -158,11 +164,12 @@ class TestMacOSManagedPythonSigning:
class TestResolveUv:
def test_existing_executable(self, tmp_path):
_make_executable(tmp_path / "bin" / "uv")
uv = tmp_path / "bin" / _UV_BINARY_NAME
_make_executable(uv)
with patch("hermes_cli.managed_uv.get_hermes_home", return_value=tmp_path):
from hermes_cli.managed_uv import resolve_uv
result = resolve_uv()
assert result == str(tmp_path / "bin" / "uv")
assert result == str(uv)
def test_non_executable_file_returns_none(self, tmp_path):
uv = tmp_path / "bin" / "uv"
@@ -182,17 +189,20 @@ class TestResolveUv:
class TestEnsureUv:
def test_installs_if_missing(self, tmp_path):
uv = tmp_path / "bin" / _UV_BINARY_NAME
with patch("hermes_cli.managed_uv.get_hermes_home", return_value=tmp_path), \
patch("hermes_cli.managed_uv.repair_vulnerable_runtime", return_value=_RRR("not-applicable")), \
patch("hermes_cli.managed_uv._uv_version", return_value="uv 0.1.2"), \
patch("hermes_cli.managed_uv._install_uv") as mock_install:
# Simulate the installer creating the binary
# Simulate the installer creating the binary (host-native name:
# uv.exe on Windows, uv on POSIX).
def fake_install(target):
_make_executable(target)
mock_install.side_effect = fake_install
from hermes_cli.managed_uv import ensure_uv
path = ensure_uv()
assert path == str(tmp_path / "bin" / "uv")
assert path == str(uv)
mock_install.assert_called_once()
def test_install_reports_runtime_repair_to_observer(self, tmp_path):
@@ -217,13 +227,16 @@ class TestEnsureUv:
), patch(
"hermes_cli.managed_uv._install_uv",
side_effect=fake_install,
), patch(
"hermes_cli.managed_uv._uv_version",
return_value="uv 0.1.2",
), patch(
"hermes_cli.managed_uv.repair_vulnerable_runtime",
return_value=repair,
):
path = ensure_uv(repair_observer=observed.append)
assert path == str(tmp_path / "bin" / "uv")
assert path == str(tmp_path / "bin" / _UV_BINARY_NAME)
assert observed == [repair]
@@ -329,25 +342,30 @@ class TestUpdateManagedUv:
def test_fresh_stamp_skips_network_self_update_but_not_repair(self, tmp_path, monkeypatch):
def test_fresh_stamp_skips_network_self_update_but_not_repair(self, tmp_path):
"""A recent success stamp must skip `uv self update` entirely while the
vulnerable-runtime repair probe still runs (CVE repair is never gated)."""
import time
from hermes_cli.managed_uv import RuntimeRepairResult, update_managed_uv
uv = tmp_path / "bin" / "uv"
uv = tmp_path / "bin" / _UV_BINARY_NAME
_make_executable(uv)
# Fresh stamp under the isolated HERMES_HOME.
import hermes_constants
stamp = hermes_constants.get_hermes_home() / "cache" / ".uv_self_update_stamp"
# The stamp reader imports get_hermes_home separately from the binary
# resolver. Give both paths the same explicit test root.
stamp = tmp_path / "cache" / ".uv_self_update_stamp"
stamp.parent.mkdir(parents=True, exist_ok=True)
stamp.touch()
# File timestamps can lead time.time() briefly on Windows. Stay well
# inside the freshness window instead of racing its age >= 0 boundary.
recent = time.time() - 60
os.utime(stamp, (recent, recent))
with patch("hermes_cli.managed_uv.get_hermes_home", return_value=tmp_path), \
patch("hermes_cli.managed_uv.subprocess.run") as mock_run, \
patch(
"hermes_cli.managed_uv.repair_vulnerable_runtime",
return_value=RuntimeRepairResult("skipped"),
) as mock_repair:
patch("hermes_cli.managed_uv._uv_self_update_stamp", return_value=stamp), \
patch("hermes_cli.managed_uv.repair_vulnerable_runtime",
return_value=RuntimeRepairResult("skipped")) as mock_repair, \
patch("hermes_cli.managed_uv.subprocess.run") as mock_run:
result = update_managed_uv()
assert result == str(uv)
@@ -361,17 +379,19 @@ class TestUpdateManagedUv:
from hermes_cli.managed_uv import UV_SELF_UPDATE_INTERVAL_SECONDS, update_managed_uv
uv = tmp_path / "bin" / "uv"
uv = tmp_path / "bin" / _UV_BINARY_NAME
_make_executable(uv)
import hermes_constants
stamp = hermes_constants.get_hermes_home() / "cache" / ".uv_self_update_stamp"
# Keep the stamp and binary resolver in the same test root.
stamp = tmp_path / "cache" / ".uv_self_update_stamp"
stamp.parent.mkdir(parents=True, exist_ok=True)
stamp.touch()
old = _time.time() - UV_SELF_UPDATE_INTERVAL_SECONDS - 60
_os.utime(stamp, (old, old))
with patch("hermes_cli.managed_uv.get_hermes_home", return_value=tmp_path), \
patch("hermes_cli.managed_uv._uv_self_update_stamp", return_value=stamp), \
patch("hermes_cli.managed_uv.repair_vulnerable_runtime", return_value=_RRR("not-applicable")), \
patch("hermes_cli.managed_uv._uv_version", return_value="uv 0.2.0"), \
patch("hermes_cli.managed_uv.subprocess.run") as mock_run:
mock_run.return_value = MagicMock(returncode=0, stdout="uv 0.2.0")
update_managed_uv()
@@ -464,47 +484,6 @@ class TestRuntimeRepair:
assert not (root / ".hermes-runtime").exists()
mock_install.assert_not_called()
def test_stage_candidate_sync_keeps_uv_project_config(self, tmp_path):
from hermes_cli.managed_uv import _stage_candidate_venv
root = tmp_path / "checkout"
root.mkdir()
(root / "uv.lock").write_text("# lock\n", encoding="utf-8")
generation = root / ".hermes-runtime" / "python" / "gen"
python = generation / "bin" / "python"
python.parent.mkdir(parents=True)
python.write_text("py", encoding="utf-8")
calls = []
def fake_run(argv, **kwargs):
calls.append((list(argv), kwargs.get("env")))
return MagicMock(returncode=0)
with patch("hermes_cli.managed_uv.subprocess.run", side_effect=fake_run), \
patch(
"hermes_cli.managed_uv._smoke_candidate_venv",
return_value=(True, "", None),
):
candidate = _stage_candidate_venv(
"uv",
project_root=root,
generation=generation,
python=python,
)
assert candidate is not None
assert len(calls) == 2
venv_argv, venv_env = calls[0]
sync_argv, sync_env = calls[1]
assert venv_argv[:2] == ["uv", "venv"]
assert "--no-config" in venv_argv
assert venv_env.get("UV_NO_CONFIG") == "1"
assert sync_argv[:2] == ["uv", "sync"]
assert "--locked" in sync_argv
assert "--no-config" not in sync_argv
assert "UV_NO_CONFIG" not in sync_env
def test_failed_candidate_preserves_live_venv(self, tmp_path):
from hermes_cli.managed_uv import (
_acquire_repair_lock,
@@ -620,6 +599,54 @@ class TestRuntimeRepair:
assert leftovers == [], f"no stale markers may remain: {leftovers}"
class TestStageCandidateVenvCrossPlatform:
"""Candidate sync preserves project config and streams progress on every host."""
def test_sync_keeps_uv_project_config_and_merges_stderr(self, tmp_path):
import subprocess
from hermes_cli.managed_uv import _stage_candidate_venv
root = tmp_path / "checkout"
root.mkdir()
(root / "uv.lock").write_text("# lock\n", encoding="utf-8")
generation = root / ".hermes-runtime" / "python" / "gen"
python = generation / "bin" / "python"
python.parent.mkdir(parents=True)
python.write_text("py", encoding="utf-8")
calls = []
def fake_run(argv, **kwargs):
calls.append((list(argv), kwargs))
return MagicMock(returncode=0)
with patch("hermes_cli.managed_uv.subprocess.run", side_effect=fake_run), \
patch(
"hermes_cli.managed_uv._smoke_candidate_venv",
return_value=(True, "", None),
):
candidate = _stage_candidate_venv(
"uv",
project_root=root,
generation=generation,
python=python,
)
assert candidate is not None
assert len(calls) == 2
venv_argv, venv_kwargs = calls[0]
sync_argv, sync_kwargs = calls[1]
assert venv_argv[:2] == ["uv", "venv"]
assert "--no-config" in venv_argv
assert venv_kwargs["env"].get("UV_NO_CONFIG") == "1"
assert sync_argv[:2] == ["uv", "sync"]
assert "--locked" in sync_argv
assert "--no-config" not in sync_argv
assert "UV_NO_CONFIG" not in sync_kwargs["env"]
assert sync_kwargs["stderr"] == subprocess.STDOUT
class TestRuntimeCutover:
def test_os_lock_blocks_concurrent_repair_and_releases(self, tmp_path):
from hermes_cli.managed_uv import _acquire_repair_lock, _release_repair_lock
@@ -674,13 +701,23 @@ class TestRuntimeCutover:
# ---------------------------------------------------------------------------
class TestInstallUvInternals:
def test_posix_sets_uv_unmanaged_install(self, tmp_path):
target = tmp_path / "bin" / "uv"
with patch("hermes_cli.managed_uv._install_uv_posix") as mock_posix:
from hermes_cli.managed_uv import _install_uv
_install_uv(target)
mock_posix.assert_called_once()
call_env = mock_posix.call_args[0][0]
def test_installer_uses_host_branch_and_managed_directory(self, tmp_path):
"""The native installer receives the managed directory, not a PATH default."""
import hermes_cli.managed_uv as managed_uv
target = tmp_path / "bin" / _UV_BINARY_NAME
with patch("hermes_cli.managed_uv._install_uv_posix") as mock_posix, \
patch("hermes_cli.managed_uv._install_uv_windows") as mock_windows:
managed_uv._install_uv(target)
host_installer, other_installer = (
(mock_windows, mock_posix) if sys.platform == "win32"
else (mock_posix, mock_windows))
host_installer.assert_called_once()
other_installer.assert_not_called()
call_env = host_installer.call_args[0][0]
assert call_env["UV_INSTALL_DIR"] == str(tmp_path / "bin")
if sys.platform != "win32":
assert call_env["UV_UNMANAGED_INSTALL"] == str(tmp_path / "bin")
@@ -1252,10 +1289,16 @@ class TestDefaultLiveVenv:
root = tmp_path / "checkout"
root.mkdir()
(root / "pyproject.toml").write_text("[project]\n", encoding="utf-8")
# Host-native venv layout: bin/python on POSIX, Scripts/python.exe on
# Windows — what _venv_python() resolves on the real host.
if sys.platform == "win32":
bin_dir_name, python_name = "Scripts", "python.exe"
else:
bin_dir_name, python_name = "bin", "python"
for d in dirs:
bin_dir = root / d / "bin"
bin_dir = root / d / bin_dir_name
bin_dir.mkdir(parents=True)
(bin_dir / "python").write_text("py", encoding="utf-8")
(bin_dir / python_name).write_text("py", encoding="utf-8")
return root
def test_dot_venv_only_is_targeted(self, tmp_path):