igc-net 0.3.0

igc-net protocol rust library — publish and add metadata to IGC flight files
Documentation
mod common;

use std::time::Duration;

use common::{init_tracing, wait_for_pilot_auth_record};
use igc_net::IgcIrohNode;

#[tokio::test]
async fn pilot_auth_did_sync_round_trips_over_network() {
    init_tracing();
    let dir_a = tempfile::tempdir().unwrap();
    let dir_b = tempfile::tempdir().unwrap();

    let node_a = IgcIrohNode::start(dir_a.path()).await.unwrap();
    let node_b = IgcIrohNode::start(dir_b.path()).await.unwrap();

    node_b.add_peer_addr(node_a.loopback_endpoint_addr().unwrap());

    let pilot_id = node_a
        .generate_pilot_identity("Test Pilot", None)
        .unwrap()
        .pilot_id();
    let initial = node_a
        .issue_initial_registered_pilot_auth_did_record(&pilot_id, "2026-05-01T09:14:00Z")
        .await
        .unwrap();

    let applied_initial = node_b
        .sync_pilot_auth_did_from_peer(node_a.iroh_node_id(), &pilot_id)
        .await
        .unwrap();
    assert_eq!(applied_initial, 1);
    let initial_state = node_b.resolve_pilot_auth_did_state(&pilot_id).unwrap();
    assert_eq!(
        initial_state.authoritative.as_ref().unwrap().record_id,
        initial.record_id
    );

    let rotated = node_a
        .rotate_registered_pilot_auth_did(&pilot_id, "2026-05-01T10:14:00Z")
        .await
        .unwrap();

    let applied_rotation = node_b
        .sync_pilot_auth_did_from_peer(node_a.iroh_node_id(), &pilot_id)
        .await
        .unwrap();
    assert_eq!(applied_rotation, 1);

    let final_state = node_b.resolve_pilot_auth_did_state(&pilot_id).unwrap();
    assert_eq!(
        final_state.authoritative.as_ref().unwrap().record_id,
        rotated.record_id
    );

    let applied_again = node_b
        .sync_pilot_auth_did_from_peer(node_a.iroh_node_id(), &pilot_id)
        .await
        .unwrap();
    assert_eq!(applied_again, 0);

    node_a.close().await;
    node_b.close().await;
}

#[tokio::test]
async fn pilot_auth_did_gossip_propagates_updates_across_peers() {
    init_tracing();
    let dir_a = tempfile::tempdir().unwrap();
    let dir_b = tempfile::tempdir().unwrap();
    let dir_c = tempfile::tempdir().unwrap();

    let node_a = IgcIrohNode::start(dir_a.path()).await.unwrap();
    let node_b = IgcIrohNode::start(dir_b.path()).await.unwrap();
    let node_c = IgcIrohNode::start(dir_c.path()).await.unwrap();

    node_b.add_peer_addr(node_a.loopback_endpoint_addr().unwrap());
    node_c.add_peer_addr(node_b.loopback_endpoint_addr().unwrap());
    node_c.add_peer_addr(node_a.loopback_endpoint_addr().unwrap());

    node_b
        .join_pilot_auth_did_gossip_peers(vec![node_a.iroh_node_id()])
        .await
        .unwrap();
    node_c
        .join_pilot_auth_did_gossip_peers(vec![node_b.iroh_node_id(), node_a.iroh_node_id()])
        .await
        .unwrap();

    tokio::time::sleep(Duration::from_millis(500)).await;

    let pilot_id = node_a
        .generate_pilot_identity("Test Pilot", None)
        .unwrap()
        .pilot_id();
    let initial = node_a
        .issue_initial_registered_pilot_auth_did_record(&pilot_id, "2026-05-01T09:14:00Z")
        .await
        .unwrap();

    assert!(
        wait_for_pilot_auth_record(
            &node_b,
            &pilot_id,
            &initial.record_id,
            Duration::from_secs(10)
        )
        .await
    );
    assert!(
        wait_for_pilot_auth_record(
            &node_c,
            &pilot_id,
            &initial.record_id,
            Duration::from_secs(10)
        )
        .await
    );

    let rotated = node_a
        .rotate_registered_pilot_auth_did(&pilot_id, "2026-05-01T10:14:00Z")
        .await
        .unwrap();

    assert!(
        wait_for_pilot_auth_record(
            &node_b,
            &pilot_id,
            &rotated.record_id,
            Duration::from_secs(10)
        )
        .await
    );
    assert!(
        wait_for_pilot_auth_record(
            &node_c,
            &pilot_id,
            &rotated.record_id,
            Duration::from_secs(10)
        )
        .await
    );

    node_a.close().await;
    node_b.close().await;
    node_c.close().await;
}

#[tokio::test]
async fn governance_topic_gossip_applies_full_pilot_auth_records() {
    init_tracing();
    let dir_a = tempfile::tempdir().unwrap();
    let dir_b = tempfile::tempdir().unwrap();

    let node_a = IgcIrohNode::start(dir_a.path()).await.unwrap();
    let node_b = IgcIrohNode::start(dir_b.path()).await.unwrap();

    node_b.add_peer_addr(node_a.loopback_endpoint_addr().unwrap());
    node_b
        .join_governance_gossip_peers(vec![node_a.iroh_node_id()])
        .await
        .unwrap();

    tokio::time::sleep(Duration::from_millis(500)).await;

    let pilot_id = node_a
        .generate_pilot_identity("Test Pilot", None)
        .unwrap()
        .pilot_id();
    let initial = node_a
        .issue_initial_registered_pilot_auth_did_record(&pilot_id, "2026-05-01T09:14:00Z")
        .await
        .unwrap();

    assert!(
        wait_for_pilot_auth_record(
            &node_b,
            &pilot_id,
            &initial.record_id,
            Duration::from_secs(10)
        )
        .await
    );

    node_a.close().await;
    node_b.close().await;
}