use noxu_rep::{
RepConfig, ReplicatedEnvironment,
elections::{ELECTION_SERVICE_NAME, PersistentAcceptorState},
};
use std::sync::Arc;
use tempfile::TempDir;
#[test]
fn acceptor_state_file_is_created_on_open() {
let dir = TempDir::new().unwrap();
let env_home = dir.path().to_path_buf();
let config = RepConfig::builder("g1", "n1", "127.0.0.1")
.node_port(0)
.env_home(&env_home)
.build();
let env = ReplicatedEnvironment::new(config).unwrap();
let state = PersistentAcceptorState::load_or_default(&env_home);
assert!(state.try_promise(7));
let file = env_home.join("acceptor.state");
assert!(file.exists(), "acceptor.state must be written");
env.close().unwrap();
}
#[test]
fn promise_survives_restart_and_rejects_stale_term() {
let dir = TempDir::new().unwrap();
let env_home = dir.path().to_path_buf();
{
let config = RepConfig::builder("g1", "n1", "127.0.0.1")
.node_port(0)
.env_home(&env_home)
.build();
let env = ReplicatedEnvironment::new(config).unwrap();
let state = PersistentAcceptorState::load_or_default(&env_home);
assert!(state.try_promise(10));
env.close().unwrap();
}
{
let config = RepConfig::builder("g1", "n1", "127.0.0.1")
.node_port(0)
.env_home(&env_home)
.build();
let env = ReplicatedEnvironment::new(config).unwrap();
let state = PersistentAcceptorState::load_or_default(&env_home);
assert_eq!(state.promised_term(), 10);
assert!(
!state.try_promise(7),
"post-restart acceptor must reject term lower than persisted promise"
);
env.close().unwrap();
}
}
#[test]
fn election_service_uses_persistent_state_end_to_end() {
use noxu_rep::elections::paxos::run_election;
use noxu_rep::net::channel::Channel;
use noxu_rep::net::service_dispatcher::connect_to_service;
use noxu_rep::{NodeType, RepGroup, RepNode};
let dir = TempDir::new().unwrap();
let env_home = dir.path().to_path_buf();
let config = RepConfig::builder("g1", "n1", "127.0.0.1")
.node_port(0)
.env_home(&env_home)
.build();
let env = ReplicatedEnvironment::new(config).unwrap();
let addr = env.bound_addr().expect("must bind");
let mut group = RepGroup::new("g1".to_string(), 1);
group.add_node(RepNode::new(
"self".into(),
NodeType::Electable,
"127.0.0.1".into(),
addr.port(),
1,
));
group.add_node(RepNode::new(
"n1".into(),
NodeType::Electable,
"127.0.0.1".into(),
addr.port(),
2,
));
let ch = connect_to_service(addr, ELECTION_SERVICE_NAME).unwrap();
let ch_arc: Arc<dyn Channel> = Arc::new(ch);
let winner = run_election(1, "self", &group, &[ch_arc], 100, 1, 5);
assert_eq!(winner, Some(1));
std::thread::sleep(std::time::Duration::from_millis(100));
let state = PersistentAcceptorState::load_or_default(&env_home);
assert_eq!(state.promised_term(), 5);
assert_eq!(state.accepted_term(), 5);
assert_eq!(state.accepted_master(), Some("self".to_string()));
let ch2 = connect_to_service(addr, ELECTION_SERVICE_NAME).unwrap();
let ch2_arc: Arc<dyn Channel> = Arc::new(ch2);
let stale = run_election(1, "self", &group, &[ch2_arc], 100, 1, 3);
assert!(
stale.is_none(),
"stale proposer at term 3 must NOT win after a term-5 promise was persisted"
);
env.close().unwrap();
}