821537186e
- Reorganized import statements in adapter tests for better readability - Replaced or_insert_with(Vec::new) with or_default() in test closures - Updated Cargo.lock with new dependency versions and checksums - Added TLS features to tonic dependency configuration - Included sqlx, chrono, and uuid dependencies with specific features - Added jsonwebtoken and arc-swap as project dependencies - Reformatted assertion statements to comply with line length limits - Adjusted base64 import order in engine codec module - Updated protobuf include statement formatting
368 lines
12 KiB
Rust
368 lines
12 KiB
Rust
use std::collections::HashSet;
|
|
use std::sync::Arc;
|
|
|
|
use imks::socket::adapter::{
|
|
Adapter, AdapterError, BroadcastFlags, BroadcastOptions, BusMessage, LocalAdapter, SocketInfo,
|
|
};
|
|
use imks::socket::packet::Packet;
|
|
use imks::socket::session_store::{InMemorySessionStore, SessionInfo, SessionStoreTrait};
|
|
|
|
#[tokio::test]
|
|
async fn test_local_adapter_add_and_del() {
|
|
let sent_packets: dashmap::DashMap<String, Vec<Packet>> = dashmap::DashMap::new();
|
|
let sent_packets_clone = sent_packets.clone();
|
|
let send_fn = move |engine_sid: &str, packet: &Packet| {
|
|
sent_packets_clone
|
|
.entry(engine_sid.to_string())
|
|
.or_default()
|
|
.value_mut()
|
|
.push(packet.clone());
|
|
Ok(())
|
|
};
|
|
|
|
let adapter = LocalAdapter::new(send_fn);
|
|
|
|
adapter.add("sid1", "room1", "/").await.unwrap();
|
|
adapter.add("sid1", "room2", "/").await.unwrap();
|
|
adapter.add("sid2", "room1", "/").await.unwrap();
|
|
|
|
let rooms = adapter.socket_rooms("sid1").await.unwrap();
|
|
assert!(rooms.contains("room1"));
|
|
assert!(rooms.contains("room2"));
|
|
|
|
adapter.del("sid1", "room1", "/").await.unwrap();
|
|
let rooms = adapter.socket_rooms("sid1").await.unwrap();
|
|
assert!(!rooms.contains("room1"));
|
|
assert!(rooms.contains("room2"));
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_local_adapter_del_all() {
|
|
let send_fn = move |_engine_sid: &str, _packet: &Packet| Ok(());
|
|
|
|
let adapter = LocalAdapter::new(send_fn);
|
|
|
|
adapter.add("sid1", "room1", "/").await.unwrap();
|
|
adapter.add("sid1", "room2", "/").await.unwrap();
|
|
|
|
adapter.del_all("sid1", "/").await.unwrap();
|
|
|
|
let rooms = adapter.socket_rooms("sid1").await.unwrap();
|
|
assert!(rooms.is_empty());
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_local_adapter_register_and_broadcast() {
|
|
let sent_packets: Arc<dashmap::DashMap<String, Vec<Packet>>> =
|
|
Arc::new(dashmap::DashMap::new());
|
|
let sent_packets_clone = sent_packets.clone();
|
|
let send_fn = move |engine_sid: &str, packet: &Packet| {
|
|
sent_packets_clone
|
|
.entry(engine_sid.to_string())
|
|
.or_default()
|
|
.value_mut()
|
|
.push(packet.clone());
|
|
Ok(())
|
|
};
|
|
|
|
let adapter = LocalAdapter::new(send_fn);
|
|
|
|
// Register socket_sid → engine_sid mapping
|
|
adapter.register("sid1", "engine1", "/").await.unwrap();
|
|
adapter.register("sid2", "engine2", "/").await.unwrap();
|
|
|
|
let packet = Packet::event("/", serde_json::json!(["test", "hello"]), None);
|
|
let opts = BroadcastOptions::default();
|
|
adapter.broadcast(&packet, &opts).await.unwrap();
|
|
|
|
assert!(sent_packets.contains_key("engine1"));
|
|
assert!(sent_packets.contains_key("engine2"));
|
|
assert_eq!(sent_packets.len(), 2);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_local_adapter_broadcast_to_room() {
|
|
let sent_packets: Arc<dashmap::DashMap<String, Vec<Packet>>> =
|
|
Arc::new(dashmap::DashMap::new());
|
|
let sent_packets_clone = sent_packets.clone();
|
|
let send_fn = move |engine_sid: &str, packet: &Packet| {
|
|
sent_packets_clone
|
|
.entry(engine_sid.to_string())
|
|
.or_default()
|
|
.value_mut()
|
|
.push(packet.clone());
|
|
Ok(())
|
|
};
|
|
|
|
let adapter = LocalAdapter::new(send_fn);
|
|
|
|
adapter.register("sid1", "engine1", "/").await.unwrap();
|
|
adapter.register("sid2", "engine2", "/").await.unwrap();
|
|
adapter.add("sid1", "room1", "/").await.unwrap();
|
|
adapter.add("sid2", "room2", "/").await.unwrap();
|
|
|
|
let packet = Packet::event("/", serde_json::json!(["test", "hello"]), None);
|
|
let opts = BroadcastOptions {
|
|
rooms: HashSet::from(["room1".to_string()]),
|
|
except: HashSet::new(),
|
|
flags: BroadcastFlags::default(),
|
|
};
|
|
adapter.broadcast(&packet, &opts).await.unwrap();
|
|
|
|
assert!(sent_packets.contains_key("engine1"));
|
|
assert!(!sent_packets.contains_key("engine2"));
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_local_adapter_broadcast_except() {
|
|
let sent_packets: Arc<dashmap::DashMap<String, Vec<Packet>>> =
|
|
Arc::new(dashmap::DashMap::new());
|
|
let sent_packets_clone = sent_packets.clone();
|
|
let send_fn = move |engine_sid: &str, packet: &Packet| {
|
|
sent_packets_clone
|
|
.entry(engine_sid.to_string())
|
|
.or_default()
|
|
.value_mut()
|
|
.push(packet.clone());
|
|
Ok(())
|
|
};
|
|
|
|
let adapter = LocalAdapter::new(send_fn);
|
|
|
|
adapter.register("sid1", "engine1", "/").await.unwrap();
|
|
adapter.register("sid2", "engine2", "/").await.unwrap();
|
|
|
|
let packet = Packet::event("/", serde_json::json!(["test", "hello"]), None);
|
|
let opts = BroadcastOptions {
|
|
rooms: HashSet::new(),
|
|
except: HashSet::from(["sid1".to_string()]),
|
|
flags: BroadcastFlags::default(),
|
|
};
|
|
adapter.broadcast(&packet, &opts).await.unwrap();
|
|
|
|
assert!(!sent_packets.contains_key("engine1"));
|
|
assert!(sent_packets.contains_key("engine2"));
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_local_adapter_fetch_sockets() {
|
|
let send_fn = move |_engine_sid: &str, _packet: &Packet| Ok(());
|
|
|
|
let adapter = LocalAdapter::new(send_fn);
|
|
|
|
adapter.register("sid1", "engine1", "/").await.unwrap();
|
|
adapter.register("sid2", "engine2", "/").await.unwrap();
|
|
adapter.add("sid1", "room1", "/").await.unwrap();
|
|
adapter.add("sid2", "room2", "/").await.unwrap();
|
|
|
|
let opts = BroadcastOptions::default();
|
|
let sockets = adapter.fetch_sockets(&opts).await.unwrap();
|
|
assert_eq!(sockets.len(), 2);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_local_adapter_server_id_unique() {
|
|
let send_fn1 = move |_engine_sid: &str, _packet: &Packet| Ok(());
|
|
let send_fn2 = move |_engine_sid: &str, _packet: &Packet| Ok(());
|
|
|
|
let adapter1 = LocalAdapter::new(send_fn1);
|
|
let adapter2 = LocalAdapter::new(send_fn2);
|
|
|
|
assert_ne!(adapter1.server_id(), adapter2.server_id());
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_in_memory_session_store() {
|
|
let store = InMemorySessionStore::new();
|
|
|
|
store.create("sid1", "polling", "server1").await.unwrap();
|
|
assert!(store.exists("sid1").await.unwrap());
|
|
|
|
let info = store.get("sid1").await.unwrap().unwrap();
|
|
assert_eq!(info.sid, "sid1");
|
|
assert_eq!(info.transport, "polling");
|
|
assert_eq!(info.state, "connecting");
|
|
assert_eq!(info.server_id, "server1");
|
|
|
|
store.set_state("sid1", "open").await.unwrap();
|
|
let info = store.get("sid1").await.unwrap().unwrap();
|
|
assert_eq!(info.state, "open");
|
|
|
|
store.set_transport("sid1", "websocket").await.unwrap();
|
|
let info = store.get("sid1").await.unwrap().unwrap();
|
|
assert_eq!(info.transport, "websocket");
|
|
|
|
store.update_ping("sid1").await.unwrap();
|
|
let info = store.get("sid1").await.unwrap().unwrap();
|
|
assert!(info.last_ping > 0);
|
|
|
|
store.remove("sid1").await.unwrap();
|
|
assert!(!store.exists("sid1").await.unwrap());
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_in_memory_session_store_not_found() {
|
|
let store = InMemorySessionStore::new();
|
|
|
|
let result = store.get("nonexistent").await.unwrap();
|
|
assert!(result.is_none());
|
|
|
|
let result = store.set_state("nonexistent", "open").await;
|
|
assert!(result.is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn test_bus_message_serialization() {
|
|
let msg = BusMessage::Broadcast {
|
|
namespace: "/".to_string(),
|
|
packet: "2[\"hello\"]".to_string(),
|
|
opts: BroadcastOptions::default(),
|
|
server_id: "server-1".to_string(),
|
|
};
|
|
|
|
let encoded = serde_json::to_vec(&msg).unwrap();
|
|
let decoded: BusMessage = serde_json::from_slice(&encoded).unwrap();
|
|
|
|
assert_eq!(decoded, msg);
|
|
}
|
|
|
|
#[test]
|
|
fn test_bus_message_socket_join() {
|
|
let msg = BusMessage::SocketJoin {
|
|
namespace: "/admin".to_string(),
|
|
sid: "sid-1".to_string(),
|
|
room: "room-1".to_string(),
|
|
server_id: "server-1".to_string(),
|
|
};
|
|
|
|
let encoded = serde_json::to_vec(&msg).unwrap();
|
|
let decoded: BusMessage = serde_json::from_slice(&encoded).unwrap();
|
|
|
|
assert_eq!(decoded, msg);
|
|
}
|
|
|
|
#[test]
|
|
fn test_bus_message_socket_leave() {
|
|
let msg = BusMessage::SocketLeave {
|
|
namespace: "/".to_string(),
|
|
sid: "sid-1".to_string(),
|
|
room: "room-1".to_string(),
|
|
server_id: "server-1".to_string(),
|
|
};
|
|
|
|
let encoded = serde_json::to_vec(&msg).unwrap();
|
|
let decoded: BusMessage = serde_json::from_slice(&encoded).unwrap();
|
|
|
|
assert_eq!(decoded, msg);
|
|
}
|
|
|
|
#[test]
|
|
fn test_bus_message_socket_disconnect() {
|
|
let msg = BusMessage::SocketDisconnect {
|
|
namespace: "/".to_string(),
|
|
sid: "sid-1".to_string(),
|
|
server_id: "server-1".to_string(),
|
|
};
|
|
|
|
let encoded = serde_json::to_vec(&msg).unwrap();
|
|
let decoded: BusMessage = serde_json::from_slice(&encoded).unwrap();
|
|
|
|
assert_eq!(decoded, msg);
|
|
}
|
|
|
|
#[test]
|
|
fn test_broadcast_options_serialization() {
|
|
let opts = BroadcastOptions {
|
|
rooms: HashSet::from(["room1".to_string(), "room2".to_string()]),
|
|
except: HashSet::from(["sid1".to_string()]),
|
|
flags: BroadcastFlags {
|
|
local_only: true,
|
|
broadcast: false,
|
|
},
|
|
};
|
|
|
|
let encoded = serde_json::to_vec(&opts).unwrap();
|
|
let decoded: BroadcastOptions = serde_json::from_slice(&encoded).unwrap();
|
|
|
|
assert_eq!(decoded.rooms, opts.rooms);
|
|
assert_eq!(decoded.except, opts.except);
|
|
assert_eq!(decoded.flags.local_only, opts.flags.local_only);
|
|
}
|
|
|
|
#[test]
|
|
fn test_socket_info_serialization() {
|
|
let info = SocketInfo {
|
|
sid: "sid-1".to_string(),
|
|
namespace: "/admin".to_string(),
|
|
rooms: HashSet::from(["room1".to_string()]),
|
|
};
|
|
|
|
let encoded = serde_json::to_vec(&info).unwrap();
|
|
let decoded: SocketInfo = serde_json::from_slice(&encoded).unwrap();
|
|
|
|
assert_eq!(decoded.sid, info.sid);
|
|
assert_eq!(decoded.namespace, info.namespace);
|
|
}
|
|
|
|
#[test]
|
|
fn test_session_info_serialization() {
|
|
let info = SessionInfo {
|
|
sid: "sid-1".to_string(),
|
|
transport: "websocket".to_string(),
|
|
state: "open".to_string(),
|
|
server_id: "server-1".to_string(),
|
|
created_at: 1234567890,
|
|
last_ping: 1234567900,
|
|
};
|
|
|
|
let encoded = serde_json::to_vec(&info).unwrap();
|
|
let decoded: SessionInfo = serde_json::from_slice(&encoded).unwrap();
|
|
|
|
assert_eq!(decoded.sid, info.sid);
|
|
assert_eq!(decoded.transport, info.transport);
|
|
assert_eq!(decoded.state, info.state);
|
|
}
|
|
|
|
#[test]
|
|
fn test_message_bus_error_display() {
|
|
let err = imks::socket::message_bus::MessageBusError::Redis("connection refused".to_string());
|
|
assert_eq!(format!("{}", err), "Redis error: connection refused");
|
|
|
|
let err = imks::socket::message_bus::MessageBusError::Nats("timeout".to_string());
|
|
assert_eq!(format!("{}", err), "NATS error: timeout");
|
|
}
|
|
|
|
#[test]
|
|
fn test_adapter_error_display() {
|
|
let err = AdapterError::Redis("SADD failed".to_string());
|
|
assert_eq!(format!("{}", err), "Redis error: SADD failed");
|
|
|
|
let err = AdapterError::Nats("publish failed".to_string());
|
|
assert_eq!(format!("{}", err), "NATS error: publish failed");
|
|
|
|
let err = AdapterError::Serialization("json error".to_string());
|
|
assert_eq!(format!("{}", err), "Serialization error: json error");
|
|
}
|
|
|
|
#[test]
|
|
fn test_session_error_display() {
|
|
let err = imks::socket::session_store::SessionError::Redis("timeout".to_string());
|
|
assert_eq!(format!("{}", err), "Redis error: timeout");
|
|
|
|
let err = imks::socket::session_store::SessionError::NotFound("sid-1".to_string());
|
|
assert_eq!(format!("{}", err), "Session not found: sid-1");
|
|
}
|
|
|
|
#[test]
|
|
fn test_is_valid_namespace() {
|
|
assert!(imks::socket::namespace::is_valid_namespace("/"));
|
|
assert!(imks::socket::namespace::is_valid_namespace("/admin"));
|
|
assert!(imks::socket::namespace::is_valid_namespace("/chat/room1"));
|
|
|
|
assert!(!imks::socket::namespace::is_valid_namespace(""));
|
|
assert!(!imks::socket::namespace::is_valid_namespace("admin"));
|
|
assert!(!imks::socket::namespace::is_valid_namespace(
|
|
&"/".repeat(257)
|
|
));
|
|
}
|