[Test Improver] test: add unit tests for install/sources.py DependencySource classes#927
Conversation
Adds 31 focused tests for LocalDependencySource, CachedDependencySource, FreshDependencySource and the make_dependency_source factory. Coverage highlights: - make_dependency_source factory routing (local/cached/fresh) - LocalDependencySource user-scope skip with diagnostic - CachedDependencySource no-targets early return and full acquire - FreshDependencySource hash mismatch abort (sys.exit(1)) - FreshDependencySource update_refs bypasses hash check - Pre-downloaded result used without calling downloader Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
APM Review Panel VerdictDisposition: APPROVE (with one pre-merge fix -- assert Per-persona findingsPython Architect: This is a routine test addition (716 lines, 31 tests) for 1. OO / class diagramclassDiagram
direction LR
class DependencySource {
<<Strategy>>
+ctx InstallContext
+dep_ref Any
+install_path Path
+dep_key str
+acquire() Materialization
}
class LocalDependencySource {
<<ConcreteStrategy>>
+INTEGRATE_ERROR_PREFIX str
+acquire() Materialization
}
class CachedDependencySource {
<<ConcreteStrategy>>
+INTEGRATE_ERROR_PREFIX str
+resolved_ref Any
+dep_locked_chk Any
+acquire() Materialization
}
class FreshDependencySource {
<<ConcreteStrategy>>
+resolved_ref Any
+dep_locked_chk Any
+ref_changed bool
+progress Any
+acquire() Materialization
}
class Materialization {
<<ValueObject>>
+package_info PackageInfo
+install_path Path
+dep_key str
+deltas Dict
}
DependencySource <|-- LocalDependencySource
DependencySource <|-- CachedDependencySource
DependencySource <|-- FreshDependencySource
DependencySource ..> Materialization : returns
note for DependencySource "Strategy: Local | Cached | Fresh\nmake_dependency_source() is the factory"
class LocalDependencySource:::touched
class CachedDependencySource:::touched
class FreshDependencySource:::touched
class Materialization:::touched
classDef touched fill:#fff3b0,stroke:#d47600
2. Execution flow diagramflowchart TD
A["make_dependency_source(ctx, dep_ref, ...)"] --> B{dep_ref.is_local?}
B -->|Yes| C["LocalDependencySource.acquire()"]
B -->|No + skip_download| D["CachedDependencySource.acquire()"]
B -->|No + needs download| E["FreshDependencySource.acquire()"]
C --> F{ctx.scope == USER?}
F -->|Yes| G["diagnostics.warn + return None"]
F -->|No| H["[FS] _copy_local_package(dep_ref, install_path)"]
H -->|None| I["diagnostics.error + return None"]
H -->|Path| J["[FS] build PackageInfo -> Materialization"]
D --> K{ctx.targets empty?}
K -->|Yes| L["return Materialization(package_info=None)"]
K -->|No| M["[FS] load apm.yml -> PackageInfo -> Materialization"]
E --> N["[NET] build_download_ref + downloader.download_package()"]
N -->|Exception| O["diagnostics.error + return None"]
N -->|Success| P{hash check: update_refs=False AND content_hash set?}
P -->|Mismatch| Q["[FS] safe_rmtree + _rich_error + sys.exit(1)"]
P -->|Match or skipped| R{ctx.targets empty?}
R -->|Yes| S["return Materialization(package_info=None)"]
R -->|No| T["return Materialization(package_info=pkg_info)"]
3. Design patternsDesign patterns
Issue (cosmetic, follow-up): CLI Logging Expert: Test-only PR -- no logging paths are modified. Key observations:
No logging concerns introduced by this PR. DevX UX Expert: Pure test PR -- no CLI command surface, flags, help text, or The tests implicitly confirm good UX behaviors that are worth preserving:
Minor: the assertion No UX concerns. Supply Chain Security Expert: This PR directly exercises the supply-chain hash verification boundary -- a critical path. Coverage is well-targeted. Strengths:
Required gap: Optional gap: No test for the case where Auth Expert: Not activated -- PR adds tests for OSS Growth Hacker: Test-only PR; no conversion surface is touched. No Side-channel note to CEO: explicit test coverage of the supply-chain abort path ( CEO arbitrationSpecialists are aligned on both the value and the gap. This is a focused, well-scoped test addition that covers a genuinely important security boundary (hash-mismatch abort) and the full factory dispatch matrix. The one required fix is concrete and small: Required actions before merge
Optional follow-ups
|
🤖 This PR was created by Test Improver, an automated AI assistant focused on improving test coverage for this repository.
Goal and Rationale
src/apm_cli/install/sources.py(595 lines) implements the Strategy pattern for the install pipeline —LocalDependencySource,CachedDependencySource, andFreshDependencySource— but only_format_package_type_labelhad tests (3 tests intest_sources_classification.py). Theacquire()methods and the factory function had zero coverage.These classes handle:
The hash-mismatch abort path is a security-critical invariant; a regression there would silently accept tampered packages.
Approach
31 focused unit tests across 6 test classes:
TestMaterializationTestMakeDependencySourceTestLocalDependencySourceAcquireTestCachedDependencySourceAcquireTestFreshDependencySourceAcquireupdate_refsbypass, pre-downloaded resultsTestIntegrateErrorPrefixHeavy mocking of
InstallContextkeeps tests fast and isolated from network/filesystem concerns.Coverage Impact
install/sources.py(_format_package_type_labelonly)Trade-offs
MagicMockforInstallContext— tests are isolated but don't exercise the full pipeline. Integration tests intests/integration/cover the end-to-end flow.pytest.raises(SystemExit)which is the correct way to assertsys.exit(1).Test Status
Reproducibility
uv sync --extra dev uv run pytest tests/unit/install/test_dependency_sources.py -v # Full suite (excluding pre-existing failure): uv run pytest tests/unit tests/test_console.py --ignore=tests/unit/commands/test_policy_status.py -q