Files
hermes-agent/tests/pm/test_tree_digest_links.py
ethernet 3af8084671 fix(pm): support junction checks during legacy Python bootstrap
Python 3.11 fails while PM records the replacement interpreter's tree digest. The bootstrap cannot require Path.is_junction before it installs the managed Python.

Use Windows reparse metadata for junction detection in the store and data cleanup. Retain junction target protection without requiring the newer pathlib API.

Verified the exact failure on real Python 3.11 before the fix. Cold provisioning and source-update relaunch pass afterward. Targeted tests: 16 passed, one Windows-only test skipped on Linux. Ruff passed. The full suite and native Windows test were not run.
2026-09-12 11:01:12 -04:00

88 lines
3.0 KiB
Python

"""Realized digests bind link text without reading the linked directory."""
import os
from pathlib import Path
import pytest
from pm.store import tree_digest
def test_digest_without_pathlib_junction_api(tmp_path, monkeypatch):
# Source updates reach the store before replacing a pre-3.12 interpreter.
monkeypatch.delattr(Path, "is_junction", raising=False)
root = tmp_path / "tree"
nested = root / "lib"
nested.mkdir(parents=True)
payload = nested / "payload"
payload.write_bytes(b"before")
first = tree_digest(root)
assert tree_digest(root) == first
payload.write_bytes(b"after")
assert tree_digest(root) != first
@pytest.mark.platforms("windows", "posix")
def test_directory_links_bind_only_their_target_text(tmp_path):
root = tmp_path / "tree"
root.mkdir()
(root / "payload").write_bytes(b"unchanged")
left, right = tmp_path / "left", tmp_path / "right"
for target in (left, right):
target.mkdir()
(target / "content").write_bytes(b"same bytes")
plain = tree_digest(root)
link = root / "directory"
try:
link.symlink_to(Path("..") / "left", target_is_directory=True)
except OSError as exc:
if getattr(exc, "winerror", None) == 1314:
pytest.skip("directory symlinks require Windows Developer Mode or privilege")
raise
first = tree_digest(root)
assert first != plain
(left / "content").write_bytes(b"changed outside the tree")
assert tree_digest(root) == first
link.unlink()
link.symlink_to(Path("..") / "right", target_is_directory=True)
assert tree_digest(root) != first
link.unlink()
link.symlink_to(".", target_is_directory=True)
cycle = tree_digest(root)
assert tree_digest(root) == cycle
assert cycle not in (plain, first)
@pytest.mark.platforms("windows")
def test_junctions_bind_target_text_without_walking_outside(tmp_path, monkeypatch):
import subprocess
monkeypatch.delattr(Path, "is_junction", raising=False)
root = tmp_path / "tree"
root.mkdir()
left, right = tmp_path / "left", tmp_path / "right"
for target in (left, right):
target.mkdir()
(target / "content").write_bytes(b"same bytes")
junction = root / "junction"
command = str(Path(os.environ["SystemRoot"]) / "System32" / "cmd.exe")
def point_at(target):
result = subprocess.run(
[command, "/d", "/c", "mklink", "/J", str(junction), str(target)],
capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=15,
)
assert result.returncode == 0, result.stdout + result.stderr
point_at(left)
try:
first = tree_digest(root)
(left / "content").write_bytes(b"changed outside the tree")
assert tree_digest(root) == first
junction.rmdir()
point_at(right)
assert tree_digest(root) != first
finally:
junction.rmdir()
assert (left / "content").read_bytes() == b"changed outside the tree"
assert (right / "content").read_bytes() == b"same bytes"