use super::*;
use crate::boundary::tests::{agent, snapshot};
use crate::git_tree::{Agent, AgentState};
use crate::state::new_snapshot_cell;
use crate::test_support::FakeClock;
use std::time::Duration;
use tempfile::TempDir;
const WS: &str = "alba";
fn asking(id: &str) -> Agent {
Agent {
notify_oid: Some("n".repeat(40)),
..agent(id, AgentState::Quiescent, 0)
}
}
fn published(dir: &TempDir, agents: Vec<Agent>) -> Arc<Snapshot> {
Arc::new(snapshot(&dir.path().join(WS), WS, agents, vec![]))
}
fn lane(agents: Vec<Agent>, scope: &[&str]) -> (TempDir, SnapshotCell, FakeClock, Attend) {
let dir = tempfile::tempdir().expect("tmp");
let cell = new_snapshot_cell(published(&dir, agents));
let clock = FakeClock::new();
let attend = Attend::new(
Arc::clone(&cell),
scope.iter().map(|s| (*s).to_owned()).collect(),
dir.path().join("ui.json"),
clock.arc(),
);
(dir, cell, clock, attend)
}
fn named(rows: &[QueueRow]) -> Vec<String> {
rows.iter().map(|row| row.agent.clone()).collect()
}
#[test]
fn the_first_frame_is_the_answer_at_connect_even_when_it_is_empty() {
let (_dir, _cell, _clock, mut attend) = lane(vec![], &[WS]);
assert_eq!(
attend.look(),
Some(vec![]),
"the answer as of connect, empty and stated"
);
assert_eq!(
attend.look(),
None,
"and the very same derivation owes nothing more"
);
}
#[test]
fn a_republish_that_changed_nothing_writes_no_frame() {
let (dir, cell, clock, mut attend) = lane(vec![asking("c-1")], &[WS]);
let first = attend.look().expect("the answer at connect");
assert_eq!(named(&first), ["c-1"], "one conversation is asking");
assert_eq!(first[0].age_secs, 0);
clock.advance(Duration::from_mins(1));
crate::state::publish_snapshot(&cell, published(&dir, vec![asking("c-1")]));
assert_eq!(
attend.look(),
None,
"a fresh derivation of the same answer is not a change"
);
}
#[test]
fn an_answer_that_changed_is_the_next_frame() {
let (dir, cell, _clock, mut attend) = lane(vec![], &[WS]);
assert_eq!(attend.look(), Some(vec![]), "quiet at connect");
crate::state::publish_snapshot(&cell, published(&dir, vec![asking("c-1")]));
let frame = attend.look().expect("the answer changed");
assert_eq!(named(&frame), ["c-1"]);
assert_eq!(
frame[0].signals[0],
crate::attention::AttentionKind::Notify,
"and it carries why it fires, so the frame IS the notification"
);
crate::state::publish_snapshot(&cell, published(&dir, vec![asking("c-1"), asking("c-2")]));
assert_eq!(
named(&attend.look().expect("a second conversation is a change")),
["c-1", "c-2"]
);
}
#[test]
fn the_lane_answers_only_this_askers_scope() {
let (_dir, _cell, _clock, mut unseated) = lane(vec![asking("c-1")], &[]);
assert_eq!(
unseated.look(),
Some(vec![]),
"a seat registered nowhere sees nothing, and is told so"
);
let (_dir, _cell, _clock, mut seated) = lane(vec![asking("c-1")], &[WS]);
assert_eq!(named(&seated.look().expect("a frame")), ["c-1"]);
}
#[test]
fn the_hold_is_bounded_and_a_quiet_lane_ends() {
let dir = tempfile::tempdir().expect("tmp");
let cell = new_snapshot_cell(published(&dir, vec![asking("c-1")]));
let mut attend = Attend::holding(
cell,
[WS.to_owned()].into_iter().collect(),
dir.path().join("ui.json"),
FakeClock::new().arc(),
2,
Duration::ZERO,
);
let Some(Reply::Attention(rows)) = attend.next() else {
unreachable!("the answer at connect is a frame")
};
assert_eq!(named(&rows), ["c-1"]);
assert_eq!(
attend.next(),
None,
"a lane with nothing more to say waits out its patience and ends"
);
}
#[test]
fn an_acknowledgement_elsewhere_empties_the_lane() {
let (dir, cell, _clock, mut attend) = lane(vec![asking("c-1")], &[WS]);
assert_eq!(named(&attend.look().expect("a frame")), ["c-1"]);
let mut ui = UiState::open(dir.path().join("ui.json"));
crate::boundary::answer::queue::mark_seen(
&crate::state::latest_snapshot(&cell),
&mut ui,
&dir.path().join(WS),
"c-1",
)
.expect("the row is answerable");
crate::state::publish_snapshot(&cell, published(&dir, vec![asking("c-1")]));
assert_eq!(
attend.look(),
Some(vec![]),
"answered, so the answer changed and the frame says so"
);
}