()
| 320 | |
| 321 | #[test] |
| 322 | fn three_node_replication() { |
| 323 | let config1 = test_config(1, vec![2, 3]); |
| 324 | let config2 = test_config(2, vec![1, 3]); |
| 325 | |
| 326 | let mut node1 = RaftNode::new(config1, MemStorage::new()); |
| 327 | let mut node2 = RaftNode::new(config2, MemStorage::new()); |
| 328 | |
| 329 | node1.election_deadline = Instant::now() - Duration::from_millis(1); |
| 330 | node1.tick(); |
| 331 | let ready = node1.take_ready(); |
| 332 | let resp2 = node2.handle_request_vote(&ready.vote_requests[0].1); |
| 333 | node1.handle_request_vote_response(2, &resp2); |
| 334 | assert_eq!(node1.role(), NodeRole::Leader); |
| 335 | |
| 336 | let heartbeat_ready = node1.take_ready(); |
| 337 | for (peer_id, msg) in &heartbeat_ready.messages { |
| 338 | if *peer_id == 2 { |
| 339 | let resp = node2.handle_append_entries(msg); |
| 340 | node1.handle_append_entries_response(2, &resp); |
| 341 | } |
| 342 | } |
| 343 | |
| 344 | let idx = node1.propose(b"cmd1".to_vec()).unwrap(); |
| 345 | assert_eq!(idx, 2); |
| 346 | |
| 347 | let ready = node1.take_ready(); |
| 348 | for (peer_id, msg) in &ready.messages { |
| 349 | if *peer_id == 2 { |
| 350 | let resp = node2.handle_append_entries(msg); |
| 351 | assert!(resp.success); |
| 352 | node1.handle_append_entries_response(2, &resp); |
| 353 | } |
| 354 | } |
| 355 | |
| 356 | let ready = node1.take_ready(); |
| 357 | let committed: Vec<_> = ready |
| 358 | .committed_entries |
| 359 | .iter() |
| 360 | .filter(|e| !e.data.is_empty()) |
| 361 | .collect(); |
| 362 | assert_eq!(committed.len(), 1); |
| 363 | assert_eq!(committed[0].data, b"cmd1"); |
| 364 | } |
| 365 | |
| 366 | /// An observer receives AppendEntries, applies them, and stays in the |
| 367 | /// Observer role. Its ack must NOT advance the source commit index. |
nothing calls this directly
no test coverage detected