//! Real-I/O checks for the telemetry envelope over loopback UDP.
use std::collections::{HashMap, HashSet};
use std::net::UdpSocket;
use std::time::Duration;
use telemetry::frame::Frame;
use telemetry::ingest::Consumer;
use telemetry::mux::Mux;
use telemetry::transport::Delivery;
use telemetry::wire::{decode_delivery, encode_delivery};
use telemetry::{ChannelId, Lifetime, NodeId, Position, Record, StreamId};
use serde::{Deserialize, Serialize};
const RESOURCE_CHANNEL: ChannelId = ChannelId(1);
const LOG_CHANNEL: ChannelId = ChannelId(2);
const MEMBERSHIP_CHANNEL: ChannelId = ChannelId(3);
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
struct ResourceSample {
tick: u64,
cpu_pct: f32,
}
impl Record for ResourceSample {
const CHANNEL: &'static str = "host.resource";
}
fn build_stream() -> (StreamId, Vec) {
let id = StreamId::new(NodeId::new("node-real"), Lifetime(1));
let mux = Mux::unbounded(id.clone());
for tick in 0..20 {
mux.submit(
RESOURCE_CHANNEL,
ResourceSample {
tick,
cpu_pct: 10.0 + tick as f32,
}
.encode(),
);
}
mux.submit(LOG_CHANNEL, b"epoch 1 complete".to_vec());
mux.submit(MEMBERSHIP_CHANNEL, b"node-x alive->suspect".to_vec());
(id, mux.drain())
}
fn carry_over_real_socket(stream: &StreamId, frames: &[Frame]) -> Vec {
let consumer = UdpSocket::bind("127.0.0.1:0").expect("bind consumer socket");
consumer
.set_read_timeout(Some(Duration::from_millis(300)))
.expect("set timeout");
let consumer_addr = consumer.local_addr().expect("consumer addr");
let node = UdpSocket::bind("127.0.0.1:0").expect("bind node socket");
for frame in frames {
let datagram = encode_delivery(stream, frame);
let _ = node.send_to(&datagram, consumer_addr);
}
let mut delivered = Vec::new();
let mut buf = vec![0u8; 64 * 1024];
loop {
match consumer.recv_from(&mut buf) {
Ok((n, _)) => {
if let Ok((s, frame)) = decode_delivery(&buf[..n]) {
delivered.push(Delivery::new(s, frame));
}
}
Err(e)
if e.kind() == std::io::ErrorKind::WouldBlock
|| e.kind() == std::io::ErrorKind::TimedOut =>
{
break;
}
Err(_) => break,
}
}
delivered
}
#[test]
fn real_transport_stays_within_the_envelope() {
let (id, sent) = build_stream();
let delivered = carry_over_real_socket(&id, &sent);
let by_position: HashMap = sent.iter().map(|f| (f.position.0, f)).collect();
let mut seen = HashSet::new();
for d in &delivered {
assert_eq!(d.stream, id, "the carrier did not alter the stream id");
let original = by_position
.get(&d.frame.position.0)
.expect("a delivered position was never sent");
assert_eq!(&d.frame, *original);
assert!(seen.insert(d.frame.position.0), "no duplicate positions");
}
}
#[test]
fn wiring_smoke_some_frames_arrive_and_reconstruct() {
let (id, sent) = build_stream();
let delivered = carry_over_real_socket(&id, &sent);
let mut consumer = Consumer::new();
consumer.ingest(delivered);
let stored = consumer
.store()
.stream(&id)
.expect("the node's frames reached the consumer");
assert!(
!stored.is_empty(),
"some frames arrived over the real transport"
);
let by_position: HashMap = sent.iter().map(|f| (f.position.0, f)).collect();
let mut prev: Option = None;
for frame in stored.frames() {
assert_eq!(
frame,
*by_position.get(&frame.position.0).expect("sent frame")
);
if let Some(p) = prev {
assert!(frame.position.0 > p, "reconstructed in position order");
}
prev = Some(frame.position.0);
}
}