use std::collections::HashSet;
use std::net::SocketAddr;
use std::sync::{Arc, Mutex};
use moqtap_codec::dispatch::{
AnyControlMessage, AnyDatagramHeader, AnyFetchHeader, AnyObjectHeader, AnySubgroupHeader,
};
use moqtap_codec::draft07::data_stream::ObjectHeader;
use moqtap_codec::draft07::types::ObjectStatus;
use moqtap_codec::draft14::data_stream::{
DatagramObject, DatagramType, FetchHeader, SubgroupHeader, SubgroupStreamType,
};
use moqtap_codec::draft14::message::{ControlMessage, GoAway};
use moqtap_codec::varint::VarInt;
use moqtap_proxy::event::*;
use moqtap_proxy::observer::*;
#[test]
fn proxy_side_copy_and_eq() {
let s = ProxySide::ClientToProxy;
let s2 = s;
assert_eq!(s, s2);
assert_ne!(ProxySide::ClientToProxy, ProxySide::RelayToProxy);
assert_ne!(ProxySide::ProxyToRelay, ProxySide::ProxyToClient);
}
#[test]
fn proxy_side_debug() {
assert_eq!(format!("{:?}", ProxySide::ClientToProxy), "ClientToProxy");
assert_eq!(format!("{:?}", ProxySide::ProxyToRelay), "ProxyToRelay");
assert_eq!(format!("{:?}", ProxySide::RelayToProxy), "RelayToProxy");
assert_eq!(format!("{:?}", ProxySide::ProxyToClient), "ProxyToClient");
}
#[test]
fn session_id_copy_eq_hash() {
let id1 = SessionId(1);
let id2 = SessionId(1);
let id3 = SessionId(2);
assert_eq!(id1, id2);
assert_ne!(id1, id3);
let mut set = HashSet::new();
set.insert(id1);
assert!(set.contains(&id2));
assert!(!set.contains(&id3));
}
#[test]
fn data_stream_header_kind_subgroup() {
let header = AnySubgroupHeader::Draft14(SubgroupHeader {
stream_type: SubgroupStreamType::from_u8(0x14).unwrap(),
track_alias: VarInt::from_u64(1).unwrap(),
group_id: VarInt::from_u64(0).unwrap(),
subgroup_id: Some(VarInt::from_u64(0).unwrap()),
publisher_priority: 128,
});
let kind = DataStreamHeaderKind::Subgroup(header);
let debug = format!("{kind:?}");
assert!(debug.contains("Subgroup"));
}
#[test]
fn data_stream_header_kind_fetch() {
let header = AnyFetchHeader::Draft14(FetchHeader { request_id: VarInt::from_u64(2).unwrap() });
let kind = DataStreamHeaderKind::Fetch(header);
let debug = format!("{kind:?}");
assert!(debug.contains("Fetch"));
}
#[test]
fn proxy_event_session_started() {
let addr: SocketAddr = "127.0.0.1:1234".parse().unwrap();
let event = ProxyEvent::SessionStarted {
session_id: SessionId(1),
client_addr: addr,
client_transport: "QUIC".into(),
};
let debug = format!("{event:?}");
assert!(debug.contains("SessionStarted"));
}
#[test]
fn proxy_event_control_message() {
let msg =
AnyControlMessage::Draft14(ControlMessage::GoAway(GoAway { new_session_uri: vec![] }));
let event = ProxyEvent::ControlMessage {
session_id: SessionId(1),
side: ProxySide::ClientToProxy,
message: msg,
};
let debug = format!("{event:?}");
assert!(debug.contains("ControlMessage"));
}
#[test]
fn proxy_event_datagram() {
let header = AnyDatagramHeader::Draft14(DatagramObject {
datagram_type: DatagramType::from_u8(0x00).unwrap(),
track_alias: VarInt::from_u64(1).unwrap(),
group_id: VarInt::from_u64(0).unwrap(),
object_id: VarInt::from_u64(0).unwrap(),
publisher_priority: 128,
extension_headers: Vec::new(),
status: None,
payload: Vec::new(),
});
let event = ProxyEvent::Datagram {
session_id: SessionId(1),
side: ProxySide::RelayToProxy,
header,
payload_len: 42,
};
let debug = format!("{event:?}");
assert!(debug.contains("Datagram"));
}
#[test]
fn proxy_event_object_header() {
let header = AnyObjectHeader::Draft07(ObjectHeader {
object_id: VarInt::from_u64(0).unwrap(),
payload_length: VarInt::from_u64(100).unwrap(),
object_status: ObjectStatus::Normal,
});
let event = ProxyEvent::ObjectHeader {
session_id: SessionId(1),
side: ProxySide::ProxyToClient,
header,
};
let debug = format!("{event:?}");
assert!(debug.contains("ObjectHeader"));
}
#[test]
fn proxy_event_stream_events() {
let bi =
ProxyEvent::BiStreamOpened { session_id: SessionId(1), side: ProxySide::ClientToProxy };
let uni =
ProxyEvent::UniStreamOpened { session_id: SessionId(2), side: ProxySide::RelayToProxy };
let closed =
ProxyEvent::StreamClosed { session_id: SessionId(3), side: ProxySide::ProxyToRelay };
assert!(format!("{bi:?}").contains("BiStreamOpened"));
assert!(format!("{uni:?}").contains("UniStreamOpened"));
assert!(format!("{closed:?}").contains("StreamClosed"));
}
#[test]
fn proxy_event_parse_error() {
let event = ProxyEvent::ParseError {
session_id: SessionId(1),
side: ProxySide::ClientToProxy,
error: "bad varint".to_string(),
};
let debug = format!("{event:?}");
assert!(debug.contains("ParseError"));
assert!(debug.contains("bad varint"));
}
#[test]
fn proxy_event_session_ended() {
let event =
ProxyEvent::SessionEnded { session_id: SessionId(1), reason: "completed".to_string() };
let debug = format!("{event:?}");
assert!(debug.contains("SessionEnded"));
}
#[test]
fn proxy_event_clone() {
let event = ProxyEvent::SessionEnded { session_id: SessionId(42), reason: "test".to_string() };
let cloned = event.clone();
assert!(format!("{cloned:?}").contains("42"));
}
#[test]
fn noop_observer_does_not_panic() {
let obs = NoOpProxyObserver;
let addr: SocketAddr = "127.0.0.1:1234".parse().unwrap();
obs.on_event(&ProxyEvent::SessionStarted {
session_id: SessionId(1),
client_addr: addr,
client_transport: "QUIC".into(),
});
obs.on_event(&ProxyEvent::SessionEnded {
session_id: SessionId(1),
reason: "done".to_string(),
});
}
#[derive(Clone)]
struct CollectingObserver {
events: Arc<Mutex<Vec<String>>>,
}
impl ProxyObserver for CollectingObserver {
fn on_event(&self, event: &ProxyEvent) {
let label = match event {
ProxyEvent::SessionStarted { .. } => "SessionStarted",
ProxyEvent::SetupMessage { .. } => "SetupMessage",
ProxyEvent::ControlMessage { .. } => "ControlMessage",
ProxyEvent::DataStreamHeader { .. } => "DataStreamHeader",
ProxyEvent::ObjectHeader { .. } => "ObjectHeader",
ProxyEvent::Datagram { .. } => "Datagram",
ProxyEvent::BiStreamOpened { .. } => "BiStreamOpened",
ProxyEvent::UniStreamOpened { .. } => "UniStreamOpened",
ProxyEvent::ParseError { .. } => "ParseError",
ProxyEvent::StreamClosed { .. } => "StreamClosed",
ProxyEvent::SessionEnded { .. } => "SessionEnded",
};
self.events.lock().unwrap().push(label.to_string());
}
}
#[test]
fn collecting_observer_receives_events() {
let events = Arc::new(Mutex::new(Vec::new()));
let obs = CollectingObserver { events: events.clone() };
let addr: SocketAddr = "127.0.0.1:1234".parse().unwrap();
obs.on_event(&ProxyEvent::SessionStarted {
session_id: SessionId(1),
client_addr: addr,
client_transport: "QUIC".into(),
});
obs.on_event(&ProxyEvent::SessionEnded {
session_id: SessionId(1),
reason: "done".to_string(),
});
let collected = events.lock().unwrap();
assert_eq!(collected.len(), 2);
assert_eq!(collected[0], "SessionStarted");
assert_eq!(collected[1], "SessionEnded");
}
#[test]
fn observer_is_object_safe() {
let events = Arc::new(Mutex::new(Vec::new()));
let obs: Box<dyn ProxyObserver> = Box::new(CollectingObserver { events: events.clone() });
let addr: SocketAddr = "127.0.0.1:5678".parse().unwrap();
obs.on_event(&ProxyEvent::SessionStarted {
session_id: SessionId(99),
client_addr: addr,
client_transport: "QUIC".into(),
});
assert_eq!(events.lock().unwrap().len(), 1);
}