#[cfg(test)]
mod tests {
use super::super::*;
use std::collections::HashSet;
use std::time::Duration;
fn create_test_vertex(id: &str, parents: Vec<&str>) -> Vertex {
let parent_ids: HashSet<VertexId> = parents
.into_iter()
.map(|p| VertexId::from_bytes(p.as_bytes().to_vec()))
.collect();
Vertex::new(
VertexId::from_bytes(id.as_bytes().to_vec()),
vec![1, 2, 3], parent_ids,
)
}
#[test]
fn test_consensus_basic_operations() {
let mut consensus = QRAvalanche::new();
let vertex_id = VertexId::new();
assert!(consensus.vertices.is_empty());
assert!(consensus.tips.is_empty());
let status = consensus.process_vertex(vertex_id.clone()).unwrap();
assert_eq!(status, ConsensusStatus::Accepted);
assert!(consensus.vertices.contains_key(&vertex_id));
assert!(consensus.tips.contains(&vertex_id));
}
#[test]
fn test_dag_consensus_genesis_vertex() {
let mut dag = DAGConsensus::new();
let genesis = create_test_vertex("genesis", vec![]);
assert!(dag.add_vertex(genesis).is_ok());
assert_eq!(dag.get_confidence("genesis"), Some(ConsensusStatus::Final));
}
#[test]
fn test_dag_consensus_chain_building() {
let mut dag = DAGConsensus::new();
let vertex_a = create_test_vertex("A", vec![]);
let vertex_b = create_test_vertex("B", vec!["A"]);
let vertex_c = create_test_vertex("C", vec!["B"]);
assert!(dag.add_vertex(vertex_a).is_ok());
assert!(dag.add_vertex(vertex_b).is_ok());
assert!(dag.add_vertex(vertex_c).is_ok());
assert_eq!(dag.get_confidence("A"), Some(ConsensusStatus::Final));
assert_eq!(dag.get_confidence("B"), Some(ConsensusStatus::Final));
assert_eq!(dag.get_confidence("C"), Some(ConsensusStatus::Final));
}
#[test]
fn test_fork_detection() {
let mut dag = DAGConsensus::new();
let vertex_a = create_test_vertex("A", vec![]);
assert!(dag.add_vertex(vertex_a).is_ok());
let fork_vertex = create_test_vertex("A", vec![]);
let result = dag.add_vertex(fork_vertex);
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("Fork detected"));
}
#[test]
fn test_missing_parent_validation() {
let mut dag = DAGConsensus::new();
let invalid_vertex = create_test_vertex("B", vec!["A"]);
let result = dag.add_vertex(invalid_vertex);
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("parent"));
}
#[test]
fn test_self_reference_prevention() {
let mut dag = DAGConsensus::new();
let self_ref_vertex = create_test_vertex("A", vec!["A"]);
let result = dag.add_vertex(self_ref_vertex);
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("references itself"));
}
#[test]
fn test_parallel_branches() {
let mut dag = DAGConsensus::new();
let root = create_test_vertex("root", vec![]);
let branch_a = create_test_vertex("A", vec!["root"]);
let branch_b = create_test_vertex("B", vec!["root"]);
let merge = create_test_vertex("merge", vec!["A", "B"]);
assert!(dag.add_vertex(root).is_ok());
assert!(dag.add_vertex(branch_a).is_ok());
assert!(dag.add_vertex(branch_b).is_ok());
assert!(dag.add_vertex(merge).is_ok());
assert_eq!(dag.get_confidence("root"), Some(ConsensusStatus::Final));
assert_eq!(dag.get_confidence("A"), Some(ConsensusStatus::Final));
assert_eq!(dag.get_confidence("B"), Some(ConsensusStatus::Final));
assert_eq!(dag.get_confidence("merge"), Some(ConsensusStatus::Final));
}
#[test]
fn test_total_order_consistency() {
let mut dag = DAGConsensus::new();
let vertices = vec!["A", "B", "C", "D"];
let mut parents = vec![];
for vertex_id in &vertices {
let vertex = if parents.is_empty() {
create_test_vertex(vertex_id, vec![])
} else {
create_test_vertex(vertex_id, vec![parents.last().unwrap()])
};
assert!(dag.add_vertex(vertex).is_ok());
parents.push(*vertex_id);
}
let order = dag.get_total_order().unwrap();
assert_eq!(order.len(), vertices.len());
for i in 1..vertices.len() {
let parent_pos = order.iter().position(|x| x == vertices[i - 1]).unwrap();
let child_pos = order.iter().position(|x| x == vertices[i]).unwrap();
assert!(
parent_pos < child_pos,
"Parent {} should come before child {}",
vertices[i - 1],
vertices[i]
);
}
}
#[test]
fn test_consensus_status_transitions() {
let mut consensus = QRAvalanche::new();
let vertex_id = VertexId::new();
assert!(!consensus.vertices.contains_key(&vertex_id));
let status = consensus.process_vertex(vertex_id.clone()).unwrap();
assert_eq!(status, ConsensusStatus::Accepted);
assert_eq!(
consensus.vertices.get(&vertex_id),
Some(&ConsensusStatus::Accepted)
);
assert!(consensus.tips.contains(&vertex_id));
}
#[test]
fn test_consensus_config_validation() {
let config = ConsensusConfig {
query_sample_size: 0,
finality_threshold: 1.5, finality_timeout: Duration::from_secs(0),
confirmation_depth: 0,
};
let _dag = DAGConsensus::with_config(config);
}
#[test]
fn test_dag_invariants() {
let mut dag = DAGConsensus::new();
let vertices = vec![
("genesis", vec![]),
("a1", vec!["genesis"]),
("a2", vec!["genesis"]),
("b1", vec!["a1"]),
("b2", vec!["a2"]),
("merge", vec!["b1", "b2"]),
];
for (id, parents) in vertices {
let vertex = create_test_vertex(id, parents);
assert!(
dag.add_vertex(vertex).is_ok(),
"Failed to add vertex {}",
id
);
}
for (id, _) in [
("genesis", vec![]),
("a1", vec!["genesis"]),
("a2", vec!["genesis"]),
("b1", vec!["a1"]),
("b2", vec!["a2"]),
("merge", vec!["b1", "b2"]),
]
.iter()
{
assert_eq!(
dag.get_confidence(id),
Some(ConsensusStatus::Final),
"Vertex {} should have final status",
id
);
}
let tips = dag.get_tips();
assert!(!tips.is_empty(), "Should have at least one tip");
}
}