()
| 82 | /// Gate 3: Ghost edge count converges to zero after rebalance. |
| 83 | #[test] |
| 84 | fn ghost_edge_convergence() { |
| 85 | let mut ghost_table = GhostTable::new(); |
| 86 | |
| 87 | // Create 10 ghost stubs with refcount=1 (simulating migration). |
| 88 | for i in 0..10 { |
| 89 | ghost_table.insert(GhostStub::new(format!("edge_{i}"), i + 10, 1)); |
| 90 | } |
| 91 | assert_eq!(ghost_table.len(), 10); |
| 92 | |
| 93 | // Path A: refcount-based fast purge. |
| 94 | // Decrement refcount to 0 → stub is immediately purged. |
| 95 | for i in 0..5 { |
| 96 | let purged = ghost_table.decrement_ref(&format!("edge_{i}")); |
| 97 | assert!(purged, "edge_{i} should be purged on decrement"); |
| 98 | } |
| 99 | assert_eq!(ghost_table.len(), 5, "5 stubs remain after fast purge"); |
| 100 | |
| 101 | // Path B: sweep-based purge for remaining stubs. |
| 102 | // Sweep calls verify_fn for each remaining stub. |
| 103 | let report = ghost_table.sweep(|_node_id, _target_shard| SweepVerdict::Purge); |
| 104 | assert_eq!(report.purged, 5, "sweep should purge remaining 5"); |
| 105 | assert_eq!(ghost_table.len(), 0, "ghost count should converge to zero"); |
| 106 | } |
nothing calls this directly
no test coverage detected