@@ -693,6 +693,115 @@ fn test_streaming_decrypt_with_parallel_retrieval() -> Result<()> {
693693 Ok ( ( ) )
694694}
695695
696+ #[ test]
697+ fn test_streaming_decrypt_from_storage_with_parallel_random_chunks ( ) -> Result < ( ) > {
698+ use std:: collections:: HashMap ;
699+ use rand:: seq:: SliceRandom ;
700+ use rand:: thread_rng;
701+
702+ let temp_dir = TempDir :: new ( ) ?;
703+
704+ // Generate 100MB content
705+ let content_size = 100 * 1024 * 1024 ; // 100MB
706+ let original_data = random_bytes ( content_size) ;
707+ println ! ( "Generated {} bytes of test data" , content_size) ;
708+
709+ // Encrypt to get datamap and encrypted_chunks
710+ let ( data_map, encrypted_chunks) = encrypt ( original_data. clone ( ) ) ?;
711+ println ! ( "Encrypted into {} chunks" , encrypted_chunks. len( ) ) ;
712+
713+ // Create a storage map from encrypted_chunks using their hashes as keys
714+ let mut chunk_storage = HashMap :: new ( ) ;
715+ for chunk in encrypted_chunks. iter ( ) {
716+ let hash = XorName :: from_content ( & chunk. content ) ;
717+ chunk_storage. insert ( hash, chunk. content . clone ( ) ) ;
718+ }
719+
720+ // Create destination file for streaming_decrypt_from_storage
721+ let output_path = temp_dir. path ( ) . join ( "decrypted_output.dat" ) ;
722+
723+ // Create parallel chunk fetcher that returns chunks in random order
724+ // This is the key function that simulates a parallel storage system where
725+ // multiple chunks are requested and returned in random order
726+ let parallel_chunk_fetcher = |chunk_requests : & [ ( usize , XorName ) ] | -> Result < Vec < ( usize , Bytes ) > > {
727+ let mut results = Vec :: new ( ) ;
728+
729+ println ! ( "Parallel fetch request for {} chunks" , chunk_requests. len( ) ) ;
730+
731+ // Collect all requested chunks first
732+ for & ( index, hash) in chunk_requests {
733+ if let Some ( content) = chunk_storage. get ( & hash) {
734+ results. push ( ( index, content. clone ( ) ) ) ;
735+ println ! ( " Found chunk {} with hash: {}" , index, hex:: encode( hash) ) ;
736+ } else {
737+ return Err ( Error :: Generic ( format ! (
738+ "Chunk not found for hash: {}" ,
739+ hex:: encode( hash)
740+ ) ) ) ;
741+ }
742+ }
743+
744+ // Randomize the order before returning to simulate parallel retrieval
745+ // where chunks arrive in non-deterministic order
746+ let mut rng = thread_rng ( ) ;
747+ results. shuffle ( & mut rng) ;
748+
749+ Ok ( results)
750+ } ;
751+
752+ streaming_decrypt_from_storage ( & data_map, & output_path, parallel_chunk_fetcher) ?;
753+ println ! ( "Successfully decrypted to file using streaming: {:?}" , output_path) ;
754+
755+ // Read back the decrypted content from disk
756+ let mut decrypted_data = Vec :: new ( ) ;
757+ File :: open ( & output_path) ?. read_to_end ( & mut decrypted_data) ?;
758+ println ! ( "Read back {} bytes from decrypted file" , decrypted_data. len( ) ) ;
759+
760+ // Compare hash first using XorName (which provides content hashing)
761+ let original_hash = XorName :: from_content ( & original_data) ;
762+ let decrypted_hash = XorName :: from_content ( & decrypted_data) ;
763+
764+ if original_hash != decrypted_hash {
765+ println ! ( "Hash mismatch detected!" ) ;
766+ println ! ( "Original hash: {}" , hex:: encode( original_hash) ) ;
767+ println ! ( "Decrypted hash: {}" , hex:: encode( decrypted_hash) ) ;
768+
769+ // Further compare the raw content for debugging
770+ assert_eq ! (
771+ original_data. len( ) ,
772+ decrypted_data. len( ) ,
773+ "Data length mismatch: original={}, decrypted={}" ,
774+ original_data. len( ) ,
775+ decrypted_data. len( )
776+ ) ;
777+
778+ // Find first difference
779+ for ( i, ( orig, decr) ) in original_data. iter ( ) . zip ( decrypted_data. iter ( ) ) . enumerate ( ) {
780+ if orig != decr {
781+ panic ! (
782+ "First difference at byte {}: original=0x{:02x}, decrypted=0x{:02x}" ,
783+ i, orig, decr
784+ ) ;
785+ }
786+ }
787+
788+ panic ! ( "Hashes don't match but raw content is identical - this shouldn't happen" ) ;
789+ } else {
790+ println ! ( "✓ Hash comparison passed!" ) ;
791+
792+ // Do a final raw content comparison for completeness
793+ assert_eq ! (
794+ original_data. as_ref( ) ,
795+ decrypted_data. as_slice( ) ,
796+ "Raw content mismatch despite matching hashes"
797+ ) ;
798+ println ! ( "✓ Raw content comparison passed!" ) ;
799+ }
800+
801+ println ! ( "Test completed successfully - 100MB data encrypted and streaming decrypted correctly with parallel randomized chunk fetching" ) ;
802+ Ok ( ( ) )
803+ }
804+
696805#[ test]
697806fn test_chunk_verification ( ) -> Result < ( ) > {
698807 let storage = StorageBackend :: new ( ) ?;
0 commit comments