use super::*;
fn agent_id(value: &str) -> tau_proto::AgentId {
tau_proto::AgentId::parse(value).expect("test agent id")
}
fn topology(
edges: &[(&str, &str)],
) -> (
HashMap<tau_proto::AgentId, Vec<tau_proto::AgentId>>,
HashMap<tau_proto::AgentId, Vec<tau_proto::AgentId>>,
) {
let mut forward: HashMap<tau_proto::AgentId, Vec<tau_proto::AgentId>> = HashMap::new();
let mut reverse: HashMap<tau_proto::AgentId, Vec<tau_proto::AgentId>> = HashMap::new();
for (watcher, watched) in edges {
forward
.entry(agent_id(watcher))
.or_default()
.push(agent_id(watched));
reverse
.entry(agent_id(watched))
.or_default()
.push(agent_id(watcher));
}
for targets in forward.values_mut() {
targets.sort();
}
for watchers in reverse.values_mut() {
watchers.sort();
}
(forward, reverse)
}
#[test]
fn projects_a_recursive_chain() {
let (forward, reverse) = topology(&[("a", "b"), ("b", "c")]);
let projection = WatchGraphProjection::new(
&forward,
&reverse,
HashSet::from([(agent_id("b"), agent_id("c"))]),
);
assert!(projection.watcher_is_active(&agent_id("a")));
assert!(projection.watcher_is_active(&agent_id("b")));
assert_eq!(
projection.effective_targets,
HashSet::from([agent_id("b"), agent_id("c")])
);
assert_eq!(
projection.witness_for(&agent_id("b"), &forward).as_deref(),
Some("c")
);
}
#[test]
fn selects_a_stable_witness_from_a_fork() {
let (forward, reverse) = topology(&[("a", "c"), ("a", "b"), ("b", "z"), ("c", "y")]);
let projection = WatchGraphProjection::new(
&forward,
&reverse,
HashSet::from([
(agent_id("b"), agent_id("z")),
(agent_id("c"), agent_id("y")),
]),
);
assert_eq!(
projection.witness_for(&agent_id("a"), &forward).as_deref(),
Some("y")
);
}
#[test]
fn deduplicates_a_shared_diamond_descendant() {
let (forward, reverse) = topology(&[("a", "b"), ("a", "c"), ("b", "d"), ("c", "d")]);
let projection = WatchGraphProjection::new(
&forward,
&reverse,
HashSet::from([
(agent_id("b"), agent_id("d")),
(agent_id("c"), agent_id("d")),
]),
);
assert_eq!(
projection.effective_targets,
HashSet::from([agent_id("b"), agent_id("c"), agent_id("d")])
);
}
#[test]
fn does_not_propagate_activity_downward() {
let (forward, reverse) = topology(&[("parent", "a"), ("a", "b")]);
let projection = WatchGraphProjection::new(
&forward,
&reverse,
HashSet::from([(agent_id("parent"), agent_id("a"))]),
);
assert!(!projection.watcher_is_active(&agent_id("a")));
assert!(!projection.watcher_is_active(&agent_id("b")));
}
#[test]
fn projects_a_deep_chain_iteratively() {
let edges: Vec<_> = (0..2_048)
.map(|index| (format!("a{index}"), format!("a{}", index + 1)))
.collect();
let borrowed: Vec<_> = edges
.iter()
.map(|(watcher, watched)| (watcher.as_str(), watched.as_str()))
.collect();
let (forward, reverse) = topology(&borrowed);
let projection = WatchGraphProjection::new(
&forward,
&reverse,
HashSet::from([(agent_id("a2047"), agent_id("a2048"))]),
);
assert!(projection.watcher_is_active(&agent_id("a0")));
assert_eq!(projection.effective_targets.len(), 2_048);
}
fn visible_ids(rows: &[VisibleWatchRow]) -> Vec<(&str, usize, Option<&str>)> {
rows.iter()
.map(|row| (row.agent_id.as_str(), row.depth, row.via.as_deref()))
.collect()
}
#[test]
fn visible_rows_switch_from_eight_to_direct_only_at_nine() {
let edges = (0..9)
.map(|index| {
let parent = if index == 0 {
agent_id("root")
} else {
agent_id(&format!("a{}", index - 1))
};
(parent, agent_id(&format!("a{index}")))
})
.collect::<Vec<_>>();
let borrowed = edges
.iter()
.map(|(watcher, watched)| (watcher.as_str(), watched.as_str()))
.collect::<Vec<_>>();
let (forward, _) = topology(&borrowed);
let eight = WatchGraphProjection::visible_rows(
&agent_id("root"),
&forward,
|agent_id| agent_id.as_str() != "a8",
8,
);
assert_eq!(eight.len(), 8);
assert_eq!(eight[7].agent_id.as_str(), "a7");
assert_eq!(eight[7].depth, 8);
let nine = WatchGraphProjection::visible_rows(&agent_id("root"), &forward, |_| true, 8);
assert_eq!(visible_ids(&nine), vec![("a0", 1, None)]);
}
#[test]
fn visible_rows_keep_every_direct_watch_above_limit() {
let edges = (0..10)
.map(|index| (agent_id("root"), format!("d{index:02}")))
.collect::<Vec<_>>();
let borrowed = edges
.iter()
.map(|(watcher, watched)| (watcher.as_str(), watched.as_str()))
.collect::<Vec<_>>();
let (forward, _) = topology(&borrowed);
let rows = WatchGraphProjection::visible_rows(&agent_id("root"), &forward, |_| true, 8);
assert_eq!(rows.len(), 10);
assert!(rows.iter().all(|row| row.depth == 1 && row.via.is_none()));
}
#[test]
fn visible_rows_are_cycle_safe_and_exclude_root() {
let (forward, _) = topology(&[("root", "a"), ("a", "b"), ("b", "root")]);
let rows = WatchGraphProjection::visible_rows(&agent_id("root"), &forward, |_| true, 8);
assert_eq!(
visible_ids(&rows),
vec![("a", 1, None), ("b", 2, Some("a"))]
);
}
#[test]
fn visible_rows_choose_shortest_then_lexicographic_paths() {
let (forward, _) = topology(&[
("root", "a"),
("root", "b"),
("root", "c"),
("a", "x"),
("x", "short"),
("b", "short"),
("b", "shared"),
("c", "shared"),
]);
let rows = WatchGraphProjection::visible_rows(&agent_id("root"), &forward, |_| true, 8);
let short = rows
.iter()
.find(|row| row.agent_id.as_str() == "short")
.expect("shortest-path row");
assert_eq!((short.depth, short.via.as_deref()), (2, Some("b")));
let shared = rows
.iter()
.find(|row| row.agent_id.as_str() == "shared")
.expect("equal-depth row");
assert_eq!((shared.depth, shared.via.as_deref()), (2, Some("b")));
}
#[test]
fn visible_rows_traverse_hidden_intermediates_and_keep_missing_stats() {
let (forward, _) = topology(&[("root", "done"), ("done", "no-stats")]);
let rows = WatchGraphProjection::visible_rows(
&agent_id("root"),
&forward,
|agent_id| agent_id.as_str() != "done",
8,
);
assert_eq!(visible_ids(&rows), vec![("no-stats", 2, Some("done"))]);
}
#[test]
fn visible_rows_order_by_depth_then_agent_id() {
let (forward, _) = topology(&[("root", "z"), ("root", "a"), ("z", "b"), ("a", "c")]);
let rows = WatchGraphProjection::visible_rows(&agent_id("root"), &forward, |_| true, 8);
assert_eq!(
visible_ids(&rows),
vec![
("a", 1, None),
("z", 1, None),
("b", 2, Some("z")),
("c", 2, Some("a")),
]
);
}