antenna-protocol 0.1.1

SansIO core driving the antenna P2P mesh protocol.
Documentation
#[cfg(test)]
mod test {
    use std::collections::HashMap;

    use crate::{
        Input, MeshNodeFSM, MsgPayload, Output, PeerID,
        test::{assert_full_mesh_connectivity, drive_bootstrap_handshake, join_mesh},
    };

    fn deliver_disconnect(peer: &mut MeshNodeFSM, from: &PeerID) -> Vec<Output<()>> {
        peer.process::<()>(Input::MessageReceived {
            peer_from: from.clone(),
            data: MsgPayload::Disconnect,
        })
        .unwrap()
    }

    fn build_three_peer_mesh() -> (PeerID, PeerID, PeerID, HashMap<PeerID, MeshNodeFSM>) {
        let mut alice = MeshNodeFSM::new();
        let alice_id = alice.id().clone();
        let mut bob = MeshNodeFSM::new();
        let bob_id = bob.id().clone();
        let charlie = MeshNodeFSM::new();
        let charlie_id = charlie.id().clone();

        drive_bootstrap_handshake::<()>(&mut alice, &mut bob);

        let mut peers: HashMap<PeerID, MeshNodeFSM> = HashMap::new();
        peers.insert(alice_id.clone(), alice);
        peers.insert(bob_id.clone(), bob);
        peers.insert(charlie_id.clone(), charlie);

        join_mesh(&charlie_id, &bob_id, &mut peers);
        assert_full_mesh_connectivity(&peers);

        (alice_id, bob_id, charlie_id, peers)
    }

    #[test]
    fn peer_leaves_then_rejoins_three_peer_mesh() {
        let (alice_id, bob_id, charlie_id, mut peers) = build_three_peer_mesh();

        let bob_leave_out = peers
            .get_mut(&bob_id)
            .unwrap()
            .process::<()>(Input::Leave)
            .unwrap();

        assert!(
            bob_leave_out
                .iter()
                .any(|o| matches!(o, Output::Disconnecting))
        );
        let disconnect_targets: Vec<PeerID> = bob_leave_out
            .iter()
            .filter_map(|o| match o {
                Output::SendMessage {
                    peer_to,
                    data: MsgPayload::Disconnect,
                } => Some(peer_to.clone()),
                _ => None,
            })
            .collect();
        assert!(disconnect_targets.contains(&alice_id));
        assert!(disconnect_targets.contains(&charlie_id));

        for peer_id in [&alice_id, &charlie_id] {
            let out = deliver_disconnect(peers.get_mut(peer_id).unwrap(), &bob_id);
            assert!(
                out.iter()
                    .any(|o| matches!(o, Output::PeerDisconnected { peer } if peer == &bob_id)),
                "{peer_id:?} should observe PeerDisconnected for bob"
            );
        }
        assert!(!peers.get(&alice_id).unwrap().is_connected(&bob_id));
        assert!(!peers.get(&charlie_id).unwrap().is_connected(&bob_id));

        join_mesh(&bob_id, &alice_id, &mut peers);

        assert_full_mesh_connectivity(&peers);
        assert!(peers.get(&alice_id).unwrap().is_connected(&bob_id));
        assert!(peers.get(&charlie_id).unwrap().is_connected(&bob_id));
    }
}