mod common;
use std::time::Duration;
use tokio::time::timeout;
use tsoracle_consensus::{ConsensusDriver, ConsensusError};
use tsoracle_core::Epoch;
use common::{TestCluster, build_three_node, eventually_eq};
async fn find_leader_idx(cluster: &TestCluster) -> usize {
timeout(Duration::from_secs(10), async {
loop {
for (idx, node) in cluster.nodes.iter().enumerate() {
if let Some(l) = node.raft.current_leader().await {
if l == node.id {
return idx;
}
}
}
tokio::time::sleep(Duration::from_millis(50)).await;
}
})
.await
.expect("some node became leader within 10s")
}
#[tokio::test(start_paused = true)]
async fn three_node_leader_persists_and_followers_converge() {
let cluster = build_three_node().await;
let leader_idx = find_leader_idx(&cluster).await;
let leader_driver = &cluster.drivers[leader_idx];
assert_eq!(leader_driver.load_high_water().await.unwrap(), 0);
let v = leader_driver
.persist_high_water(100, Epoch(1))
.await
.unwrap();
assert_eq!(v, 100);
for i in 0..3 {
let sm = cluster.nodes[i].sm.clone();
eventually_eq(100u64, Duration::from_secs(5), || {
let sm = sm.clone();
async move { sm.current_value().await }
})
.await;
}
}
#[tokio::test(start_paused = true)]
async fn three_node_follower_returns_not_leader() {
let cluster = build_three_node().await;
let leader_idx = find_leader_idx(&cluster).await;
let follower_idx = (0..3)
.find(|i| *i != leader_idx)
.expect("a follower exists");
let follower_driver = &cluster.drivers[follower_idx];
let err = follower_driver
.persist_high_water(50, Epoch(1))
.await
.expect_err("follower must reject write");
match err {
ConsensusError::NotLeader { observed: None } => {} other => panic!("expected NotLeader{{ observed: None }}, got {other:?}"),
}
}