Skip to content
Open
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
8 changes: 7 additions & 1 deletion src/apm_cli/integration/base_integrator.py
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,13 @@ def init_link_resolver(self, package_info, project_root: Path) -> None:
"""Initialise and register the link resolver for a package."""
self.link_resolver = UnifiedLinkResolver(project_root)
try:
primitives = discover_primitives(package_info.install_path)
scan_root = package_info.install_path
# When install_path is $HOME (user-scope local package),
# only scan the .apm/ subdirectory to avoid recursive-
# globbing the entire home tree. See issue #830.
if scan_root == Path.home():
scan_root = scan_root / ".apm"
primitives = discover_primitives(scan_root)
self.link_resolver.register_contexts(primitives)
except Exception:
self.link_resolver = None
Expand Down
39 changes: 39 additions & 0 deletions tests/unit/integration/test_base_integrator.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from unittest.mock import MagicMock, patch

from apm_cli.integration.base_integrator import BaseIntegrator, IntegrationResult
from apm_cli.primitives.discovery import discover_primitives


# ---------------------------------------------------------------------------
Expand Down Expand Up @@ -592,3 +593,41 @@ class TestShouldIntegrate:
def test_always_returns_true(self):
bi = BaseIntegrator()
assert bi.should_integrate(Path("/any/path")) is True


# ---------------------------------------------------------------------------
# init_link_resolver — home-directory scoping (#830)
# ---------------------------------------------------------------------------

class TestInitLinkResolverHomeScoping:
"""When install_path is $HOME, init_link_resolver must scope
discover_primitives to ~/.apm/ to avoid recursive-globbing the
entire home directory. See issue #830."""

@patch("apm_cli.integration.base_integrator.discover_primitives")
@patch("apm_cli.integration.base_integrator.UnifiedLinkResolver")
def test_scopes_to_apm_subdir_when_install_path_is_home(
self, mock_resolver_cls, mock_discover
):
mock_discover.return_value = []
bi = BaseIntegrator()
pkg_info = MagicMock()
pkg_info.install_path = Path.home()

bi.init_link_resolver(pkg_info, Path.home())

mock_discover.assert_called_once_with(Path.home() / ".apm")

@patch("apm_cli.integration.base_integrator.discover_primitives")
@patch("apm_cli.integration.base_integrator.UnifiedLinkResolver")
def test_uses_install_path_when_not_home(
self, mock_resolver_cls, mock_discover, tmp_path
):
mock_discover.return_value = []
bi = BaseIntegrator()
pkg_info = MagicMock()
pkg_info.install_path = tmp_path

bi.init_link_resolver(pkg_info, tmp_path)

mock_discover.assert_called_once_with(tmp_path)
18 changes: 17 additions & 1 deletion tests/unit/test_local_content_install.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,14 +174,30 @@ def test_skips_root_skill_md(self, mock_integrate, tmp_path):

@patch("apm_cli.install.services.integrate_package_primitives")
def test_package_info_install_path_is_project_root(self, mock_integrate, tmp_path):
"""The synthetic PackageInfo must point to project_root, not .apm/."""
"""The synthetic PackageInfo must point to project_root at project scope."""
mock_integrate.return_value = _zero_counters()

_integrate_local_content(tmp_path, **_make_integrators())

package_info = mock_integrate.call_args[0][0]
assert package_info.install_path == tmp_path

@patch("apm_cli.install.services.integrate_package_primitives")
def test_user_scope_install_path_stays_project_root(self, mock_integrate, tmp_path):
"""At user scope, install_path must remain project_root so that
integrators can still find <project_root>/.apm/<type>/.
The recursive-glob fix lives in init_link_resolver, not here.
Regression check for #830."""
from apm_cli.core.scope import InstallScope

mock_integrate.return_value = _zero_counters()
(tmp_path / ".apm").mkdir(exist_ok=True)

_integrate_local_content(tmp_path, **_make_integrators(), scope=InstallScope.USER)

package_info = mock_integrate.call_args[0][0]
assert package_info.install_path == tmp_path

Comment thread
guwenqing marked this conversation as resolved.
@patch("apm_cli.install.services.integrate_package_primitives")
def test_returns_zero_counters_when_nothing_deployed(self, mock_integrate, tmp_path):
"""When nothing is deployed the result counters are all zero."""
Expand Down