Skip to content

[Test Improver] test: add unit tests for deps list, tree, and info subcommands#532

Merged
danielmeppiel merged 4 commits intomainfrom
test-assist/deps-list-tree-info-23878586888-9a438426396fded3
Apr 2, 2026
Merged

[Test Improver] test: add unit tests for deps list, tree, and info subcommands#532
danielmeppiel merged 4 commits intomainfrom
test-assist/deps-list-tree-info-23878586888-9a438426396fded3

Conversation

@danielmeppiel
Copy link
Copy Markdown
Collaborator

@danielmeppiel danielmeppiel commented Apr 2, 2026

Description

Adds missing unit test coverage for the apm deps list, apm deps tree, and apm deps info subcommands. The clean and update subcommands already had dedicated test files — this PR fills the gap for the remaining three.

File added: tests/unit/test_deps_list_tree_info.py — 20 tests across 3 classes.

TestDepsListCommand (7 tests)

  • No apm_modules/ directory → "No APM dependencies installed" message
  • Package present in text-fallback mode → org/repo shown in output
  • --global flag routes to user scope (InstallScope.USER)
  • --all flag calls both project and user scopes
  • Empty apm_modules/ → "no valid packages" message
  • Orphaned package (not in apm.yml) → orphan warning shown
  • Version from apm.yml appears in output

TestDepsTreeCommand (5 tests)

  • No apm_modules/ + no Rich → "No dependencies installed" in text output
  • Package present with no lockfile → project name from apm.yml shown
  • --global flag routes to user scope
  • With mocked lockfile → dep_key@version shown in text tree
  • Project name is read from apm.yml root node

TestDepsInfoCommand (8 tests)

  • No apm_modules/ → exit code 1
  • Package not found → exit code 1 with error message
  • Package details (name, version, description, author) in fallback mode
  • Short name resolution (shortrepo finds shortorg/shortrepo)
  • Not found → lists available packages as hint
  • Skill-only package (SKILL.md, no apm.yml) → exit 0 with repo name
  • Bare package → "No context files found" message
  • Bare package → "No agent workflows found" message

Infrastructure

All imports in deps/cli.py are lazy (inside function bodies), so the tests use:

  • patch("apm_cli.core.scope.get_apm_dir", ...) for scope routing
  • A _force_rich_fallback() helper using types.ModuleType stubs (with __getattr__ raising ImportError) to exercise the click.echo text paths captured by CliRunner
  • A shared _DepsCmdBase class providing setup_method, teardown_method, and _chdir_tmp — inherited by all three test classes to eliminate duplication

Type of change

  • Bug fix
  • New feature
  • Documentation
  • Maintenance / refactor

Testing

  • Tested locally
  • All existing tests pass
  • Added tests for new functionality (if applicable)

20 tests covering the previously untested list, tree, and info CLI
subcommands in src/apm_cli/commands/deps/cli.py:

- TestDepsListCommand (7): no apm_modules, package shown in text
  fallback, --global flag, --all flag (both scopes), empty apm_modules,
  orphaned-package warning, version display
- TestDepsTreeCommand (5): no apm_modules fallback, project name from
  apm.yml, --global flag, tree with lockfile data, package with no
  lockfile
- TestDepsInfoCommand (8): missing apm_modules, package not found,
  package details (name/version/description/author), short-name
  resolution, available-packages hint on error, skill-only package,
  no-context-files message, no-workflows message

Uses a _force_rich_fallback() helper that removes rich.* modules from
sys.modules to exercise the click.echo text paths testable via
CliRunner captured stdout.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@danielmeppiel danielmeppiel marked this pull request as ready for review April 2, 2026 12:07
Copilot AI review requested due to automatic review settings April 2, 2026 12:07
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds missing unit test coverage for the user-facing apm deps list, apm deps tree, and apm deps info subcommands, aligning these commands with existing test coverage for other deps subcommands.

Changes:

  • Added a new unit test module covering deps list, deps tree, and deps info behaviors across project/global scope routing and fallback (no-Rich) rendering paths.
  • Added coverage for common edge cases (missing apm_modules/, orphan detection, lockfile-backed tree rendering, short-name resolution, and missing context/workflow outputs).

Comment thread tests/unit/test_deps_list_tree_info.py Outdated
Comment on lines +16 to +52
def _force_rich_fallback():
"""Context-manager patches that force the text-only code path.

Rich imports inside function bodies are resolved from ``sys.modules`` at
call time, so we stub out the modules there instead of the per-attribute
path used when the symbols are in a module-level namespace.
"""
import contextlib

@contextlib.contextmanager
def _ctx():
fakes = {
"rich": None,
"rich.console": None,
"rich.table": None,
"rich.tree": None,
"rich.panel": None,
"rich.text": None,
}
# Stash originals (None if not imported yet)
originals = {k: sys.modules.get(k) for k in fakes}
# Mark each as failed import by removing from sys.modules so the
# ``from rich.xxx import Yyy`` inside function bodies raises ImportError
for k in fakes:
sys.modules.pop(k, None)
# Now install a sentinel module that raises on attribute access
sentinel = MagicMock()
sentinel.__path__ = [] # make it look like a package

class _BrokenModule:
def __getattr__(self, name):
raise ImportError(f"rich not available in test")

broken = _BrokenModule()
broken.__path__ = []
for k in fakes:
sys.modules[k] = broken # type: ignore[assignment]
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

_force_rich_fallback() injects instances of a custom class into sys.modules for "rich" and submodules. Importlib typically expects real module objects (types.ModuleType) with spec/loader semantics; using non-modules can lead to brittle behavior if other code does import rich (vs from rich.x import y) or if future Python/importlib behavior changes. Consider creating ModuleType stubs (and optionally making import rich itself fail) and remove the unused sentinel variable to keep the helper minimal and predictable.

Copilot uses AI. Check for mistakes.
Comment on lines +68 to +91
def setup_method(self):
self.runner = CliRunner()
try:
self.original_dir = os.getcwd()
except FileNotFoundError:
self.original_dir = str(Path(__file__).parent.parent.parent)
os.chdir(self.original_dir)

def teardown_method(self):
try:
os.chdir(self.original_dir)
except (FileNotFoundError, OSError):
repo_root = Path(__file__).parent.parent.parent
os.chdir(str(repo_root))

@contextlib.contextmanager
def _chdir_tmp(self):
with tempfile.TemporaryDirectory() as tmp_dir:
try:
os.chdir(tmp_dir)
yield Path(tmp_dir)
finally:
os.chdir(self.original_dir)

Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

This test module repeats identical setup_method/teardown_method and _chdir_tmp helpers across three classes. Consolidating into a shared base class or (preferably) a pytest fixture would reduce duplication and make future updates (e.g., CWD handling) less error-prone.

Copilot uses AI. Check for mistakes.
@danielmeppiel
Copy link
Copy Markdown
Collaborator Author

@copilot address pr review comments

…te test base class

Agent-Logs-Url: https://github.com/microsoft/apm/sessions/219ed7d3-f6bf-48a0-966c-4185448421f1

Co-authored-by: danielmeppiel <51440732+danielmeppiel@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 2, 2026

@copilot address pr review comments

Both review comments addressed in commit 5e1ae9e:

  1. _force_rich_fallback() stubs — replaced the custom _BrokenModule class with proper types.ModuleType stubs. Each stub gets __path__ = [] (marks it as a package) and a __getattr__ that raises ImportError. The unused sentinel = MagicMock() variable was removed.

  2. Duplicated setup helpers — extracted setup_method, teardown_method, and _chdir_tmp into a single _DepsCmdBase class. All three test classes now inherit from it, reducing ~45 lines of duplication.

All 20 tests still pass.

@danielmeppiel danielmeppiel merged commit c625704 into main Apr 2, 2026
5 checks passed
@danielmeppiel danielmeppiel deleted the test-assist/deps-list-tree-info-23878586888-9a438426396fded3 branch April 2, 2026 12:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants