Files
hermes-agent/tests/hermes_cli/test_home_directory_diagnostics.py
ethernet 071ccfbcdf test: restore platforms() host gating that merges reverted to the legacy trio
Four origin/main merges brought back `linux_only` / `macos_only` /
`windows_only` marks in 41 test files, along with the pre-platforms()
versions of scripts/ci/list_os_marked_tests.py and check_os_marker_fakes.py.
Because the legacy names are no longer registered, pytest treated them as
unknown marks — a warning — so every Windows- or macOS-only test RAN on
Linux (test_local_runtime_recovery.py tripped the live-system kill guard).

Rewrite the marks, restore the platforms()-aware CI scripts (keeping main's
os.walk fix for vanishing __pycache__ dirs), drop the stale _BASELINE entries,
and make the conftest reject the retired marks outright so the next merge
cannot resurrect them silently.
2026-09-18 19:06:52 -04:00

130 lines
5.1 KiB
Python

"""Home initialization must respect operator-owned links and diagnose storage."""
import stat
from pathlib import Path
import pytest
from hermes_cli import config
@pytest.mark.platforms("linux")
@pytest.mark.parametrize("subdir", (".", *config._HERMES_HOME_SUBDIRS))
def test_unavailable_directory_links_are_diagnosed_without_creating_targets(tmp_path, monkeypatch, subdir):
home = tmp_path / "hermes"
link = home / subdir
link.parent.mkdir(parents=True, exist_ok=True)
target = tmp_path / "unmounted" / "external"
link.symlink_to(target, target_is_directory=True)
monkeypatch.setattr(config, "get_hermes_home", lambda: home)
monkeypatch.setattr(config, "is_managed", lambda: False)
config._HERMES_HOME_ENSURED.discard(str(home))
issues = config.validate_config_structure()
assert issues
text = " ".join(str(issue) for issue in issues)
assert str(link) in text and str(target) in text
assert "mount" in text.lower() and "setup" not in text.lower()
assert link.is_symlink() and link.readlink() == target
assert not target.parent.exists()
assert str(home) not in config._HERMES_HOME_ENSURED
target.mkdir(parents=True, mode=0o750)
config.ensure_hermes_home()
assert link.is_symlink() and target.stat().st_mode & 0o777 == 0o750
assert str(home) in config._HERMES_HOME_ENSURED
@pytest.mark.platforms("linux")
@pytest.mark.parametrize("linked", ("plain", "logs", "home"))
def test_initialization_preserves_external_directory_modes(tmp_path, monkeypatch, linked):
home = tmp_path / "hermes"
target = tmp_path / "shared"
target.mkdir(mode=0o750)
if linked == "home":
home.symlink_to(target, target_is_directory=True)
else:
home.mkdir()
curator = target / "curator"
curator.mkdir(mode=0o750)
if linked == "logs":
(home / "logs").symlink_to(target, target_is_directory=True)
monkeypatch.setattr(config, "get_hermes_home", lambda: home)
monkeypatch.setattr(config, "is_managed", lambda: False)
config._HERMES_HOME_ENSURED.discard(str(home))
config.ensure_hermes_home()
monkeypatch.setattr(config, "get_hermes_home", lambda: home.resolve())
config.ensure_hermes_home()
assert all((home / name).is_dir() for name in config._HERMES_HOME_SUBDIRS)
assert (home / "SOUL.md").is_file()
if linked != "plain":
assert (home if linked == "home" else home / "logs").is_symlink()
assert target.stat().st_mode & 0o777 == 0o750
assert curator.stat().st_mode & 0o777 == 0o750
else:
assert (home / "logs").stat().st_mode & 0o777 == 0o700
@pytest.mark.platforms("linux")
@pytest.mark.parametrize("existing", (False, True))
def test_symlinked_parent_above_home_is_not_an_operator_home_link(
tmp_path, monkeypatch, existing
):
"""A link above HERMES_HOME is not an operator-owned home link.
macOS aliases ``/tmp`` -> ``/private/tmp`` and ``/var`` -> ``/private/var``, so a home
under the default temp root arrives with a symlinked parent; the same happens for any
user whose own directory is aliased. The home and its subdirectories are still ours to
secure: they must end up 0o700, both when created fresh and when a previous run left them
at the 0o755 default.
"""
real_root = tmp_path / "real"
real_root.mkdir()
alias = tmp_path / "alias"
alias.symlink_to(real_root, target_is_directory=True)
home = alias / "hermes"
if existing:
home.mkdir(parents=True, mode=0o755)
for name in config._HERMES_HOME_SUBDIRS:
(home / name).mkdir(mode=0o755)
assert stat.S_IMODE(home.stat().st_mode) == 0o755
monkeypatch.setattr(config, "get_hermes_home", lambda: home)
monkeypatch.setattr(config, "is_managed", lambda: False)
config._HERMES_HOME_ENSURED.discard(str(home))
config._HERMES_HOME_ENSURED.discard(str(home.resolve()))
config.ensure_hermes_home()
assert stat.S_IMODE(home.stat().st_mode) == 0o700
for name in config._HERMES_HOME_SUBDIRS:
mode = stat.S_IMODE((home / name).stat().st_mode)
assert mode == 0o700, f"{name} should be 0700, got 0o{mode:o}"
@pytest.mark.platforms("linux")
def test_aliased_parent_still_leaves_an_operator_home_link_alone(tmp_path, monkeypatch):
"""An operator-owned link at the home boundary keeps owning the mode, aliased parent or not."""
real_root = tmp_path / "real"
real_root.mkdir()
alias = tmp_path / "alias"
alias.symlink_to(real_root, target_is_directory=True)
shared = real_root / "shared"
shared.mkdir(mode=0o750)
(shared / "curator").mkdir(mode=0o750)
home = alias / "hermes"
home.symlink_to(shared, target_is_directory=True)
monkeypatch.setattr(config, "get_hermes_home", lambda: home)
monkeypatch.setattr(config, "is_managed", lambda: False)
config._HERMES_HOME_ENSURED.discard(str(home))
config._HERMES_HOME_ENSURED.discard(str(home.resolve()))
config.ensure_hermes_home()
assert home.is_symlink() and home.readlink() == shared
assert stat.S_IMODE(shared.stat().st_mode) == 0o750
assert stat.S_IMODE((shared / "curator").stat().st_mode) == 0o750