"""Desktop hand-off self-heal for venvs bricked by the reverted macOS TCC anchor (#95759), exercised against the REAL ``posix.sh`` functions. The reverted anchor (#95425/#95541) left installs with a real-file ``venv/bin/python`` copy, a ``.tcc-anchor-source`` marker, and ``python3*`` aliases that die at interpreter init (``No module named 'encodings'``). ``venv/bin/hermes`` execs ``venv/bin/python3``, so the desktop update hand-off — and every other CLI entrypoint, doctor included — dies before any Python-side heal can run. The heal therefore lives in the hand-off shell. These tests drive ``posix.sh --self-test-tcc-heal`` (the script's real ``tcc_anchor_heal`` / ``tcc_pick_update_invoke`` functions, no re-implementation) against synthetic venv trees on Linux. macOS itself is NOT live-tested here — there is no mac runner — but the heal logic is platform-independent shell and the self-test path runs it without the ``uname`` gate. Interpreter stubs: the probe is `` -c 'import encodings'``; a stub that exits 0 stands in for a bootable interpreter and one that prints the real crash text and exits 1 stands in for a bricked one. """ from __future__ import annotations import os import stat import subprocess from pathlib import Path import pytest REPO_ROOT = Path(__file__).resolve().parent.parent POSIX_SH = REPO_ROOT / "scripts" / "desktop-update" / "posix.sh" GOOD_STUB = "#!/bin/bash\n# bootable interpreter stub\nexit 0\n" BAD_STUB = ( "#!/bin/bash\n" "echo \"Fatal Python error: init_fs_encoding: failed to get the Python " "codec of the filesystem encoding\" >&2\n" "echo \"ModuleNotFoundError: No module named 'encodings'\" >&2\n" "exit 1\n" ) # Boots only when invoked via a path whose basename is exactly `python`: # used to force the post-heal verification probe to fail (rollback path). PICKY_STUB = ( "#!/bin/bash\n" "[ \"$(basename \"$0\")\" = \"python\" ] && exit 0\n" "exit 1\n" ) requires_bash = pytest.mark.skipif( not os.path.exists("/bin/bash"), reason="posix.sh needs /bin/bash" ) def _write_exe(path: Path, content: str) -> None: path.write_text(content, encoding="utf-8") path.chmod(path.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH) def make_venv(tmp_path: Path, *, python: str | None, python3: str | None, marker: str | None, store_python: str | None = GOOD_STUB, alias_symlinks: bool = False) -> Path: """Build a synthetic install root with a venv/bin tree. ``python``/``python3`` are stub bodies (None = absent). ``marker`` is the ``.tcc-anchor-source`` content (None = absent, "STORE" = point it at the created store interpreter). ``store_python`` is the store interpreter stub body (None = do not create — the vanished-uv-store class). """ root = tmp_path / "install" bin_dir = root / "venv" / "bin" bin_dir.mkdir(parents=True) store = tmp_path / "uv-store" / "bin" / "python3.11" if store_python is not None: store.parent.mkdir(parents=True) _write_exe(store, store_python) if python is not None: _write_exe(bin_dir / "python", python) for name in ("python3", "python3.11"): if python3 is None: continue if alias_symlinks: (bin_dir / name).symlink_to("python") else: _write_exe(bin_dir / name, python3) if marker is not None: (bin_dir / ".tcc-anchor-source").write_text( str(store) if marker == "STORE" else marker, encoding="utf-8" ) return root def run_selftest(root: Path) -> str: proc = subprocess.run( ["/bin/bash", str(POSIX_SH), "--self-test-tcc-heal", "--no-ui", "--install-root", str(root)], capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=60, ) assert proc.returncode == 0, proc.stderr return proc.stdout.strip().splitlines()[-1] @requires_bash class TestAnchorHeal: def test_healthy_venv_untouched(self, tmp_path): """A bootable python3 short-circuits the heal — nothing is examined.""" root = make_venv(tmp_path, python=GOOD_STUB, python3=GOOD_STUB, marker="STORE") before = (root / "venv/bin/python3").read_text(encoding="utf-8") out = run_selftest(root) assert "state=healthy" in out assert (root / "venv/bin/python3").read_text(encoding="utf-8") == before assert (root / "venv/bin/.tcc-anchor-source").exists() def test_alias_brick_healed_to_real_files(self, tmp_path): """#95541 class: anchored copy boots, alias symlinks are dead. The heal must replace the aliases with REAL FILES of the anchor (an alias symlink onto the copy is the crash shape) and KEEP the marker so ensure_tcc_anchor recognises the layout as active (no ping-pong). """ root = make_venv(tmp_path, python=GOOD_STUB, python3=BAD_STUB, marker="STORE") out = run_selftest(root) assert "state=healed-aliases" in out bin_dir = root / "venv/bin" for name in ("python3", "python3.11"): alias = bin_dir / name assert not alias.is_symlink() assert alias.read_text(encoding="utf-8") == GOOD_STUB assert (bin_dir / ".tcc-anchor-source").exists() assert not list(bin_dir.glob("*tcc-heal*")) assert "invoke=" in out and out.endswith("/venv/bin/hermes") def test_full_brick_restored_to_symlinks(self, tmp_path): """Anchored copy AND aliases dead, marker source alive: restore the pre-anchor symlink layout and drop the marker.""" root = make_venv(tmp_path, python=BAD_STUB, python3=BAD_STUB, marker="STORE") out = run_selftest(root) assert "state=healed-symlinks" in out bin_dir = root / "venv/bin" py = bin_dir / "python" assert py.is_symlink() assert py.resolve() == (tmp_path / "uv-store/bin/python3.11") for name in ("python3", "python3.11"): alias = bin_dir / name assert alias.is_symlink() assert os.readlink(alias) == "python" assert not (bin_dir / ".tcc-anchor-source").exists() assert not list(bin_dir.glob("*tcc-heal*")) def test_missing_source_fails_closed(self, tmp_path): """Vanished uv store (the #95759 comment class): touch nothing.""" root = make_venv(tmp_path, python=BAD_STUB, python3=BAD_STUB, marker="STORE", store_python=None) out = run_selftest(root) assert "state=source-missing" in out bin_dir = root / "venv/bin" assert (bin_dir / "python").read_text(encoding="utf-8") == BAD_STUB assert (bin_dir / ".tcc-anchor-source").exists() def test_no_marker_is_not_ours_to_heal(self, tmp_path): root = make_venv(tmp_path, python=BAD_STUB, python3=BAD_STUB, marker=None) out = run_selftest(root) assert "state=no-marker" in out assert (root / "venv/bin/python").read_text(encoding="utf-8") == BAD_STUB def test_marker_inside_venv_is_unsafe(self, tmp_path): root = make_venv(tmp_path, python=BAD_STUB, python3=BAD_STUB, marker="PLACEHOLDER") bin_dir = root / "venv/bin" inside = bin_dir / "recorded-python" _write_exe(inside, GOOD_STUB) (bin_dir / ".tcc-anchor-source").write_text(str(inside), encoding="utf-8") out = run_selftest(root) assert "state=unsafe-source" in out assert (bin_dir / "python").read_text(encoding="utf-8") == BAD_STUB def test_relative_marker_is_invalid(self, tmp_path): root = make_venv(tmp_path, python=BAD_STUB, python3=BAD_STUB, marker="../python") out = run_selftest(root) assert "state=invalid-marker" in out def test_failed_verification_rolls_back(self, tmp_path): """If python3 still refuses to boot after the repair, every replaced file is restored and no staging/backup litter remains.""" root = make_venv(tmp_path, python=PICKY_STUB, python3=BAD_STUB, marker="STORE") out = run_selftest(root) assert "state=failed" in out bin_dir = root / "venv/bin" assert (bin_dir / "python3").read_text(encoding="utf-8") == BAD_STUB assert (bin_dir / "python3.11").read_text(encoding="utf-8") == BAD_STUB assert (bin_dir / ".tcc-anchor-source").exists() assert not list(bin_dir.glob("*tcc-heal*")) @requires_bash class TestUpdateInvokeFallback: def test_dead_alias_no_marker_falls_back_to_module_invocation(self, tmp_path): """No marker to heal from, but the venv python itself boots (the launchd-gateway shape): drive the update as python -m hermes_cli.main.""" root = make_venv(tmp_path, python=GOOD_STUB, python3=BAD_STUB, marker=None) out = run_selftest(root) assert "state=no-marker" in out assert out.endswith("/venv/bin/python -m hermes_cli.main") def test_healthy_venv_keeps_hermes_entrypoint(self, tmp_path): root = make_venv(tmp_path, python=GOOD_STUB, python3=GOOD_STUB, marker=None) out = run_selftest(root) assert out.endswith("/venv/bin/hermes") @requires_bash class TestHandoffSurvivesBrickAB: """A/B analog of the reported loop: `venv/bin/hermes` execs python3.""" def _make_hermes(self, root: Path) -> Path: hermes = root / "venv/bin/hermes" _write_exe( hermes, "#!/bin/bash\n" 'exec "$(cd "$(dirname "$0")" && pwd)/python3" -c "import encodings"\n', ) return hermes def test_bricked_entrypoint_fails_before_and_boots_after_heal(self, tmp_path): root = make_venv(tmp_path, python=GOOD_STUB, python3=BAD_STUB, marker="STORE") hermes = self._make_hermes(root) # BEFORE the heal: the entrypoint dies exactly like the field logs. before = subprocess.run([str(hermes)], capture_output=True, text=True, encoding="utf-8", errors="replace") assert before.returncode == 1 assert "No module named 'encodings'" in before.stderr # Heal (real posix.sh function), then the same entrypoint boots. assert "state=healed-aliases" in run_selftest(root) after = subprocess.run([str(hermes)], capture_output=True, text=True, encoding="utf-8", errors="replace") assert after.returncode == 0