Skip to content
Merged
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
89 changes: 62 additions & 27 deletions crates/socket-patch-cli/tests/mode_migration_vlt.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
//! Real-vlt mode migrations (suite `migration`, DESIGN §8.3, §4.10).
//!
//! Hosted ⇄ vendored takeovers (both drivers), dry-run parity, scoped and
//! unscoped unwinds, agent-mode interplay (r-tests T21), package-manager
//! switches (T37) and the vlt upgrade (T19, `SOCKET_PATCH_VLT_E2E_UPGRADE_JS`)
//! against the REAL vlt under test, every terminal state proven by a fresh
//! checkout's locked install. Each leg is `vlt_pinned_matrix_migration_<leg>`
//! Hosted ⇄ vendored migrations (scan takeovers, the `vendor` eject),
//! dry-run parity, scoped and unscoped unwinds, agent-mode interplay
//! (r-tests T21), package-manager switches (T37) and the vlt upgrade (T19,
//! `SOCKET_PATCH_VLT_E2E_UPGRADE_JS`) against the REAL vlt under test,
//! every terminal state proven by a fresh checkout's locked install. Each leg is `vlt_pinned_matrix_migration_<leg>`
//! and prints one `VLT-LEG` line.

#[path = "common/rollback_json.rs"]
Expand Down Expand Up @@ -111,7 +111,8 @@ fn api_upstream(fx: &Fixture, dir: &Path, head: &[&str], extra: &[&str]) -> Sock
}

/// `vendor` over a (possibly) hosted project: no `--offline`, since the
/// takeover restores the hosted pin's upstream entry first.
/// eject fetches the hosted pin's record and restores its upstream entry
/// first.
fn vendor_upstream(fx: &Fixture, dir: &Path, extra: &[&str]) -> SocketOut {
api_upstream(fx, dir, &["vendor"], extra)
}
Expand Down Expand Up @@ -286,10 +287,12 @@ async fn vlt_pinned_matrix_migration_vendored_then_hosted() {
fx.leg.ran();
}

/// Hosted → vendored through `scan --mode vendored` and through `vendor`
/// (a staged manifest): the takeover restores the hosted pin's upstream
/// entry first, the lock is vendored, fresh checkouts are patched, and
/// `vendor --revert` restores the pristine (upstream) lock.
/// Hosted → vendored through `scan --mode vendored` (a per-purl takeover)
/// and through `vendor` (a staged manifest, but a hosted pin is in scope,
/// so `vendor` ejects the lockfile's hosted pin instead): either way the
/// hosted pin's upstream entry is restored first, the lock is vendored,
/// fresh checkouts are patched, and `vendor --revert` restores the
/// pristine (upstream) lock.
#[tokio::test(flavor = "multi_thread")]
#[ignore = "real vlt: SOCKET_PATCH_VLT_E2E_JS"]
async fn vlt_pinned_matrix_migration_hosted_then_vendored() {
Expand All @@ -302,17 +305,22 @@ async fn vlt_pinned_matrix_migration_hosted_then_vendored() {
fx.vlt_ok(&fx.proj, &fx.leg.locked_install_args());
for driver in ["scan", "vendor"] {
let dir = copy_project(&fx, &fx.proj, &format!("drv-{driver}"));
let out = if driver == "scan" {
vendored_scan(&fx, &dir, &[])
} else {
let eject = driver == "vendor";
let out = if eject {
stage_manifest(&dir, &[fx.t()]);
vendor_upstream(&fx, &dir, &[])
let manifest = std::fs::read(dir.join(".socket/manifest.json")).unwrap();
let out = vendor_upstream(&fx, &dir, &[]);
assert_eq!(
std::fs::read(dir.join(".socket/manifest.json")).unwrap(),
manifest,
"{driver}: the eject leaves the manifest untouched: {out}"
);
out
} else {
vendored_scan(&fx, &dir, &[])
};
assert_eq!(out.code, 0, "{driver}: {out}");
assert!(
has_code(&out.json(), "vendor_takeover_reverted_redirect"),
"{driver}: {out}"
);
assert_vendored_over_hosted(eject, driver, &out);
assert_pure_vendored(&dir, fx.t());
assert_fresh(
&fx,
Expand All @@ -332,15 +340,35 @@ async fn vlt_pinned_matrix_migration_hosted_then_vendored() {
fx.leg.ran();
}

/// The wet hosted → vendored advisories. `scan --mode vendored` announces
/// its per-purl takeover; plain `vendor` with a hosted pin in scope
/// (`eject`) runs the eject instead, which announces no takeover. Both
/// heal the warm vlt store and ask for a `vlt install`.
fn assert_vendored_over_hosted(eject: bool, tag: &str, out: &SocketOut) {
let doc = out.json();
assert_eq!(
has_code(&doc, "vendor_takeover_reverted_redirect"),
!eject,
"{tag}: only the scan takeover announces a takeover: {out}"
);
// The eject reports a failed restore as a run error (exit 1); the scan
// takeover reports it per event, so check it there.
assert!(!has_code(&doc, "redirect_revert_failed"), "{tag}: {out}");
assert!(
has_code(&doc, "redirect_vlt_reinstall_required"),
"{tag}: the warm vlt store is healed: {out}"
);
}

fn vendor_revert_all(dir: &Path) -> SocketOut {
vendor_cmd(dir, &["--revert"])
}

// ── 3. dry-run parity ─────────────────────────────────────────────────────

/// Over a live hosted redirect `vendor --dry-run` previews the takeover
/// and `scan --mode vendored --dry-run` says `would_vendor`; over a live
/// vendored state `scan --mode hosted --dry-run` previews
/// Over a live hosted redirect `vendor --dry-run` previews the eject of
/// the hosted pin (`eject_planned`) and `scan --mode vendored --dry-run`
/// says `would_vendor`; over a live vendored state `scan --mode hosted --dry-run` previews
/// `redirect_would_revert_vendored`. No preview writes a byte, and the wet
/// runs then do what was previewed.
#[tokio::test(flavor = "multi_thread")]
Expand All @@ -357,10 +385,20 @@ async fn vlt_pinned_matrix_migration_dry_run_parity() {
let before = project_bytes(&fx.proj);
let out = vendor_upstream(&fx, &fx.proj, &["--dry-run"]);
assert_eq!(out.code, 0, "{out}");
let doc = out.json();
let planned: Vec<&Value> = doc["events"]
.as_array()
.map(|evs| {
evs.iter()
.filter(|e| e["errorCode"] == "eject_planned")
.collect()
})
.unwrap_or_default();
assert!(
has_code(&out.json(), "vendor_would_revert_redirect"),
"{out}"
planned.len() == 1 && planned[0]["purl"] == fx.t().purl(),
"one eject planned, for the hosted pin only: {out}"
);
assert!(!has_code(&doc, "vendor_would_revert_redirect"), "{out}");
assert!(!out.stdout.contains("vendor_lock_entry_not_found"), "{out}");
let out = vendored_scan(&fx, &fx.proj, &["--dry-run"]);
assert_eq!(out.code, 0, "{out}");
Expand All @@ -382,10 +420,7 @@ async fn vlt_pinned_matrix_migration_dry_run_parity() {
);
let out = vendor_upstream(&fx, &fx.proj, &[]);
assert_eq!(out.code, 0, "{out}");
assert!(
has_code(&out.json(), "vendor_takeover_reverted_redirect"),
"{out}"
);
assert_vendored_over_hosted(true, "vendor", &out);
assert_pure_vendored(&fx.proj, fx.t());
let before = project_bytes(&fx.proj);
let out = hosted_scan(&fx, &fx.proj, &["--dry-run"]);
Expand Down
Loading