@@ -335,7 +335,6 @@ def test_no_overwrite_flag_multipart_upload_when_object_not_exists_on_target(
335335 # Create a large file that will trigger multipart upload
336336 full_path = self .files .create_file ('foo.txt' , 'a' * 10 * (1024 ** 2 ))
337337 cmdline = f'{ self .prefix } { full_path } s3://bucket --no-overwrite'
338-
339338 # Set up responses for multipart upload
340339 self .parsed_responses = [
341340 {'UploadId' : 'foo' }, # CreateMultipartUpload response
@@ -397,6 +396,160 @@ def test_no_overwrite_flag_multipart_upload_when_object_exists_on_target(
397396 # Verify the IfNoneMatch condition was set in the CompleteMultipartUpload request
398397 self .assertEqual (self .operations_called [3 ][1 ]['IfNoneMatch' ], '*' )
399398
399+ def test_no_overwrite_flag_on_copy_when_small_object_does_not_exist_on_target (
400+ self ,
401+ ):
402+ cmdline = f'{ self .prefix } s3://bucket1/key.txt s3://bucket/key1.txt --no-overwrite'
403+ # Set up responses for multipart copy (since no-overwrite always uses multipart)
404+ self .parsed_responses = [
405+ self .head_object_response (), # HeadObject to get source metadata
406+ self .create_mpu_response ('foo' ), # CreateMultipartUpload response
407+ self .upload_part_copy_response (), # UploadPartCopy response
408+ {}, # CompleteMultipartUpload response
409+ ]
410+ self .run_cmd (cmdline , expected_rc = 0 )
411+ # Verify all multipart operations were called
412+ self .assertEqual (len (self .operations_called ), 4 )
413+ self .assertEqual (self .operations_called [0 ][0 ].name , 'HeadObject' )
414+ self .assertEqual (
415+ self .operations_called [1 ][0 ].name , 'CreateMultipartUpload'
416+ )
417+ self .assertEqual (self .operations_called [2 ][0 ].name , 'UploadPartCopy' )
418+ self .assertEqual (
419+ self .operations_called [3 ][0 ].name , 'CompleteMultipartUpload'
420+ )
421+ # Verify the IfNoneMatch condition was set in the CompleteMultipartUpload request
422+ self .assertEqual (self .operations_called [3 ][1 ]['IfNoneMatch' ], '*' )
423+
424+ def test_no_overwrite_flag_on_copy_when_small_object_exists_on_target (
425+ self ,
426+ ):
427+ cmdline = f'{ self .prefix } s3://bucket1/key.txt s3://bucket/key.txt --no-overwrite'
428+ # Set up responses for multipart copy (since no-overwrite always uses multipart)
429+ self .parsed_responses = [
430+ self .head_object_response (), # HeadObject to get source metadata
431+ self .create_mpu_response ('foo' ), # CreateMultipartUpload response
432+ self .upload_part_copy_response (), # UploadPartCopy response
433+ self .precondition_failed_error_response (), # CompleteMultipartUpload
434+ {}, # AbortMultipartUpload response
435+ ]
436+ self .run_cmd (cmdline , expected_rc = 0 )
437+ # Verify all multipart operations were called
438+ self .assertEqual (len (self .operations_called ), 5 )
439+ self .assertEqual (self .operations_called [0 ][0 ].name , 'HeadObject' )
440+ self .assertEqual (
441+ self .operations_called [1 ][0 ].name , 'CreateMultipartUpload'
442+ )
443+ self .assertEqual (self .operations_called [2 ][0 ].name , 'UploadPartCopy' )
444+ self .assertEqual (
445+ self .operations_called [3 ][0 ].name , 'CompleteMultipartUpload'
446+ )
447+ self .assertEqual (
448+ self .operations_called [4 ][0 ].name , 'AbortMultipartUpload'
449+ )
450+ # Verify the IfNoneMatch condition was set in the CompleteMultipartUpload request
451+ self .assertEqual (self .operations_called [3 ][1 ]['IfNoneMatch' ], '*' )
452+
453+ def test_no_overwrite_flag_on_copy_when_zero_size_object_exists_at_destination (
454+ self ,
455+ ):
456+ cmdline = f'{ self .prefix } s3://bucket1/file.txt s3://bucket2/file.txt --no-overwrite'
457+ self .parsed_responses = [
458+ self .head_object_response (
459+ ContentLength = 0
460+ ), # Source object (zero size)
461+ self .head_object_response (), # Checking the object at destination
462+ ]
463+ self .run_cmd (cmdline , expected_rc = 0 )
464+ self .assertEqual (len (self .operations_called ), 2 )
465+ self .assertEqual (self .operations_called [0 ][0 ].name , 'HeadObject' )
466+ self .assertEqual (self .operations_called [1 ][0 ].name , 'HeadObject' )
467+
468+ def test_no_overwrite_flag_on_copy_when_zero_size_object_not_exists_at_destination (
469+ self ,
470+ ):
471+ cmdline = f'{ self .prefix } s3://bucket1/file.txt s3://bucket2/file1.txt --no-overwrite'
472+ self .parsed_responses = [
473+ self .head_object_response (
474+ ContentLength = 0
475+ ), # Source object (zero size)
476+ {
477+ 'Error' : {'Code' : '404' , 'Message' : 'Not Found' }
478+ }, # At destination object does not exists
479+ self .copy_object_response (), # Copy Request when object does not exists
480+ ]
481+ self .run_cmd (cmdline , expected_rc = 0 )
482+ self .assertEqual (len (self .operations_called ), 3 )
483+ self .assertEqual (self .operations_called [0 ][0 ].name , 'HeadObject' )
484+ self .assertEqual (self .operations_called [1 ][0 ].name , 'HeadObject' )
485+ self .assertEqual (self .operations_called [2 ][0 ].name , 'CopyObject' )
486+
487+ def test_no_overwrite_flag_on_copy_when_large_object_exists_on_target (
488+ self ,
489+ ):
490+ cmdline = f'{ self .prefix } s3://bucket1/key.txt s3://bucket/key.txt --no-overwrite'
491+ # Set up responses for multipart copy with large object
492+ self .parsed_responses = [
493+ self .head_object_response (
494+ ContentLength = 10 * (1024 ** 2 )
495+ ), # HeadObject with large content
496+ self .get_object_tagging_response ({}), # GetObjectTagging response
497+ self .create_mpu_response ('foo' ), # CreateMultipartUpload response
498+ self .upload_part_copy_response (), # UploadPartCopy response part 1
499+ self .upload_part_copy_response (), # UploadPartCopy response part 2
500+ self .precondition_failed_error_response (), # CompleteMultipartUpload fails with PreconditionFailed
501+ {}, # AbortMultipartUpload response
502+ ]
503+ self .run_cmd (cmdline , expected_rc = 0 )
504+ # Verify all multipart operations were called
505+ self .assertEqual (len (self .operations_called ), 7 )
506+ self .assertEqual (self .operations_called [0 ][0 ].name , 'HeadObject' )
507+ self .assertEqual (self .operations_called [1 ][0 ].name , 'GetObjectTagging' )
508+ self .assertEqual (
509+ self .operations_called [2 ][0 ].name , 'CreateMultipartUpload'
510+ )
511+ self .assertEqual (self .operations_called [3 ][0 ].name , 'UploadPartCopy' )
512+ self .assertEqual (self .operations_called [4 ][0 ].name , 'UploadPartCopy' )
513+ self .assertEqual (
514+ self .operations_called [5 ][0 ].name , 'CompleteMultipartUpload'
515+ )
516+ self .assertEqual (
517+ self .operations_called [6 ][0 ].name , 'AbortMultipartUpload'
518+ )
519+ # Verify the IfNoneMatch condition was set in the CompleteMultipartUpload request
520+ self .assertEqual (self .operations_called [5 ][1 ]['IfNoneMatch' ], '*' )
521+
522+ def test_no_overwrite_flag_on_copy_when_large_object_does_not_exist_on_target (
523+ self ,
524+ ):
525+ cmdline = f'{ self .prefix } s3://bucket1/key.txt s3://bucket/key1.txt --no-overwrite'
526+ # Set up responses for multipart copy with large object
527+ self .parsed_responses = [
528+ self .head_object_response (
529+ ContentLength = 10 * (1024 ** 2 )
530+ ), # HeadObject with large content
531+ self .get_object_tagging_response ({}), # GetObjectTagging response
532+ self .create_mpu_response ('foo' ), # CreateMultipartUpload response
533+ self .upload_part_copy_response (), # UploadPartCopy response part 1
534+ self .upload_part_copy_response (), # UploadPartCopy response part 2
535+ {}, # CompleteMultipartUpload response
536+ ]
537+ self .run_cmd (cmdline , expected_rc = 0 )
538+ # Verify all multipart operations were called
539+ self .assertEqual (len (self .operations_called ), 6 )
540+ self .assertEqual (self .operations_called [0 ][0 ].name , 'HeadObject' )
541+ self .assertEqual (self .operations_called [1 ][0 ].name , 'GetObjectTagging' )
542+ self .assertEqual (
543+ self .operations_called [2 ][0 ].name , 'CreateMultipartUpload'
544+ )
545+ self .assertEqual (self .operations_called [3 ][0 ].name , 'UploadPartCopy' )
546+ self .assertEqual (self .operations_called [4 ][0 ].name , 'UploadPartCopy' )
547+ self .assertEqual (
548+ self .operations_called [5 ][0 ].name , 'CompleteMultipartUpload'
549+ )
550+ # Verify the IfNoneMatch condition was set in the CompleteMultipartUpload request
551+ self .assertEqual (self .operations_called [5 ][1 ]['IfNoneMatch' ], '*' )
552+
400553 def test_dryrun_download (self ):
401554 self .parsed_responses = [self .head_object_response ()]
402555 target = self .files .full_path ('file.txt' )
0 commit comments