This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

core / knot2 / crates / knot-maintenance / tests / cruft.rs
4.7 kB 143 lines
1use knot_git::Repo; 2use knot_maintenance::{Options, PruneGrace, ReflogRetention, run_repo}; 3use knot_types::{ObjectFormat, UnixSeconds}; 4 5mod common; 6use common::{ 7 EMPTY_TREE_SHA1, assert_fsck_clean, commit_on, create_repo, delete_ref, empty_tree, 8 has_cruft_pack, now, options, set_ref, set_reflog_seconds, 9}; 10 11fn opts(grace: PruneGrace) -> Options { 12 Options { 13 prune_grace: grace, 14 commit_graph: false, 15 multi_pack_index: false, 16 bitmap: false, 17 ..options() 18 } 19} 20 21fn retain_repeat_then_expire(format: ObjectFormat) { 22 let scan = tempfile::tempdir().unwrap(); 23 let empty = empty_tree(format); 24 let repo = create_repo(scan.path(), format, "did:plc:cuttle"); 25 26 let base = commit_on(&repo, empty, 0, Vec::new()); 27 let doomed = commit_on(&repo, empty, 1, vec![base]); 28 set_ref(&repo, "refs/heads/main", base); 29 set_ref(&repo, "refs/heads/feature", doomed); 30 run_repo(&repo, now(), &opts(PruneGrace::from_secs(86_400))).unwrap(); 31 assert!(repo.contains(doomed), "doomed is packed while reachable"); 32 33 delete_ref(&repo, "refs/heads/feature"); 34 set_ref( 35 &repo, 36 "refs/heads/main", 37 commit_on(&repo, empty, 2, vec![base]), 38 ); 39 40 let retained = run_repo(&repo, now(), &opts(PruneGrace::from_secs(86_400))).unwrap(); 41 assert!( 42 retained.prune.crufted.get() >= 1, 43 "young unreachable objects are crufted, not dropped" 44 ); 45 let reopened = Repo::open(repo.git().git_dir()).unwrap(); 46 assert!( 47 has_cruft_pack(&reopened) && reopened.contains(doomed), 48 "a cruft pack holds the young object" 49 ); 50 assert_fsck_clean(&reopened); 51 52 run_repo(&reopened, now(), &opts(PruneGrace::from_secs(86_400))).unwrap(); 53 let recycled = Repo::open(repo.git().git_dir()).unwrap(); 54 assert!( 55 recycled.contains(doomed), 56 "the young object survives a repeat cruft cycle within grace" 57 ); 58 assert_fsck_clean(&recycled); 59 60 assert!( 61 run_repo(&recycled, now(), &opts(PruneGrace::from_secs(0))) 62 .unwrap() 63 .prune 64 .ran 65 ); 66 let settled = Repo::open(repo.git().git_dir()).unwrap(); 67 assert!( 68 !settled.contains(doomed), 69 "the past-grace object is finally dropped" 70 ); 71 assert!( 72 settled.contains(base) && !has_cruft_pack(&settled), 73 "base survives, no cruft lingers" 74 ); 75 assert_fsck_clean(&settled); 76} 77 78#[test] 79fn cruft_retains_survives_a_repeat_cycle_then_expires_sha1() { 80 retain_repeat_then_expire(ObjectFormat::SHA1); 81} 82 83#[test] 84fn cruft_retains_survives_a_repeat_cycle_then_expires_sha256() { 85 retain_repeat_then_expire(ObjectFormat::SHA256); 86} 87 88#[test] 89fn a_reflog_floor_above_the_retention_minimum_keeps_referenced_objects() { 90 let scan = tempfile::tempdir().unwrap(); 91 let repo = create_repo(scan.path(), ObjectFormat::SHA1, "did:plc:whelk"); 92 93 let base = commit_on(&repo, EMPTY_TREE_SHA1, 0, Vec::new()); 94 let doomed = commit_on(&repo, EMPTY_TREE_SHA1, 1, vec![base]); 95 set_ref(&repo, "refs/heads/main", doomed); 96 set_ref( 97 &repo, 98 "refs/heads/main", 99 commit_on(&repo, EMPTY_TREE_SHA1, 2, vec![base]), 100 ); 101 102 let opts = Options { 103 reflog_floor: ReflogRetention::from_secs(3_000_000_000), 104 ..opts(PruneGrace::from_secs(0)) 105 }; 106 run_repo(&repo, UnixSeconds::new(4_000_000_000), &opts).unwrap(); 107 108 let reopened = Repo::open(repo.git().git_dir()).unwrap(); 109 assert!( 110 reopened.contains(doomed), 111 "an object held only by a retained reflog entry is a prune root" 112 ); 113} 114 115#[test] 116fn a_rewound_away_commit_survives_on_its_reflog_old_pointer_alone() { 117 let scan = tempfile::tempdir().unwrap(); 118 let repo = create_repo(scan.path(), ObjectFormat::SHA1, "did:plc:periwinkle"); 119 120 let lost = commit_on(&repo, EMPTY_TREE_SHA1, 7, Vec::new()); 121 set_ref(&repo, "refs/heads/main", lost); 122 let survivor = commit_on(&repo, EMPTY_TREE_SHA1, 9, Vec::new()); 123 set_ref(&repo, "refs/heads/main", survivor); 124 125 set_reflog_seconds(&repo, "refs/heads/main", &[1_000_000, 2_000_000_000]); 126 127 let opts = Options { 128 reflog_floor: ReflogRetention::from_secs(500_000_000), 129 ..opts(PruneGrace::from_secs(0)) 130 }; 131 run_repo(&repo, UnixSeconds::new(2_000_000_000), &opts).unwrap(); 132 133 let reopened = Repo::open(repo.git().git_dir()).unwrap(); 134 assert!( 135 reopened.contains(lost), 136 "a force-rewound commit reachable only through a reflog old-pointer must not be pruned" 137 ); 138 assert!( 139 reopened.contains(survivor), 140 "the live tip survives the prune" 141 ); 142 assert_fsck_clean(&reopened); 143}