@@ -411,3 +411,195 @@ impl Archive {
411411 Ok ( ( ) )
412412 }
413413}
414+
415+ #[ cfg( test) ]
416+ mod test {
417+ use super :: * ;
418+
419+ use std:: fs;
420+ use std:: io:: Read ;
421+
422+ use assert_fs:: prelude:: * ;
423+ use assert_fs:: TempDir ;
424+
425+ use crate :: monitor:: test:: TestMonitor ;
426+ use crate :: test_fixtures:: store_two_versions;
427+ use crate :: test_fixtures:: TreeFixture ;
428+
429+ #[ tokio:: test]
430+ async fn create_then_open_archive ( ) {
431+ let testdir = TempDir :: new ( ) . unwrap ( ) ;
432+ let arch_path = testdir. path ( ) . join ( "arch" ) ;
433+ let arch = Archive :: create_path ( & arch_path) . await . unwrap ( ) ;
434+
435+ assert ! ( arch. list_band_ids( ) . await . unwrap( ) . is_empty( ) ) ;
436+
437+ // We can re-open it.
438+ Archive :: open_path ( & arch_path) . await . unwrap ( ) ;
439+ assert ! ( arch. list_band_ids( ) . await . unwrap( ) . is_empty( ) ) ;
440+ assert ! ( arch. last_complete_band( ) . await . unwrap( ) . is_none( ) ) ;
441+ }
442+
443+ #[ tokio:: test]
444+ async fn fails_on_non_empty_directory ( ) {
445+ let temp = TempDir :: new ( ) . unwrap ( ) ;
446+
447+ temp. child ( "i am already here" ) . touch ( ) . unwrap ( ) ;
448+
449+ let result = Archive :: create_path ( temp. path ( ) ) . await ;
450+ assert_eq ! (
451+ result. as_ref( ) . unwrap_err( ) . to_string( ) ,
452+ "Directory for new archive is not empty" ,
453+ "{result:?}"
454+ ) ;
455+ temp. close ( ) . unwrap ( ) ;
456+ }
457+
458+ /// A new archive contains just one header file.
459+ /// The header is readable json containing only a version number.
460+ #[ tokio:: test]
461+ async fn empty_archive ( ) {
462+ let af = Archive :: create_temp ( ) . await ;
463+
464+ assert ! ( af. transport( ) . local_path( ) . unwrap( ) . is_dir( ) ) ;
465+ assert ! ( af
466+ . transport( )
467+ . local_path( )
468+ . unwrap( )
469+ . join( "CONSERVE" )
470+ . is_file( ) ) ;
471+ assert ! ( af. transport( ) . local_path( ) . unwrap( ) . join( "d" ) . is_dir( ) ) ;
472+
473+ let header_path = af. transport ( ) . local_path ( ) . unwrap ( ) . join ( "CONSERVE" ) ;
474+ let mut header_file = fs:: File :: open ( header_path) . unwrap ( ) ;
475+ let mut contents = String :: new ( ) ;
476+ header_file. read_to_string ( & mut contents) . unwrap ( ) ;
477+ assert_eq ! ( contents, "{\" conserve_archive_version\" :\" 0.6\" }\n " ) ;
478+
479+ assert ! (
480+ af. last_band_id( ) . await . unwrap( ) . is_none( ) ,
481+ "Archive should have no bands yet"
482+ ) ;
483+ assert ! (
484+ af. last_complete_band( ) . await . unwrap( ) . is_none( ) ,
485+ "Archive should have no bands yet"
486+ ) ;
487+ assert_eq ! (
488+ af. referenced_blocks( & af. list_band_ids( ) . await . unwrap( ) , TestMonitor :: arc( ) )
489+ . await
490+ . unwrap( )
491+ . len( ) ,
492+ 0
493+ ) ;
494+ assert_eq ! ( af. all_blocks( TestMonitor :: arc( ) ) . await . unwrap( ) . len( ) , 0 ) ;
495+ }
496+
497+ #[ tokio:: test]
498+ async fn delete_all_bands ( ) {
499+ let af = Archive :: create_temp ( ) . await ;
500+ store_two_versions ( & af) . await ;
501+
502+ let stats = af
503+ . delete_bands (
504+ & [ BandId :: new ( & [ 0 ] ) , BandId :: new ( & [ 1 ] ) ] ,
505+ & Default :: default ( ) ,
506+ TestMonitor :: arc ( ) ,
507+ )
508+ . await
509+ . expect ( "delete_bands" ) ;
510+
511+ assert_eq ! ( stats. deleted_block_count, 2 ) ;
512+ assert_eq ! ( stats. deleted_band_count, 2 ) ;
513+ }
514+
515+ #[ tokio:: test]
516+ async fn unreferenced_blocks ( ) {
517+ let archive = Archive :: create_temp ( ) . await ;
518+ let tf = TreeFixture :: new ( ) ;
519+ tf. create_file ( "hello" ) ;
520+ let content_hash: BlockHash =
521+ "9063990e5c5b2184877f92adace7c801a549b00c39cd7549877f06d5dd0d3a6ca6eee42d5\
522+ 896bdac64831c8114c55cee664078bd105dc691270c92644ccb2ce7"
523+ . parse ( )
524+ . unwrap ( ) ;
525+
526+ let _copy_stats = backup (
527+ & archive,
528+ tf. path ( ) ,
529+ & BackupOptions :: default ( ) ,
530+ TestMonitor :: arc ( ) ,
531+ )
532+ . await
533+ . expect ( "backup" ) ;
534+
535+ // Delete the band and index
536+ std:: fs:: remove_dir_all ( archive. transport ( ) . local_path ( ) . unwrap ( ) . join ( "b0000" ) ) . unwrap ( ) ;
537+ let monitor = TestMonitor :: arc ( ) ;
538+
539+ let unreferenced: Vec < BlockHash > =
540+ archive. unreferenced_blocks ( monitor. clone ( ) ) . await . unwrap ( ) ;
541+ assert_eq ! ( unreferenced, [ content_hash] ) ;
542+
543+ // Delete dry run.
544+ let delete_stats = archive
545+ . delete_bands (
546+ & [ ] ,
547+ & DeleteOptions {
548+ dry_run : true ,
549+ break_lock : false ,
550+ } ,
551+ monitor. clone ( ) ,
552+ )
553+ . await
554+ . unwrap ( ) ;
555+ assert_eq ! (
556+ delete_stats,
557+ DeleteStats {
558+ unreferenced_block_count: 1 ,
559+ unreferenced_block_bytes: 10 ,
560+ deletion_errors: 0 ,
561+ deleted_block_count: 0 ,
562+ deleted_band_count: 0 ,
563+ elapsed: delete_stats. elapsed,
564+ }
565+ ) ;
566+
567+ // Delete unreferenced blocks.
568+ let options = DeleteOptions {
569+ dry_run : false ,
570+ break_lock : false ,
571+ } ;
572+ let delete_stats = archive
573+ . delete_bands ( & [ ] , & options, monitor. clone ( ) )
574+ . await
575+ . unwrap ( ) ;
576+ assert_eq ! (
577+ delete_stats,
578+ DeleteStats {
579+ unreferenced_block_count: 1 ,
580+ unreferenced_block_bytes: 10 ,
581+ deletion_errors: 0 ,
582+ deleted_block_count: 1 ,
583+ deleted_band_count: 0 ,
584+ elapsed: delete_stats. elapsed,
585+ }
586+ ) ;
587+
588+ // Try again to delete: should find no garbage.
589+ let delete_stats = archive
590+ . delete_bands ( & [ ] , & options, monitor. clone ( ) )
591+ . await
592+ . unwrap ( ) ;
593+ assert_eq ! (
594+ delete_stats,
595+ DeleteStats {
596+ unreferenced_block_count: 0 ,
597+ unreferenced_block_bytes: 0 ,
598+ deletion_errors: 0 ,
599+ deleted_block_count: 0 ,
600+ deleted_band_count: 0 ,
601+ elapsed: delete_stats. elapsed,
602+ }
603+ ) ;
604+ }
605+ }
0 commit comments