mod common;
use common::{Judges, fixture, home_lock};
use magi::graph::{Pause, Runner};
use magi::run::RunStatus;
#[tokio::test]
async fn a_parked_run_keeps_its_work_and_resumes_into_the_next_node() {
let _home = home_lock().await;
let fx = fixture(_home, Judges::Unanimous, false);
let pause = Pause::new();
let mut runner = Runner::start(&fx.repo, "create note.txt".to_owned(), fx.config.clone())
.await
.expect("start");
runner.on_pause(pause.clone());
pause.park();
runner.execute().await.expect("execute parks cleanly");
let id = runner.state.id.clone();
assert!(runner.state.parked, "the run records that it parked");
assert!(
!runner.state.status.done(),
"a park is not a terminal status: {:?}",
runner.state.status
);
assert_eq!(
runner.state.candidates.len(),
3,
"prep finished, so its worktrees are kept"
);
assert!(
runner.state.judgements.is_empty(),
"and the graph stopped before judging"
);
assert!(
runner
.state
.events
.iter()
.any(|e| e.node == "park" && e.message.contains("resume to carry on")),
"the timeline says why it is sitting there: {:?}",
runner.state.events.last()
);
let mut resumed = Runner::resume(&id).expect("resume");
resumed.execute().await.expect("execute to a verdict");
assert_eq!(
resumed.state.candidates.len(),
3,
"the same three candidates, not a fresh set"
);
assert!(
matches!(
resumed.state.status,
RunStatus::Ready | RunStatus::Merged | RunStatus::Blocked
),
"the run reached a terminal status: {:?}",
resumed.state.status
);
assert!(
!resumed.state.parked,
"and it is no longer parked once it has been carried on"
);
}
#[tokio::test]
async fn a_resumed_run_drops_a_stale_active_marker_left_by_a_killed_process() {
let _home = home_lock().await;
let fx = fixture(_home, Judges::Unanimous, false);
let pause = Pause::new();
let mut runner = Runner::start(&fx.repo, "create note.txt".to_owned(), fx.config.clone())
.await
.expect("start");
runner.on_pause(pause.clone());
pause.park();
runner.execute().await.expect("execute parks cleanly");
runner.state.seat_started(
"implement",
"impl-B",
std::time::Duration::from_secs(3600),
0,
);
runner.state.save().expect("save");
let id = runner.state.id.clone();
drop(runner);
let resumed = Runner::resume(&id).expect("resume");
assert!(
resumed.state.active.contains_key("impl-B"),
"loading a run reflects exactly what was on disk, stale or not"
);
let mut resumed = resumed;
resumed.execute().await.expect("execute to a verdict");
assert!(
!resumed.state.active.contains_key("impl-B"),
"a resumed run must not go on claiming a leftover seat is still \
answering: {:?}",
resumed.state.active
);
}
#[tokio::test]
async fn a_park_asked_for_mid_walk_stops_at_the_boundary_after_it() {
let _home = home_lock().await;
let fx = fixture(_home, Judges::Unanimous, false);
let pause = Pause::new();
let mut runner = Runner::start(&fx.repo, "create note.txt".to_owned(), fx.config.clone())
.await
.expect("start");
runner.on_pause(pause.clone());
let asker = tokio::spawn({
let pause = pause.clone();
async move {
tokio::time::sleep(std::time::Duration::from_millis(50)).await;
pause.park();
}
});
runner.execute().await.expect("execute");
asker.await.expect("asker");
assert!(runner.state.parked);
assert!(
!runner.state.candidates.is_empty(),
"whatever node it was in finished and recorded its work"
);
assert!(
!runner.state.status.done(),
"and it stopped short of a verdict: {:?}",
runner.state.status
);
}