Skip to content

Commit 5ca20fa

Browse files
committed
Test vendored eval_gemfile refusal with Bundler
A direct dependency declared through eval_gemfile must be refused before any write (#482), and Bundler must still install the project frozen and unfrozen. Assisted-by: Claude Code:claude-opus-5-5
1 parent 5d814c7 commit 5ca20fa

1 file changed

Lines changed: 75 additions & 0 deletions

File tree

‎crates/socket-patch-cli/tests/e2e_vendor_gem_build.rs‎

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1562,3 +1562,78 @@ fn gem_vendor_refuses_a_bundle_gemfile_dual_boot() {
15621562
],
15631563
);
15641564
}
1565+
1566+
/// #482: a direct dependency declared through `eval_gemfile` is invisible to
1567+
/// the Gemfile line grammar. Vendor used to treat it as transitive, append a
1568+
/// managed `path:` declaration, exit 0, and leave every `bundle install`
1569+
/// failing on the duplicate; it must refuse before any write.
1570+
#[test]
1571+
#[ignore = "host capstone: shells out to a real bundler >= 1.17; the unpinned `test` job \
1572+
skips it, the e2e job runs it with a pinned toolchain via --ignored"]
1573+
fn gem_vendor_refuses_an_eval_gemfile_direct_dep() {
1574+
let Some((_tmp, proj, _bundler, purl)) = staged_rack_project("eval_gemfile") else {
1575+
return;
1576+
};
1577+
std::fs::write(proj.join("Gemfile.common"), "gem \"rack\", \"~> 3.1\"\n").unwrap();
1578+
std::fs::write(
1579+
proj.join("Gemfile"),
1580+
"source \"https://rubygems.org\"\n\neval_gemfile \"Gemfile.common\"\n",
1581+
)
1582+
.unwrap();
1583+
let relock = bundle(&proj, &["install"], false);
1584+
assert!(
1585+
relock.status.success(),
1586+
"the eval_gemfile layout installs (test premise):\n{}",
1587+
String::from_utf8_lossy(&relock.stderr)
1588+
);
1589+
let files = ["Gemfile", "Gemfile.common", "Gemfile.lock"];
1590+
let before: Vec<Vec<u8>> = files
1591+
.iter()
1592+
.map(|f| std::fs::read(proj.join(f)).unwrap())
1593+
.collect();
1594+
let (code, stdout, stderr) = run_socket(
1595+
&proj,
1596+
&[
1597+
"vendor",
1598+
"--json",
1599+
"--offline",
1600+
"--cwd",
1601+
proj.to_str().unwrap(),
1602+
],
1603+
);
1604+
assert_ne!(
1605+
code, 0,
1606+
"vendor must not succeed.\nstdout:\n{stdout}\nstderr:\n{stderr}"
1607+
);
1608+
let env = parse_envelope(&stdout);
1609+
assert_eq!(env["summary"]["applied"], 0, "nothing vendored: {env}");
1610+
let event = env["events"]
1611+
.as_array()
1612+
.unwrap()
1613+
.iter()
1614+
.find(|e| e["purl"] == purl)
1615+
.unwrap_or_else(|| panic!("an event for {purl}: {env}"));
1616+
assert_eq!(
1617+
event["errorCode"], "gemfile_declaration_not_editable",
1618+
"event: {event}"
1619+
);
1620+
for (file, before) in files.iter().zip(before) {
1621+
assert_eq!(
1622+
std::fs::read(proj.join(file)).unwrap(),
1623+
before,
1624+
"{file} must be byte-untouched"
1625+
);
1626+
}
1627+
assert!(
1628+
!proj.join(".socket/vendor/gem").exists(),
1629+
"no vendored copy is written"
1630+
);
1631+
for frozen in [false, true] {
1632+
let install = bundle(&proj, &["install"], frozen);
1633+
assert!(
1634+
install.status.success(),
1635+
"bundle install (frozen: {frozen}) must still succeed:\n{}",
1636+
String::from_utf8_lossy(&install.stderr)
1637+
);
1638+
}
1639+
}

0 commit comments

Comments
 (0)