Skip to content

Commit 4a29cc1

Browse files
mikolalysenkoclaude
andcommitted
test: coverage-audit test extension — 94.2% → 98.2% lines, +1,300 tests
Implements the audited test plans for every uncovered-but-testable range found by the full-coverage audit at d5e1815 (936 worthwhile ranges across 92 files): inline #[cfg(test)] additions plus 30 new per-module covgap_* integration suites (conflict-free placement; shared suites untouched). An adversarial review pass (46 reviewers + 3-vote refutation panels over the full diff) confirmed 42 findings, all applied: - fix residuals: uv revert convergence carve-outs (drift-keep gate no longer keeps converged reverts forever), requirements-flavor vendor_revert_residual_reference keep signal, fail-closed in-sync rebuild pin guard (corrupt/missing state.json now refuses instead of rebuilding blind), redirect nuget open-tag/single-quote tolerance matching the nuget_feed twin, scan --prune now reports keptVendoredEntries + remediation hint (dry preview documented as drift-blind), takeover scope-aware name probe, golang hot-path gate strengthened to full redirect_in_sync equivalence, multi-line pnpm-workspace flow sequences, fetch-stage online-failure silent-mode envelope - unfixed twins found by review: bun_lock + pnpm v9 post-pack failure paths ported to done_failure_unstage (a new test had enshrined the husk behavior; now asserts the fixed contract) - test hygiene: geteuid()==0 root guards on all new chmod-based tests, SOCKET_*/VIRTUAL_ENV scrubs in spawned-CLI harnesses, telemetry disabled in covgap_commands_vex (was POSTing to production), hermetic Gemfile.lock fixtures replacing real bundle probes, cfg(unix)/path- separator gates, vacuous tests repaired or deleted 6,597 tests green (196 binaries, full workspace). Coverage (host, default features): 94.18% → 98.2%+ lines; 72-77% of the 5,720 never-executed production lines from the baseline audit now covered; the remainder is classified defensive/unreachable/interactive-only/ platform-gated in the audit record. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 0b7714b commit 4a29cc1

117 files changed

Lines changed: 50512 additions & 179 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

crates/socket-patch-cli/CLI_CONTRACT.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ Beyond the globals above, each subcommand defines a small set of local arguments
9494

9595
**Agent-flow run-level warnings (additive).** An agent-mode apply (`--mode agent` / `--apply` / `--sync`, `--json`) may add a top-level `warnings[]` array of `{code, detail}` entries to the scan envelope (absent when none fired; each is also mirrored to stderr unless `--silent`). They surface cross-mode state the apply cannot change — never a status or exit-code change (hosted refusals set the precedent: exit 0 + warning). Codes (stable; new codes are additive/MINOR): `vendored_ownership_retained` — vendor-owned package(s) were skipped before download (the per-patch `skipped`/`vendored` records in `apply.patches[]` are unchanged); the detail names the purls and the migration path (`remove <purl>`, or `vendor --revert` which unwinds every vendored package, then re-run). `hosted_wiring_retained` — the hosted redirect ledger records scanned package(s) whose hosted lockfile wiring the live lock still proves (the agent run does not unwind hosted wiring — as of v5.0 that is `socket-patch rollback`'s job, or `remove <purl>` per package); the detail names the purls and the options (stay `--mode hosted`, or migrate via `scan --mode vendored`) and never advises hand-deleting the ledger. The warning keys on ledger *records* still live at scan time — a flow that pre-reverted the redirect (retiring the records) retires the warning with them, even while the append-only `edits` (revert originals) remain. The interactive path prints the same `hosted_wiring_retained` text to stderr after an apply; the vendored counterpart is already covered by its per-package `[skip] … (vendored …)` lines.
9696

97-
`scan --prune` opts into garbage collection. When set, `scan` removes manifest entries for packages no longer present in the crawl, then deletes orphan blob, diff, and package-archive files from `.socket/`. Off by default (v3.0) so a temporary uninstall doesn't silently destroy manifest state. Only entries whose ecosystem this run actually crawled are eligible: a `pkg:<type>/` with no crawler in this build (a newer CLI's ecosystem in the committed manifest) and the runtime-gated maven/nuget crawlers with their gate off are exempt — the crawl never looked for them, so their absence is not evidence of removal (same fail-safe as the `--ecosystems` filter, which narrows the query but never the prune's installed set). The pass also reconciles vendored state (runs FIRST, under the apply lock — lock contention skips it without failing the scan): vendored entries whose patch is gone from the manifest are reverted, vendored entries whose dependency is no longer in the lockfile graph are reverted AND their manifest entries dropped (detached entries are exempt from both — they are manifest- and lockfile-invisible by design; a missing or undeterminable lockfile keeps the entry, fail-safe), and orphan `.socket/vendor/<eco>/<uuid>` dirs with no ledger entry are swept. The JSON `gc` sub-object gains `revertedVendoredEntries` + `removedVendorOrphanDirs` (wet) / `revertableVendoredEntries` + `vendorOrphanDirs` (preview).
97+
`scan --prune` opts into garbage collection. When set, `scan` removes manifest entries for packages no longer present in the crawl, then deletes orphan blob, diff, and package-archive files from `.socket/`. Off by default (v3.0) so a temporary uninstall doesn't silently destroy manifest state. Only entries whose ecosystem this run actually crawled are eligible: a `pkg:<type>/` with no crawler in this build (a newer CLI's ecosystem in the committed manifest) and the runtime-gated maven/nuget crawlers with their gate off are exempt — the crawl never looked for them, so their absence is not evidence of removal (same fail-safe as the `--ecosystems` filter, which narrows the query but never the prune's installed set). The pass also reconciles vendored state (runs FIRST, under the apply lock — lock contention skips it without failing the scan): vendored entries whose patch is gone from the manifest are reverted, vendored entries whose dependency is no longer in the lockfile graph are reverted AND their manifest entries dropped (detached entries are exempt from both — they are manifest- and lockfile-invisible by design; a missing or undeterminable lockfile keeps the entry, fail-safe), and orphan `.socket/vendor/<eco>/<uuid>` dirs with no ledger entry are swept. The JSON `gc` sub-object gains `revertedVendoredEntries` + `keptVendoredEntries` + `removedVendorOrphanDirs` (wet) / `revertableVendoredEntries` + `vendorOrphanDirs` (preview). `keptVendoredEntries` lists drift-kept entries the revert deliberately preserved (`vendor_artifact_kept` — undo the drift and re-run `vendor --revert` to finish); the preview cannot see drift (backends return before the wiring replay on dry runs), so `revertableVendoredEntries` may over-promise what a wet run will actually reclaim.
9898

9999
`scan` queries the patch API in `--batch-size` chunks. Authenticated runs POST `/v0/orgs/{slug}/patches/batch`; token-less runs POST `{proxy}/patch/batch` on the public proxy and degrade to per-package `GET /patch/by-package/:purl` requests in two cases: the deployed proxy predates the batch endpoint (legacy proxies answer the POST with their `400 "Unsupported endpoint"` catch-all), or the all-or-nothing batch validation rejects the chunk (e.g. a crawled PURL type the server doesn't recognize, such as `pkg:jsr/…` — the per-package path tolerates those individually, preserving the pre-batch scan semantics). Rate limits and over-capacity 503s surface instead of silently degrading.
100100

crates/socket-patch-cli/src/commands/fetch_stage.rs

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,10 @@ pub(crate) async fn stage_vendor_sources_in_memory(
428428
let mut complete = true;
429429
for (file, info) in &patch.files {
430430
let (Some(b64), Some(hash)) = (&info.blob_content, &info.after_hash) else {
431-
if !quiet {
431+
// An error, not progress chatter: prints even
432+
// under --silent (same rule as
433+
// report_offline_missing above).
434+
if !common.json {
432435
eprintln!(" [error] {purl}: no blob content served for {file}");
433436
}
434437
complete = false;
@@ -458,7 +461,12 @@ pub(crate) async fn stage_vendor_sources_in_memory(
458461
}
459462
}
460463
if !failed.is_empty() {
461-
if !quiet {
464+
// An error, not progress chatter: the vendor caller only marks
465+
// the envelope (printed exclusively under --json), so muting
466+
// this under --silent meant exit 1 with zero output — the
467+
// CLI_CONTRACT violation ("errors only", NEVER nothing) fixed
468+
// for the disk stager's arms above.
469+
if !common.json {
462470
eprintln!(
463471
"Error: could not fetch patch content for {} patch(es):",
464472
failed.len()
@@ -790,4 +798,51 @@ mod tests {
790798
);
791799
assert!(!dst.join("subdir").exists(), "directories are not mirrored");
792800
}
801+
802+
/// The hardlink-failure copy fallback — the PRIMARY mirror path when
803+
/// `.socket/` and the overlay tempdir sit on different filesystems
804+
/// (EXDEV; e.g. tmpfs /tmp on Linux). Same-volume tempdirs always
805+
/// hardlink, so force the arm deterministically: a DANGLING symlink at
806+
/// the destination makes `metadata` err (follows the link — the
807+
/// existing-file skip does not fire), makes `hard_link` fail (the link
808+
/// occupies the path), and lets `copy` succeed by writing THROUGH the
809+
/// link into its target.
810+
#[cfg(unix)]
811+
#[tokio::test]
812+
async fn overlay_dir_falls_back_to_copy_when_hardlink_fails() {
813+
let tmp = tempfile::tempdir().unwrap();
814+
let src = tmp.path().join("src");
815+
let dst = tmp.path().join("dst");
816+
std::fs::create_dir_all(&src).unwrap();
817+
std::fs::create_dir_all(&dst).unwrap();
818+
std::fs::write(src.join("a"), b"from-src").unwrap();
819+
// Dangling link: the target does not exist yet.
820+
let resolved = tmp.path().join("resolved");
821+
std::os::unix::fs::symlink(&resolved, dst.join("a")).unwrap();
822+
823+
overlay_dir(&src, &dst).await;
824+
825+
// hard_link never replaces an occupied path, so the entry must
826+
// still be the symlink — the bytes can only have arrived via the
827+
// copy arm.
828+
assert!(
829+
dst.join("a")
830+
.symlink_metadata()
831+
.unwrap()
832+
.file_type()
833+
.is_symlink(),
834+
"the destination entry stays a symlink (hard_link cannot have run)"
835+
);
836+
assert_eq!(
837+
std::fs::read(dst.join("a")).unwrap(),
838+
b"from-src",
839+
"the mirrored bytes are readable at the destination path"
840+
);
841+
assert_eq!(
842+
std::fs::read(&resolved).unwrap(),
843+
b"from-src",
844+
"proof the copy arm ran: only a write-through-the-link copy \
845+
creates the link target"
846+
);
847+
}
793848
}

0 commit comments

Comments
 (0)