use std::time::{Duration, Instant};
use noxu_rep::rep_node::RepNode;
use noxu_rep::{NodeState, NodeType, RepConfig, ReplicatedEnvironment};
fn config(node_name: &str) -> RepConfig {
RepConfig::builder("group_f6", node_name, "127.0.0.1")
.node_port(0)
.node_type(NodeType::Electable)
.election_timeout(Duration::from_millis(100))
.build()
}
#[test]
fn f6_single_node_open_becomes_master() {
let env = ReplicatedEnvironment::open(config("solo")).unwrap();
let deadline = Instant::now() + Duration::from_secs(2);
while env.get_state() != NodeState::Master && Instant::now() < deadline {
std::thread::sleep(Duration::from_millis(20));
}
assert_eq!(
env.get_state(),
NodeState::Master,
"single-node open() must elect self as master (F6)"
);
assert!(env.is_master());
assert_eq!(env.get_master_name().as_deref(), Some("solo"),);
let _ = env.close();
}
#[test]
fn f6_two_node_cluster_resolves_via_election_driver() {
let env_a = ReplicatedEnvironment::open(config("node_a")).unwrap();
let addr_a = env_a.bound_addr().expect("node_a must bind");
let deadline = Instant::now() + Duration::from_secs(2);
while env_a.get_state() != NodeState::Master && Instant::now() < deadline {
std::thread::sleep(Duration::from_millis(20));
}
assert_eq!(env_a.get_state(), NodeState::Master);
let env_b = ReplicatedEnvironment::open(config("node_b")).unwrap();
let addr_b = env_b.bound_addr().expect("node_b must bind");
env_a
.add_peer(RepNode::new(
"node_b".into(),
NodeType::Electable,
addr_b.ip().to_string(),
addr_b.port(),
2,
))
.unwrap();
env_b
.add_peer(RepNode::new(
"node_a".into(),
NodeType::Electable,
addr_a.ip().to_string(),
addr_a.port(),
1,
))
.unwrap();
let deadline = Instant::now() + Duration::from_secs(5);
loop {
let s = env_b.get_state();
if matches!(s, NodeState::Master | NodeState::Replica) {
break;
}
if Instant::now() >= deadline {
panic!(
"node_b stuck in {:?} after 5 s; election driver did not \
wire through (F6 not closed)",
s
);
}
std::thread::sleep(Duration::from_millis(50));
}
let _ = env_b.close();
let _ = env_a.close();
}
#[test]
fn f6_new_does_not_auto_start_driver() {
let env = ReplicatedEnvironment::new(config("manual")).unwrap();
std::thread::sleep(Duration::from_millis(500));
assert_eq!(
env.get_state(),
NodeState::Detached,
"new() must leave the env in Detached without explicit driver start"
);
let _ = env.close();
}
#[test]
fn f6_start_election_driver_idempotent() {
use std::sync::Arc;
let env = Arc::new(ReplicatedEnvironment::new(config("idem")).unwrap());
env.start_election_driver();
env.start_election_driver();
env.start_election_driver();
let deadline = Instant::now() + Duration::from_secs(2);
while env.get_state() != NodeState::Master && Instant::now() < deadline {
std::thread::sleep(Duration::from_millis(20));
}
assert_eq!(env.get_state(), NodeState::Master);
let _ = env.close();
}