@@ -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 \n eval_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.\n stdout:\n {stdout}\n stderr:\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