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.
130 lines
5.1 KiB
Python
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
|