use tempfile::TempDir;
use super::manager::ManagedError;
use super::record::ManagedSessionState;
use super::tests::make_manager;
#[tokio::test]
async fn manager_resume_reuses_live_pane_without_recreate() {
let dir = TempDir::new().unwrap();
let workspace_dir = TempDir::new().unwrap();
let (mgr, fake) = make_manager(&dir).await;
let workspace_path = workspace_dir.path().to_owned();
let record = mgr
.create(
"task".into(),
Some(workspace_path.clone()),
Some("my-live-session".into()),
Some(workspace_path.clone()),
Some("https://github.com/owner/repo".into()),
Some("main".into()),
)
.await
.expect("create");
mgr.set_workspace(
&record.id,
workspace_path.clone(),
ManagedSessionState::Active,
)
.await
.expect("set Active");
mgr.mark_runtime_exited_stopped(&record.id)
.await
.expect("mark_runtime_exited_stopped");
let kills_before = fake.kill_calls.lock().unwrap().len();
let creates_before = fake.create_cwd_calls.lock().unwrap().len();
let resumed = mgr.resume(&record.id).await.expect("resume");
assert_eq!(resumed.state, ManagedSessionState::Active);
assert_eq!(
fake.kill_calls.lock().unwrap().len(),
kills_before,
"resume must NOT kill a tmux pane that is still alive (#2148)"
);
assert_eq!(
fake.create_cwd_calls.lock().unwrap().len(),
creates_before,
"resume must NOT recreate a tmux pane that is still alive (#2148)"
);
}
#[tokio::test]
async fn manager_resume_errors_when_recreated_pane_cwd_mismatches() {
let dir = TempDir::new().unwrap();
let workspace_dir = TempDir::new().unwrap();
let wrong_dir = TempDir::new().unwrap();
let (mgr, fake) = make_manager(&dir).await;
let workspace_path = workspace_dir.path().to_owned();
let record = mgr
.create(
"task".into(),
Some(workspace_path.clone()),
Some("cwd-mismatch-session".into()),
Some(workspace_path),
Some("https://github.com/owner/repo".into()),
Some("main".into()),
)
.await
.expect("create");
mgr.stop(&record.id).await.expect("stop");
*fake.pane_cwd_override.lock().unwrap() = Some(wrong_dir.path().to_owned());
let err = mgr
.resume(&record.id)
.await
.expect_err("mismatched pane cwd must fail the resume, not silently proceed");
assert!(
matches!(err, ManagedError::TmuxUnavailable(_)),
"expected TmuxUnavailable on pane-cwd mismatch, got: {err:?}"
);
let after = mgr.get(&record.id).await.expect("get after failed resume");
assert_eq!(
after.state,
ManagedSessionState::Stopped,
"a failed resume must leave the record Stopped, not Active"
);
}
#[tokio::test]
async fn resume_refuses_when_stored_pane_gone_but_session_alive() {
let dir = TempDir::new().unwrap();
let workspace_dir = TempDir::new().unwrap();
let (mgr, fake) = make_manager(&dir).await;
let workspace_path = workspace_dir.path().to_owned();
*fake.pane_id_override.lock().unwrap() = Some("%6015".to_string());
let record = mgr
.create(
"task".into(),
Some(workspace_path.clone()),
Some("my-hijacked-session".into()),
Some(workspace_path.clone()),
Some("https://github.com/owner/repo".into()),
Some("main".into()),
)
.await
.expect("create");
assert_eq!(
record.pane_id.as_deref(),
Some("%6015"),
"sanity: create() must have captured the seeded pane_id"
);
mgr.set_workspace(
&record.id,
workspace_path.clone(),
ManagedSessionState::Active,
)
.await
.expect("set Active");
mgr.mark_runtime_exited_stopped(&record.id)
.await
.expect("mark_runtime_exited_stopped");
*fake.pane_exists_override.lock().unwrap() = Some(false);
let kills_before = fake.kill_calls.lock().unwrap().len();
let creates_before = fake.create_cwd_calls.lock().unwrap().len();
let err = mgr
.resume(&record.id)
.await
.expect_err("resume must refuse when the recorded pane is confirmed gone");
assert!(
matches!(&err, ManagedError::PaneGone(sid, pid) if sid == &record.id.to_string() && pid == "%6015"),
"expected PaneGone(session_id, \"%6015\"), got: {err:?}"
);
assert_eq!(
fake.kill_calls.lock().unwrap().len(),
kills_before,
"resume must NOT kill the tmux session (which would destroy the sibling window too) \
when only the recorded pane is confirmed gone"
);
assert_eq!(
fake.create_cwd_calls.lock().unwrap().len(),
creates_before,
"resume must NOT recreate a session when only the recorded pane is confirmed gone"
);
let after = mgr.get(&record.id).await.expect("get after refused resume");
assert_eq!(
after.state,
ManagedSessionState::Stopped,
"a refused resume must leave the record Stopped, not Active"
);
}