#[allow(clippy::wildcard_imports)]
use super::super::*;
#[allow(clippy::wildcard_imports)]
use super::helpers::*;
use std::fs;
#[test]
fn refresh_from_disk_picks_up_new_file() {
let (tmp, mut app) = make_app();
let dir = tmp.path().join("issues");
assert_eq!(app.board.column_cards(0).len(), 0);
let mut watcher = crate::watcher::FsWatcher::new(&dir);
write_issue(&dir, "2026-01-01-a.md", 1, "A", "todo", None);
let delta = watcher.poll().expect("the new file is reported");
app.refresh_from_disk(&delta).unwrap();
assert_eq!(app.board.column_cards(0).len(), 1);
}
#[test]
fn refresh_from_disk_reflects_external_status_move() {
let (tmp, mut app) = make_app();
let dir = tmp.path().join("issues");
let mut watcher = crate::watcher::FsWatcher::new(&dir);
write_issue(&dir, "2026-01-01-a.md", 1, "A", "todo", None);
let delta = watcher.poll().unwrap();
app.refresh_from_disk(&delta).unwrap();
assert_eq!(app.board.column_cards(0).len(), 1);
write_issue(&dir, "2026-01-01-a.md", 1, "A", "done", None);
let delta = watcher.poll().expect("the rewrite is reported");
app.refresh_from_disk(&delta).unwrap();
assert_eq!(app.board.column_cards(0).len(), 0);
assert_eq!(app.board.column_cards(3).len(), 1);
}
#[test]
fn refresh_from_disk_keeps_board_on_bad_file() {
let (tmp, mut app) = make_app();
let dir = tmp.path().join("issues");
let mut watcher = crate::watcher::FsWatcher::new(&dir);
write_issue(&dir, "2026-01-01-a.md", 1, "A", "todo", None);
let delta = watcher.poll().unwrap();
app.refresh_from_disk(&delta).unwrap();
assert_eq!(app.board.column_cards(0).len(), 1);
fs::write(dir.join("2026-01-01-bad.md"), "not frontmatter").unwrap();
let delta = watcher.poll().expect("the malformed file is reported");
assert!(app.refresh_from_disk(&delta).is_err());
assert_eq!(
app.board.column_cards(0).len(),
1,
"a malformed file must not wipe the board"
);
}
#[test]
fn incremental_refresh_matches_a_full_corpus_rebuild() {
let (tmp, mut app) = make_app();
let dir = tmp.path().join("issues");
fs::create_dir_all(dir.join("alice")).unwrap();
write_issue(&dir, "alice/2026-01-01-a.md", 1, "A", "todo", None);
write_issue(&dir, "2026-01-02-b.md", 2, "B", "todo", None);
app.refresh_corpus().unwrap();
let mut watcher = crate::watcher::FsWatcher::new(&dir);
write_issue(&dir, "alice/2026-01-01-a.md", 1, "A changed", "done", None);
write_issue(&dir, "2026-01-03-c.md", 3, "C", "in progress", None);
fs::remove_file(dir.join("2026-01-02-b.md")).unwrap();
let delta = watcher.poll().expect("the burst is reported");
app.refresh_from_disk(&delta).unwrap();
let rebuilt = crate::search::Corpus::build(&app.store).unwrap();
let incremental: Vec<(u32, String)> = app
.corpus
.issues
.iter()
.map(|i| (i.number, i.title.clone()))
.collect();
let expected: Vec<(u32, String)> = rebuilt
.issues
.iter()
.map(|i| (i.number, i.title.clone()))
.collect();
assert_eq!(incremental, expected);
assert_eq!(app.corpus.texts(), rebuilt.texts());
}
#[test]
fn external_archive_leaves_the_active_board() {
let (tmp, mut app) = make_app();
let dir = tmp.path().join("issues");
fs::create_dir_all(dir.join("alice")).unwrap();
write_issue(&dir, "alice/2026-01-01-a.md", 1, "A", "todo", None);
app.refresh_corpus().unwrap();
assert_eq!(app.board.column_cards(0).len(), 1);
let mut watcher = crate::watcher::FsWatcher::new(&dir);
fs::rename(
dir.join("alice/2026-01-01-a.md"),
dir.join("alice/.2026-01-01-a.md"),
)
.unwrap();
let delta = watcher.poll().expect("the rename is reported");
app.refresh_from_disk(&delta).unwrap();
assert_eq!(
app.board.column_cards(0).len(),
0,
"an issue archived outside `tix` leaves the active board"
);
assert!(
app.corpus.issues.is_empty(),
"the active corpus does not gain the archived file"
);
}
#[test]
fn a_coalesced_burst_rebuilds_once() {
let (tmp, mut app) = make_app();
let dir = tmp.path().join("issues");
let mut watcher = crate::watcher::FsWatcher::new(&dir);
let mut coalescer = crate::watcher::DeltaCoalescer::default();
let mut applies = 0;
for (i, name) in ["a.md", "b.md", "c.md"].iter().enumerate() {
write_issue(&dir, name, i as u32 + 1, "T", "todo", None);
assert!(
coalescer.observe(watcher.poll()).is_none(),
"a pending delta is not applied until the tree settles"
);
}
if let Some(delta) = coalescer.observe(None) {
applies += 1;
app.refresh_from_disk(&delta).unwrap();
}
assert_eq!(applies, 1, "a burst costs exactly one rebuild");
assert_eq!(app.board.column_cards(0).len(), 3);
assert!(coalescer.observe(None).is_none(), "nothing is left pending");
}
#[test]
fn refresh_rebuilds_board() {
let (tmp, mut app) = make_app();
let dir = tmp.path().join("issues");
write_issue(&dir, "2026-01-01-a.md", 1, "A", "todo", None);
let _ = app.handle_key(press(KeyCode::Char('r'))).unwrap();
assert_eq!(app.board.column_cards(0).len(), 1);
assert_eq!(app.status_msg, Some("Refreshed".to_string()));
}