use tempfile::TempDir;
use super::manager::SessionManager;
use super::record::ManagedSessionId;
use super::tests::FakeTmuxDriver;
#[tokio::test]
async fn reap_orphaned_worktrees_removes_orphan_preserves_live() {
let store_dir = TempDir::new().unwrap();
let mgr = SessionManager::new(store_dir.path(), FakeTmuxDriver::new())
.await
.expect("manager");
let fx = super::worktree_git_fixture::GitWorktreeFixture::new();
let live_wt = fx.add_worktree("live-session");
let orphan_wt = fx.add_worktree("orphan-session");
let aged = chrono::Utc::now()
- super::worktree_ownership::OWNERLESS_GRACE
- chrono::Duration::minutes(1);
std::fs::write(
orphan_wt.join(super::decommission::WORKTREE_SENTINEL_FILE),
serde_json::to_vec(&super::worktree_ownership::WorktreeSentinel {
owner_session_id: ManagedSessionId::new(),
created_at: aged,
})
.expect("serialize aged sentinel"),
)
.expect("write ownerless sentinel");
let _record = mgr
.create_with_id(
ManagedSessionId::new(),
"task".into(),
Some(live_wt.clone()),
None,
Some(live_wt.clone()),
None,
None,
crate::runtime::RuntimeKind::default(),
false,
false,
)
.await
.expect("create live record");
let outcome = mgr
.reap_orphaned_worktrees(&fx.repos_root)
.await
.expect("reap must not error");
let removed = outcome.removed;
assert!(
!orphan_wt.exists(),
"orphaned worktree dir must be removed by the auto-reaper (#1838)"
);
assert!(
live_wt.exists(),
"live session worktree must be preserved by the auto-reaper (#1838)"
);
assert!(
removed.iter().any(|p| p.ends_with("orphan-session")),
"removed set must include the orphan; got {removed:?}"
);
assert!(
!removed.iter().any(|p| p.ends_with("live-session")),
"removed set must NOT include the live worktree; got {removed:?}"
);
assert!(
outcome.owner_unknown.is_empty(),
"no owner-unknown candidates expected in this fixture; got {:?}",
outcome.owner_unknown
);
}
#[tokio::test]
async fn reap_spares_a_stopped_records_workspace() {
use super::record::{ManagedSessionState, SessionRecord};
use super::worktree_git_fixture::GitWorktreeFixture;
let store_dir = TempDir::new().unwrap();
let mgr = SessionManager::new(store_dir.path(), FakeTmuxDriver::new())
.await
.expect("manager");
let fx = GitWorktreeFixture::new();
let wt = fx.add_worktree("stopped-but-live");
GitWorktreeFixture::stamp_reclaimable_sentinel(&wt);
let control = mgr
.prune_orphaned_worktrees(&fx.repos_root, &[], true, super::DirtyWorktreePolicy::Skip)
.await
.expect("control sweep must not error");
assert!(
control.removed.contains(&wt),
"CONTROL: {} must be reclaimable with an empty active set, otherwise \
this test proves nothing; removed={:?} owner_unknown={:?} skipped_dirty={:?}",
wt.display(),
control.removed,
control.owner_unknown,
control.skipped_dirty
);
let record = mgr
.create_with_id(
ManagedSessionId::new(),
"pinned by #4288".into(),
Some(wt.clone()),
None,
Some(wt.clone()),
None,
None,
crate::runtime::RuntimeKind::default(),
false,
false,
)
.await
.expect("seed session record");
let stopped = SessionRecord {
state: ManagedSessionState::Stopped,
..record
};
mgr.store
.write()
.await
.upsert(stopped)
.await
.expect("persist the Stopped record");
let outcome = mgr
.reap_orphaned_worktrees(&fx.repos_root)
.await
.expect("reap must not error");
assert!(
!outcome.removed.contains(&wt),
"a Stopped record's workspace must NOT be removed by the automatic \
orphan-GC sweep; {} appeared in the removed set {:?}",
wt.display(),
outcome.removed
);
assert!(
!outcome.owner_unknown.contains(&wt),
"a spared workspace is never even a candidate, so it must not be \
reported as owner-unknown either; got {:?}",
outcome.owner_unknown
);
assert!(
wt.exists(),
"{} must still exist on disk after the automatic sweep",
wt.display()
);
}