Skip to content

Commit ae7a035

Browse files
committed
Keep vlt restore admission consistent with forward rewrites
1 parent d03ae6b commit ae7a035

1 file changed

Lines changed: 70 additions & 22 deletions

File tree

  • crates/socket-patch-core/src/patch/redirect/upstream

‎crates/socket-patch-core/src/patch/redirect/upstream/vlt.rs‎

Lines changed: 70 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,18 @@
1919
//!
2020
//! Every hosted instance of a pin is restored together, and every other
2121
//! byte of the lock (flags, trailing slots, indent, comma, `\r`) is kept.
22+
//! Default-registry admission and sibling conventions use the same raw
23+
//! segment policy as forward rewrite, heal and vendor. Only the shared
24+
//! `registry_base` normalizes a modern empty segment when resolving a URL.
2225
2326
use serde_json::{Map, Value};
2427

2528
use super::npm::{by_uuid, fetch_dists, read_or_refuse, refuse_all_in};
2629
use super::{Ctx, FormatResult, HostedPin, View};
2730
use crate::vendor::vlt_lock_text::{
2831
default_registry_alias, entry_text, is_default_registry, nodes_block, parse_node_line,
29-
registry_base, registry_segment, render_entry_line, render_tuple_with_slots, sniff_lock,
30-
split_dep_id, split_lines, DepIdEra, DepIdKind, LockSniff,
32+
registry_base, render_entry_line, render_tuple_with_slots, sniff_lock, split_dep_id,
33+
split_lines, DepIdEra, DepIdKind, LockSniff,
3134
};
3235

3336
/// One hosted node line to restore.
@@ -56,7 +59,6 @@ fn records_url(
5659
siblings: &[(DepIdEra, bool)],
5760
options: Option<&Map<String, Value>>,
5861
) -> bool {
59-
let segment = registry_segment(era, segment);
6062
let same_era: Vec<bool> = siblings
6163
.iter()
6264
.filter(|(e, _)| *e == era)
@@ -175,8 +177,7 @@ pub(crate) async fn restore(
175177
let hosted = slot3.as_deref().and_then(|u| ctx.hosted_uuid(u));
176178
let Some(uuid) = hosted else {
177179
if let Some(dep_id) = dep_id.filter(|d| {
178-
d.kind == DepIdKind::Registry
179-
&& is_default_registry(registry_segment(d.era, &d.first), options)
180+
d.kind == DepIdKind::Registry && is_default_registry(&d.first, options)
180181
}) {
181182
siblings.push((dep_id.era, slot3.is_some()));
182183
}
@@ -208,7 +209,7 @@ pub(crate) async fn restore(
208209
);
209210
continue;
210211
};
211-
if !is_default_registry(registry_segment(dep_id.era, &dep_id.first), options) {
212+
if !is_default_registry(&dep_id.first, options) {
212213
result.refuse(
213214
&uuid,
214215
format!(
@@ -540,8 +541,8 @@ mod tests {
540541
let (outcome, after) =
541542
run(&text, &[pin("pkg:npm/left-pad@1.3.0", LP_UUID)], false).await;
542543
let opts = opts(options);
543-
if is_default_registry(registry_segment(*era, segment), Some(&opts)) {
544-
let want = want.expect("a default registry always has a base");
544+
let admitted = is_default_registry(segment, Some(&opts));
545+
if let (true, Some(want)) = (admitted, *want) {
545546
assert!(
546547
refused(&outcome).is_empty(),
547548
"{segment:?} {options}: {:?}",
@@ -553,10 +554,12 @@ mod tests {
553554
assert!(after.contains(&upstream), "{segment:?} {options}: {after}");
554555
} else {
555556
let why = refused(&outcome);
556-
assert!(
557-
why[0].contains("not on vlt's default registry"),
558-
"{segment:?} {options}: {why:?}"
559-
);
557+
let reason = if admitted {
558+
"maps no registry"
559+
} else {
560+
"not on vlt's default registry"
561+
};
562+
assert!(why[0].contains(reason), "{segment:?} {options}: {why:?}");
560563
assert_eq!(after, text);
561564
}
562565
}
@@ -565,7 +568,7 @@ mod tests {
565568

566569
#[tokio::test]
567570
#[serial_test::serial]
568-
async fn restore_empty_tilde_resolution_and_sibling_admission() {
571+
async fn restore_empty_tilde_resolution_and_raw_sibling_convention() {
569572
let server = registry(&[("left-pad", "1.3.0", Some(LP_UPSTREAM))]).await;
570573
std::env::set_var("SOCKET_NPM_REGISTRY", server.uri());
571574
let url = hosted(LP_UUID, "left-pad-1.3.0.tgz");
@@ -574,21 +577,21 @@ mod tests {
574577
"",
575578
r#"{"registry":"https://a.example/","registries":{"npm":"https://b.example/"}}"#,
576579
"",
577-
"https://b.example/",
580+
Some("https://b.example/"),
578581
),
579582
(
580583
"npm",
581584
r#"{"registry":"https://a.example/","registries":{"npm":"https://b.example/"}}"#,
582585
"",
583-
"https://b.example/",
586+
Some("https://b.example/"),
584587
),
585588
(
586589
"corp",
587590
r#"{"default-registry-alias":"corp","registries":{"npm":"https://b.example/","corp":"https://c.example/"}}"#,
588-
// Empty tilde means npm, which is foreign here. Its lack
589-
// of slot 3 must not determine the corp node's convention.
591+
// Keep forward rewrite's raw-empty admission policy when
592+
// choosing siblings, so its 3-tuple convention is retained.
590593
",\n \"~~ms@2.1.3\": [0,\"ms\",\"sha512-M==\"]",
591-
"https://c.example/",
594+
None,
592595
),
593596
] {
594597
let key = format!("~{segment}~left-pad@1.3.0");
@@ -599,16 +602,61 @@ mod tests {
599602
run(&text, &[pin("pkg:npm/left-pad@1.3.0", LP_UUID)], false).await;
600603
assert!(refused(&outcome).is_empty(), "{:?}", refused(&outcome));
601604
let after: Value = serde_json::from_str(&after).unwrap();
602-
assert_eq!(
603-
after["nodes"][&key],
604-
serde_json::json!([
605+
let expected = match base {
606+
Some(base) => serde_json::json!([
605607
0,
606608
"left-pad",
607609
LP_UPSTREAM,
608610
format!("{base}left-pad/-/left-pad-1.3.0.tgz")
609611
]),
610-
"{segment:?} {options}"
612+
None => serde_json::json!([0, "left-pad", LP_UPSTREAM]),
613+
};
614+
assert_eq!(after["nodes"][&key], expected, "{segment:?} {options}");
615+
}
616+
std::env::remove_var("SOCKET_NPM_REGISTRY");
617+
}
618+
619+
#[tokio::test]
620+
#[serial_test::serial]
621+
async fn rewritten_empty_tilde_under_custom_alias_restores_byte_for_byte() {
622+
use crate::patch::redirect::{DepOverride, RewriteResult};
623+
624+
let server = registry(&[("left-pad", "1.3.0", Some(LP_UPSTREAM))]).await;
625+
std::env::set_var("SOCKET_NPM_REGISTRY", server.uri());
626+
let url = hosted(LP_UUID, "left-pad-1.3.0.tgz");
627+
let dep: DepOverride = serde_json::from_value(serde_json::json!({
628+
"ecosystem": "npm",
629+
"name": "left-pad",
630+
"version": "1.3.0",
631+
"token": "t",
632+
"patchUuid": LP_UUID,
633+
"artifactUrl": url,
634+
"integrity": { "sha512": "sha512-PATCHED==" },
635+
}))
636+
.unwrap();
637+
for sibling in [
638+
"",
639+
",\n \"~~ms@2.1.3\": [0,\"ms\",\"sha512-M==\",\"https://b.example/ms/-/ms-2.1.3.tgz\"]",
640+
] {
641+
let original = format!(
642+
"{{\n \"lockfileVersion\": 1,\n \"options\": {{\"default-registry-alias\":\"corp\",\"registry\":\"https://a.example/\",\"registries\":{{\"corp\":\"https://a.example/\",\"npm\":\"https://b.example/\"}}}},\n \"nodes\": {{\n \"~~left-pad@1.3.0\": [0,\"left-pad\",\"{LP_UPSTREAM}\",\"https://b.example/left-pad/-/left-pad-1.3.0.tgz\"]{sibling}\n }},\n \"edges\": {{}}\n}}\n"
611643
);
644+
let files = [("vlt-lock.json".to_string(), original.clone())]
645+
.into_iter()
646+
.collect();
647+
let mut rewritten = RewriteResult::default();
648+
crate::patch::redirect::vlt::rewrite_vlt_lock(
649+
&files,
650+
std::slice::from_ref(&dep),
651+
false,
652+
&mut rewritten,
653+
);
654+
let pinned = rewritten.files.get("vlt-lock.json").expect("forward rewrite admits the raw empty segment");
655+
assert!(pinned.contains(&url), "{pinned}");
656+
let (outcome, restored) =
657+
run(pinned, &[pin("pkg:npm/left-pad@1.3.0", LP_UUID)], false).await;
658+
assert!(refused(&outcome).is_empty(), "{:?}", refused(&outcome));
659+
assert_eq!(restored, original);
612660
}
613661
std::env::remove_var("SOCKET_NPM_REGISTRY");
614662
}

0 commit comments

Comments
 (0)