1717//! cargo test -p socket-patch-cli --test e2e_gem -- --ignored
1818//! ```
1919
20+ use std:: collections:: HashMap ;
2021use std:: path:: { Path , PathBuf } ;
2122use std:: process:: { Command , Output } ;
2223
@@ -27,25 +28,8 @@ use sha2::{Digest, Sha256};
2728// ---------------------------------------------------------------------------
2829
2930const GEM_UUID : & str = "4bf7fe0b-dc57-4ea8-945f-bc4a04c47a15" ;
30- #[ allow( dead_code) ]
3131const GEM_PURL : & str = "pkg:gem/activestorage@5.2.0" ;
3232
33- /// File hashes for the 3 patched files in activestorage 5.2.0.
34-
35- const VARIATION_RB_BEFORE : & str = "96b72bac68797be5c69d4dd46fbb67b7e6bb2d576fbe2c87490e53714739da06" ;
36- const VARIATION_RB_AFTER : & str = "27bc60b5399e459d9e75a69e21ca1ff9e0a3e36e56e3ef5d0d05ad44e8f18aed" ;
37-
38- const ACTIVE_STORAGE_RB_BEFORE : & str = "507653326a9fffbc7da4ebaab5c8cb55c8e81be5dc6dde5a1b0b4e0b17f3a8d2" ;
39- const ACTIVE_STORAGE_RB_AFTER : & str = "962e44b7e3b3c59c6c8c14d16cf9f80752e56ad1fb1c6ff6c2b51b15fcaf7df9" ;
40-
41- const ENGINE_RB_BEFORE : & str = "09fc2486c5e02c5f29e7c61ef3e7b0e17f6c6b1f5ddfe6e1d08e4c06f3adf8c4" ;
42- const ENGINE_RB_AFTER : & str = "4693b4d8f1a7c06e5d09b24f8c3e7a1d6b5f0e2c9a8d7b6f4e3c2a1b0d9e8f7" ;
43-
44- /// Relative paths of patched files inside the gem directory.
45- const VARIATION_RB : & str = "app/models/active_storage/variation.rb" ;
46- const ACTIVE_STORAGE_RB : & str = "lib/active_storage.rb" ;
47- const ENGINE_RB : & str = "lib/active_storage/engine.rb" ;
48-
4933// ---------------------------------------------------------------------------
5034// Helpers
5135// ---------------------------------------------------------------------------
@@ -140,23 +124,85 @@ fn find_gem_dir(cwd: &Path) -> PathBuf {
140124 ) ;
141125}
142126
143- /// Verify all 3 files match expected hashes.
144- fn assert_hashes ( gem_dir : & Path , variation : & str , active_storage : & str , engine : & str ) {
145- assert_eq ! (
146- git_sha256_file( & gem_dir. join( VARIATION_RB ) ) ,
147- variation,
148- "variation.rb hash mismatch"
149- ) ;
150- assert_eq ! (
151- git_sha256_file( & gem_dir. join( ACTIVE_STORAGE_RB ) ) ,
152- active_storage,
153- "active_storage.rb hash mismatch"
154- ) ;
155- assert_eq ! (
156- git_sha256_file( & gem_dir. join( ENGINE_RB ) ) ,
157- engine,
158- "engine.rb hash mismatch"
159- ) ;
127+ /// Read the manifest and return the files map for the gem patch.
128+ fn read_patch_files ( manifest_path : & Path ) -> serde_json:: Value {
129+ let manifest: serde_json:: Value =
130+ serde_json:: from_str ( & std:: fs:: read_to_string ( manifest_path) . unwrap ( ) ) . unwrap ( ) ;
131+ let patch = & manifest[ "patches" ] [ GEM_PURL ] ;
132+ assert ! ( patch. is_object( ) , "manifest should contain {GEM_PURL}" ) ;
133+ patch[ "files" ] . clone ( )
134+ }
135+
136+ /// Record hashes of all files in the gem dir that will be patched.
137+ fn record_original_hashes ( gem_dir : & Path , files : & serde_json:: Value ) -> HashMap < String , String > {
138+ let mut hashes = HashMap :: new ( ) ;
139+ for ( rel_path, _) in files. as_object ( ) . expect ( "files object" ) {
140+ let full_path = gem_dir. join ( rel_path) ;
141+ let hash = if full_path. exists ( ) {
142+ git_sha256_file ( & full_path)
143+ } else {
144+ String :: new ( )
145+ } ;
146+ hashes. insert ( rel_path. clone ( ) , hash) ;
147+ }
148+ hashes
149+ }
150+
151+ /// Verify all patched files match their afterHash from the manifest.
152+ fn assert_after_hashes ( gem_dir : & Path , files : & serde_json:: Value ) {
153+ for ( rel_path, info) in files. as_object ( ) . expect ( "files object" ) {
154+ let after_hash = info[ "afterHash" ]
155+ . as_str ( )
156+ . expect ( "afterHash should be a string" ) ;
157+ let full_path = gem_dir. join ( rel_path) ;
158+ assert ! (
159+ full_path. exists( ) ,
160+ "patched file should exist: {}" ,
161+ full_path. display( )
162+ ) ;
163+ assert_eq ! (
164+ git_sha256_file( & full_path) ,
165+ after_hash,
166+ "hash mismatch for {rel_path} after patching"
167+ ) ;
168+ }
169+ }
170+
171+ /// Verify all patched files match their beforeHash (or are removed if new).
172+ fn assert_before_hashes ( gem_dir : & Path , files : & serde_json:: Value ) {
173+ for ( rel_path, info) in files. as_object ( ) . expect ( "files object" ) {
174+ let before_hash = info[ "beforeHash" ] . as_str ( ) . unwrap_or ( "" ) ;
175+ let full_path = gem_dir. join ( rel_path) ;
176+ if before_hash. is_empty ( ) {
177+ assert ! (
178+ !full_path. exists( ) ,
179+ "new file {rel_path} should be removed after rollback"
180+ ) ;
181+ } else {
182+ assert_eq ! (
183+ git_sha256_file( & full_path) ,
184+ before_hash,
185+ "{rel_path} should match beforeHash"
186+ ) ;
187+ }
188+ }
189+ }
190+
191+ /// Verify files match the originally recorded hashes.
192+ fn assert_original_hashes ( gem_dir : & Path , original_hashes : & HashMap < String , String > ) {
193+ for ( rel_path, orig_hash) in original_hashes {
194+ if orig_hash. is_empty ( ) {
195+ continue ;
196+ }
197+ let full_path = gem_dir. join ( rel_path) ;
198+ if full_path. exists ( ) {
199+ assert_eq ! (
200+ git_sha256_file( & full_path) ,
201+ * orig_hash,
202+ "{rel_path} should match original hash"
203+ ) ;
204+ }
205+ }
160206}
161207
162208// ---------------------------------------------------------------------------
@@ -263,18 +309,6 @@ fn test_gem_full_lifecycle() {
263309 bundle_run ( cwd, & [ "install" , "--path" , "vendor/bundle" ] ) ;
264310
265311 let gem_dir = find_gem_dir ( cwd) ;
266- assert ! (
267- gem_dir. join( VARIATION_RB ) . exists( ) ,
268- "variation.rb must exist after bundle install"
269- ) ;
270-
271- // Confirm original files match expected before-hashes.
272- assert_hashes (
273- & gem_dir,
274- VARIATION_RB_BEFORE ,
275- ACTIVE_STORAGE_RB_BEFORE ,
276- ENGINE_RB_BEFORE ,
277- ) ;
278312
279313 // -- GET: download + apply patch ------------------------------------------
280314 assert_run_ok ( cwd, & [ "get" , GEM_UUID ] , "get" ) ;
@@ -288,14 +322,15 @@ fn test_gem_full_lifecycle() {
288322 assert ! ( patch. is_object( ) , "manifest should contain {GEM_PURL}" ) ;
289323 assert_eq ! ( patch[ "uuid" ] . as_str( ) . unwrap( ) , GEM_UUID ) ;
290324
291- // Files should now be patched.
292- assert_hashes (
293- & gem_dir,
294- VARIATION_RB_AFTER ,
295- ACTIVE_STORAGE_RB_AFTER ,
296- ENGINE_RB_AFTER ,
325+ let files = & patch[ "files" ] ;
326+ assert ! (
327+ files. as_object( ) . map_or( false , |f| !f. is_empty( ) ) ,
328+ "patch should modify at least one file"
297329 ) ;
298330
331+ // Files should now be patched — verify against afterHash from manifest.
332+ assert_after_hashes ( & gem_dir, files) ;
333+
299334 // -- LIST: verify JSON output ---------------------------------------------
300335 let ( stdout, _) = assert_run_ok ( cwd, & [ "list" , "--json" ] , "list --json" ) ;
301336 let list: serde_json:: Value = serde_json:: from_str ( & stdout) . unwrap ( ) ;
@@ -318,33 +353,15 @@ fn test_gem_full_lifecycle() {
318353
319354 // -- ROLLBACK: restore original files -------------------------------------
320355 assert_run_ok ( cwd, & [ "rollback" ] , "rollback" ) ;
321-
322- assert_hashes (
323- & gem_dir,
324- VARIATION_RB_BEFORE ,
325- ACTIVE_STORAGE_RB_BEFORE ,
326- ENGINE_RB_BEFORE ,
327- ) ;
356+ assert_before_hashes ( & gem_dir, files) ;
328357
329358 // -- APPLY: re-apply from manifest ----------------------------------------
330359 assert_run_ok ( cwd, & [ "apply" ] , "apply" ) ;
331-
332- assert_hashes (
333- & gem_dir,
334- VARIATION_RB_AFTER ,
335- ACTIVE_STORAGE_RB_AFTER ,
336- ENGINE_RB_AFTER ,
337- ) ;
360+ assert_after_hashes ( & gem_dir, files) ;
338361
339362 // -- REMOVE: rollback + remove from manifest ------------------------------
340363 assert_run_ok ( cwd, & [ "remove" , GEM_UUID ] , "remove" ) ;
341-
342- assert_hashes (
343- & gem_dir,
344- VARIATION_RB_BEFORE ,
345- ACTIVE_STORAGE_RB_BEFORE ,
346- ENGINE_RB_BEFORE ,
347- ) ;
364+ assert_before_hashes ( & gem_dir, files) ;
348365
349366 let manifest: serde_json:: Value =
350367 serde_json:: from_str ( & std:: fs:: read_to_string ( & manifest_path) . unwrap ( ) ) . unwrap ( ) ;
@@ -370,43 +387,25 @@ fn test_gem_dry_run() {
370387 bundle_run ( cwd, & [ "install" , "--path" , "vendor/bundle" ] ) ;
371388
372389 let gem_dir = find_gem_dir ( cwd) ;
373- assert_hashes (
374- & gem_dir,
375- VARIATION_RB_BEFORE ,
376- ACTIVE_STORAGE_RB_BEFORE ,
377- ENGINE_RB_BEFORE ,
378- ) ;
379390
380391 // Download without applying.
381392 assert_run_ok ( cwd, & [ "get" , GEM_UUID , "--no-apply" ] , "get --no-apply" ) ;
382393
383- // Files should still be original .
384- assert_hashes (
385- & gem_dir ,
386- VARIATION_RB_BEFORE ,
387- ACTIVE_STORAGE_RB_BEFORE ,
388- ENGINE_RB_BEFORE ,
389- ) ;
394+ // Read manifest to get file list and expected hashes .
395+ let manifest_path = cwd . join ( ".socket/manifest.json" ) ;
396+ let files = read_patch_files ( & manifest_path ) ;
397+ let original_hashes = record_original_hashes ( & gem_dir , & files ) ;
398+
399+ // Files should still be original (not patched).
400+ assert_original_hashes ( & gem_dir , & original_hashes ) ;
390401
391402 // Dry-run should succeed but leave files untouched.
392403 assert_run_ok ( cwd, & [ "apply" , "--dry-run" ] , "apply --dry-run" ) ;
393-
394- assert_hashes (
395- & gem_dir,
396- VARIATION_RB_BEFORE ,
397- ACTIVE_STORAGE_RB_BEFORE ,
398- ENGINE_RB_BEFORE ,
399- ) ;
404+ assert_original_hashes ( & gem_dir, & original_hashes) ;
400405
401406 // Real apply should work.
402407 assert_run_ok ( cwd, & [ "apply" ] , "apply" ) ;
403-
404- assert_hashes (
405- & gem_dir,
406- VARIATION_RB_AFTER ,
407- ACTIVE_STORAGE_RB_AFTER ,
408- ENGINE_RB_AFTER ,
409- ) ;
408+ assert_after_hashes ( & gem_dir, & files) ;
410409}
411410
412411/// `get --save-only` should save the patch to the manifest without applying.
@@ -425,27 +424,17 @@ fn test_gem_save_only() {
425424 bundle_run ( cwd, & [ "install" , "--path" , "vendor/bundle" ] ) ;
426425
427426 let gem_dir = find_gem_dir ( cwd) ;
428- assert_hashes (
429- & gem_dir,
430- VARIATION_RB_BEFORE ,
431- ACTIVE_STORAGE_RB_BEFORE ,
432- ENGINE_RB_BEFORE ,
433- ) ;
434427
435428 // Download with --save-only.
436429 assert_run_ok ( cwd, & [ "get" , GEM_UUID , "--save-only" ] , "get --save-only" ) ;
437430
438- // Files should still be original.
439- assert_hashes (
440- & gem_dir,
441- VARIATION_RB_BEFORE ,
442- ACTIVE_STORAGE_RB_BEFORE ,
443- ENGINE_RB_BEFORE ,
444- ) ;
445-
446- // Manifest should exist with the patch.
431+ // Read manifest to get file list and expected hashes.
447432 let manifest_path = cwd. join ( ".socket/manifest.json" ) ;
448- assert ! ( manifest_path. exists( ) , "manifest should exist after get --save-only" ) ;
433+ let files = read_patch_files ( & manifest_path) ;
434+ let original_hashes = record_original_hashes ( & gem_dir, & files) ;
435+
436+ // Files should still be original (not patched).
437+ assert_original_hashes ( & gem_dir, & original_hashes) ;
449438
450439 let manifest: serde_json:: Value =
451440 serde_json:: from_str ( & std:: fs:: read_to_string ( & manifest_path) . unwrap ( ) ) . unwrap ( ) ;
@@ -455,11 +444,5 @@ fn test_gem_save_only() {
455444
456445 // Real apply should work.
457446 assert_run_ok ( cwd, & [ "apply" ] , "apply" ) ;
458-
459- assert_hashes (
460- & gem_dir,
461- VARIATION_RB_AFTER ,
462- ACTIVE_STORAGE_RB_AFTER ,
463- ENGINE_RB_AFTER ,
464- ) ;
447+ assert_after_hashes ( & gem_dir, & files) ;
465448}
0 commit comments