1use std::collections::HashMap;
4use std::sync::Arc;
5use std::time::{Duration, Instant};
6use tokio::sync::RwLock;
7
8use crate::events::*;
9use crate::packets::{BinaryReqType, ControlType, PacketType};
10use crate::parsing::*;
11use crate::{Result, CHANNEL_INFO_LEN, CHANNEL_NAME_LEN, CHANNEL_SECRET_LEN};
12
13#[derive(Debug, Clone)]
15#[allow(dead_code)]
16struct BinaryRequest {
17 request_type: BinaryReqType,
19 pubkey_prefix: Vec<u8>,
21 expires_at: Instant,
23 context: HashMap<String, String>,
25 is_anon: bool,
27}
28
29pub struct MessageReader {
31 dispatcher: Arc<EventDispatcher>,
33 pending_requests: Arc<RwLock<HashMap<String, BinaryRequest>>>,
35 pending_contacts: Arc<RwLock<Vec<Contact>>>,
37 contacts_last_modification_timestamp: Arc<RwLock<u32>>,
39}
40
41impl MessageReader {
42 pub fn new(dispatcher: Arc<EventDispatcher>) -> Self {
44 Self {
45 dispatcher,
46 pending_requests: Arc::new(RwLock::new(HashMap::new())),
47 pending_contacts: Arc::new(RwLock::new(Vec::new())),
48 contacts_last_modification_timestamp: Arc::new(RwLock::new(0)),
49 }
50 }
51
52 pub async fn register_binary_request(
54 &self,
55 tag: &[u8],
56 request_type: BinaryReqType,
57 pubkey_prefix: Vec<u8>,
58 timeout: Duration,
59 context: HashMap<String, String>,
60 is_anon: bool,
61 ) {
62 let tag_hex = hex_encode(tag);
63 let request = BinaryRequest {
64 request_type,
65 pubkey_prefix,
66 expires_at: Instant::now() + timeout,
67 context,
68 is_anon,
69 };
70
71 self.pending_requests.write().await.insert(tag_hex, request);
72 }
73
74 async fn cleanup_expired(&self) {
76 let now = Instant::now();
77 self.pending_requests
78 .write()
79 .await
80 .retain(|_, req| req.expires_at > now);
81 }
82
83 pub async fn handle_rx(&self, data: Vec<u8>) -> Result<()> {
85 if data.is_empty() {
86 return Ok(());
87 }
88
89 self.cleanup_expired().await;
91
92 let packet_type = PacketType::from(data[0]);
93 let payload = if data.len() > 1 { &data[1..] } else { &[] };
94
95 match packet_type {
96 PacketType::Ok => {
97 self.dispatcher.emit(MeshCoreEvent::ok()).await;
98 }
99
100 PacketType::Error => {
101 let msg = if !payload.is_empty() {
102 String::from_utf8_lossy(payload).to_string()
103 } else {
104 "Unknown error".to_string()
105 };
106 self.dispatcher.emit(MeshCoreEvent::error(msg)).await;
107 }
108
109 PacketType::ContactStart => {
110 self.pending_contacts.write().await.clear();
112 }
113
114 PacketType::Contact | PacketType::PushCodeNewAdvert => {
115 if let Ok(contact) = parse_contact(payload) {
116 if packet_type == PacketType::PushCodeNewAdvert {
117 let event = MeshCoreEvent::new(
119 EventType::NewContact,
120 EventPayload::Contact(contact),
121 );
122 self.dispatcher.emit(event).await;
123 } else {
124 self.pending_contacts.write().await.push(contact);
126 }
127 }
128 }
129
130 PacketType::ContactEnd => {
131 let last_modification_timestamp = if payload.len() >= 4 {
133 read_u32_le(payload, 0).unwrap_or(0)
134 } else {
135 0
136 };
137 *self.contacts_last_modification_timestamp.write().await =
138 last_modification_timestamp;
139
140 let contacts = std::mem::take(&mut *self.pending_contacts.write().await);
142 let event =
143 MeshCoreEvent::new(EventType::Contacts, EventPayload::Contacts(contacts))
144 .with_attribute("lastmod", last_modification_timestamp.to_string());
145 self.dispatcher.emit(event).await;
146 }
147
148 PacketType::SelfInfo => {
149 if let Ok(info) = parse_self_info(payload) {
150 let event =
151 MeshCoreEvent::new(EventType::SelfInfo, EventPayload::SelfInfo(info));
152 self.dispatcher.emit(event).await;
153 }
154 }
155
156 PacketType::DeviceInfo => {
157 let device_info = parse_device_info(payload);
158 let event = MeshCoreEvent::new(
159 EventType::DeviceInfo,
160 EventPayload::DeviceInfo(device_info),
161 );
162 self.dispatcher.emit(event).await;
163 }
164
165 PacketType::Battery => {
166 if payload.len() >= 2 {
167 let battery_mv = read_u16_le(payload, 0).unwrap_or(0);
168 let (used_kb, total_kb) = if payload.len() >= 10 {
170 (
171 Some(read_u32_le(payload, 2).unwrap_or(0)),
172 Some(read_u32_le(payload, 6).unwrap_or(0)),
173 )
174 } else {
175 (None, None)
176 };
177 let event = MeshCoreEvent::new(
178 EventType::Battery,
179 EventPayload::Battery(BatteryInfo {
180 battery_mv,
181 used_kb,
182 total_kb,
183 }),
184 );
185 self.dispatcher.emit(event).await;
186 }
187 }
188
189 PacketType::CurrentTime => {
190 if payload.len() >= 4 {
191 let time = read_u32_le(payload, 0).unwrap_or(0);
192 let event =
193 MeshCoreEvent::new(EventType::CurrentTime, EventPayload::Time(time));
194 self.dispatcher.emit(event).await;
195 }
196 }
197
198 PacketType::MsgSent => {
199 if payload.len() >= 9 {
200 let message_type = payload[0];
201 let expected_ack: [u8; 4] = read_bytes(payload, 1).unwrap_or([0; 4]);
202 let suggested_timeout = read_u32_le(payload, 5).unwrap_or(5000);
203
204 let event = MeshCoreEvent::new(
205 EventType::MsgSent,
206 EventPayload::MsgSent(MsgSentInfo {
207 message_type,
208 expected_ack,
209 suggested_timeout,
210 }),
211 )
212 .with_attribute("tag", hex_encode(&expected_ack));
213 self.dispatcher.emit(event).await;
214 }
215 }
216
217 PacketType::ContactMsgRecv => {
218 if let Ok(msg) = parse_contact_msg(payload) {
219 let event = MeshCoreEvent::new(
220 EventType::ContactMsgRecv,
221 EventPayload::ContactMessage(msg),
222 );
223 self.dispatcher.emit(event).await;
224 }
225 }
226
227 PacketType::ContactMsgRecvV3 => {
228 if let Ok(msg) = parse_contact_msg_v3(payload) {
229 let event = MeshCoreEvent::new(
230 EventType::ContactMsgRecv,
231 EventPayload::ContactMessage(msg),
232 );
233 self.dispatcher.emit(event).await;
234 }
235 }
236
237 PacketType::ChannelMsgRecv => {
238 if let Ok(msg) = parse_channel_msg(payload) {
239 let event = MeshCoreEvent::new(
240 EventType::ChannelMsgRecv,
241 EventPayload::ChannelMessage(msg),
242 );
243 self.dispatcher.emit(event).await;
244 }
245 }
246
247 PacketType::ChannelMsgRecvV3 => {
248 if let Ok(msg) = parse_channel_msg_v3(payload) {
249 let event = MeshCoreEvent::new(
250 EventType::ChannelMsgRecv,
251 EventPayload::ChannelMessage(msg),
252 );
253 self.dispatcher.emit(event).await;
254 }
255 }
256
257 PacketType::NoMoreMsgs => {
258 let event = MeshCoreEvent::new(EventType::NoMoreMessages, EventPayload::None);
259 self.dispatcher.emit(event).await;
260 }
261
262 PacketType::ContactUri => {
263 let uri = String::from_utf8_lossy(payload).to_string();
264 let event = MeshCoreEvent::new(EventType::ContactUri, EventPayload::String(uri));
265 self.dispatcher.emit(event).await;
266 }
267
268 PacketType::PrivateKey => {
269 if payload.len() >= 64 {
270 let key: [u8; 64] = read_bytes(payload, 0).unwrap_or([0; 64]);
271 let event =
272 MeshCoreEvent::new(EventType::PrivateKey, EventPayload::PrivateKey(key));
273 self.dispatcher.emit(event).await;
274 }
275 }
276
277 PacketType::Disabled => {
278 let msg = String::from_utf8_lossy(payload).to_string();
279 let event = MeshCoreEvent::new(EventType::Disabled, EventPayload::String(msg));
280 self.dispatcher.emit(event).await;
281 }
282
283 PacketType::ChannelInfo => {
284 if payload.len() >= CHANNEL_INFO_LEN {
286 let channel_idx = payload[0];
287 let name = read_string(payload, 1, CHANNEL_NAME_LEN);
288 let secret: [u8; CHANNEL_SECRET_LEN] =
289 read_bytes(payload, 1 + CHANNEL_NAME_LEN)
290 .unwrap_or([0; CHANNEL_SECRET_LEN]);
291
292 let event = MeshCoreEvent::new(
293 EventType::ChannelInfo,
294 EventPayload::ChannelInfo(ChannelInfoData {
295 channel_idx,
296 name,
297 secret,
298 }),
299 );
300 self.dispatcher.emit(event).await;
301 }
302 }
303
304 PacketType::SignStart => {
305 if payload.len() >= 4 {
306 let max_length = read_u32_le(payload, 0).unwrap_or(0);
307 let event = MeshCoreEvent::new(
308 EventType::SignStart,
309 EventPayload::SignStart { max_length },
310 );
311 self.dispatcher.emit(event).await;
312 }
313 }
314
315 PacketType::Signature => {
316 let event = MeshCoreEvent::new(
317 EventType::Signature,
318 EventPayload::Signature(payload.to_vec()),
319 );
320 self.dispatcher.emit(event).await;
321 }
322
323 PacketType::CustomVars => {
324 let mut vars = HashMap::new();
326 let text = String::from_utf8_lossy(payload);
327 for line in text.lines() {
328 if let Some((key, value)) = line.split_once('=') {
329 vars.insert(key.to_string(), value.to_string());
330 }
331 }
332 let event =
333 MeshCoreEvent::new(EventType::CustomVars, EventPayload::CustomVars(vars));
334 self.dispatcher.emit(event).await;
335 }
336
337 PacketType::Stats => {
338 if !payload.is_empty() {
340 let category = match payload[0] {
341 0 => StatsCategory::Core,
342 1 => StatsCategory::Radio,
343 2 => StatsCategory::Packets,
344 _ => StatsCategory::Core,
345 };
346 let event_type = match category {
347 StatsCategory::Core => EventType::StatsCore,
348 StatsCategory::Radio => EventType::StatsRadio,
349 StatsCategory::Packets => EventType::StatsPackets,
350 };
351 let event = MeshCoreEvent::new(
352 event_type,
353 EventPayload::Stats(StatsData {
354 category,
355 raw: payload[1..].to_vec(),
356 }),
357 );
358 self.dispatcher.emit(event).await;
359 }
360 }
361
362 PacketType::AutoaddConfig => {
363 let flags = if !payload.is_empty() { payload[0] } else { 0 };
364 let event = MeshCoreEvent::new(
365 EventType::AutoAddConfig,
366 EventPayload::AutoAddConfig { flags },
367 );
368 self.dispatcher.emit(event).await;
369 }
370
371 PacketType::Advertisement => {
372 if payload.len() >= 14 {
373 let prefix: [u8; 6] = read_bytes(payload, 0).unwrap_or([0; 6]);
374 let name = read_string(payload, 6, 32);
375 let lat = if payload.len() >= 42 {
376 read_i32_le(payload, 38).unwrap_or(0)
377 } else {
378 0
379 };
380 let lon = if payload.len() >= 46 {
381 read_i32_le(payload, 42).unwrap_or(0)
382 } else {
383 0
384 };
385
386 let event = MeshCoreEvent::new(
387 EventType::Advertisement,
388 EventPayload::Advertisement(AdvertisementData {
389 prefix,
390 name,
391 lat,
392 lon,
393 }),
394 );
395 self.dispatcher.emit(event).await;
396 }
397 }
398
399 PacketType::PathUpdate => {
400 if payload.len() >= 7 {
401 let prefix: [u8; 6] = read_bytes(payload, 0).unwrap_or([0; 6]);
402 let path_len = payload[6] as i8;
403 let path = if payload.len() > 7 {
404 payload[7..].to_vec()
405 } else {
406 Vec::new()
407 };
408
409 let event = MeshCoreEvent::new(
410 EventType::PathUpdate,
411 EventPayload::PathUpdate(PathUpdateData {
412 prefix,
413 path_len,
414 path,
415 }),
416 );
417 self.dispatcher.emit(event).await;
418 }
419 }
420
421 PacketType::Ack => {
422 if payload.len() >= 4 {
423 let tag: [u8; 4] = read_bytes(payload, 0).unwrap_or([0; 4]);
424 let event = MeshCoreEvent::new(EventType::Ack, EventPayload::Ack { tag })
425 .with_attribute("tag", hex_encode(&tag));
426 self.dispatcher.emit(event).await;
427 }
428 }
429
430 PacketType::MessagesWaiting => {
431 let event = MeshCoreEvent::new(EventType::MessagesWaiting, EventPayload::None);
432 self.dispatcher.emit(event).await;
433 }
434
435 PacketType::LoginSuccess => {
436 let event = MeshCoreEvent::new(EventType::LoginSuccess, EventPayload::None);
437 self.dispatcher.emit(event).await;
438 }
439
440 PacketType::LoginFailed => {
441 let event = MeshCoreEvent::new(EventType::LoginFailed, EventPayload::None);
442 self.dispatcher.emit(event).await;
443 }
444
445 PacketType::StatusResponse => {
446 if payload.len() >= 58 {
447 let sender_prefix: [u8; 6] = read_bytes(payload, 0).unwrap_or([0; 6]);
449 if let Ok(status) = parse_status(&payload[6..], sender_prefix) {
450 let tag_hex = hex_encode(&sender_prefix);
451 let event = MeshCoreEvent::new(
452 EventType::StatusResponse,
453 EventPayload::Status(status),
454 )
455 .with_attribute("prefix", tag_hex);
456 self.dispatcher.emit(event).await;
457 }
458 }
459 }
460
461 PacketType::TelemetryResponse => {
462 if payload.len() >= 4 {
464 let tag: [u8; 4] = read_bytes(payload, 0).unwrap_or([0; 4]);
465 let telemetry = payload[4..].to_vec();
466 let event = MeshCoreEvent::new(
467 EventType::TelemetryResponse,
468 EventPayload::Telemetry(telemetry),
469 )
470 .with_attribute("tag", hex_encode(&tag));
471 self.dispatcher.emit(event).await;
472 }
473 }
474
475 PacketType::BinaryResponse => {
476 if payload.len() >= 4 {
477 let tag: [u8; 4] = read_bytes(payload, 0).unwrap_or([0; 4]);
478 let data = payload[4..].to_vec();
479
480 let tag_hex = hex_encode(&tag);
482 let request = self.pending_requests.write().await.remove(&tag_hex);
483
484 if let Some(req) = request {
485 let event = match req.request_type {
487 BinaryReqType::Status => {
488 if let Ok(status) = parse_status(&data, [0; 6]) {
489 MeshCoreEvent::new(
490 EventType::StatusResponse,
491 EventPayload::Status(status),
492 )
493 } else {
494 MeshCoreEvent::new(
495 EventType::BinaryResponse,
496 EventPayload::BinaryResponse { tag, data },
497 )
498 }
499 }
500 BinaryReqType::Telemetry => MeshCoreEvent::new(
501 EventType::TelemetryResponse,
502 EventPayload::Telemetry(data),
503 ),
504 BinaryReqType::Mma => {
505 let entries = parse_mma(&data);
506 MeshCoreEvent::new(
507 EventType::MmaResponse,
508 EventPayload::Mma(entries),
509 )
510 }
511 BinaryReqType::Acl => {
512 let entries = parse_acl(&data);
513 MeshCoreEvent::new(
514 EventType::AclResponse,
515 EventPayload::Acl(entries),
516 )
517 }
518 BinaryReqType::Neighbours => {
519 if let Ok(neighbours) = parse_neighbours(&data, 6) {
521 MeshCoreEvent::new(
522 EventType::NeighboursResponse,
523 EventPayload::Neighbours(neighbours),
524 )
525 } else {
526 MeshCoreEvent::new(
527 EventType::BinaryResponse,
528 EventPayload::BinaryResponse { tag, data },
529 )
530 }
531 }
532 BinaryReqType::KeepAlive => MeshCoreEvent::new(
533 EventType::BinaryResponse,
534 EventPayload::BinaryResponse { tag, data },
535 ),
536 }
537 .with_attribute("tag", tag_hex);
538
539 self.dispatcher.emit(event).await;
540 } else {
541 let event = MeshCoreEvent::new(
543 EventType::BinaryResponse,
544 EventPayload::BinaryResponse { tag, data },
545 )
546 .with_attribute("tag", tag_hex);
547 self.dispatcher.emit(event).await;
548 }
549 }
550 }
551
552 PacketType::ControlData => {
553 if !payload.is_empty() {
554 let control_type = ControlType::from(payload[0]);
555 match control_type {
556 ControlType::NodeDiscoverResp => {
557 let mut entries = Vec::new();
559 let mut offset = 1;
560 while offset + 38 <= payload.len() {
561 let pubkey = payload[offset..offset + 32].to_vec();
562 let name = read_string(payload, offset + 32, 32);
563 entries.push(DiscoverEntry { pubkey, name });
564 offset += 64;
565 }
566 let event = MeshCoreEvent::new(
567 EventType::DiscoverResponse,
568 EventPayload::DiscoverResponse(entries),
569 );
570 self.dispatcher.emit(event).await;
571 }
572 _ => {
573 let event = MeshCoreEvent::new(
574 EventType::ControlData,
575 EventPayload::Bytes(payload.to_vec()),
576 );
577 self.dispatcher.emit(event).await;
578 }
579 }
580 }
581 }
582
583 PacketType::TraceData => {
584 let mut hops = Vec::new();
586 let mut offset = 0;
587 while offset + 7 <= payload.len() {
588 let prefix: [u8; 6] = read_bytes(payload, offset).unwrap_or([0; 6]);
589 let snr_raw = payload[offset + 6] as i8;
590 let snr = snr_raw as f32 / 4.0;
591 hops.push(TraceHop { prefix, snr });
592 offset += 7;
593 }
594 let event = MeshCoreEvent::new(
595 EventType::TraceData,
596 EventPayload::TraceData(TraceInfo { hops }),
597 );
598 self.dispatcher.emit(event).await;
599 }
600
601 PacketType::AdvertResponse => {
602 if payload.len() >= 42 {
603 let tag: [u8; 4] = read_bytes(payload, 0).unwrap_or([0; 4]);
604 let pubkey: [u8; 32] = read_bytes(payload, 4).unwrap_or([0; 32]);
605 let adv_type = payload[36];
606 let node_name = read_string(payload, 37, 32);
607 let timestamp = read_u32_le(payload, 69).unwrap_or(0);
608 let flags = if payload.len() > 73 { payload[73] } else { 0 };
609
610 let (lat, lon, node_desc) = if payload.len() >= 82 {
611 let lat = Some(read_i32_le(payload, 74).unwrap_or(0));
612 let lon = Some(read_i32_le(payload, 78).unwrap_or(0));
613 let desc = if payload.len() > 82 {
614 Some(read_string(payload, 82, 32))
615 } else {
616 None
617 };
618 (lat, lon, desc)
619 } else {
620 (None, None, None)
621 };
622
623 let event = MeshCoreEvent::new(
624 EventType::AdvertResponse,
625 EventPayload::AdvertResponse(AdvertResponseData {
626 tag,
627 pubkey,
628 adv_type,
629 node_name,
630 timestamp,
631 flags,
632 lat,
633 lon,
634 node_desc,
635 }),
636 )
637 .with_attribute("tag", hex_encode(&tag));
638 self.dispatcher.emit(event).await;
639 }
640 }
641 PacketType::BinaryReq => {}
642 PacketType::FactoryReset => {}
643 PacketType::PathDiscovery => {}
644 PacketType::SetFloodScope => {}
645 PacketType::SendControlData => {}
646 PacketType::RawData => {}
647 PacketType::LogData => {
648 if payload.len() >= 2 {
653 let snr_byte = payload[0] as i8;
654 let snr = snr_byte as f32 / 4.0;
655
656 let rssi = payload[1] as i8 as i16;
657
658 let rf_payload = if payload.len() > 2 {
659 payload[2..].to_vec()
660 } else {
661 Vec::new()
662 };
663
664 let log_data = LogData {
665 snr,
666 rssi,
667 payload: rf_payload,
668 };
669 let event =
670 MeshCoreEvent::new(EventType::LogData, EventPayload::LogData(log_data));
671 self.dispatcher.emit(event).await;
672 }
673 }
674 PacketType::PathDiscoveryResponse => {}
675 _ => {
676 tracing::debug!("Unknown packet type: {:?}", packet_type);
678 let event = MeshCoreEvent::new(EventType::Unknown, EventPayload::Bytes(data));
679 self.dispatcher.emit(event).await;
680 }
681 }
682
683 Ok(())
684 }
685}
686
687#[cfg(test)]
688mod tests {
689 use super::*;
690 use std::time::Duration;
691
692 fn create_reader() -> (MessageReader, Arc<EventDispatcher>) {
693 let dispatcher = Arc::new(EventDispatcher::new());
694 let reader = MessageReader::new(dispatcher.clone());
695 (reader, dispatcher)
696 }
697
698 #[tokio::test]
699 async fn test_handle_rx_empty() {
700 let (reader, _dispatcher) = create_reader();
701 let result = reader.handle_rx(vec![]).await;
702 assert!(result.is_ok());
703 }
704
705 #[tokio::test]
706 async fn test_handle_rx_ok() {
707 let (reader, dispatcher) = create_reader();
708 let mut receiver = dispatcher.receiver();
709
710 reader.handle_rx(vec![PacketType::Ok as u8]).await.unwrap();
711
712 let event = tokio::time::timeout(Duration::from_millis(100), receiver.recv())
713 .await
714 .unwrap()
715 .unwrap();
716
717 assert_eq!(event.event_type, EventType::Ok);
718 }
719
720 #[tokio::test]
721 async fn test_handle_rx_error_with_message() {
722 let (reader, dispatcher) = create_reader();
723 let mut receiver = dispatcher.receiver();
724
725 let mut data = vec![PacketType::Error as u8];
726 data.extend_from_slice(b"Test error");
727
728 reader.handle_rx(data).await.unwrap();
729
730 let event = tokio::time::timeout(Duration::from_millis(100), receiver.recv())
731 .await
732 .unwrap()
733 .unwrap();
734
735 assert_eq!(event.event_type, EventType::Error);
736 match event.payload {
737 EventPayload::String(s) => assert_eq!(s, "Test error"),
738 _ => panic!("Expected String payload"),
739 }
740 }
741
742 #[tokio::test]
743 async fn test_handle_rx_error_empty() {
744 let (reader, dispatcher) = create_reader();
745 let mut receiver = dispatcher.receiver();
746
747 reader
748 .handle_rx(vec![PacketType::Error as u8])
749 .await
750 .unwrap();
751
752 let event = tokio::time::timeout(Duration::from_millis(100), receiver.recv())
753 .await
754 .unwrap()
755 .unwrap();
756
757 assert_eq!(event.event_type, EventType::Error);
758 match event.payload {
759 EventPayload::String(s) => assert_eq!(s, "Unknown error"),
760 _ => panic!("Expected String payload"),
761 }
762 }
763
764 #[tokio::test]
765 async fn test_handle_rx_contact_start() {
766 let (reader, _dispatcher) = create_reader();
767
768 reader.pending_contacts.write().await.push(Contact {
770 public_key: [0u8; 32],
771 contact_type: 1,
772 flags: 0,
773 path_len: 0,
774 out_path: vec![],
775 adv_name: "Old".to_string(),
776 last_advert: 0,
777 adv_lat: 0,
778 adv_lon: 0,
779 last_modification_timestamp: 0,
780 });
781
782 reader
783 .handle_rx(vec![PacketType::ContactStart as u8])
784 .await
785 .unwrap();
786
787 assert!(reader.pending_contacts.read().await.is_empty());
789 }
790
791 #[tokio::test]
792 async fn test_handle_rx_battery() {
793 let (reader, dispatcher) = create_reader();
794 let mut receiver = dispatcher.receiver();
795
796 let mut data = vec![PacketType::Battery as u8];
798 data.extend_from_slice(&4200u16.to_le_bytes()); reader.handle_rx(data).await.unwrap();
801
802 let event = tokio::time::timeout(Duration::from_millis(100), receiver.recv())
803 .await
804 .unwrap()
805 .unwrap();
806
807 assert_eq!(event.event_type, EventType::Battery);
808 match event.payload {
809 EventPayload::Battery(info) => {
810 assert_eq!(info.battery_mv, 4200);
811 assert!(info.used_kb.is_none());
812 assert!(info.total_kb.is_none());
813 assert!((info.voltage() - 4.2).abs() < 0.001);
814 }
815 _ => panic!("Expected Battery payload"),
816 }
817 }
818
819 #[tokio::test]
820 async fn test_handle_rx_battery_with_storage() {
821 let (reader, dispatcher) = create_reader();
822 let mut receiver = dispatcher.receiver();
823
824 let mut data = vec![PacketType::Battery as u8];
826 data.extend_from_slice(&3700u16.to_le_bytes()); data.extend_from_slice(&512u32.to_le_bytes()); data.extend_from_slice(&4096u32.to_le_bytes()); reader.handle_rx(data).await.unwrap();
831
832 let event = tokio::time::timeout(Duration::from_millis(100), receiver.recv())
833 .await
834 .unwrap()
835 .unwrap();
836
837 assert_eq!(event.event_type, EventType::Battery);
838 match event.payload {
839 EventPayload::Battery(info) => {
840 assert_eq!(info.battery_mv, 3700);
841 assert_eq!(info.used_kb, Some(512));
842 assert_eq!(info.total_kb, Some(4096));
843 assert!((info.voltage() - 3.7).abs() < 0.001);
844 }
845 _ => panic!("Expected Battery payload"),
846 }
847 }
848
849 #[tokio::test]
850 async fn test_handle_rx_current_time() {
851 let (reader, dispatcher) = create_reader();
852 let mut receiver = dispatcher.receiver();
853
854 let mut data = vec![PacketType::CurrentTime as u8];
855 data.extend_from_slice(&1234567890u32.to_le_bytes());
856
857 reader.handle_rx(data).await.unwrap();
858
859 let event = tokio::time::timeout(Duration::from_millis(100), receiver.recv())
860 .await
861 .unwrap()
862 .unwrap();
863
864 assert_eq!(event.event_type, EventType::CurrentTime);
865 match event.payload {
866 EventPayload::Time(t) => assert_eq!(t, 1234567890),
867 _ => panic!("Expected Time payload"),
868 }
869 }
870
871 #[tokio::test]
872 async fn test_handle_rx_no_more_msgs() {
873 let (reader, dispatcher) = create_reader();
874 let mut receiver = dispatcher.receiver();
875
876 reader
877 .handle_rx(vec![PacketType::NoMoreMsgs as u8])
878 .await
879 .unwrap();
880
881 let event = tokio::time::timeout(Duration::from_millis(100), receiver.recv())
882 .await
883 .unwrap()
884 .unwrap();
885
886 assert_eq!(event.event_type, EventType::NoMoreMessages);
887 }
888
889 #[tokio::test]
890 async fn test_handle_rx_contact_uri() {
891 let (reader, dispatcher) = create_reader();
892 let mut receiver = dispatcher.receiver();
893
894 let mut data = vec![PacketType::ContactUri as u8];
895 data.extend_from_slice(b"mod.rs://contact/abc123");
896
897 reader.handle_rx(data).await.unwrap();
898
899 let event = tokio::time::timeout(Duration::from_millis(100), receiver.recv())
900 .await
901 .unwrap()
902 .unwrap();
903
904 assert_eq!(event.event_type, EventType::ContactUri);
905 match event.payload {
906 EventPayload::String(s) => assert_eq!(s, "mod.rs://contact/abc123"),
907 _ => panic!("Expected String payload"),
908 }
909 }
910
911 #[tokio::test]
912 async fn test_handle_rx_private_key() {
913 let (reader, dispatcher) = create_reader();
914 let mut receiver = dispatcher.receiver();
915
916 let mut data = vec![PacketType::PrivateKey as u8];
917 let key = [0xAA; 64];
918 data.extend_from_slice(&key);
919
920 reader.handle_rx(data).await.unwrap();
921
922 let event = tokio::time::timeout(Duration::from_millis(100), receiver.recv())
923 .await
924 .unwrap()
925 .unwrap();
926
927 assert_eq!(event.event_type, EventType::PrivateKey);
928 match event.payload {
929 EventPayload::PrivateKey(k) => assert_eq!(k, [0xAA; 64]),
930 _ => panic!("Expected PrivateKey payload"),
931 }
932 }
933
934 #[tokio::test]
935 async fn test_handle_rx_disabled() {
936 let (reader, dispatcher) = create_reader();
937 let mut receiver = dispatcher.receiver();
938
939 let mut data = vec![PacketType::Disabled as u8];
940 data.extend_from_slice(b"Feature disabled");
941
942 reader.handle_rx(data).await.unwrap();
943
944 let event = tokio::time::timeout(Duration::from_millis(100), receiver.recv())
945 .await
946 .unwrap()
947 .unwrap();
948
949 assert_eq!(event.event_type, EventType::Disabled);
950 match event.payload {
951 EventPayload::String(s) => assert_eq!(s, "Feature disabled"),
952 _ => panic!("Expected String payload"),
953 }
954 }
955
956 #[tokio::test]
957 async fn test_handle_rx_sign_start() {
958 let (reader, dispatcher) = create_reader();
959 let mut receiver = dispatcher.receiver();
960
961 let mut data = vec![PacketType::SignStart as u8];
962 data.extend_from_slice(&1024u32.to_le_bytes());
963
964 reader.handle_rx(data).await.unwrap();
965
966 let event = tokio::time::timeout(Duration::from_millis(100), receiver.recv())
967 .await
968 .unwrap()
969 .unwrap();
970
971 assert_eq!(event.event_type, EventType::SignStart);
972 match event.payload {
973 EventPayload::SignStart { max_length } => assert_eq!(max_length, 1024),
974 _ => panic!("Expected SignStart payload"),
975 }
976 }
977
978 #[tokio::test]
979 async fn test_handle_rx_signature() {
980 let (reader, dispatcher) = create_reader();
981 let mut receiver = dispatcher.receiver();
982
983 let mut data = vec![PacketType::Signature as u8];
984 data.extend_from_slice(&[0x01, 0x02, 0x03, 0x04]);
985
986 reader.handle_rx(data).await.unwrap();
987
988 let event = tokio::time::timeout(Duration::from_millis(100), receiver.recv())
989 .await
990 .unwrap()
991 .unwrap();
992
993 assert_eq!(event.event_type, EventType::Signature);
994 match event.payload {
995 EventPayload::Signature(sig) => assert_eq!(sig, vec![0x01, 0x02, 0x03, 0x04]),
996 _ => panic!("Expected Signature payload"),
997 }
998 }
999
1000 #[tokio::test]
1001 async fn test_handle_rx_custom_vars() {
1002 let (reader, dispatcher) = create_reader();
1003 let mut receiver = dispatcher.receiver();
1004
1005 let mut data = vec![PacketType::CustomVars as u8];
1006 data.extend_from_slice(b"key1=value1\nkey2=value2");
1007
1008 reader.handle_rx(data).await.unwrap();
1009
1010 let event = tokio::time::timeout(Duration::from_millis(100), receiver.recv())
1011 .await
1012 .unwrap()
1013 .unwrap();
1014
1015 assert_eq!(event.event_type, EventType::CustomVars);
1016 match event.payload {
1017 EventPayload::CustomVars(vars) => {
1018 assert_eq!(vars.get("key1"), Some(&"value1".to_string()));
1019 assert_eq!(vars.get("key2"), Some(&"value2".to_string()));
1020 }
1021 _ => panic!("Expected CustomVars payload"),
1022 }
1023 }
1024
1025 #[tokio::test]
1026 async fn test_handle_rx_msg_sent() {
1027 let (reader, dispatcher) = create_reader();
1028 let mut receiver = dispatcher.receiver();
1029
1030 let mut data = vec![PacketType::MsgSent as u8];
1031 data.push(1); data.extend_from_slice(&[0xAA, 0xBB, 0xCC, 0xDD]); data.extend_from_slice(&5000u32.to_le_bytes()); reader.handle_rx(data).await.unwrap();
1036
1037 let event = tokio::time::timeout(Duration::from_millis(100), receiver.recv())
1038 .await
1039 .unwrap()
1040 .unwrap();
1041
1042 assert_eq!(event.event_type, EventType::MsgSent);
1043 match event.payload {
1044 EventPayload::MsgSent(info) => {
1045 assert_eq!(info.message_type, 1);
1046 assert_eq!(info.expected_ack, [0xAA, 0xBB, 0xCC, 0xDD]);
1047 assert_eq!(info.suggested_timeout, 5000);
1048 }
1049 _ => panic!("Expected MsgSent payload"),
1050 }
1051 assert_eq!(event.attributes.get("tag"), Some(&"aabbccdd".to_string()));
1052 }
1053
1054 #[tokio::test]
1055 async fn test_handle_rx_ack() {
1056 let (reader, dispatcher) = create_reader();
1057 let mut receiver = dispatcher.receiver();
1058
1059 let mut data = vec![PacketType::Ack as u8];
1060 data.extend_from_slice(&[0x01, 0x02, 0x03, 0x04]);
1061
1062 reader.handle_rx(data).await.unwrap();
1063
1064 let event = tokio::time::timeout(Duration::from_millis(100), receiver.recv())
1065 .await
1066 .unwrap()
1067 .unwrap();
1068
1069 assert_eq!(event.event_type, EventType::Ack);
1070 match event.payload {
1071 EventPayload::Ack { tag } => assert_eq!(tag, [0x01, 0x02, 0x03, 0x04]),
1072 _ => panic!("Expected Ack payload"),
1073 }
1074 }
1075
1076 #[tokio::test]
1077 async fn test_handle_rx_messages_waiting() {
1078 let (reader, dispatcher) = create_reader();
1079 let mut receiver = dispatcher.receiver();
1080
1081 reader
1082 .handle_rx(vec![PacketType::MessagesWaiting as u8])
1083 .await
1084 .unwrap();
1085
1086 let event = tokio::time::timeout(Duration::from_millis(100), receiver.recv())
1087 .await
1088 .unwrap()
1089 .unwrap();
1090
1091 assert_eq!(event.event_type, EventType::MessagesWaiting);
1092 }
1093
1094 #[tokio::test]
1095 async fn test_handle_rx_login_success() {
1096 let (reader, dispatcher) = create_reader();
1097 let mut receiver = dispatcher.receiver();
1098
1099 reader
1100 .handle_rx(vec![PacketType::LoginSuccess as u8])
1101 .await
1102 .unwrap();
1103
1104 let event = tokio::time::timeout(Duration::from_millis(100), receiver.recv())
1105 .await
1106 .unwrap()
1107 .unwrap();
1108
1109 assert_eq!(event.event_type, EventType::LoginSuccess);
1110 }
1111
1112 #[tokio::test]
1113 async fn test_handle_rx_login_failed() {
1114 let (reader, dispatcher) = create_reader();
1115 let mut receiver = dispatcher.receiver();
1116
1117 reader
1118 .handle_rx(vec![PacketType::LoginFailed as u8])
1119 .await
1120 .unwrap();
1121
1122 let event = tokio::time::timeout(Duration::from_millis(100), receiver.recv())
1123 .await
1124 .unwrap()
1125 .unwrap();
1126
1127 assert_eq!(event.event_type, EventType::LoginFailed);
1128 }
1129
1130 #[tokio::test]
1131 async fn test_handle_rx_stats_core() {
1132 let (reader, dispatcher) = create_reader();
1133 let mut receiver = dispatcher.receiver();
1134
1135 let mut data = vec![PacketType::Stats as u8];
1136 data.push(0); data.extend_from_slice(&[0x01, 0x02, 0x03]);
1138
1139 reader.handle_rx(data).await.unwrap();
1140
1141 let event = tokio::time::timeout(Duration::from_millis(100), receiver.recv())
1142 .await
1143 .unwrap()
1144 .unwrap();
1145
1146 assert_eq!(event.event_type, EventType::StatsCore);
1147 match event.payload {
1148 EventPayload::Stats(stats) => {
1149 assert_eq!(stats.category, StatsCategory::Core);
1150 assert_eq!(stats.raw, vec![0x01, 0x02, 0x03]);
1151 }
1152 _ => panic!("Expected Stats payload"),
1153 }
1154 }
1155
1156 #[tokio::test]
1157 async fn test_handle_rx_stats_radio() {
1158 let (reader, dispatcher) = create_reader();
1159 let mut receiver = dispatcher.receiver();
1160
1161 let mut data = vec![PacketType::Stats as u8];
1162 data.push(1); data.extend_from_slice(&[0x04, 0x05]);
1164
1165 reader.handle_rx(data).await.unwrap();
1166
1167 let event = tokio::time::timeout(Duration::from_millis(100), receiver.recv())
1168 .await
1169 .unwrap()
1170 .unwrap();
1171
1172 assert_eq!(event.event_type, EventType::StatsRadio);
1173 }
1174
1175 #[tokio::test]
1176 async fn test_handle_rx_stats_packets() {
1177 let (reader, dispatcher) = create_reader();
1178 let mut receiver = dispatcher.receiver();
1179
1180 let mut data = vec![PacketType::Stats as u8];
1181 data.push(2); data.extend_from_slice(&[0x06, 0x07]);
1183
1184 reader.handle_rx(data).await.unwrap();
1185
1186 let event = tokio::time::timeout(Duration::from_millis(100), receiver.recv())
1187 .await
1188 .unwrap()
1189 .unwrap();
1190
1191 assert_eq!(event.event_type, EventType::StatsPackets);
1192 }
1193
1194 #[tokio::test]
1195 async fn test_handle_rx_autoadd_config() {
1196 let (reader, dispatcher) = create_reader();
1197 let mut receiver = dispatcher.receiver();
1198
1199 let data = vec![PacketType::AutoaddConfig as u8, 0x03];
1200
1201 reader.handle_rx(data).await.unwrap();
1202
1203 let event = tokio::time::timeout(Duration::from_millis(100), receiver.recv())
1204 .await
1205 .unwrap()
1206 .unwrap();
1207
1208 assert_eq!(event.event_type, EventType::AutoAddConfig);
1209 match event.payload {
1210 EventPayload::AutoAddConfig { flags } => assert_eq!(flags, 0x03),
1211 _ => panic!("Expected AutoAddConfig payload"),
1212 }
1213 }
1214
1215 #[tokio::test]
1216 async fn test_handle_rx_device_info_minimal() {
1217 let (reader, dispatcher) = create_reader();
1218 let mut receiver = dispatcher.receiver();
1219
1220 let mut data = vec![PacketType::DeviceInfo as u8];
1222 data.push(0x02); reader.handle_rx(data).await.unwrap();
1225
1226 let event = tokio::time::timeout(Duration::from_millis(100), receiver.recv())
1227 .await
1228 .unwrap()
1229 .unwrap();
1230
1231 assert_eq!(event.event_type, EventType::DeviceInfo);
1232 match event.payload {
1233 EventPayload::DeviceInfo(info) => {
1234 assert_eq!(info.fw_version_code, 0x02);
1235 assert!(info.max_contacts.is_none());
1236 assert!(info.model.is_none());
1237 }
1238 _ => panic!("Expected DeviceInfo payload"),
1239 }
1240 }
1241
1242 #[tokio::test]
1243 async fn test_handle_rx_device_info_full() {
1244 let (reader, dispatcher) = create_reader();
1245 let mut receiver = dispatcher.receiver();
1246
1247 let mut data = vec![PacketType::DeviceInfo as u8];
1248 data.push(9); data.push(50); data.push(8); data.extend_from_slice(&1234u32.to_le_bytes()); let mut fw_build = [0u8; 12];
1256 fw_build[..11].copy_from_slice(b"Feb 15 2025");
1257 data.extend_from_slice(&fw_build);
1258
1259 let mut model = [0u8; 40];
1261 model[..10].copy_from_slice(b"T-Deck Pro");
1262 data.extend_from_slice(&model);
1263
1264 let mut version = [0u8; 20];
1266 version[..5].copy_from_slice(b"1.2.3");
1267 data.extend_from_slice(&version);
1268
1269 data.push(1); reader.handle_rx(data).await.unwrap();
1273
1274 let event = tokio::time::timeout(Duration::from_millis(100), receiver.recv())
1275 .await
1276 .unwrap()
1277 .unwrap();
1278
1279 assert_eq!(event.event_type, EventType::DeviceInfo);
1280 match event.payload {
1281 EventPayload::DeviceInfo(info) => {
1282 assert_eq!(info.fw_version_code, 9);
1283 assert_eq!(info.max_contacts, Some(100));
1284 assert_eq!(info.max_channels, Some(8));
1285 assert_eq!(info.ble_pin, Some(1234));
1286 assert_eq!(info.fw_build.as_deref(), Some("Feb 15 2025"));
1287 assert_eq!(info.model.as_deref(), Some("T-Deck Pro"));
1288 assert_eq!(info.version.as_deref(), Some("1.2.3"));
1289 assert_eq!(info.repeat, Some(true));
1290 }
1291 _ => panic!("Expected DeviceInfo payload"),
1292 }
1293 }
1294
1295 #[tokio::test]
1296 async fn test_handle_rx_path_update() {
1297 let (reader, dispatcher) = create_reader();
1298 let mut receiver = dispatcher.receiver();
1299
1300 let mut data = vec![PacketType::PathUpdate as u8];
1301 data.extend_from_slice(&[0x01, 0x02, 0x03, 0x04, 0x05, 0x06]); data.push(3); data.extend_from_slice(&[0x0A, 0x0B, 0x0C]); reader.handle_rx(data).await.unwrap();
1306
1307 let event = tokio::time::timeout(Duration::from_millis(100), receiver.recv())
1308 .await
1309 .unwrap()
1310 .unwrap();
1311
1312 assert_eq!(event.event_type, EventType::PathUpdate);
1313 match event.payload {
1314 EventPayload::PathUpdate(update) => {
1315 assert_eq!(update.prefix, [0x01, 0x02, 0x03, 0x04, 0x05, 0x06]);
1316 assert_eq!(update.path_len, 3);
1317 assert_eq!(update.path, vec![0x0A, 0x0B, 0x0C]);
1318 }
1319 _ => panic!("Expected PathUpdate payload"),
1320 }
1321 }
1322
1323 #[tokio::test]
1324 async fn test_handle_rx_telemetry_response() {
1325 let (reader, dispatcher) = create_reader();
1326 let mut receiver = dispatcher.receiver();
1327
1328 let mut data = vec![PacketType::TelemetryResponse as u8];
1329 data.extend_from_slice(&[0x01, 0x02, 0x03, 0x04]); data.extend_from_slice(&[0xAA, 0xBB, 0xCC]); reader.handle_rx(data).await.unwrap();
1333
1334 let event = tokio::time::timeout(Duration::from_millis(100), receiver.recv())
1335 .await
1336 .unwrap()
1337 .unwrap();
1338
1339 assert_eq!(event.event_type, EventType::TelemetryResponse);
1340 match event.payload {
1341 EventPayload::Telemetry(data) => assert_eq!(data, vec![0xAA, 0xBB, 0xCC]),
1342 _ => panic!("Expected Telemetry payload"),
1343 }
1344 }
1345
1346 #[tokio::test]
1347 async fn test_handle_rx_trace_data() {
1348 let (reader, dispatcher) = create_reader();
1349 let mut receiver = dispatcher.receiver();
1350
1351 let mut data = vec![PacketType::TraceData as u8];
1352 data.extend_from_slice(&[0x01, 0x02, 0x03, 0x04, 0x05, 0x06]);
1354 data.push(40); data.extend_from_slice(&[0x11, 0x12, 0x13, 0x14, 0x15, 0x16]);
1357 data.push(20); reader.handle_rx(data).await.unwrap();
1360
1361 let event = tokio::time::timeout(Duration::from_millis(100), receiver.recv())
1362 .await
1363 .unwrap()
1364 .unwrap();
1365
1366 assert_eq!(event.event_type, EventType::TraceData);
1367 match event.payload {
1368 EventPayload::TraceData(info) => {
1369 assert_eq!(info.hops.len(), 2);
1370 assert_eq!(info.hops[0].snr, 10.0);
1371 assert_eq!(info.hops[1].snr, 5.0);
1372 }
1373 _ => panic!("Expected TraceData payload"),
1374 }
1375 }
1376
1377 #[tokio::test]
1378 async fn test_handle_rx_unknown() {
1379 let (reader, dispatcher) = create_reader();
1380 let mut receiver = dispatcher.receiver();
1381
1382 let data = vec![0xFE, 0x01, 0x02, 0x03];
1383
1384 reader.handle_rx(data.clone()).await.unwrap();
1385
1386 let event = tokio::time::timeout(Duration::from_millis(100), receiver.recv())
1387 .await
1388 .unwrap()
1389 .unwrap();
1390
1391 assert_eq!(event.event_type, EventType::Unknown);
1392 match event.payload {
1393 EventPayload::Bytes(d) => assert_eq!(d, data),
1394 _ => panic!("Expected Bytes payload"),
1395 }
1396 }
1397
1398 #[tokio::test]
1399 async fn test_register_binary_request() {
1400 let (reader, _dispatcher) = create_reader();
1401
1402 reader
1403 .register_binary_request(
1404 &[0x01, 0x02, 0x03, 0x04],
1405 BinaryReqType::Status,
1406 vec![0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF],
1407 Duration::from_secs(30),
1408 HashMap::new(),
1409 false,
1410 )
1411 .await;
1412
1413 let requests = reader.pending_requests.read().await;
1414 assert!(requests.contains_key("01020304"));
1415 }
1416
1417 #[tokio::test]
1418 async fn test_cleanup_expired() {
1419 let (reader, _dispatcher) = create_reader();
1420
1421 reader
1423 .register_binary_request(
1424 &[0x01, 0x02, 0x03, 0x04],
1425 BinaryReqType::Status,
1426 vec![],
1427 Duration::from_millis(1),
1428 HashMap::new(),
1429 false,
1430 )
1431 .await;
1432
1433 tokio::time::sleep(Duration::from_millis(10)).await;
1435
1436 reader.cleanup_expired().await;
1438
1439 let requests = reader.pending_requests.read().await;
1440 assert!(requests.is_empty());
1441 }
1442
1443 #[tokio::test]
1444 async fn test_handle_rx_binary_response_with_pending_request() {
1445 let (reader, dispatcher) = create_reader();
1446 let mut receiver = dispatcher.receiver();
1447
1448 reader
1450 .register_binary_request(
1451 &[0x01, 0x02, 0x03, 0x04],
1452 BinaryReqType::Telemetry,
1453 vec![],
1454 Duration::from_secs(30),
1455 HashMap::new(),
1456 false,
1457 )
1458 .await;
1459
1460 let mut data = vec![PacketType::BinaryResponse as u8];
1461 data.extend_from_slice(&[0x01, 0x02, 0x03, 0x04]); data.extend_from_slice(&[0xAA, 0xBB, 0xCC]); reader.handle_rx(data).await.unwrap();
1465
1466 let event = tokio::time::timeout(Duration::from_millis(100), receiver.recv())
1467 .await
1468 .unwrap()
1469 .unwrap();
1470
1471 assert_eq!(event.event_type, EventType::TelemetryResponse);
1473 }
1474
1475 #[tokio::test]
1476 async fn test_handle_rx_binary_response_no_pending() {
1477 let (reader, dispatcher) = create_reader();
1478 let mut receiver = dispatcher.receiver();
1479
1480 let mut data = vec![PacketType::BinaryResponse as u8];
1481 data.extend_from_slice(&[0x01, 0x02, 0x03, 0x04]); data.extend_from_slice(&[0xAA, 0xBB, 0xCC]); reader.handle_rx(data).await.unwrap();
1485
1486 let event = tokio::time::timeout(Duration::from_millis(100), receiver.recv())
1487 .await
1488 .unwrap()
1489 .unwrap();
1490
1491 assert_eq!(event.event_type, EventType::BinaryResponse);
1493 }
1494
1495 #[tokio::test]
1496 async fn test_handle_rx_channel_info() {
1497 let (reader, dispatcher) = create_reader();
1498 let mut receiver = dispatcher.receiver();
1499
1500 let mut data = vec![PacketType::ChannelInfo as u8];
1501 data.push(1); let mut name = [0u8; CHANNEL_NAME_LEN];
1503 name[..7].copy_from_slice(b"General");
1504 data.extend_from_slice(&name);
1505 data.extend_from_slice(&[0xAA; CHANNEL_SECRET_LEN]); reader.handle_rx(data).await.unwrap();
1508
1509 let event = tokio::time::timeout(Duration::from_millis(100), receiver.recv())
1510 .await
1511 .unwrap()
1512 .unwrap();
1513
1514 assert_eq!(event.event_type, EventType::ChannelInfo);
1515 match event.payload {
1516 EventPayload::ChannelInfo(info) => {
1517 assert_eq!(info.channel_idx, 1);
1518 assert_eq!(info.name, "General");
1519 assert_eq!(info.secret, [0xAA; CHANNEL_SECRET_LEN]);
1520 }
1521 _ => panic!("Expected ChannelInfo payload"),
1522 }
1523 }
1524
1525 #[tokio::test]
1526 async fn test_handle_rx_channel_info_zero_secret() {
1527 let (reader, dispatcher) = create_reader();
1528 let mut receiver = dispatcher.receiver();
1529
1530 let mut data = vec![PacketType::ChannelInfo as u8];
1531 data.push(2); let mut name = [0u8; CHANNEL_NAME_LEN];
1533 name[..4].copy_from_slice(b"Test");
1534 data.extend_from_slice(&name);
1535 data.extend_from_slice(&[0u8; CHANNEL_SECRET_LEN]); reader.handle_rx(data).await.unwrap();
1538
1539 let event = tokio::time::timeout(Duration::from_millis(100), receiver.recv())
1540 .await
1541 .unwrap()
1542 .unwrap();
1543
1544 assert_eq!(event.event_type, EventType::ChannelInfo);
1545 match event.payload {
1546 EventPayload::ChannelInfo(info) => {
1547 assert_eq!(info.channel_idx, 2);
1548 assert_eq!(info.name, "Test");
1549 assert_eq!(info.secret, [0; CHANNEL_SECRET_LEN]);
1550 }
1551 _ => panic!("Expected ChannelInfo payload"),
1552 }
1553 }
1554
1555 #[tokio::test]
1556 async fn test_handle_rx_channel_info_too_short() {
1557 let (reader, dispatcher) = create_reader();
1558 let mut receiver = dispatcher.receiver();
1559
1560 let mut data = vec![PacketType::ChannelInfo as u8];
1562 data.push(1); let name = [0u8; CHANNEL_NAME_LEN];
1564 data.extend_from_slice(&name);
1565 reader.handle_rx(data).await.unwrap();
1568
1569 let result = tokio::time::timeout(Duration::from_millis(50), receiver.recv()).await;
1571 assert!(result.is_err(), "Should not emit event for short payload");
1572 }
1573
1574 #[tokio::test]
1575 async fn test_handle_rx_channel_info_max_name_length() {
1576 let (reader, dispatcher) = create_reader();
1577 let mut receiver = dispatcher.receiver();
1578
1579 let mut data = vec![PacketType::ChannelInfo as u8];
1580 data.push(3); let mut name = [0u8; CHANNEL_NAME_LEN];
1583 let long_name = b"This is a very long channel nam"; name[..31].copy_from_slice(long_name);
1585 data.extend_from_slice(&name);
1586 data.extend_from_slice(&[0xBB; CHANNEL_SECRET_LEN]);
1587
1588 reader.handle_rx(data).await.unwrap();
1589
1590 let event = tokio::time::timeout(Duration::from_millis(100), receiver.recv())
1591 .await
1592 .unwrap()
1593 .unwrap();
1594
1595 assert_eq!(event.event_type, EventType::ChannelInfo);
1596 match event.payload {
1597 EventPayload::ChannelInfo(info) => {
1598 assert_eq!(info.channel_idx, 3);
1599 assert_eq!(info.name, "This is a very long channel nam");
1600 assert_eq!(info.name.len(), 31);
1601 assert_eq!(info.secret, [0xBB; CHANNEL_SECRET_LEN]);
1602 }
1603 _ => panic!("Expected ChannelInfo payload"),
1604 }
1605 }
1606
1607 #[tokio::test]
1608 async fn test_handle_rx_advertisement() {
1609 let (reader, dispatcher) = create_reader();
1610 let mut receiver = dispatcher.receiver();
1611
1612 let mut data = vec![PacketType::Advertisement as u8];
1613 data.extend_from_slice(&[0x01, 0x02, 0x03, 0x04, 0x05, 0x06]); let mut name_bytes = [0u8; 32];
1616 name_bytes[..5].copy_from_slice(b"Node1");
1617 data.extend_from_slice(&name_bytes);
1618 data.extend_from_slice(&37774900i32.to_le_bytes());
1620 data.extend_from_slice(&(-122419400i32).to_le_bytes());
1622
1623 reader.handle_rx(data).await.unwrap();
1624
1625 let event = tokio::time::timeout(Duration::from_millis(100), receiver.recv())
1626 .await
1627 .unwrap()
1628 .unwrap();
1629
1630 assert_eq!(event.event_type, EventType::Advertisement);
1631 match event.payload {
1632 EventPayload::Advertisement(adv) => {
1633 assert_eq!(adv.prefix, [0x01, 0x02, 0x03, 0x04, 0x05, 0x06]);
1634 assert_eq!(adv.name, "Node1");
1635 assert_eq!(adv.lat, 37774900);
1636 assert_eq!(adv.lon, -122419400);
1637 }
1638 _ => panic!("Expected Advertisement payload"),
1639 }
1640 }
1641
1642 #[tokio::test]
1643 async fn test_handle_rx_advertisement_minimal() {
1644 let (reader, dispatcher) = create_reader();
1645 let mut receiver = dispatcher.receiver();
1646
1647 let mut data = vec![PacketType::Advertisement as u8];
1648 data.extend_from_slice(&[0x01, 0x02, 0x03, 0x04, 0x05, 0x06]); data.extend_from_slice(b"ShortNam");
1651
1652 reader.handle_rx(data).await.unwrap();
1653
1654 let event = tokio::time::timeout(Duration::from_millis(100), receiver.recv())
1655 .await
1656 .unwrap()
1657 .unwrap();
1658
1659 assert_eq!(event.event_type, EventType::Advertisement);
1660 match event.payload {
1661 EventPayload::Advertisement(adv) => {
1662 assert_eq!(adv.lat, 0); assert_eq!(adv.lon, 0);
1664 }
1665 _ => panic!("Expected Advertisement payload"),
1666 }
1667 }
1668
1669 #[tokio::test]
1670 async fn test_handle_rx_self_info() {
1671 let (reader, dispatcher) = create_reader();
1672 let mut receiver = dispatcher.receiver();
1673
1674 let mut data = vec![PacketType::SelfInfo as u8];
1675 let mut payload = vec![0u8; 60];
1677 payload[0] = 1; payload[1] = 20; payload[2] = 30; payload[35..39].copy_from_slice(&37774900i32.to_le_bytes()); payload[39..43].copy_from_slice(&(-122419400i32).to_le_bytes()); payload[47..51].copy_from_slice(&915000000u32.to_le_bytes()); payload[51..55].copy_from_slice(&125000u32.to_le_bytes()); payload[55] = 7; payload[56] = 5; payload[57..60].copy_from_slice(b"Dev");
1687 data.extend_from_slice(&payload);
1688
1689 reader.handle_rx(data).await.unwrap();
1690
1691 let event = tokio::time::timeout(Duration::from_millis(100), receiver.recv())
1692 .await
1693 .unwrap()
1694 .unwrap();
1695
1696 assert_eq!(event.event_type, EventType::SelfInfo);
1697 match event.payload {
1698 EventPayload::SelfInfo(info) => {
1699 assert_eq!(info.tx_power, 20);
1700 assert_eq!(info.radio_freq, 915000000);
1701 }
1702 _ => panic!("Expected SelfInfo payload"),
1703 }
1704 }
1705
1706 #[tokio::test]
1707 async fn test_handle_rx_contact_msg_recv() {
1708 let (reader, dispatcher) = create_reader();
1709 let mut receiver = dispatcher.receiver();
1710
1711 let mut data = vec![PacketType::ContactMsgRecv as u8];
1712 data.extend_from_slice(&[0x01, 0x02, 0x03, 0x04, 0x05, 0x06]); data.push(2); data.push(1); data.extend_from_slice(&1234567890u32.to_le_bytes()); data.extend_from_slice(b"Hello!"); reader.handle_rx(data).await.unwrap();
1719
1720 let event = tokio::time::timeout(Duration::from_millis(100), receiver.recv())
1721 .await
1722 .unwrap()
1723 .unwrap();
1724
1725 assert_eq!(event.event_type, EventType::ContactMsgRecv);
1726 match event.payload {
1727 EventPayload::ContactMessage(msg) => {
1728 assert_eq!(msg.text, "Hello!");
1729 assert_eq!(msg.path_len, 2);
1730 }
1731 _ => panic!("Expected ContactMessage payload"),
1732 }
1733 }
1734
1735 #[tokio::test]
1736 async fn test_handle_rx_contact_msg_recv_v3() {
1737 let (reader, dispatcher) = create_reader();
1738 let mut receiver = dispatcher.receiver();
1739
1740 let mut data = vec![PacketType::ContactMsgRecvV3 as u8];
1741 data.push(40); data.extend_from_slice(&[0x00, 0x00]); data.extend_from_slice(&[0x01, 0x02, 0x03, 0x04, 0x05, 0x06]); data.push(3); data.push(1); data.extend_from_slice(&1234567890u32.to_le_bytes()); data.extend_from_slice(b"V3 msg!"); reader.handle_rx(data).await.unwrap();
1750
1751 let event = tokio::time::timeout(Duration::from_millis(100), receiver.recv())
1752 .await
1753 .unwrap()
1754 .unwrap();
1755
1756 assert_eq!(event.event_type, EventType::ContactMsgRecv);
1757 match event.payload {
1758 EventPayload::ContactMessage(msg) => {
1759 assert_eq!(msg.text, "V3 msg!");
1760 assert_eq!(msg.snr, Some(10.0));
1761 }
1762 _ => panic!("Expected ContactMessage payload"),
1763 }
1764 }
1765
1766 #[tokio::test]
1767 async fn test_handle_rx_channel_msg_recv() {
1768 let (reader, dispatcher) = create_reader();
1769 let mut receiver = dispatcher.receiver();
1770
1771 let mut data = vec![PacketType::ChannelMsgRecv as u8];
1772 data.push(5); data.push(1); data.push(0); data.extend_from_slice(&1234567890u32.to_le_bytes()); data.extend_from_slice(b"Channel msg"); reader.handle_rx(data).await.unwrap();
1779
1780 let event = tokio::time::timeout(Duration::from_millis(100), receiver.recv())
1781 .await
1782 .unwrap()
1783 .unwrap();
1784
1785 assert_eq!(event.event_type, EventType::ChannelMsgRecv);
1786 match event.payload {
1787 EventPayload::ChannelMessage(msg) => {
1788 assert_eq!(msg.channel_idx, 5);
1789 assert_eq!(msg.text, "Channel msg");
1790 }
1791 _ => panic!("Expected ChannelMessage payload"),
1792 }
1793 }
1794
1795 #[tokio::test]
1796 async fn test_handle_rx_status_response() {
1797 let (reader, dispatcher) = create_reader();
1798 let mut receiver = dispatcher.receiver();
1799
1800 let mut data = vec![PacketType::StatusResponse as u8];
1801 data.extend_from_slice(&[0x01, 0x02, 0x03, 0x04, 0x05, 0x06]);
1803 let mut status_data = vec![0u8; 52];
1805 status_data[0..2].copy_from_slice(&4200u16.to_le_bytes()); status_data[2..4].copy_from_slice(&5u16.to_le_bytes()); status_data[20..24].copy_from_slice(&86400u32.to_le_bytes()); data.extend_from_slice(&status_data);
1809
1810 reader.handle_rx(data).await.unwrap();
1811
1812 let event = tokio::time::timeout(Duration::from_millis(100), receiver.recv())
1813 .await
1814 .unwrap()
1815 .unwrap();
1816
1817 assert_eq!(event.event_type, EventType::StatusResponse);
1818 match event.payload {
1819 EventPayload::Status(status) => {
1820 assert_eq!(status.battery_mv, 4200);
1821 assert_eq!(status.uptime, 86400);
1822 }
1823 _ => panic!("Expected Status payload"),
1824 }
1825 }
1826
1827 #[tokio::test]
1828 async fn test_handle_rx_new_contact() {
1829 let (reader, dispatcher) = create_reader();
1830 let mut receiver = dispatcher.receiver();
1831
1832 let mut data = vec![PacketType::PushCodeNewAdvert as u8];
1833 let mut contact_data = vec![0u8; 149];
1835 contact_data[0..6].copy_from_slice(&[0x01, 0x02, 0x03, 0x04, 0x05, 0x06]);
1836 contact_data[32] = 1; contact_data[99..104].copy_from_slice(b"New\0\0");
1838 data.extend_from_slice(&contact_data);
1839
1840 reader.handle_rx(data).await.unwrap();
1841
1842 let event = tokio::time::timeout(Duration::from_millis(100), receiver.recv())
1843 .await
1844 .unwrap()
1845 .unwrap();
1846
1847 assert_eq!(event.event_type, EventType::NewContact);
1848 match event.payload {
1849 EventPayload::Contact(contact) => {
1850 assert_eq!(contact.adv_name, "New");
1851 }
1852 _ => panic!("Expected Contact payload"),
1853 }
1854 }
1855
1856 #[tokio::test]
1857 async fn test_handle_rx_contact_list_flow() {
1858 let (reader, dispatcher) = create_reader();
1859 let mut receiver = dispatcher.receiver();
1860
1861 reader
1863 .handle_rx(vec![PacketType::ContactStart as u8])
1864 .await
1865 .unwrap();
1866
1867 let mut contact_data = vec![PacketType::Contact as u8];
1869 let mut contact = vec![0u8; 149];
1870 contact[0..6].copy_from_slice(&[0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF]);
1871 contact[32] = 1;
1872 contact[99..105].copy_from_slice(b"Test1\0");
1873 contact_data.extend_from_slice(&contact);
1874 reader.handle_rx(contact_data).await.unwrap();
1875
1876 let mut end_data = vec![PacketType::ContactEnd as u8];
1878 end_data.extend_from_slice(&999u32.to_le_bytes());
1879 reader.handle_rx(end_data).await.unwrap();
1880
1881 let event = tokio::time::timeout(Duration::from_millis(100), receiver.recv())
1882 .await
1883 .unwrap()
1884 .unwrap();
1885
1886 assert_eq!(event.event_type, EventType::Contacts);
1887 match event.payload {
1888 EventPayload::Contacts(contacts) => {
1889 assert_eq!(contacts.len(), 1);
1890 assert_eq!(contacts[0].adv_name, "Test1");
1891 }
1892 _ => panic!("Expected Contacts payload"),
1893 }
1894 }
1895
1896 #[tokio::test]
1897 async fn test_handle_rx_binary_response_acl() {
1898 let (reader, dispatcher) = create_reader();
1899 let mut receiver = dispatcher.receiver();
1900
1901 reader
1903 .register_binary_request(
1904 &[0x01, 0x02, 0x03, 0x04],
1905 BinaryReqType::Acl,
1906 vec![],
1907 Duration::from_secs(30),
1908 HashMap::new(),
1909 false,
1910 )
1911 .await;
1912
1913 let mut data = vec![PacketType::BinaryResponse as u8];
1914 data.extend_from_slice(&[0x01, 0x02, 0x03, 0x04]); data.extend_from_slice(&[0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x01]);
1917
1918 reader.handle_rx(data).await.unwrap();
1919
1920 let event = tokio::time::timeout(Duration::from_millis(100), receiver.recv())
1921 .await
1922 .unwrap()
1923 .unwrap();
1924
1925 assert_eq!(event.event_type, EventType::AclResponse);
1926 match event.payload {
1927 EventPayload::Acl(entries) => {
1928 assert_eq!(entries.len(), 1);
1929 assert_eq!(entries[0].permissions, 0x01);
1930 }
1931 _ => panic!("Expected Acl payload"),
1932 }
1933 }
1934
1935 #[tokio::test]
1936 async fn test_handle_rx_binary_response_mma() {
1937 let (reader, dispatcher) = create_reader();
1938 let mut receiver = dispatcher.receiver();
1939
1940 reader
1942 .register_binary_request(
1943 &[0x01, 0x02, 0x03, 0x04],
1944 BinaryReqType::Mma,
1945 vec![],
1946 Duration::from_secs(30),
1947 HashMap::new(),
1948 false,
1949 )
1950 .await;
1951
1952 let mut data = vec![PacketType::BinaryResponse as u8];
1953 data.extend_from_slice(&[0x01, 0x02, 0x03, 0x04]); data.push(1); data.push(2); data.extend_from_slice(&100i32.to_le_bytes()); data.extend_from_slice(&200i32.to_le_bytes()); data.extend_from_slice(&150i32.to_le_bytes()); reader.handle_rx(data).await.unwrap();
1962
1963 let event = tokio::time::timeout(Duration::from_millis(100), receiver.recv())
1964 .await
1965 .unwrap()
1966 .unwrap();
1967
1968 assert_eq!(event.event_type, EventType::MmaResponse);
1969 }
1970
1971 #[tokio::test]
1972 async fn test_handle_rx_binary_response_neighbours() {
1973 let (reader, dispatcher) = create_reader();
1974 let mut receiver = dispatcher.receiver();
1975
1976 reader
1978 .register_binary_request(
1979 &[0x01, 0x02, 0x03, 0x04],
1980 BinaryReqType::Neighbours,
1981 vec![],
1982 Duration::from_secs(30),
1983 HashMap::new(),
1984 false,
1985 )
1986 .await;
1987
1988 let mut data = vec![PacketType::BinaryResponse as u8];
1989 data.extend_from_slice(&[0x01, 0x02, 0x03, 0x04]); data.extend_from_slice(&1u16.to_le_bytes()); data.extend_from_slice(&1u16.to_le_bytes()); data.extend_from_slice(&[0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF]);
1995 data.extend_from_slice(&300i32.to_le_bytes());
1996 data.push(40); reader.handle_rx(data).await.unwrap();
1999
2000 let event = tokio::time::timeout(Duration::from_millis(100), receiver.recv())
2001 .await
2002 .unwrap()
2003 .unwrap();
2004
2005 assert_eq!(event.event_type, EventType::NeighboursResponse);
2006 }
2007
2008 #[tokio::test]
2009 async fn test_handle_rx_binary_response_status() {
2010 let (reader, dispatcher) = create_reader();
2011 let mut receiver = dispatcher.receiver();
2012
2013 reader
2015 .register_binary_request(
2016 &[0x01, 0x02, 0x03, 0x04],
2017 BinaryReqType::Status,
2018 vec![],
2019 Duration::from_secs(30),
2020 HashMap::new(),
2021 false,
2022 )
2023 .await;
2024
2025 let mut data = vec![PacketType::BinaryResponse as u8];
2026 data.extend_from_slice(&[0x01, 0x02, 0x03, 0x04]); let mut status_data = vec![0u8; 52];
2029 status_data[0..2].copy_from_slice(&100u16.to_le_bytes());
2030 data.extend_from_slice(&status_data);
2031
2032 reader.handle_rx(data).await.unwrap();
2033
2034 let event = tokio::time::timeout(Duration::from_millis(100), receiver.recv())
2035 .await
2036 .unwrap()
2037 .unwrap();
2038
2039 assert_eq!(event.event_type, EventType::StatusResponse);
2040 }
2041
2042 #[tokio::test]
2043 async fn test_handle_rx_binary_response_keepalive() {
2044 let (reader, dispatcher) = create_reader();
2045 let mut receiver = dispatcher.receiver();
2046
2047 reader
2049 .register_binary_request(
2050 &[0x01, 0x02, 0x03, 0x04],
2051 BinaryReqType::KeepAlive,
2052 vec![],
2053 Duration::from_secs(30),
2054 HashMap::new(),
2055 false,
2056 )
2057 .await;
2058
2059 let mut data = vec![PacketType::BinaryResponse as u8];
2060 data.extend_from_slice(&[0x01, 0x02, 0x03, 0x04]); data.extend_from_slice(&[0xAA, 0xBB]);
2062
2063 reader.handle_rx(data).await.unwrap();
2064
2065 let event = tokio::time::timeout(Duration::from_millis(100), receiver.recv())
2066 .await
2067 .unwrap()
2068 .unwrap();
2069
2070 assert_eq!(event.event_type, EventType::BinaryResponse);
2071 }
2072
2073 #[tokio::test]
2074 async fn test_handle_rx_control_data_discover_resp() {
2075 let (reader, dispatcher) = create_reader();
2076 let mut receiver = dispatcher.receiver();
2077
2078 let mut data = vec![PacketType::ControlData as u8];
2079 data.push(ControlType::NodeDiscoverResp as u8);
2080 let mut entry = vec![0u8; 64];
2082 entry[0..6].copy_from_slice(&[0x01, 0x02, 0x03, 0x04, 0x05, 0x06]);
2083 entry[32..37].copy_from_slice(b"Node1");
2084 data.extend_from_slice(&entry);
2085
2086 reader.handle_rx(data).await.unwrap();
2087
2088 let event = tokio::time::timeout(Duration::from_millis(100), receiver.recv())
2089 .await
2090 .unwrap()
2091 .unwrap();
2092
2093 assert_eq!(event.event_type, EventType::DiscoverResponse);
2094 match event.payload {
2095 EventPayload::DiscoverResponse(entries) => {
2096 assert_eq!(entries.len(), 1);
2097 assert_eq!(entries[0].name, "Node1");
2098 }
2099 _ => panic!("Expected DiscoverResponse payload"),
2100 }
2101 }
2102
2103 #[tokio::test]
2104 async fn test_handle_rx_control_data_other() {
2105 let (reader, dispatcher) = create_reader();
2106 let mut receiver = dispatcher.receiver();
2107
2108 let mut data = vec![PacketType::ControlData as u8];
2109 data.push(ControlType::NodeDiscoverReq as u8); data.extend_from_slice(&[0x01, 0x02, 0x03]);
2111
2112 reader.handle_rx(data).await.unwrap();
2113
2114 let event = tokio::time::timeout(Duration::from_millis(100), receiver.recv())
2115 .await
2116 .unwrap()
2117 .unwrap();
2118
2119 assert_eq!(event.event_type, EventType::ControlData);
2120 match event.payload {
2121 EventPayload::Bytes(d) => {
2122 assert_eq!(d[0], ControlType::NodeDiscoverReq as u8);
2123 }
2124 _ => panic!("Expected Bytes payload"),
2125 }
2126 }
2127
2128 #[tokio::test]
2129 async fn test_handle_rx_advert_response() {
2130 let (reader, dispatcher) = create_reader();
2131 let mut receiver = dispatcher.receiver();
2132
2133 let mut data = vec![PacketType::AdvertResponse as u8];
2134 data.extend_from_slice(&[0x01, 0x02, 0x03, 0x04]); data.extend_from_slice(&[0xAA; 32]); data.push(1); let mut name = [0u8; 32];
2139 name[..5].copy_from_slice(b"Node1");
2140 data.extend_from_slice(&name); data.extend_from_slice(&1234567890u32.to_le_bytes()); data.push(0x01); data.extend_from_slice(&37774900i32.to_le_bytes());
2145 data.extend_from_slice(&(-122419400i32).to_le_bytes());
2146
2147 reader.handle_rx(data).await.unwrap();
2148
2149 let event = tokio::time::timeout(Duration::from_millis(100), receiver.recv())
2150 .await
2151 .unwrap()
2152 .unwrap();
2153
2154 assert_eq!(event.event_type, EventType::AdvertResponse);
2155 match event.payload {
2156 EventPayload::AdvertResponse(resp) => {
2157 assert_eq!(resp.tag, [0x01, 0x02, 0x03, 0x04]);
2158 assert_eq!(resp.adv_type, 1);
2159 assert_eq!(resp.node_name, "Node1");
2160 assert_eq!(resp.lat, Some(37774900));
2161 }
2162 _ => panic!("Expected AdvertResponse payload"),
2163 }
2164 }
2165
2166 #[tokio::test]
2167 async fn test_handle_rx_contact_end_with_timestamp() {
2168 let (reader, dispatcher) = create_reader();
2169 let mut receiver = dispatcher.receiver();
2170
2171 reader.pending_contacts.write().await.push(Contact {
2173 public_key: [0u8; 32],
2174 contact_type: 1,
2175 flags: 0,
2176 path_len: 0,
2177 out_path: vec![],
2178 adv_name: "Test".to_string(),
2179 last_advert: 0,
2180 adv_lat: 0,
2181 adv_lon: 0,
2182 last_modification_timestamp: 0,
2183 });
2184
2185 let mut data = vec![PacketType::ContactEnd as u8];
2186 data.extend_from_slice(&1234567890u32.to_le_bytes());
2187
2188 reader.handle_rx(data).await.unwrap();
2189
2190 let event = tokio::time::timeout(Duration::from_millis(100), receiver.recv())
2191 .await
2192 .unwrap()
2193 .unwrap();
2194
2195 assert_eq!(event.event_type, EventType::Contacts);
2196 assert_eq!(
2197 event.attributes.get("lastmod"),
2198 Some(&"1234567890".to_string())
2199 );
2200 match event.payload {
2201 EventPayload::Contacts(contacts) => assert_eq!(contacts.len(), 1),
2202 _ => panic!("Expected Contacts payload"),
2203 }
2204 }
2205}