A clean-up with the downloads folder gone forgets no file (#293)
missing_files took every path that did not exist() as deleted by hand. With /downloads out of reach (the array stopped, shfs dropped) that is every file, and each would have been marked reaped at once. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -45,9 +45,17 @@ pub fn aged(candidates: &[Candidate], cutoff: i64) -> Vec<Candidate> {
|
|||||||
pub async fn run(cfg: &Config, db: &Db, dry_run: bool) -> Result<Report> {
|
pub async fn run(cfg: &Config, db: &Db, dry_run: bool) -> Result<Report> {
|
||||||
let mut report = Report::default();
|
let mut report = Report::default();
|
||||||
|
|
||||||
// Someone may have deleted a file by hand; the row must stop claiming it exists.
|
// Someone may have deleted a file by hand; the row must stop claiming it exists. Not while
|
||||||
|
// the folder itself is gone (the array stopped, shfs dropped): every file would look deleted
|
||||||
|
// and the whole library be forgotten at once (#293).
|
||||||
// Each step is a span of the reap's trace, so a slow one says which part was slow.
|
// Each step is a span of the reap's trace, so a slow one says which part was slow.
|
||||||
for (id, path) in db.missing_files().instrument(step("missing_files")).await? {
|
let missing = if cfg.general.download_dir.is_dir() {
|
||||||
|
db.missing_files().instrument(step("missing_files")).await?
|
||||||
|
} else {
|
||||||
|
tracing::warn!(dir = %cfg.general.download_dir.display(), "the downloads folder is not there; left every file as it was");
|
||||||
|
vec![]
|
||||||
|
};
|
||||||
|
for (id, path) in missing {
|
||||||
if !dry_run {
|
if !dry_run {
|
||||||
db.mark_reaped(id).await?;
|
db.mark_reaped(id).await?;
|
||||||
}
|
}
|
||||||
@@ -204,6 +212,23 @@ mod tests {
|
|||||||
assert_eq!(db.prune_entries(500).await.unwrap(), 1, "the old item goes, a leaver's pin or not");
|
assert_eq!(db.prune_entries(500).await.unwrap(), 1, "the old item goes, a leaver's pin or not");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn a_downloads_folder_that_is_gone_forgets_no_file() {
|
||||||
|
let db = Db::memory().await.unwrap();
|
||||||
|
db.exec_for_test(
|
||||||
|
"INSERT INTO enclosures (id, feed_id, guid, url, path, state) VALUES
|
||||||
|
(1, 'f', 'a', 'u1', '/nowhere/ipx-test/a.mp3', 'done');",
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let mut cfg = Config::default();
|
||||||
|
cfg.general.download_dir = "/nowhere/ipx-test".into();
|
||||||
|
assert_eq!(run(&cfg, &db, false).await.unwrap().reconciled, 0, "the folder is gone, not the file");
|
||||||
|
|
||||||
|
cfg.general.download_dir = std::env::temp_dir();
|
||||||
|
assert_eq!(run(&cfg, &db, false).await.unwrap().reconciled, 1, "the folder is there and the file is not");
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn prune_keeps_entries_that_still_have_a_file() {
|
async fn prune_keeps_entries_that_still_have_a_file() {
|
||||||
let db = Db::memory().await.unwrap();
|
let db = Db::memory().await.unwrap();
|
||||||
|
|||||||
Reference in New Issue
Block a user