@@ -3,6 +3,7 @@ use crate::utils::*;
33
44use std:: sync:: Arc ;
55
6+ use base64:: Engine as _;
67use s3s_test:: Result ;
78use s3s_test:: TestFixture ;
89use s3s_test:: TestSuite ;
@@ -14,6 +15,8 @@ use aws_sdk_s3::primitives::SdkBody;
1415use bytes:: Bytes ;
1516use futures:: StreamExt as _;
1617use http_body_util:: StreamBody ;
18+ use sha2:: Digest as _;
19+ use sha2:: Sha256 ;
1720
1821pub fn register ( tcx : & mut TestContext ) {
1922 case ! ( tcx, Basic , Essential , test_list_buckets) ;
@@ -25,6 +28,7 @@ pub fn register(tcx: &mut TestContext) {
2528 case ! ( tcx, Basic , Put , test_put_object_with_metadata) ;
2629 case ! ( tcx, Basic , Put , test_put_object_larger) ;
2730 case ! ( tcx, Basic , Put , test_put_object_with_checksum_algorithm) ;
31+ case ! ( tcx, Basic , Put , test_put_object_with_content_checksums) ;
2832 case ! ( tcx, Basic , Copy , test_copy_object) ;
2933}
3034
@@ -435,6 +439,100 @@ impl Put {
435439
436440 Ok ( ( ) )
437441 }
442+
443+ async fn test_put_object_with_content_checksums ( self : Arc < Self > ) -> Result {
444+ use base64:: Engine ;
445+ use sha2:: { Digest , Sha256 } ;
446+
447+ let s3 = & self . s3 ;
448+ let bucket = self . bucket . as_str ( ) ;
449+ let key = "file-with-content-checksums" ;
450+
451+ // Create test content
452+ let content = "Hello, World! This is a test content for checksum validation. 你好世界!" ;
453+ let content_bytes = content. as_bytes ( ) ;
454+
455+ // Calculate MD5 hash
456+ let md5_digest = md5:: compute ( content_bytes) ;
457+ let md5_hash = base64_simd:: STANDARD . encode_to_string ( md5_digest. as_ref ( ) ) ;
458+
459+ // Test with Content-MD5
460+ s3. put_object ( )
461+ . bucket ( bucket)
462+ . key ( format ! ( "{key}-md5" ) )
463+ . body ( ByteStream :: from_static ( content_bytes) )
464+ . content_md5 ( & md5_hash)
465+ . send ( )
466+ . await ?;
467+
468+ // Test with different content sizes and MD5
469+ let large_content = "x" . repeat ( 2048 ) ;
470+ let large_md5_digest = md5:: compute ( large_content. as_bytes ( ) ) ;
471+ let large_md5_hash = base64:: engine:: general_purpose:: STANDARD . encode ( large_md5_digest. as_ref ( ) ) ;
472+
473+ s3. put_object ( )
474+ . bucket ( bucket)
475+ . key ( format ! ( "{key}-large" ) )
476+ . body ( ByteStream :: from ( large_content. clone ( ) . into_bytes ( ) ) )
477+ . content_md5 ( & large_md5_hash)
478+ . send ( )
479+ . await ?;
480+
481+ // Test with empty content and MD5
482+ let empty_content = "" ;
483+ let empty_md5_digest = md5:: compute ( empty_content. as_bytes ( ) ) ;
484+ let empty_md5_hash = base64:: engine:: general_purpose:: STANDARD . encode ( empty_md5_digest. as_ref ( ) ) ;
485+
486+ s3. put_object ( )
487+ . bucket ( bucket)
488+ . key ( format ! ( "{key}-empty" ) )
489+ . body ( ByteStream :: from_static ( empty_content. as_bytes ( ) ) )
490+ . content_md5 ( & empty_md5_hash)
491+ . send ( )
492+ . await ?;
493+
494+ // Verify all objects were uploaded correctly
495+ for ( suffix, expected_content) in [ ( "md5" , content) , ( "large" , & large_content) , ( "empty" , empty_content) ] {
496+ let resp = s3. get_object ( ) . bucket ( bucket) . key ( format ! ( "{key}-{suffix}" ) ) . send ( ) . await ?;
497+
498+ let body = resp. body . collect ( ) . await ?;
499+ let body = String :: from_utf8 ( body. to_vec ( ) ) ?;
500+ assert_eq ! ( body, expected_content) ;
501+ }
502+
503+ // Test with incorrect MD5 (should fail)
504+ let incorrect_md5 = base64:: engine:: general_purpose:: STANDARD . encode ( b"incorrect_md5_hash" ) ;
505+ let result = s3
506+ . put_object ( )
507+ . bucket ( bucket)
508+ . key ( format ! ( "{key}-incorrect-md5" ) )
509+ . body ( ByteStream :: from_static ( content_bytes) )
510+ . content_md5 ( & incorrect_md5)
511+ . send ( )
512+ . await ;
513+
514+ // This should fail with a checksum mismatch error
515+ assert ! ( result. is_err( ) , "Expected checksum mismatch error for incorrect MD5" ) ;
516+
517+ // Test with correct MD5 but wrong content (should fail)
518+ let wrong_content = "This is different content" ;
519+ let wrong_md5_digest = md5:: compute ( wrong_content. as_bytes ( ) ) ;
520+ let wrong_md5_hash = base64:: engine:: general_purpose:: STANDARD . encode ( wrong_md5_digest. as_ref ( ) ) ;
521+
522+ let result = s3
523+ . put_object ( )
524+ . bucket ( bucket)
525+ . key ( format ! ( "{key}-wrong-content" ) )
526+ . body ( ByteStream :: from_static ( content_bytes) ) // Using original content
527+ . content_md5 ( & wrong_md5_hash) // But wrong MD5
528+ . send ( )
529+ . await ;
530+
531+ // This should also fail
532+ assert ! ( result. is_err( ) , "Expected checksum mismatch error for wrong content with correct MD5" ) ;
533+
534+ Ok ( ( ) )
535+ }
438536}
439537
440538// Copy test fixture
0 commit comments