@@ -315,8 +315,102 @@ def test_download_with_checksum_mode_crc32(self):
315315 self .assertEqual (
316316 self .operations_called [1 ][1 ]['ChecksumMode' ], 'ENABLED'
317317 )
318-
319-
318+
319+ def test_mv_no_overwrite_flag_when_object_not_exists_on_target (self ):
320+ """Testing when object doesnt exist using no-overwrite"""
321+ full_path = self .files .create_file ('foo.txt' , 'contents' )
322+ cmdline = f'{ self .prefix } { full_path } s3://bucket --no-overwrite'
323+ self .run_cmd (cmdline , expected_rc = 0 )
324+ # Verify putObject was called
325+ self .assertEqual (len (self .operations_called ),1 )
326+ self .assertEqual (self .operations_called [0 ][0 ].name , 'PutObject' )
327+ # Verify the IfNoneMatch condition was set in the request
328+ self .assertEqual (self .operations_called [0 ][1 ]['IfNoneMatch' ], '*' )
329+ # Verify source file was deleted (move operation)
330+ self .assertFalse (os .path .exists (full_path ))
331+
332+ def test_mv_no_overwrite_flag_when_object_exists_on_target (self ):
333+ """Testing when object already exists using no-overwrite"""
334+ full_path = self .files .create_file ('foo.txt' , 'mycontent' )
335+ cmdline = f'{ self .prefix } { full_path } s3://bucket/foo.txt --no-overwrite'
336+ # Set up the response to simulate a PreconditionFailed error
337+ self .http_response .status_code = 412
338+ self .parsed_responses = [
339+ {
340+ 'Error' : {
341+ 'Code' : 'PreconditionFailed' ,
342+ 'Message' : 'At least one of the pre-conditions you specified did not hold' ,
343+ 'Condition' : 'If-None-match'
344+ }
345+ }
346+ ]
347+ self .run_cmd (cmdline , expected_rc = 0 )
348+ # Verify PutObject was attempted with IfNoneMatch
349+ self .assertEqual (len (self .operations_called ), 1 )
350+ self .assertEqual (self .operations_called [0 ][0 ].name , 'PutObject' )
351+ self .assertEqual (self .operations_called [0 ][1 ]['IfNoneMatch' ], '*' )
352+ # Verify source file was not deleted
353+ self .assertTrue (os .path .exists (full_path ))
354+
355+ def test_mv_no_overwrite_flag_multipart_upload_when_object_not_exists_on_target (self ):
356+ """Testing multipart upload with no-overwrite flag when object doesn't exist"""
357+ # Create a large file that will trigger multipart upload
358+ full_path = self .files .create_file ('foo.txt' , 'a' * 10 * (1024 ** 2 ))
359+ cmdline = f'{ self .prefix } { full_path } s3://bucket --no-overwrite'
360+ # Set up responses for multipart upload
361+ self .parsed_responses = [
362+ {'UploadId' : 'foo' }, # CreateMultipartUpload response
363+ {'ETag' : '"foo-1"' }, # UploadPart response
364+ {'ETag' : '"foo-2"' }, # UploadPart response
365+ {} # CompleteMultipartUpload response
366+ ]
367+ self .run_cmd (cmdline , expected_rc = 0 )
368+ # Verify all multipart operations were called
369+ self .assertEqual (len (self .operations_called ), 4 )
370+ self .assertEqual (self .operations_called [0 ][0 ].name , 'CreateMultipartUpload' )
371+ self .assertEqual (self .operations_called [1 ][0 ].name , 'UploadPart' )
372+ self .assertEqual (self .operations_called [2 ][0 ].name , 'UploadPart' )
373+ self .assertEqual (self .operations_called [3 ][0 ].name , 'CompleteMultipartUpload' )
374+ # Verify the IfNoneMatch condition was set in the CompleteMultipartUpload request
375+ self .assertEqual (self .operations_called [3 ][1 ]['IfNoneMatch' ], '*' )
376+ # Verify source file was deleted (successful move operation)
377+ self .assertFalse (os .path .exists (full_path ))
378+
379+ def test_mv_no_overwrite_flag_multipart_upload_when_object_exists_on_target (self ):
380+ """Testing multipart upload with no-overwrite flag when object exist at the target"""
381+ # Create a large file that will trigger multipart upload
382+ full_path = self .files .create_file ('foo.txt' , 'a' * 10 * (1024 ** 2 ))
383+ cmdline = f'{ self .prefix } { full_path } s3://bucket --no-overwrite'
384+ # Set up responses for multipart upload
385+ self .parsed_responses = [
386+ {'UploadId' : 'foo' }, # CreateMultipartUpload response
387+ {'ETag' : '"foo-1"' }, # UploadPart response
388+ {'ETag' : '"foo-2"' }, # UploadPart response
389+ {
390+ 'Error' : {
391+ 'Code' : 'PreconditionFailed' ,
392+ 'Message' : 'At least one of the pre-conditions you specified did not hold' ,
393+ 'Condition' : 'If-None-match'
394+ }
395+ }, # CompleteMultipartUpload response
396+ {} # Abort Multipart
397+ ]
398+ self .run_cmd (cmdline , expected_rc = 0 )
399+ # Set up the response to simulate a PreconditionFailed error
400+ self .http_response .status_code = 412
401+ # Verify all multipart operations were called
402+ self .assertEqual (len (self .operations_called ), 5 )
403+ self .assertEqual (self .operations_called [0 ][0 ].name , 'CreateMultipartUpload' )
404+ self .assertEqual (self .operations_called [1 ][0 ].name , 'UploadPart' )
405+ self .assertEqual (self .operations_called [2 ][0 ].name , 'UploadPart' )
406+ self .assertEqual (self .operations_called [3 ][0 ].name , 'CompleteMultipartUpload' )
407+ self .assertEqual (self .operations_called [4 ][0 ].name , 'AbortMultipartUpload' )
408+ # Verify the IfNoneMatch condition was set in the CompleteMultipartUpload request
409+ self .assertEqual (self .operations_called [3 ][1 ]['IfNoneMatch' ], '*' )
410+ # Verify source file was not deleted (failed move operation due to PreconditionFailed)
411+ self .assertTrue (os .path .exists (full_path ))
412+
413+
320414class TestMvWithCRTClient (BaseCRTTransferClientTest ):
321415 def test_upload_move_using_crt_client (self ):
322416 filename = self .files .create_file ('myfile' , 'mycontent' )
0 commit comments