@@ -45,6 +45,14 @@ pub fn tests(op: &Operator, tests: &mut Vec<Trial>) {
4545 test_copy_with_if_not_exists_to_existing_file
4646 ) )
4747 }
48+
49+ if cap. read && cap. write && cap. copy && cap. copy_with_if_match {
50+ tests. extend ( async_trials ! (
51+ op,
52+ test_copy_with_if_match_success,
53+ test_copy_with_if_match_failure
54+ ) )
55+ }
4856}
4957
5058/// Copy a file with ascii name and test contents.
@@ -313,3 +321,68 @@ pub async fn test_copy_with_if_not_exists_to_existing_file(op: Operator) -> Resu
313321 op. delete ( & target_path) . await . expect ( "delete must succeed" ) ;
314322 Ok ( ( ) )
315323}
324+
325+ /// Copy with if_match should succeed when ETag matches.
326+ pub async fn test_copy_with_if_match_success ( op : Operator ) -> Result < ( ) > {
327+ if !op. info ( ) . full_capability ( ) . copy_with_if_match {
328+ return Ok ( ( ) ) ;
329+ }
330+
331+ let source_path = uuid:: Uuid :: new_v4 ( ) . to_string ( ) ;
332+ let ( source_content, _) = gen_bytes ( op. info ( ) . full_capability ( ) ) ;
333+
334+ op. write ( & source_path, source_content. clone ( ) ) . await ?;
335+
336+ // Get the ETag of the source file
337+ let source_meta = op. stat ( & source_path) . await ?;
338+ let etag = source_meta. etag ( ) . expect ( "source should have etag" ) ;
339+
340+ let target_path = uuid:: Uuid :: new_v4 ( ) . to_string ( ) ;
341+
342+ // Copy with matching ETag should succeed
343+ op. copy_with ( & source_path, & target_path)
344+ . if_match ( etag)
345+ . await ?;
346+
347+ let target_content = op
348+ . read ( & target_path)
349+ . await
350+ . expect ( "read must succeed" )
351+ . to_bytes ( ) ;
352+ assert_eq ! (
353+ format!( "{:x}" , Sha256 :: digest( target_content) ) ,
354+ format!( "{:x}" , Sha256 :: digest( & source_content) ) ,
355+ ) ;
356+
357+ op. delete ( & source_path) . await . expect ( "delete must succeed" ) ;
358+ op. delete ( & target_path) . await . expect ( "delete must succeed" ) ;
359+ Ok ( ( ) )
360+ }
361+
362+ /// Copy with if_match should fail when ETag doesn't match.
363+ pub async fn test_copy_with_if_match_failure ( op : Operator ) -> Result < ( ) > {
364+ if !op. info ( ) . full_capability ( ) . copy_with_if_match {
365+ return Ok ( ( ) ) ;
366+ }
367+
368+ let source_path = uuid:: Uuid :: new_v4 ( ) . to_string ( ) ;
369+ let ( source_content, _) = gen_bytes ( op. info ( ) . full_capability ( ) ) ;
370+
371+ op. write ( & source_path, source_content. clone ( ) ) . await ?;
372+
373+ let target_path = uuid:: Uuid :: new_v4 ( ) . to_string ( ) ;
374+
375+ // Copy with non-matching ETag should fail
376+ let err = op
377+ . copy_with ( & source_path, & target_path)
378+ . if_match ( "invalid-etag" )
379+ . await
380+ . expect_err ( "copy must fail" ) ;
381+ assert_eq ! ( err. kind( ) , ErrorKind :: ConditionNotMatch ) ;
382+
383+ // Verify target file was not created
384+ assert ! ( !op. exists( & target_path) . await ?) ;
385+
386+ op. delete ( & source_path) . await . expect ( "delete must succeed" ) ;
387+ Ok ( ( ) )
388+ }
0 commit comments