Files
games/crates/common/game-realtime-webtransport-lib/tests/realtime_connection.rs
2026-09-21 22:55:21 +02:00

90 lines
5.2 KiB
Rust

// file: crates/common/game-realtime-webtransport-lib/tests/realtime_connection.rs
// version: 1
//! Deterministic loopback proof for the primary reliable WebTransport stream and transport-neutral framing contract.
const TEST_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(5);
#[tokio::test(flavor = "current_thread")]
async fn primary_stream_round_trip_is_binary_ordered_and_closes_cleanly() {
let identity = match game_realtime_webtransport_lib::WebTransportServerIdentity::generate_loopback() {
Ok(value) => value,
Err(error) => panic!("loopback identity generation failed: {error}"),
};
let certificate_hash = identity.certificate_hash().clone();
let server_config = game_realtime_webtransport_lib::WebTransportServerConfig::new(std::net::SocketAddr::from(([127, 0, 0, 1], 0)), identity);
let mut listener = match game_realtime_webtransport_lib::WebTransportListener::bind(server_config) {
Ok(value) => value,
Err(error) => panic!("WebTransport listener bind failed: {error}"),
};
let endpoint = format!("https://{}/realtime", listener.local_addr());
let client_config = match game_realtime_webtransport_lib::WebTransportClientConfig::new(endpoint.as_str(), certificate_hash) {
Ok(value) => value,
Err(error) => panic!("WebTransport client configuration failed: {error}"),
};
let sessions = tokio::time::timeout(TEST_TIMEOUT, async {
return tokio::join!(listener.accept(), game_realtime_webtransport_lib::connect(&client_config));
})
.await;
let (server_session, client_session) = match sessions {
Ok((Ok(server), Ok(client))) => (server, client),
Ok((Err(error), _)) => panic!("WebTransport server establishment failed: {error}"),
Ok((_, Err(error))) => panic!("WebTransport client establishment failed: {error}"),
Err(_) => panic!("WebTransport loopback establishment timed out"),
};
let client_connection = match client_session.open_primary_connection().await {
Ok(value) => value,
Err(error) => panic!("client primary stream open failed: {error}"),
};
let (mut client_sender, mut client_receiver) = game_realtime_transport_lib::RealtimeConnection::split(client_connection);
let server_connection = match tokio::time::timeout(TEST_TIMEOUT, server_session.accept_primary_connection()).await {
Ok(Ok(connection)) => connection,
Ok(Err(error)) => panic!("server primary stream accept failed: {error}"),
Err(_) => panic!("server primary stream accept timed out"),
};
let (mut server_sender, mut server_receiver) = game_realtime_transport_lib::RealtimeConnection::split(server_connection);
let first_payload = Vec::new();
send_payload(&mut client_sender, first_payload.clone(), "client first send").await;
assert_received_payload(&mut server_receiver, first_payload.clone(), "server first receive").await;
send_payload(&mut server_sender, first_payload.clone(), "server first echo").await;
assert_received_payload(&mut client_receiver, first_payload, "client first echo receive").await;
let remaining_payloads = [vec![0x00, 0x7f, 0x80, 0xff], b"third-message".to_vec()];
for payload in remaining_payloads {
send_payload(&mut client_sender, payload.clone(), "client ordered send").await;
assert_received_payload(&mut server_receiver, payload.clone(), "server ordered receive").await;
send_payload(&mut server_sender, payload.clone(), "server ordered echo").await;
assert_received_payload(&mut client_receiver, payload, "client ordered echo receive").await;
}
if let Err(error) = game_realtime_transport_lib::RealtimeSender::close(&mut client_sender).await {
panic!("client sender close failed: {error}");
}
assert_closed(&mut server_receiver, "server remote close").await;
if let Err(error) = game_realtime_transport_lib::RealtimeSender::close(&mut server_sender).await {
panic!("server sender close failed: {error}");
}
assert_closed(&mut client_receiver, "client remote close").await;
}
async fn assert_closed(receiver: &mut game_realtime_webtransport_lib::WebTransportReceiver, label: &str) {
let received = match game_realtime_transport_lib::RealtimeReceiver::receive(receiver).await {
Ok(value) => value,
Err(error) => panic!("{label} failed: {error}"),
};
assert_eq!(received, game_realtime_transport_lib::TransportReceive::Closed);
}
async fn assert_received_payload(receiver: &mut game_realtime_webtransport_lib::WebTransportReceiver, payload: Vec<u8>, label: &str) {
let received = match game_realtime_transport_lib::RealtimeReceiver::receive(receiver).await {
Ok(value) => value,
Err(error) => panic!("{label} failed: {error}"),
};
assert_eq!(received, game_realtime_transport_lib::TransportReceive::Message(game_realtime_transport_lib::TransportMessage::new(payload)));
}
async fn send_payload(sender: &mut game_realtime_webtransport_lib::WebTransportSender, payload: Vec<u8>, label: &str) {
let message = game_realtime_transport_lib::TransportMessage::new(payload);
if let Err(error) = game_realtime_transport_lib::RealtimeSender::send(sender, message).await {
panic!("{label} failed: {error}");
}
}