swactor/crates/iroh-driver/tests/telemetry_transport.rs
Zachery Aaron Shores-Chmielewski cd870d3141 feat(data-plane): complete virtual blob namespace
Add durable namespace control, Iroh-backed source transfer, session-independent publication lifetime, and wire managed Myelin jobs and bindings through the reusable data-plane API.
2026-08-22 21:16:56 +04:00

200 lines
7.2 KiB
Rust

use std::net::{IpAddr, SocketAddr};
use std::sync::Arc;
use std::time::{Duration, Instant};
use iroh::{Endpoint, EndpointAddr, RelayMode};
use iroh_driver::{
PullCollectorConfig, TELEMETRY_ALPN, TelemetryQuicHeader, read_next_uni_from_connection,
spawn_pull_collector, write_available_subscription,
};
use swactor::config::RuntimeConfig;
use swactor::runtime::RuntimeParts;
use swactor_engine::{Engine, TokioBackend, TokioConfig};
use telemetry::frame::TelemetryEvent;
use telemetry::{
ChannelContent, DeliveryFanout, Lifetime, NodeId, Position, StreamId, SubscriptionRequest,
TelemetryEndpoint,
};
/// Telemetry transport test scheduled through `EngineHandle`, not an ambient
/// `#[tokio::test]` runtime (ENGINE_SPEC.md).
#[test]
fn iroh_telemetry_alpn_carries_catalog_and_numeric_frames() {
let parts = RuntimeParts::new(RuntimeConfig::default());
let engine = Engine::new(
parts,
TokioBackend::new(TokioConfig::default()).expect("test backend"),
)
.expect("test engine");
let handle = engine.handle();
let (done_tx, done_rx) = std::sync::mpsc::channel::<Result<(), String>>();
let h = handle.clone();
handle.spawn(async move {
let source = test_endpoint().await;
let collector = test_endpoint().await;
let collector_addr = endpoint_addr(&collector);
// Accept the incoming connection through an engine-hosted task + oneshot,
// since EngineHandle::spawn is fire-and-forget (no JoinHandle).
let (accept_tx, accept_rx) = tokio::sync::oneshot::channel();
{
let collector = collector.clone();
h.spawn(async move {
let conn = collector
.accept()
.await
.expect("incoming connection")
.await
.expect("accepted connection");
let _ = accept_tx.send(conn);
});
}
let stream = StreamId::new(NodeId::new("source-node"), Lifetime(1));
let endpoint = TelemetryEndpoint::with_capacity(stream.clone(), 8, 8);
let producer = endpoint.producer();
let runtime_log = producer.register_channel("runtime.log", ChannelContent::TextStream);
let subscription = endpoint.subscribe_all("iroh");
producer.submit_text(runtime_log, "alpha");
producer.submit_text(runtime_log, "beta");
endpoint.tick();
let conn = source
.connect(collector_addr, TELEMETRY_ALPN)
.await
.expect("connect telemetry ALPN");
let send = conn.open_uni().await.expect("open uni stream");
let header =
TelemetryQuicHeader::from_snapshot([7; 16], b"token".to_vec(), subscription.snapshot())
.expect("header from subscription snapshot");
let wrote = write_available_subscription(&h, send, &header, &subscription)
.await
.expect("write subscription");
assert_eq!(wrote.events, 2);
let accepted = accept_rx.await.expect("collector accept task");
let read = read_next_uni_from_connection(&accepted)
.await
.expect("read telemetry uni stream");
assert_eq!(read.header, header);
assert_eq!(read.header.stream.stream, stream);
assert!(
read.header
.channels
.iter()
.any(|descriptor| descriptor.id == runtime_log && descriptor.name == "runtime.log")
);
assert_eq!(read.events.len(), 2);
match &read.events[0] {
TelemetryEvent::Frame(frame) => {
assert_eq!(frame.channel.stream, stream);
assert_eq!(frame.channel.channel, runtime_log);
assert_eq!(frame.position, Position(0));
assert_eq!(frame.payload, b"alpha");
}
other => panic!("expected frame event, got {other:?}"),
}
match &read.events[1] {
TelemetryEvent::Frame(frame) => {
assert_eq!(frame.position, Position(1));
assert_eq!(frame.payload, b"beta");
}
other => panic!("expected frame event, got {other:?}"),
}
source.close().await;
collector.close().await;
let _ = done_tx.send(Ok(()));
});
match done_rx.recv() {
Ok(Ok(())) => {}
Ok(Err(e)) => panic!("test failed: {e}"),
Err(_) => panic!("test task dropped"),
}
}
#[test]
fn pull_collector_cancellation_interrupts_inflight_io() {
let parts = RuntimeParts::new(RuntimeConfig::default());
let engine = Engine::new(
parts,
TokioBackend::new(TokioConfig::default()).expect("test backend"),
)
.expect("test engine");
let handle = engine.handle();
let (resource_tx, resource_rx) = std::sync::mpsc::channel();
let setup_handle = handle.clone();
handle.spawn(async move {
let collector_endpoint = test_endpoint().await;
let silent_peer = test_endpoint().await;
let (header_tx, header_rx) = std::sync::mpsc::channel();
let collector = spawn_pull_collector(
&setup_handle,
PullCollectorConfig {
endpoint: collector_endpoint.clone(),
peer: endpoint_addr(&silent_peer),
flow_id: [3; 16],
token: Vec::new(),
request: SubscriptionRequest::all(),
fanout: Arc::new(DeliveryFanout::new(8)),
},
header_tx,
);
resource_tx
.send((collector, collector_endpoint, silent_peer, header_rx))
.expect("return collector resources");
});
let (collector, collector_endpoint, silent_peer, _header_rx) = resource_rx
.recv_timeout(Duration::from_secs(5))
.expect("collector setup");
std::thread::sleep(Duration::from_millis(100));
assert!(
!collector.is_finished(),
"collector was not retained in silent-peer network I/O"
);
collector.cancel();
let stopped_deadline = Instant::now() + Duration::from_secs(2);
while !collector.is_finished() && Instant::now() < stopped_deadline {
std::thread::sleep(Duration::from_millis(10));
}
assert!(
collector.is_finished(),
"cancelled collector remained blocked in network I/O"
);
drop((collector_endpoint, silent_peer));
}
async fn test_endpoint() -> Endpoint {
Endpoint::builder(iroh::endpoint::presets::Minimal)
.relay_mode(RelayMode::Disabled)
.alpns(vec![TELEMETRY_ALPN.to_vec()])
.bind()
.await
.expect("bind test endpoint")
}
fn endpoint_addr(endpoint: &Endpoint) -> EndpointAddr {
let mut addr = EndpointAddr::new(endpoint.id());
for socket in endpoint.bound_sockets() {
addr = addr.with_ip_addr(loopback_if_unspecified(socket));
}
addr
}
fn loopback_if_unspecified(socket: SocketAddr) -> SocketAddr {
match socket.ip() {
IpAddr::V4(ip) if ip.is_unspecified() => {
SocketAddr::new(IpAddr::V4(std::net::Ipv4Addr::LOCALHOST), socket.port())
}
IpAddr::V6(ip) if ip.is_unspecified() => {
SocketAddr::new(IpAddr::V6(std::net::Ipv6Addr::LOCALHOST), socket.port())
}
_ => socket,
}
}