Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 82 additions & 0 deletions tests/tools/test_lazy_deps_managed.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
"""Tests for lazy_deps.ensure() managed-install guard (issue #48628)."""

from __future__ import annotations

from unittest.mock import patch

import pytest

from tools.lazy_deps import FeatureUnavailable, ensure


class TestEnsureManagedInstallGuard:
"""Verify that ensure() raises early on managed/package-manager installs."""

def test_raises_when_managed(self, monkeypatch):
"""Managed install (NixOS/Homebrew) β†’ FeatureUnavailable before any pip attempt."""
# Pick a feature whose deps are NOT installed so ensure() doesn't
# short-circuit at the ``if not missing: return`` check.
# We mock feature_missing to return a non-empty tuple.
monkeypatch.setattr(
"tools.lazy_deps.feature_missing",
lambda _feat: ("some-pkg==1.0",),
)
with (
patch("hermes_cli.config.is_managed", return_value=True),
patch("hermes_cli.config.get_managed_system", return_value="NixOS"),
):
with pytest.raises(FeatureUnavailable, match="cannot install on NixOS"):
ensure("provider.anthropic")

def test_proceeds_when_not_managed(self, monkeypatch):
"""Non-managed install β†’ does NOT raise at the managed guard."""
# Mock feature_missing to return empty (all deps satisfied) so
# ensure() returns cleanly without reaching the install path.
monkeypatch.setattr(
"tools.lazy_deps.feature_missing",
lambda _feat: (),
)
with (
patch("hermes_cli.config.is_managed", return_value=False),
):
# Should return without error (deps already satisfied).
ensure("provider.anthropic")

def test_proceeds_when_config_unreadable(self, monkeypatch):
"""Config import failure β†’ fail open, proceed normally."""
monkeypatch.setattr(
"tools.lazy_deps.feature_missing",
lambda _feat: (),
)
# Simulate config import failure by making is_managed raise.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

feature_missing() returns empty here, so ensure() exits at its early no-missing-dependencies return before calling the mocked is_managed(). Return a missing spec, stub _venv_pip_install, and assert the fallback proceeds after the simulated config failure.

with patch(
"hermes_cli.config.is_managed",
side_effect=ImportError("no module"),
):
ensure("provider.anthropic")

def test_homebrew_managed_message(self, monkeypatch):
"""Homebrew managed install β†’ message mentions Homebrew."""
monkeypatch.setattr(
"tools.lazy_deps.feature_missing",
lambda _feat: ("some-pkg==1.0",),
)
with (
patch("hermes_cli.config.is_managed", return_value=True),
patch("hermes_cli.config.get_managed_system", return_value="Homebrew"),
):
with pytest.raises(FeatureUnavailable, match="cannot install on Homebrew"):
ensure("provider.anthropic")

def test_unknown_managed_system_fallback(self, monkeypatch):
"""Unknown managed system β†’ generic 'managed' in message."""
monkeypatch.setattr(
"tools.lazy_deps.feature_missing",
lambda _feat: ("some-pkg==1.0",),
)
with (
patch("hermes_cli.config.is_managed", return_value=True),
patch("hermes_cli.config.get_managed_system", return_value=None),
):
with pytest.raises(FeatureUnavailable, match="cannot install on managed"):
ensure("provider.anthropic")
19 changes: 19 additions & 0 deletions tools/lazy_deps.py
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,25 @@ def ensure(feature: str, *, prompt: bool = True) -> None:
if not missing:
return

# Managed installs (NixOS, Homebrew) have read-only package stores;
# attempting pip install would bootstrap ensurepip (~15s CPU) only to
# fail against the read-only filesystem. Bail out early with a
# clear message directing the user to the system package manager.
try:
from hermes_cli.config import get_managed_system, is_managed

if is_managed():
mgr = get_managed_system() or "managed"
raise FeatureUnavailable(
feature, missing,
f"cannot install on {mgr} system; "

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reason is later formatted by FeatureUnavailable._format(), which always appends uv pip install and pip install guidance. That contradicts the managed-install remediation; use a managed-specific rendered hint or suppress the generic pip command for this case.

"install deps via system package manager",
)
except FeatureUnavailable:
raise
except Exception:
pass # config unreadable β€” proceed normally

# Validate every spec against the allowlist + safety regex. Belt and
# braces β€” the keys-in-LAZY_DEPS check above already constrains this.
for spec in missing:
Expand Down
Loading