test(protocol,core,bridge): add 83 unit tests for untested modules (TODO-015,018,019,021)

Protocol DTO: 32 serde roundtrip + edge case tests.
Core events: 27 event construction + variant coverage tests.
Bridge: 24 error mapping + DTO roundtrip tests.
Add serde_json dev-dependency to protocol and bridge crates.
This commit is contained in:
Edison Jwa
2026-06-11 11:05:11 +09:00
parent 2ab8d5aae2
commit e14dd73570
6 changed files with 1252 additions and 3 deletions
+433
View File
@@ -262,3 +262,436 @@ pub enum ProtocolDelta {
needed_talk_power: Option<i32>,
},
}
#[cfg(test)]
mod tests {
use super::*;
fn roundtrip_json<T: serde::Serialize + serde::de::DeserializeOwned + PartialEq + std::fmt::Debug>(
value: &T,
) {
let json = serde_json::to_string(value).expect("serialize");
let back: T = serde_json::from_str(&json).expect("deserialize");
assert_eq!(&back, value, "roundtrip failed");
}
#[test]
fn channel_id_serde_roundtrip() {
roundtrip_json(&ChannelId(0));
roundtrip_json(&ChannelId(1));
roundtrip_json(&ChannelId(u64::MAX));
}
#[test]
fn client_id_serde_roundtrip() {
roundtrip_json(&ClientId(0));
roundtrip_json(&ClientId(42));
roundtrip_json(&ClientId(u64::MAX));
}
#[test]
fn channel_id_root_is_zero() {
assert_eq!(ChannelId::ROOT, ChannelId(0));
}
#[test]
fn channel_info_serde_roundtrip() {
let info = ChannelInfo {
id: ChannelId(1),
parent: ChannelId(0),
name: "General".to_string(),
order: 0,
has_password: false,
needed_talk_power: None,
};
roundtrip_json(&info);
}
#[test]
fn channel_info_with_all_fields() {
let info = ChannelInfo {
id: ChannelId(99),
parent: ChannelId(5),
name: "AFK".to_string(),
order: -1,
has_password: true,
needed_talk_power: Some(75),
};
roundtrip_json(&info);
}
#[test]
fn channel_info_empty_name() {
let info = ChannelInfo {
id: ChannelId(1),
parent: ChannelId(0),
name: String::new(),
order: 0,
has_password: false,
needed_talk_power: None,
};
roundtrip_json(&info);
}
#[test]
fn channel_info_unicode_name() {
let info = ChannelInfo {
id: ChannelId(1),
parent: ChannelId(0),
name: "🎮 Spielsaal 🎮".to_string(),
order: 0,
has_password: false,
needed_talk_power: None,
};
roundtrip_json(&info);
}
#[test]
fn message_target_serde_roundtrip() {
roundtrip_json(&MessageTarget::Server);
roundtrip_json(&MessageTarget::Channel);
roundtrip_json(&MessageTarget::Client(12345));
roundtrip_json(&MessageTarget::Poke(67890));
}
#[test]
fn message_target_json_shape() {
let json = serde_json::to_string(&MessageTarget::Server).unwrap();
assert_eq!(json, "\"Server\"");
let json = serde_json::to_string(&MessageTarget::Client(42)).unwrap();
assert!(json.contains("\"Client\""));
assert!(json.contains("42"));
}
#[test]
fn chat_message_serde_roundtrip() {
let msg = ChatMessage {
sender_id: ClientId(1),
sender_name: "Alice".to_string(),
message: "Hello world".to_string(),
target: MessageTarget::Channel,
poke_strength: None,
};
roundtrip_json(&msg);
}
#[test]
fn chat_message_with_poke_strength() {
let msg = ChatMessage {
sender_id: ClientId(5),
sender_name: "Bob".to_string(),
message: "".to_string(),
target: MessageTarget::Poke(99),
poke_strength: Some(PokeStrength::Strong),
};
roundtrip_json(&msg);
}
#[test]
fn chat_message_unicode_content() {
let msg = ChatMessage {
sender_id: ClientId(1),
sender_name: "日本語ネーム".to_string(),
message: "🎉 こんにちは世界 🌍".to_string(),
target: MessageTarget::Server,
poke_strength: None,
};
roundtrip_json(&msg);
}
#[test]
fn server_activity_serde_roundtrip() {
let act = ServerActivity {
message: "User joined".to_string(),
};
roundtrip_json(&act);
}
#[test]
fn server_activity_empty_message() {
let act = ServerActivity {
message: String::new(),
};
roundtrip_json(&act);
}
#[test]
fn client_info_serde_roundtrip() {
let info = ClientInfo {
id: ClientId(1),
channel: ChannelId(2),
name: "Player".to_string(),
input_muted: false,
output_muted: true,
is_speaking: false,
is_server_query: false,
talk_power: 0,
talk_power_granted: false,
};
roundtrip_json(&info);
}
#[test]
fn client_info_server_query_with_talk_power() {
let info = ClientInfo {
id: ClientId(100),
channel: ChannelId(3),
name: "Bot".to_string(),
input_muted: true,
output_muted: true,
is_speaking: false,
is_server_query: true,
talk_power: 75,
talk_power_granted: true,
};
roundtrip_json(&info);
}
#[test]
fn server_snapshot_serde_roundtrip() {
let snap = ServerSnapshot {
server_name: "Test Server".to_string(),
welcome_message: "Welcome!".to_string(),
platform: "Linux".to_string(),
version: "3.13.7".to_string(),
channels: vec![
ChannelInfo {
id: ChannelId(1),
parent: ChannelId(0),
name: "Root".to_string(),
order: 0,
has_password: false,
needed_talk_power: None,
},
],
clients: vec![
ClientInfo {
id: ClientId(1),
channel: ChannelId(1),
name: "User1".to_string(),
input_muted: false,
output_muted: false,
is_speaking: false,
is_server_query: false,
talk_power: 0,
talk_power_granted: false,
},
],
own_client_id: 1,
};
roundtrip_json(&snap);
}
#[test]
fn server_snapshot_empty_channels_and_clients() {
let snap = ServerSnapshot {
server_name: String::new(),
welcome_message: String::new(),
platform: String::new(),
version: String::new(),
channels: vec![],
clients: vec![],
own_client_id: 0,
};
roundtrip_json(&snap);
}
#[test]
fn client_profile_serde_roundtrip() {
let profile = ClientProfile {
id: ClientId(1),
channel: ChannelId(2),
name: "Player".to_string(),
unique_id: "abc123".to_string(),
database_id: Some(42),
country_code: "DE".to_string(),
description: String::new(),
version: "3.5.0".to_string(),
platform: "Windows".to_string(),
created_unix_seconds: Some(1609459200),
last_connected_unix_seconds: Some(1700000000),
connections_total: Some(100),
online_seconds: Some(3600),
idle_milliseconds: Some(500),
ping_milliseconds: Some(42),
ping_deviation_milliseconds: Some(5),
client_address: String::new(),
server_groups: vec!["Admin".to_string(), "Mod".to_string()],
channel_group: "Channel Admin".to_string(),
avatar_path: String::new(),
bytes_downloaded_month: Some(1024),
bytes_uploaded_month: Some(512),
bytes_downloaded_total: Some(4096),
bytes_uploaded_total: Some(2048),
packet_loss_client_to_server_total: Some(0.01),
packet_loss_server_to_client_total: Some(0.02),
};
roundtrip_json(&profile);
}
#[test]
fn client_profile_minimal_fields() {
let profile = ClientProfile {
id: ClientId(1),
channel: ChannelId(0),
name: String::new(),
unique_id: String::new(),
database_id: None,
country_code: String::new(),
description: String::new(),
version: String::new(),
platform: String::new(),
created_unix_seconds: None,
last_connected_unix_seconds: None,
connections_total: None,
online_seconds: None,
idle_milliseconds: None,
ping_milliseconds: None,
ping_deviation_milliseconds: None,
client_address: String::new(),
server_groups: vec![],
channel_group: String::new(),
avatar_path: String::new(),
bytes_downloaded_month: None,
bytes_uploaded_month: None,
bytes_downloaded_total: None,
bytes_uploaded_total: None,
packet_loss_client_to_server_total: None,
packet_loss_server_to_client_total: None,
};
roundtrip_json(&profile);
}
#[test]
fn protocol_delta_client_moved() {
let delta = ProtocolDelta::ClientMoved {
client_id: 1,
new_channel_id: 2,
};
roundtrip_json(&delta);
}
#[test]
fn protocol_delta_client_joined() {
let delta = ProtocolDelta::ClientJoined {
client_id: 5,
channel_id: 3,
name: "NewUser".to_string(),
input_muted: false,
output_muted: false,
is_server_query: false,
talk_power: 0,
talk_power_granted: false,
};
roundtrip_json(&delta);
}
#[test]
fn protocol_delta_client_left() {
let delta = ProtocolDelta::ClientLeft {
client_id: 5,
name: "Departing".to_string(),
};
roundtrip_json(&delta);
}
#[test]
fn protocol_delta_client_updated() {
let delta = ProtocolDelta::ClientUpdated {
client_id: 10,
input_muted: true,
output_muted: false,
is_server_query: false,
talk_power: 50,
talk_power_granted: true,
};
roundtrip_json(&delta);
}
#[test]
fn protocol_delta_channel_added() {
let delta = ProtocolDelta::ChannelAdded {
id: 7,
parent: 1,
name: "New Channel".to_string(),
order: 5,
has_password: true,
needed_talk_power: Some(25),
};
roundtrip_json(&delta);
}
#[test]
fn protocol_delta_channel_removed() {
let delta = ProtocolDelta::ChannelRemoved { id: 7 };
roundtrip_json(&delta);
}
#[test]
fn protocol_delta_channel_updated() {
let delta = ProtocolDelta::ChannelUpdated {
id: 7,
name: "Renamed".to_string(),
has_password: false,
needed_talk_power: None,
};
roundtrip_json(&delta);
}
#[test]
fn protocol_delta_unicode_names() {
let delta = ProtocolDelta::ClientJoined {
client_id: 1,
channel_id: 1,
name: "ユーザー".to_string(),
input_muted: false,
output_muted: false,
is_server_query: false,
talk_power: 0,
talk_power_granted: false,
};
roundtrip_json(&delta);
}
#[test]
fn protocol_delta_boundary_values() {
let delta = ProtocolDelta::ChannelAdded {
id: u64::MAX,
parent: u64::MAX,
name: String::new(),
order: i64::MIN,
has_password: true,
needed_talk_power: Some(i32::MAX),
};
roundtrip_json(&delta);
}
#[test]
fn poke_strength_serde_roundtrip() {
roundtrip_json(&PokeStrength::Strong);
roundtrip_json(&PokeStrength::Suppressed);
roundtrip_json(&PokeStrength::SuppressedOverflow);
}
#[test]
fn client_info_equality_and_clone() {
let a = ClientId(42);
let b = a;
assert_eq!(a, b);
let c = ClientId(42);
assert_eq!(a, c);
let d = ClientId(43);
assert_ne!(a, d);
}
#[test]
fn channel_id_hash_consistency() {
use std::collections::HashSet;
let mut set = HashSet::new();
set.insert(ChannelId(1));
set.insert(ChannelId(1));
set.insert(ChannelId(2));
assert_eq!(set.len(), 2);
}
}