use elara_runtime::{Node, NodeConfig};
use elara_core::{NodeId, SessionId, Event, EventType, StateId, MutationOp};
use std::time::Instant;
use std::collections::HashMap;
pub struct TestNode {
node: Node,
node_id: NodeId,
pub(crate) peers: HashMap<NodeId, usize>,
messages_sent: u64,
messages_received: u64,
}
impl TestNode {
pub fn spawn(config: NodeConfig) -> Result<Self, String> {
let node = Node::with_config(config);
let node_id = node.node_id();
Ok(Self {
node,
node_id,
peers: HashMap::new(),
messages_sent: 0,
messages_received: 0,
})
}
pub fn spawn_default() -> Result<Self, String> {
Self::spawn(NodeConfig::default())
}
pub fn node_id(&self) -> NodeId {
self.node_id
}
pub fn join_session(&mut self, session_id: SessionId, session_key: [u8; 32]) {
self.node.join_session(session_id, session_key);
}
pub fn join_session_unsecured(&mut self, session_id: SessionId) {
self.node.join_session_unsecured(session_id);
}
pub fn connect_to(&mut self, peer: &TestNode) -> Result<(), String> {
let peer_id = peer.node_id();
let peer_index = self.peers.len();
self.peers.insert(peer_id, peer_index);
Ok(())
}
pub fn send_message(&mut self, payload: Vec<u8>) -> Result<Instant, String> {
let start = Instant::now();
let seq = self.node.next_event_seq();
let event = Event::new(
self.node_id,
seq,
EventType::TextAppend,
StateId::new(0),
MutationOp::Append(payload),
);
self.node.queue_local_event(event);
self.node.tick();
self.messages_sent += 1;
Ok(start)
}
pub fn receive_from(&mut self, peer: &mut TestNode) -> usize {
let mut received_count = 0;
while let Some(frame) = peer.node.pop_outgoing() {
self.node.queue_incoming(frame);
received_count += 1;
}
if received_count > 0 {
self.node.tick();
self.messages_received += received_count as u64;
}
received_count
}
pub fn tick(&mut self) {
self.node.tick();
}
pub fn messages_sent(&self) -> u64 {
self.messages_sent
}
pub fn messages_received(&self) -> u64 {
self.messages_received
}
pub fn node(&self) -> &Node {
&self.node
}
pub fn node_mut(&mut self) -> &mut Node {
&mut self.node
}
pub fn shutdown(self) {
drop(self);
}
}
pub fn generate_test_message(size: usize) -> Vec<u8> {
vec![0u8; size]
}
#[allow(dead_code)]
pub fn generate_random_message(size: usize) -> Vec<u8> {
use std::collections::hash_map::RandomState;
use std::hash::{BuildHasher, Hash, Hasher};
let mut data = Vec::with_capacity(size);
let hasher_builder = RandomState::new();
for i in 0..size {
let mut hasher = hasher_builder.build_hasher();
i.hash(&mut hasher);
data.push((hasher.finish() & 0xFF) as u8);
}
data
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_node_spawn() {
let node = TestNode::spawn_default();
assert!(node.is_ok());
}
#[test]
fn test_node_connection() {
let mut node1 = TestNode::spawn_default().unwrap();
let node2 = TestNode::spawn_default().unwrap();
let result = node1.connect_to(&node2);
assert!(result.is_ok());
assert_eq!(node1.peers.len(), 1);
}
#[test]
fn test_message_generation() {
let msg = generate_test_message(100);
assert_eq!(msg.len(), 100);
let random_msg = generate_random_message(100);
assert_eq!(random_msg.len(), 100);
}
#[test]
fn test_send_message() {
let mut node = TestNode::spawn_default().unwrap();
let session_id = SessionId::new(1);
node.join_session_unsecured(session_id);
let payload = generate_test_message(64);
let result = node.send_message(payload);
assert!(result.is_ok());
assert_eq!(node.messages_sent(), 1);
}
#[test]
fn test_message_exchange() {
let mut node1 = TestNode::spawn_default().unwrap();
let mut node2 = TestNode::spawn_default().unwrap();
let session_id = SessionId::new(1);
node1.join_session_unsecured(session_id);
node2.join_session_unsecured(session_id);
node1.connect_to(&node2).unwrap();
let payload = generate_test_message(64);
node1.send_message(payload).unwrap();
let _received = node2.receive_from(&mut node1);
assert_eq!(node1.messages_sent(), 1);
}
}