#[cfg(test)]
mod tests {
use crate::mqtt_serde::control_packet::{MqttControlPacket, MqttPacket};
use crate::mqtt_serde::mqttv5::auth::MqttAuth;
use crate::mqtt_serde::mqttv5::common::properties::Property;
use crate::mqtt_serde::mqttv5::disconnect::MqttDisconnect;
use crate::mqtt_serde::mqttv5::pingreq::MqttPingReq;
use crate::mqtt_serde::mqttv5::pingresp::MqttPingResp;
use crate::mqtt_serde::mqttv5::suback::MqttSubAck;
use crate::mqtt_serde::mqttv5::subscribe::{MqttSubscribe, TopicSubscription};
use crate::mqtt_serde::mqttv5::unsuback::MqttUnsubAck;
use crate::mqtt_serde::mqttv5::unsubscribe::MqttUnsubscribe;
use crate::mqtt_serde::parser::ParseOk;
#[test]
fn test_subscription_workflow_integration() {
let subscriptions = vec![
TopicSubscription::new("home/temperature".to_string(), 1, false, false, 0),
TopicSubscription::new("home/humidity".to_string(), 2, true, false, 1),
];
let subscribe = MqttSubscribe::new_simple(1234, subscriptions);
let subscribe_bytes = subscribe.to_bytes().unwrap();
match MqttPacket::from_bytes_v5(&subscribe_bytes).unwrap() {
ParseOk::Packet(MqttPacket::Subscribe5(parsed_subscribe), _) => {
assert_eq!(parsed_subscribe.packet_id, 1234);
assert_eq!(parsed_subscribe.subscriptions.len(), 2);
let reason_codes = vec![0x01, 0x02]; let suback = MqttSubAck::new_simple(parsed_subscribe.packet_id, reason_codes);
let suback_bytes = suback.to_bytes().unwrap();
match MqttPacket::from_bytes_v5(&suback_bytes).unwrap() {
ParseOk::Packet(MqttPacket::SubAck5(parsed_suback), _) => {
assert_eq!(parsed_suback.packet_id, 1234);
assert_eq!(parsed_suback.reason_codes, vec![0x01, 0x02]);
}
_ => panic!("Expected SUBACK packet"),
}
}
_ => panic!("Expected SUBSCRIBE packet"),
}
}
#[test]
fn test_unsubscription_workflow_integration() {
let topic_filters = vec!["home/temperature".to_string(), "home/humidity".to_string()];
let unsubscribe = MqttUnsubscribe::new_simple(5678, topic_filters);
let unsubscribe_bytes = unsubscribe.to_bytes().unwrap();
match MqttPacket::from_bytes_v5(&unsubscribe_bytes).unwrap() {
ParseOk::Packet(MqttPacket::Unsubscribe5(parsed_unsubscribe), _) => {
assert_eq!(parsed_unsubscribe.packet_id, 5678);
assert_eq!(parsed_unsubscribe.topic_filters.len(), 2);
let reason_codes = vec![0x00, 0x00]; let unsuback = MqttUnsubAck::new_simple(parsed_unsubscribe.packet_id, reason_codes);
let unsuback_bytes = unsuback.to_bytes().unwrap();
match MqttPacket::from_bytes_v5(&unsuback_bytes).unwrap() {
ParseOk::Packet(MqttPacket::UnsubAck5(parsed_unsuback), _) => {
assert_eq!(parsed_unsuback.packet_id, 5678);
assert_eq!(parsed_unsuback.reason_codes, vec![0x00, 0x00]);
}
_ => panic!("Expected UNSUBACK packet"),
}
}
_ => panic!("Expected UNSUBSCRIBE packet"),
}
}
#[test]
fn test_all_new_packet_types_through_control_system() {
let subscribe = MqttSubscribe::new_simple(
1,
vec![TopicSubscription::new_simple("test".to_string(), 1)],
);
let subscribe_bytes = subscribe.to_bytes().unwrap();
assert!(matches!(
MqttPacket::from_bytes_v5(&subscribe_bytes).unwrap(),
ParseOk::Packet(MqttPacket::Subscribe5(_), _)
));
let suback = MqttSubAck::new_simple(2, vec![0x01]);
let suback_bytes = suback.to_bytes().unwrap();
assert!(matches!(
MqttPacket::from_bytes_v5(&suback_bytes).unwrap(),
ParseOk::Packet(MqttPacket::SubAck5(_), _)
));
let unsubscribe = MqttUnsubscribe::new_simple(3, vec!["test".to_string()]);
let unsubscribe_bytes = unsubscribe.to_bytes().unwrap();
assert!(matches!(
MqttPacket::from_bytes_v5(&unsubscribe_bytes).unwrap(),
ParseOk::Packet(MqttPacket::Unsubscribe5(_), _)
));
let unsuback = MqttUnsubAck::new_simple(4, vec![0x00]);
let unsuback_bytes = unsuback.to_bytes().unwrap();
assert!(matches!(
MqttPacket::from_bytes_v5(&unsuback_bytes).unwrap(),
ParseOk::Packet(MqttPacket::UnsubAck5(_), _)
));
let pingreq = MqttPingReq::new();
let pingreq_bytes = pingreq.to_bytes().unwrap();
assert!(matches!(
MqttPacket::from_bytes_v5(&pingreq_bytes).unwrap(),
ParseOk::Packet(MqttPacket::PingReq5(_), _)
));
let pingresp = MqttPingResp::new();
let pingresp_bytes = pingresp.to_bytes().unwrap();
assert!(matches!(
MqttPacket::from_bytes_v5(&pingresp_bytes).unwrap(),
ParseOk::Packet(MqttPacket::PingResp5(_), _)
));
let disconnect = MqttDisconnect::new_normal();
let disconnect_bytes = disconnect.to_bytes().unwrap();
assert!(matches!(
MqttPacket::from_bytes_v5(&disconnect_bytes).unwrap(),
ParseOk::Packet(MqttPacket::Disconnect5(_), _)
));
let auth = MqttAuth::new_continue_authentication();
let auth_bytes = auth.to_bytes().unwrap();
assert!(matches!(
MqttPacket::from_bytes_v5(&auth_bytes).unwrap(),
ParseOk::Packet(MqttPacket::Auth(_), _)
));
}
#[test]
fn test_ping_workflow_integration() {
let pingreq = MqttPingReq::new();
let pingreq_bytes = pingreq.to_bytes().unwrap();
match MqttPacket::from_bytes_v5(&pingreq_bytes).unwrap() {
ParseOk::Packet(MqttPacket::PingReq5(_parsed_pingreq), _) => {
let pingresp = MqttPingResp::new();
let pingresp_bytes = pingresp.to_bytes().unwrap();
match MqttPacket::from_bytes_v5(&pingresp_bytes).unwrap() {
ParseOk::Packet(MqttPacket::PingResp5(_parsed_pingresp), _) => {
assert_eq!(pingreq_bytes, vec![0xC0, 0x00]);
assert_eq!(pingresp_bytes, vec![0xD0, 0x00]);
}
_ => panic!("Expected PINGRESP packet"),
}
}
_ => panic!("Expected PINGREQ packet"),
}
}
#[test]
fn test_disconnect_workflow_integration() {
let disconnect_normal = MqttDisconnect::new_normal();
let disconnect_bytes = disconnect_normal.to_bytes().unwrap();
match MqttPacket::from_bytes_v5(&disconnect_bytes).unwrap() {
ParseOk::Packet(MqttPacket::Disconnect5(parsed_disconnect), _) => {
assert_eq!(parsed_disconnect.reason_code, 0x00);
assert!(parsed_disconnect.properties.is_empty());
assert_eq!(disconnect_bytes, vec![0xE0, 0x00]); }
_ => panic!("Expected DISCONNECT packet"),
}
let disconnect_error = MqttDisconnect::new_protocol_error();
let disconnect_error_bytes = disconnect_error.to_bytes().unwrap();
match MqttPacket::from_bytes_v5(&disconnect_error_bytes).unwrap() {
ParseOk::Packet(MqttPacket::Disconnect5(parsed_disconnect), _) => {
assert_eq!(parsed_disconnect.reason_code, 0x82);
assert!(parsed_disconnect.properties.is_empty());
assert_eq!(disconnect_error_bytes, vec![0xE0, 0x02, 0x82, 0x00]);
}
_ => panic!("Expected DISCONNECT packet"),
}
}
#[test]
fn test_auth_workflow_integration() {
let auth_method = "SCRAM-SHA-256".to_string();
let client_first_data = b"client-first-message".to_vec();
let auth_continue =
MqttAuth::new_continue_with_data(auth_method.clone(), client_first_data.clone());
let auth_bytes = auth_continue.to_bytes().unwrap();
match MqttPacket::from_bytes_v5(&auth_bytes).unwrap() {
ParseOk::Packet(MqttPacket::Auth(parsed_auth), _) => {
assert_eq!(parsed_auth.reason_code, 0x18); assert_eq!(parsed_auth.properties.len(), 2);
let has_method = parsed_auth
.properties
.iter()
.any(|p| matches!(p, Property::AuthenticationMethod(m) if m == &auth_method));
let has_data = parsed_auth.properties.iter().any(
|p| matches!(p, Property::AuthenticationData(d) if d == &client_first_data),
);
assert!(
has_method,
"AUTH packet should contain authentication method"
);
assert!(has_data, "AUTH packet should contain authentication data");
}
_ => panic!("Expected AUTH packet"),
}
let server_challenge = b"server-challenge-data".to_vec();
let server_auth =
MqttAuth::new_continue_with_data(auth_method.clone(), server_challenge.clone());
let server_auth_bytes = server_auth.to_bytes().unwrap();
match MqttPacket::from_bytes_v5(&server_auth_bytes).unwrap() {
ParseOk::Packet(MqttPacket::Auth(parsed_server_auth), _) => {
assert_eq!(parsed_server_auth.reason_code, 0x18);
let has_challenge = parsed_server_auth.properties.iter().any(
|p| matches!(p, Property::AuthenticationData(d) if d == &server_challenge),
);
assert!(has_challenge, "Server AUTH should contain challenge data");
}
_ => panic!("Expected AUTH packet from server"),
}
let auth_success = MqttAuth::new_success();
let success_bytes = auth_success.to_bytes().unwrap();
match MqttPacket::from_bytes_v5(&success_bytes).unwrap() {
ParseOk::Packet(MqttPacket::Auth(parsed_success), _) => {
assert_eq!(parsed_success.reason_code, 0x00); assert_eq!(success_bytes, vec![0xF0, 0x02, 0x00, 0x00]); }
_ => panic!("Expected AUTH success packet"),
}
}
}