use crate::config::types::io::daily_backup;
use std::fs;
fn bak_names(dir: &std::path::Path) -> Vec<String> {
let mut names: Vec<String> = fs::read_dir(dir)
.expect("read_dir")
.filter_map(|e| e.ok())
.map(|e| e.file_name().to_string_lossy().to_string())
.filter(|n| n.ends_with(".bak"))
.collect();
names.sort();
names
}
fn read(dir: &std::path::Path, name: &str) -> String {
fs::read_to_string(dir.join(name)).expect("read backup")
}
#[test]
fn same_day_changed_content_rotates_to_timestamped_sibling() {
let tmp = tempfile::tempdir().expect("tempdir");
let file = tmp.path().join("f.toml");
let today = chrono::Local::now().format("%Y-%m-%d").to_string();
let today_bak = format!("f.toml.{today}.bak");
fs::write(&file, "v1").expect("write v1");
daily_backup(&file, 7);
assert!(
tmp.path().join(&today_bak).exists(),
"today's snapshot missing"
);
assert_eq!(read(tmp.path(), &today_bak), "v1", "first snapshot wrong");
fs::write(&file, "v2").expect("write v2");
daily_backup(&file, 7);
let names = bak_names(tmp.path());
assert_eq!(names.len(), 2, "expected exactly 2 backups, got {names:?}");
assert!(
names.contains(&today_bak),
"day's first snapshot must survive"
);
let rotated = names
.iter()
.find(|n| **n != today_bak)
.expect("rotated sibling");
assert!(
rotated.starts_with(&format!("f.toml.{today}T")),
"rotated name must be timestamped, got {rotated}"
);
assert_eq!(
read(tmp.path(), rotated),
"v2",
"rotated copy must hold the newer pre-write state"
);
assert_eq!(
read(tmp.path(), &today_bak),
"v1",
"first snapshot was clobbered"
);
}
#[test]
fn same_day_identical_content_is_no_op() {
let tmp = tempfile::tempdir().expect("tempdir");
let file = tmp.path().join("f.toml");
fs::write(&file, "v1").expect("write");
daily_backup(&file, 7);
daily_backup(&file, 7);
daily_backup(&file, 7);
let names = bak_names(tmp.path());
assert_eq!(
names.len(),
1,
"identical re-runs must not spawn backups: {names:?}"
);
assert_eq!(fs::read_to_string(&file).expect("read file"), "v1");
}
#[test]
fn prune_counts_rotated_siblings_under_max_days() {
let tmp = tempfile::tempdir().expect("tempdir");
let file = tmp.path().join("f.toml");
let today = chrono::Local::now().format("%Y-%m-%d").to_string();
let today_bak = format!("f.toml.{today}.bak");
for day in ["2026-09-04", "2026-09-03"] {
fs::write(tmp.path().join(format!("f.toml.{day}.bak")), "old").expect("seed");
}
fs::write(tmp.path().join(&today_bak), "v0").expect("seed today");
fs::write(&file, "v1").expect("write v1");
daily_backup(&file, 3);
let names = bak_names(tmp.path());
assert_eq!(
names.len(),
3,
"max_days=3 must keep exactly 3 files after rotation, got {names:?}"
);
assert!(
names.contains(&today_bak)
&& names
.iter()
.any(|n| n.starts_with(&format!("f.toml.{today}T"))),
"today's plain + rotated snapshots must both survive: {names:?}"
);
assert!(
!names.iter().any(|n| n.contains("2026-09-03")),
"oldest backup must be pruned, got {names:?}"
);
}