Skip to content

Test coverage: NuGet/update/install files reverted by #654 (issue #630)#671

Merged
azchohfi merged 3 commits into
mainfrom
azchohfi-azchohfi-tests-nuget-revert
Jul 17, 2026
Merged

Test coverage: NuGet/update/install files reverted by #654 (issue #630)#671
azchohfi merged 3 commits into
mainfrom
azchohfi-azchohfi-tests-nuget-revert

Conversation

@azchohfi

Copy link
Copy Markdown
Collaborator

What

Raises per-file test coverage to ≥95% for three files that PR #654 restored to their pre-migration state (issue #630):

File Before After
Commands/UpdateCommand.cs 3.4% 100%
Services/PackageInstallationService.cs 65.6% 100%
Services/NugetService.cs 85.2% 99.4%

NugetService.cs's only remaining uncovered lines are a genuinely-unreachable defensive guard in InstallPackageRecursiveAsync (public entry seeds an empty dict and ResolveDependenciesAsync skips present deps before recursing), documented in-source with a <remarks> referencing #630. No [ExcludeFromCodeCoverage], no coverage.runsettings edits.

Product changes (minimal, behavior-preserving)

  • Two internal static Func test seams on NugetService: HttpGetAsync (defaults to the shared HttpClient.GetAsync — the single real network boundary; tests inject canned in-memory responses) and GetUserProfileDirectory (defaults to Environment.GetFolderPath(UserProfile); lets the default %USERPROFILE%\.nuget\packages create-branch be exercised hermetically against a temp dir instead of mutating the real user profile). Both default delegates are byte-for-byte the original behavior.
  • One correctness fix (called out separately): ResolveDependenciesAsync now normalizes a dependency's version via ParseMinimumVersion before recursing into GetPackageDependenciesAsync. Previously the raw value from FetchDirectDependenciesAsync (which strips only brackets, "[2.0.0, )""2.0.0, ") built a malformed flat-container URL, silently dropping transitive dependencies nested under a range-versioned package. No-op for exact versions; the sibling on-disk path and the direct consumer already applied this normalization. The offline test fakes now match the slash-delimited /<id>/<version>/ URL segment so this path genuinely 404s without the fix (the transitive test fails if the fix is reverted).

Tests

  • NugetServiceOfflineTests — restore/download/version-resolution/transitive+diamond-dedup, listed-version filtering, nuspec parsing, HTTP-error paths, the hermetic default-fallback create-branch, and the range-versioned transitive walk. [DoNotParallelize]; all process-wide seams + NuGet env vars saved/restored per test.
  • PackageInstallationServiceTests, UpdateCommandTests — version resolution, already-present top-up merges, updates-found/all-up-to-date/preview, per-package + build-tools failure paths.

Validation

  • Debug solution + Release (both WinApp.Cli and WinApp.Cli.Tests, warnings-as-errors): 0 W / 0 E each.
  • Scoped coverage run: 97/97 passed, deterministic.
  • Reviewed with the repo pr-review skill (7 specialists + a GPT multi-model cross-check) before opening; the two accepted findings (real-profile mutation risk + a fake that masked the range-version bug) are fixed in this branch.

⚠️ Caveat

This wave re-covers the pre-migration NuGet client that #654 restored (#654 reverted the #629 migration + its #647 tests together). If maintainers re-attempt the migration, these three test files and the two seams will likely need rework. The ParseMinimumVersion correctness fix has standalone value while this code ships.

azchohfi and others added 2 commits July 16, 2026 18:15
PR #654 reverted the #629 NuGet-client migration and its coverage tests
(#647) together, restoring UpdateCommand, PackageInstallationService, and
NugetService to their pre-migration versions and deleting their dedicated
test files. This re-covers that current (pre-migration) code on main.

Product change (minimal, behavior-preserving test seam only):
- NugetService: add internal static HttpGetAsync delegate defaulting to the
  shared HttpClient.GetAsync (production behavior byte-for-byte unchanged);
  route the four flat-container/registration GETs through it so download,
  version-resolution, and dependency-parsing paths run offline in tests.
- Document two genuine, non-unit-coverable ceilings in source via <remarks>
  referencing issue #630: the %USERPROFILE%\.nuget\packages Create() branch
  (GetFolderPath ignores env redirection) and the defensive ContainsKey
  short-circuit in InstallPackageRecursiveAsync (unreachable via public API).

Tests (meaningful use-case first, then residual-branch units):
- NugetServiceOfflineTests.cs (29): restore/download/version-resolution/
  source-handling, transitive + diamond dedup, listed-version filtering,
  nuspec dependency parsing, error paths. [DoNotParallelize]; the HttpGetAsync
  seam and cache/env vars are saved and restored per test.
- PackageInstallationServiceTests.cs (16): version resolution (config/pinned/
  ignoreConfig), already-present transitive top-up, fresh-install merge,
  EnsurePackageAsync success/failure, workspace init.
- UpdateCommandTests.cs (11): version compare, updates found/none, preview
  mode, per-package + build-tools failures, runtime handling, JSON/output.

Coverage: UpdateCommand 147/147 (100%), PackageInstallationService 131/131
(100%), NugetService 302/307 (98.37%; the 5 uncovered lines are the two
documented ceilings above).

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Two review-driven fixes, both raising real coverage without gaming:

1. Correctness (transitive deps under a version range): ResolveDependenciesAsync
   now normalizes a dependency's version via ParseMinimumVersion before recursing
   into GetPackageDependenciesAsync. FetchDirectDependenciesAsync only strips
   brackets ("[2.0.0, )" -> "2.0.0, "), so the raw value built a malformed
   flat-container URL and silently dropped dependencies nested under a
   range-versioned package. The offline fakes now match the slash-delimited
   "/<id>/<version>/" segment so a malformed URL genuinely 404s, making the
   transitive test fail without the fix.

2. Test hygiene / no machine mutation: extract a GetUserProfileDirectory seam
   (defaults to Environment.GetFolderPath(UserProfile)) so the default
   %USERPROFILE%\.nuget\packages create-branch is exercised hermetically against
   a temp dir instead of being a documented ceiling that would otherwise mutate
   the real user profile. Added a test pinning the seam's production default.

NugetService.cs 98.4% -> 99.4% (only the genuinely-unreachable defensive guard
remains, documented). PackageInstallationService.cs / UpdateCommand.cs stay 100%.
Debug + Release (both projects) 0W/0E; 97/97 scoped tests pass.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 17, 2026 02:25

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Improves unit-test coverage for the restored (pre-migration) NuGet/update/install code paths, primarily by adding offline/deterministic tests and introducing limited test seams in NugetService, plus a small correctness fix for transitive dependency walking when dependency versions are specified as ranges.

Changes:

  • Add offline NugetService tests using an injectable HTTP GET seam and hermetic user-profile resolution.
  • Add behavior tests for winapp update and PackageInstallationService with controllable fakes.
  • Fix NugetService.GetPackageDependenciesAsync transitive recursion to normalize range versions via ParseMinimumVersion.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
src/winapp-CLI/WinApp.Cli/Services/NugetService.cs Adds test seams for HTTP/profile path and fixes transitive dependency recursion for range versions.
src/winapp-CLI/WinApp.Cli.Tests/UpdateCommandTests.cs Adds behavior coverage for winapp update handler paths via controllable fakes.
src/winapp-CLI/WinApp.Cli.Tests/PackageInstallationServiceTests.cs Adds branch-level coverage for install/version-resolution/top-up behavior using an in-memory INugetService fake.
src/winapp-CLI/WinApp.Cli.Tests/NugetServiceOfflineTests.cs Adds hermetic, offline coverage for NuGet download/extract/deps/version-listing/cache-path behaviors.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

// transitive nuspec URL is well-formed. FetchDirectDependenciesAsync only strips brackets
// (e.g. "[2.0.0, )" -> "2.0.0, "), which would otherwise build a malformed flat-container
// URL and silently drop transitive dependencies nested under a range-versioned package.
var transitiveDeps = await GetPackageDependenciesAsync(depId, ParseMinimumVersion(depVersion), cancellationToken);

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Good catch — fixed in 23f80bd. An open-lower-bound/unparseable range (e.g. (,2.0.0]) normalizes to empty, so the loop now skips the transitive fetch when the minimum version is empty (mirroring the existing on-disk ResolveDependenciesAsync guard) instead of building a malformed .../<id>//<id>.nuspec URL. The direct dependency is still returned; added a covering test with a unique package id to avoid the static DependencyCache colliding with other tests.

@github-actions

github-actions Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Build Metrics Report

Binary Sizes

Artifact Baseline Current Delta
CLI (ARM64) 32.40 MB 32.40 MB ✅ 0.0 KB (0.00%)
CLI (x64) 32.70 MB 32.70 MB 📉 -0.5 KB (-0.00%)
MSIX (ARM64) N/A 13.57 MB N/A
MSIX (x64) N/A 14.43 MB N/A
NPM Package N/A 28.32 MB N/A
NuGet Package N/A 28.33 MB N/A

Test Results

3492 passed, 4 skipped out of 3496 tests in 601.9s (+58 tests, +73.9s vs. baseline)

Test Coverage

94.8% line coverage, 89% branch coverage · ✅ +1.0% vs. baseline

CLI Startup Time

33ms median (x64, winapp --version) · ✅ -9ms vs. baseline


Updated 2026-07-17 03:14:55 UTC · commit 23f80bd · workflow run

…review)

Addresses the Copilot PR-review finding on the ParseMinimumVersion normalization:
an open-lower-bound or otherwise unparseable range (e.g. "(,2.0.0]") normalizes to
an empty string, which would recurse into GetPackageDependenciesAsync with an empty
version and build a malformed ".../<id>//<id>.nuspec" flat-container URL (pointless
work + a bogus empty-version cache key). Skip the transitive fetch when the minimum
version is empty, mirroring the existing on-disk ResolveDependenciesAsync guard; the
direct dependency is already in the result set. Added a covering test (unique package
id to avoid the static DependencyCache colliding with other tests).

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@azchohfi
azchohfi merged commit 5beb701 into main Jul 17, 2026
22 checks passed
@azchohfi
azchohfi deleted the azchohfi-azchohfi-tests-nuget-revert branch July 17, 2026 03:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants