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:
@@ -36,3 +36,6 @@ ndk-context = "0.1"
|
||||
|
||||
[lints.rust]
|
||||
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(frb_expand)'] }
|
||||
|
||||
[dev-dependencies]
|
||||
serde_json = "1"
|
||||
|
||||
@@ -51,7 +51,7 @@ use thiserror::Error;
|
||||
/// Errors raised at the bridge boundary. Production code must keep
|
||||
/// these user-safe — no secrets, no protocol details, no path
|
||||
/// information beyond what the redaction policy permits.
|
||||
#[derive(Debug, Error, Clone, serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Debug, Error, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub enum BridgeError {
|
||||
/// The caller submitted a malformed command DTO.
|
||||
#[error("invalid command: {0}")]
|
||||
@@ -125,3 +125,308 @@ impl From<chanora_core::CoreError> for BridgeError {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[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 bridge_error_invalid_command() {
|
||||
let err = BridgeError::InvalidCommand("bad".to_string());
|
||||
assert_eq!(err.to_string(), "invalid command: bad");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bridge_error_dns_failed() {
|
||||
let err = BridgeError::DnsFailed {
|
||||
host: "example.com".to_string(),
|
||||
reason: "timeout".to_string(),
|
||||
};
|
||||
let msg = err.to_string();
|
||||
assert!(msg.contains("example.com"));
|
||||
assert!(msg.contains("timeout"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bridge_error_connection() {
|
||||
let err = BridgeError::Connection("refused".to_string());
|
||||
assert_eq!(err.to_string(), "connection: refused");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bridge_error_not_connected() {
|
||||
let err = BridgeError::NotConnected;
|
||||
assert_eq!(err.to_string(), "not connected");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bridge_error_already_connected() {
|
||||
let err = BridgeError::AlreadyConnected;
|
||||
assert_eq!(err.to_string(), "already connected");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bridge_error_server_rejected() {
|
||||
let err = BridgeError::ServerRejected {
|
||||
code: 2568,
|
||||
message: "insufficient permissions".to_string(),
|
||||
};
|
||||
let msg = err.to_string();
|
||||
assert!(msg.contains("2568"));
|
||||
assert!(msg.contains("insufficient permissions"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bridge_error_unmapped() {
|
||||
let err = BridgeError::Unmapped("mystery".to_string());
|
||||
assert_eq!(err.to_string(), "unmapped: mystery");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bridge_error_serde_roundtrip() {
|
||||
roundtrip_json(&BridgeError::InvalidCommand("test".to_string()));
|
||||
roundtrip_json(&BridgeError::NotConnected);
|
||||
roundtrip_json(&BridgeError::AlreadyConnected);
|
||||
roundtrip_json(&BridgeError::Connection("fail".to_string()));
|
||||
roundtrip_json(&BridgeError::Unmapped("x".to_string()));
|
||||
roundtrip_json(&BridgeError::DnsFailed {
|
||||
host: "h".to_string(),
|
||||
reason: "r".to_string(),
|
||||
});
|
||||
roundtrip_json(&BridgeError::ServerRejected {
|
||||
code: 42,
|
||||
message: "nope".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bridge_error_clone_preserves() {
|
||||
let err = BridgeError::InvalidCommand("orig".to_string());
|
||||
let cloned = err.clone();
|
||||
assert_eq!(cloned.to_string(), err.to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_core_error_not_connected() {
|
||||
let core_err = chanora_core::CoreError::NotConnected;
|
||||
let bridge_err: BridgeError = core_err.into();
|
||||
assert!(matches!(bridge_err, BridgeError::NotConnected));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_core_error_already_connected() {
|
||||
let core_err = chanora_core::CoreError::AlreadyConnected;
|
||||
let bridge_err: BridgeError = core_err.into();
|
||||
assert!(matches!(bridge_err, BridgeError::AlreadyConnected));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_core_error_audio_not_started() {
|
||||
let core_err = chanora_core::CoreError::AudioNotStarted;
|
||||
let bridge_err: BridgeError = core_err.into();
|
||||
match bridge_err {
|
||||
BridgeError::InvalidCommand(msg) => {
|
||||
assert!(msg.contains("audio not started"));
|
||||
}
|
||||
other => panic!("expected InvalidCommand, got {other:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_core_error_protocol_dns_failed() {
|
||||
let core_err = chanora_core::CoreError::Protocol(
|
||||
chanora_core::ProtocolError::DnsFailed {
|
||||
host: "bad.host".to_string(),
|
||||
reason: "no address".to_string(),
|
||||
},
|
||||
);
|
||||
let bridge_err: BridgeError = core_err.into();
|
||||
match bridge_err {
|
||||
BridgeError::DnsFailed { host, reason } => {
|
||||
assert_eq!(host, "bad.host");
|
||||
assert_eq!(reason, "no address");
|
||||
}
|
||||
other => panic!("expected DnsFailed, got {other:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_core_error_protocol_server_rejected() {
|
||||
let core_err = chanora_core::CoreError::Protocol(
|
||||
chanora_core::ProtocolError::ServerRejected {
|
||||
code: 0x0501,
|
||||
message: "channel password wrong".to_string(),
|
||||
},
|
||||
);
|
||||
let bridge_err: BridgeError = core_err.into();
|
||||
match bridge_err {
|
||||
BridgeError::ServerRejected { code, message } => {
|
||||
assert_eq!(code, 0x0501);
|
||||
assert_eq!(message, "channel password wrong");
|
||||
}
|
||||
other => panic!("expected ServerRejected, got {other:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_core_error_protocol_file_transfer() {
|
||||
let core_err = chanora_core::CoreError::Protocol(
|
||||
chanora_core::ProtocolError::FileTransfer("disk full".to_string()),
|
||||
);
|
||||
let bridge_err: BridgeError = core_err.into();
|
||||
match bridge_err {
|
||||
BridgeError::Connection(msg) => {
|
||||
assert!(msg.contains("file transfer"));
|
||||
assert!(msg.contains("disk full"));
|
||||
}
|
||||
other => panic!("expected Connection, got {other:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_core_error_protocol_generic() {
|
||||
let core_err = chanora_core::CoreError::Protocol(
|
||||
chanora_core::ProtocolError::Connect("refused".to_string()),
|
||||
);
|
||||
let bridge_err: BridgeError = core_err.into();
|
||||
match bridge_err {
|
||||
BridgeError::Connection(msg) => {
|
||||
assert!(msg.contains("refused"));
|
||||
}
|
||||
other => panic!("expected Connection, got {other:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_core_error_protocol_lost() {
|
||||
let core_err = chanora_core::CoreError::Protocol(
|
||||
chanora_core::ProtocolError::Lost("timeout".to_string()),
|
||||
);
|
||||
let bridge_err: BridgeError = core_err.into();
|
||||
match bridge_err {
|
||||
BridgeError::Connection(msg) => {
|
||||
assert!(msg.contains("timeout"));
|
||||
}
|
||||
other => panic!("expected Connection, got {other:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_core_error_protocol_invalid() {
|
||||
let core_err = chanora_core::CoreError::Protocol(
|
||||
chanora_core::ProtocolError::Invalid("bad config".to_string()),
|
||||
);
|
||||
let bridge_err: BridgeError = core_err.into();
|
||||
match bridge_err {
|
||||
BridgeError::Connection(msg) => {
|
||||
assert!(msg.contains("bad config"));
|
||||
}
|
||||
other => panic!("expected Connection, got {other:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_core_error_protocol_disconnected_early() {
|
||||
let core_err = chanora_core::CoreError::Protocol(
|
||||
chanora_core::ProtocolError::DisconnectedEarly("premature".to_string()),
|
||||
);
|
||||
let bridge_err: BridgeError = core_err.into();
|
||||
match bridge_err {
|
||||
BridgeError::Connection(msg) => {
|
||||
assert!(msg.contains("premature"));
|
||||
}
|
||||
other => panic!("expected Connection, got {other:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_core_error_protocol_identity() {
|
||||
let core_err = chanora_core::CoreError::Protocol(
|
||||
chanora_core::ProtocolError::Identity("parse error".to_string()),
|
||||
);
|
||||
let bridge_err: BridgeError = core_err.into();
|
||||
match bridge_err {
|
||||
BridgeError::Connection(msg) => {
|
||||
assert!(msg.contains("parse error"));
|
||||
}
|
||||
other => panic!("expected Connection, got {other:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_core_error_protocol_timeout() {
|
||||
let core_err =
|
||||
chanora_core::CoreError::Protocol(chanora_core::ProtocolError::Timeout);
|
||||
let bridge_err: BridgeError = core_err.into();
|
||||
match bridge_err {
|
||||
BridgeError::Connection(msg) => {
|
||||
assert!(msg.contains("timeout"));
|
||||
}
|
||||
other => panic!("expected Connection, got {other:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_core_error_protocol_backend() {
|
||||
let core_err = chanora_core::CoreError::Protocol(
|
||||
chanora_core::ProtocolError::Backend("raw".to_string()),
|
||||
);
|
||||
let bridge_err: BridgeError = core_err.into();
|
||||
match bridge_err {
|
||||
BridgeError::Connection(msg) => {
|
||||
assert!(msg.contains("raw"));
|
||||
}
|
||||
other => panic!("expected Connection, got {other:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_core_error_invariant() {
|
||||
let core_err = chanora_core::CoreError::Invariant("broken");
|
||||
let bridge_err: BridgeError = core_err.into();
|
||||
match bridge_err {
|
||||
BridgeError::Unmapped(msg) => {
|
||||
assert!(msg.contains("broken"));
|
||||
}
|
||||
other => panic!("expected Unmapped, got {other:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_surfaces_all_protocol_error_variants() {
|
||||
let protocol_errors: Vec<chanora_core::ProtocolError> = vec![
|
||||
chanora_core::ProtocolError::Invalid("x".into()),
|
||||
chanora_core::ProtocolError::DnsFailed {
|
||||
host: "h".into(),
|
||||
reason: "r".into(),
|
||||
},
|
||||
chanora_core::ProtocolError::Connect("c".into()),
|
||||
chanora_core::ProtocolError::DisconnectedEarly("d".into()),
|
||||
chanora_core::ProtocolError::Lost("l".into()),
|
||||
chanora_core::ProtocolError::Identity("i".into()),
|
||||
chanora_core::ProtocolError::Timeout,
|
||||
chanora_core::ProtocolError::ServerRejected {
|
||||
code: 1,
|
||||
message: "m".into(),
|
||||
},
|
||||
chanora_core::ProtocolError::Backend("b".into()),
|
||||
chanora_core::ProtocolError::FileTransfer("f".into()),
|
||||
];
|
||||
for p_err in protocol_errors {
|
||||
let core_err = chanora_core::CoreError::Protocol(p_err);
|
||||
let bridge_err: BridgeError = core_err.into();
|
||||
let msg = bridge_err.to_string();
|
||||
assert!(!msg.is_empty(), "BridgeError message must not be empty");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,3 +42,6 @@ reqwest = { version = "0.13", default-features = false, features = ["charset", "
|
||||
# Android cross-builds should not pull OpenSSL. Use rustls here while keeping
|
||||
# native-tls for Apple targets where aws-lc/rustls is problematic for iOS.
|
||||
reqwest = { version = "0.13", default-features = false, features = ["charset", "http2", "rustls"] }
|
||||
|
||||
[dev-dependencies]
|
||||
serde_json = "1"
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user