1use std::sync::atomic::{AtomicBool, AtomicI16, AtomicU32, AtomicU64, Ordering};
16use std::time::Duration;
17
18use async_trait::async_trait;
19use tokio::sync::RwLock;
20
21use aranet_types::{CurrentReading, DeviceInfo, DeviceType, HistoryRecord, Status};
22
23use crate::error::{Error, Result};
24use crate::history::{HistoryInfo, HistoryOptions};
25use crate::settings::{CalibrationData, MeasurementInterval};
26use crate::traits::AranetDevice;
27
28pub struct MockDevice {
51 name: String,
52 address: String,
53 device_type: DeviceType,
54 connected: AtomicBool,
55 current_reading: RwLock<CurrentReading>,
56 device_info: RwLock<DeviceInfo>,
57 history: RwLock<Vec<HistoryRecord>>,
58 interval: RwLock<MeasurementInterval>,
59 calibration: RwLock<CalibrationData>,
60 battery: RwLock<u8>,
61 rssi: AtomicI16,
62 read_count: AtomicU32,
63 should_fail: AtomicBool,
64 fail_message: RwLock<String>,
65 read_latency_ms: AtomicU64,
67 connect_latency_ms: AtomicU64,
69 fail_count: AtomicU32,
71 remaining_failures: AtomicU32,
73}
74
75impl std::fmt::Debug for MockDevice {
76 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
77 f.debug_struct("MockDevice")
78 .field("name", &self.name)
79 .field("address", &self.address)
80 .field("device_type", &self.device_type)
81 .field("connected", &self.connected.load(Ordering::Relaxed))
82 .finish()
83 }
84}
85
86impl MockDevice {
87 pub fn new(name: &str, device_type: DeviceType) -> Self {
89 Self {
90 name: name.to_string(),
91 address: format!("MOCK-{:06X}", rand::random::<u32>() % 0xFFFFFF),
92 device_type,
93 connected: AtomicBool::new(false),
94 current_reading: RwLock::new(Self::default_reading()),
95 device_info: RwLock::new(Self::default_info(name)),
96 history: RwLock::new(Vec::new()),
97 interval: RwLock::new(MeasurementInterval::FiveMinutes),
98 calibration: RwLock::new(CalibrationData::default()),
99 battery: RwLock::new(85),
100 rssi: AtomicI16::new(-50),
101 read_count: AtomicU32::new(0),
102 should_fail: AtomicBool::new(false),
103 fail_message: RwLock::new("Mock failure".to_string()),
104 read_latency_ms: AtomicU64::new(0),
105 connect_latency_ms: AtomicU64::new(0),
106 fail_count: AtomicU32::new(0),
107 remaining_failures: AtomicU32::new(0),
108 }
109 }
110
111 fn default_reading() -> CurrentReading {
112 CurrentReading {
113 co2: 800,
114 temperature: 22.5,
115 pressure: 1013.2,
116 humidity: 50,
117 battery: 85,
118 status: Status::Green,
119 interval: 300,
120 age: 60,
121 captured_at: None,
122 radon: None,
123 radiation_rate: None,
124 radiation_total: None,
125 radon_avg_24h: None,
126 radon_avg_7d: None,
127 radon_avg_30d: None,
128 }
129 }
130
131 fn default_info(name: &str) -> DeviceInfo {
132 DeviceInfo {
133 name: name.to_string(),
134 model: "Aranet4".to_string(),
135 serial: "MOCK-12345".to_string(),
136 firmware: "v1.5.0".to_string(),
137 hardware: "1.0".to_string(),
138 software: "1.5.0".to_string(),
139 manufacturer: "SAF Tehnika".to_string(),
140 }
141 }
142
143 pub async fn connect(&self) -> Result<()> {
145 use crate::error::DeviceNotFoundReason;
146
147 let latency = self.connect_latency_ms.load(Ordering::Relaxed);
149 if latency > 0 {
150 tokio::time::sleep(Duration::from_millis(latency)).await;
151 }
152
153 if self.remaining_failures.load(Ordering::Relaxed) > 0 {
155 self.remaining_failures.fetch_sub(1, Ordering::Relaxed);
156 return Err(Error::DeviceNotFound(DeviceNotFoundReason::NotFound {
157 identifier: self.name.clone(),
158 }));
159 }
160
161 if self.should_fail.load(Ordering::Relaxed) {
162 return Err(Error::DeviceNotFound(DeviceNotFoundReason::NotFound {
163 identifier: self.name.clone(),
164 }));
165 }
166 self.connected.store(true, Ordering::Relaxed);
167 Ok(())
168 }
169
170 pub async fn disconnect(&self) -> Result<()> {
172 self.connected.store(false, Ordering::Relaxed);
173 Ok(())
174 }
175
176 pub fn is_connected_sync(&self) -> bool {
178 self.connected.load(Ordering::Relaxed)
179 }
180
181 pub fn name(&self) -> &str {
183 &self.name
184 }
185
186 pub fn address(&self) -> &str {
188 &self.address
189 }
190
191 pub fn device_type(&self) -> DeviceType {
193 self.device_type
194 }
195
196 pub async fn read_current(&self) -> Result<CurrentReading> {
198 self.check_connected()?;
199 self.check_should_fail().await?;
200
201 self.read_count.fetch_add(1, Ordering::Relaxed);
202 Ok(*self.current_reading.read().await)
203 }
204
205 pub async fn read_battery(&self) -> Result<u8> {
207 self.check_connected()?;
208 self.check_should_fail().await?;
209 Ok(*self.battery.read().await)
210 }
211
212 pub async fn read_rssi(&self) -> Result<i16> {
214 self.check_connected()?;
215 self.check_should_fail().await?;
216 Ok(self.rssi.load(Ordering::Relaxed))
217 }
218
219 pub async fn read_device_info(&self) -> Result<DeviceInfo> {
221 self.check_connected()?;
222 self.check_should_fail().await?;
223 Ok(self.device_info.read().await.clone())
224 }
225
226 pub async fn get_history_info(&self) -> Result<HistoryInfo> {
228 self.check_connected()?;
229 self.check_should_fail().await?;
230
231 let history = self.history.read().await;
232 let interval = self.interval.read().await;
233
234 Ok(HistoryInfo {
235 total_readings: history.len() as u16,
236 interval_seconds: interval.as_seconds(),
237 seconds_since_update: 60,
238 })
239 }
240
241 pub async fn download_history(&self) -> Result<Vec<HistoryRecord>> {
243 self.check_connected()?;
244 self.check_should_fail().await?;
245 Ok(self.history.read().await.clone())
246 }
247
248 pub async fn download_history_with_options(
250 &self,
251 options: HistoryOptions,
252 ) -> Result<Vec<HistoryRecord>> {
253 self.check_connected()?;
254 self.check_should_fail().await?;
255
256 let history = self.history.read().await;
257 let start = options.start_index.unwrap_or(0) as usize;
258 let end = options
259 .end_index
260 .map(|e| e as usize)
261 .unwrap_or(history.len());
262
263 if let Some(ref _callback) = options.progress_callback {
265 let progress = crate::history::HistoryProgress::new(
267 crate::history::HistoryParam::Co2,
268 1,
269 1,
270 history.len().min(end).saturating_sub(start),
271 );
272 options.report_progress(&progress);
273 }
274
275 Ok(history
276 .iter()
277 .skip(start)
278 .take(end.saturating_sub(start))
279 .cloned()
280 .collect())
281 }
282
283 pub async fn get_interval(&self) -> Result<MeasurementInterval> {
285 self.check_connected()?;
286 self.check_should_fail().await?;
287 Ok(*self.interval.read().await)
288 }
289
290 pub async fn set_interval(&self, interval: MeasurementInterval) -> Result<()> {
292 self.check_connected()?;
293 self.check_should_fail().await?;
294 *self.interval.write().await = interval;
295 Ok(())
296 }
297
298 pub async fn get_calibration(&self) -> Result<CalibrationData> {
300 self.check_connected()?;
301 self.check_should_fail().await?;
302 Ok(self.calibration.read().await.clone())
303 }
304
305 fn check_connected(&self) -> Result<()> {
306 if !self.connected.load(Ordering::Relaxed) {
307 Err(Error::NotConnected)
308 } else {
309 Ok(())
310 }
311 }
312
313 async fn check_should_fail(&self) -> Result<()> {
314 let latency = self.read_latency_ms.load(Ordering::Relaxed);
316 if latency > 0 {
317 tokio::time::sleep(Duration::from_millis(latency)).await;
318 }
319
320 if self.remaining_failures.load(Ordering::Relaxed) > 0 {
322 self.remaining_failures.fetch_sub(1, Ordering::Relaxed);
323 return Err(Error::InvalidData(self.fail_message.read().await.clone()));
324 }
325
326 if self.should_fail.load(Ordering::Relaxed) {
327 Err(Error::InvalidData(self.fail_message.read().await.clone()))
328 } else {
329 Ok(())
330 }
331 }
332
333 pub async fn set_reading(&self, reading: CurrentReading) {
337 *self.current_reading.write().await = reading;
338 }
339
340 pub async fn set_co2(&self, co2: u16) {
342 self.current_reading.write().await.co2 = co2;
343 }
344
345 pub async fn set_temperature(&self, temp: f32) {
347 self.current_reading.write().await.temperature = temp;
348 }
349
350 pub async fn set_battery(&self, level: u8) {
352 *self.battery.write().await = level;
353 self.current_reading.write().await.battery = level;
354 }
355
356 pub fn set_rssi(&self, rssi: i16) {
358 self.rssi.store(rssi, Ordering::Relaxed);
359 }
360
361 pub async fn add_history(&self, records: Vec<HistoryRecord>) {
363 self.history.write().await.extend(records);
364 }
365
366 pub async fn set_should_fail(&self, fail: bool, message: Option<&str>) {
368 self.should_fail.store(fail, Ordering::Relaxed);
369 if let Some(msg) = message {
370 *self.fail_message.write().await = msg.to_string();
371 }
372 }
373
374 pub fn read_count(&self) -> u32 {
376 self.read_count.load(Ordering::Relaxed)
377 }
378
379 pub fn reset_read_count(&self) {
381 self.read_count.store(0, Ordering::Relaxed);
382 }
383
384 pub fn set_read_latency(&self, latency: Duration) {
389 self.read_latency_ms
390 .store(latency.as_millis() as u64, Ordering::Relaxed);
391 }
392
393 pub fn set_connect_latency(&self, latency: Duration) {
398 self.connect_latency_ms
399 .store(latency.as_millis() as u64, Ordering::Relaxed);
400 }
401
402 pub fn set_transient_failures(&self, count: u32) {
418 self.fail_count.store(count, Ordering::Relaxed);
419 self.remaining_failures.store(count, Ordering::Relaxed);
420 }
421
422 pub fn reset_transient_failures(&self) {
424 self.remaining_failures
425 .store(self.fail_count.load(Ordering::Relaxed), Ordering::Relaxed);
426 }
427
428 pub fn remaining_failures(&self) -> u32 {
430 self.remaining_failures.load(Ordering::Relaxed)
431 }
432}
433
434#[async_trait]
436impl AranetDevice for MockDevice {
437 async fn is_connected(&self) -> bool {
440 self.is_connected_sync()
441 }
442
443 async fn disconnect(&self) -> Result<()> {
444 MockDevice::disconnect(self).await
445 }
446
447 fn name(&self) -> Option<&str> {
450 Some(MockDevice::name(self))
451 }
452
453 fn address(&self) -> &str {
454 MockDevice::address(self)
455 }
456
457 fn device_type(&self) -> Option<DeviceType> {
458 Some(MockDevice::device_type(self))
459 }
460
461 async fn read_current(&self) -> Result<CurrentReading> {
464 MockDevice::read_current(self).await
465 }
466
467 async fn read_device_info(&self) -> Result<DeviceInfo> {
468 MockDevice::read_device_info(self).await
469 }
470
471 async fn read_rssi(&self) -> Result<i16> {
472 MockDevice::read_rssi(self).await
473 }
474
475 async fn read_battery(&self) -> Result<u8> {
478 MockDevice::read_battery(self).await
479 }
480
481 async fn get_history_info(&self) -> Result<crate::history::HistoryInfo> {
484 MockDevice::get_history_info(self).await
485 }
486
487 async fn download_history(&self) -> Result<Vec<HistoryRecord>> {
488 MockDevice::download_history(self).await
489 }
490
491 async fn download_history_with_options(
492 &self,
493 options: HistoryOptions,
494 ) -> Result<Vec<HistoryRecord>> {
495 MockDevice::download_history_with_options(self, options).await
496 }
497
498 async fn get_interval(&self) -> Result<MeasurementInterval> {
501 MockDevice::get_interval(self).await
502 }
503
504 async fn set_interval(&self, interval: MeasurementInterval) -> Result<()> {
505 MockDevice::set_interval(self, interval).await
506 }
507
508 async fn get_calibration(&self) -> Result<CalibrationData> {
509 MockDevice::get_calibration(self).await
510 }
511}
512
513#[derive(Debug)]
515pub struct MockDeviceBuilder {
516 name: String,
517 device_type: DeviceType,
518 co2: u16,
519 temperature: f32,
520 pressure: f32,
521 humidity: u8,
522 battery: u8,
523 status: Status,
524 auto_connect: bool,
525}
526
527impl Default for MockDeviceBuilder {
528 fn default() -> Self {
529 Self {
530 name: "Mock Aranet4".to_string(),
531 device_type: DeviceType::Aranet4,
532 co2: 800,
533 temperature: 22.5,
534 pressure: 1013.2,
535 humidity: 50,
536 battery: 85,
537 status: Status::Green,
538 auto_connect: true,
539 }
540 }
541}
542
543impl MockDeviceBuilder {
544 #[must_use]
546 pub fn new() -> Self {
547 Self::default()
548 }
549
550 #[must_use]
552 pub fn name(mut self, name: &str) -> Self {
553 self.name = name.to_string();
554 self
555 }
556
557 #[must_use]
559 pub fn device_type(mut self, device_type: DeviceType) -> Self {
560 self.device_type = device_type;
561 self
562 }
563
564 #[must_use]
566 pub fn co2(mut self, co2: u16) -> Self {
567 self.co2 = co2;
568 self
569 }
570
571 #[must_use]
573 pub fn temperature(mut self, temp: f32) -> Self {
574 self.temperature = temp;
575 self
576 }
577
578 #[must_use]
580 pub fn pressure(mut self, pressure: f32) -> Self {
581 self.pressure = pressure;
582 self
583 }
584
585 #[must_use]
587 pub fn humidity(mut self, humidity: u8) -> Self {
588 self.humidity = humidity;
589 self
590 }
591
592 #[must_use]
594 pub fn battery(mut self, battery: u8) -> Self {
595 self.battery = battery;
596 self
597 }
598
599 #[must_use]
601 pub fn status(mut self, status: Status) -> Self {
602 self.status = status;
603 self
604 }
605
606 #[must_use]
608 pub fn auto_connect(mut self, auto: bool) -> Self {
609 self.auto_connect = auto;
610 self
611 }
612
613 #[must_use]
618 pub fn build(self) -> MockDevice {
619 let reading = CurrentReading {
620 co2: self.co2,
621 temperature: self.temperature,
622 pressure: self.pressure,
623 humidity: self.humidity,
624 battery: self.battery,
625 status: self.status,
626 interval: 300,
627 age: 60,
628 captured_at: None,
629 radon: None,
630 radiation_rate: None,
631 radiation_total: None,
632 radon_avg_24h: None,
633 radon_avg_7d: None,
634 radon_avg_30d: None,
635 };
636
637 MockDevice {
638 name: self.name.clone(),
639 address: format!("MOCK-{:06X}", rand::random::<u32>() % 0xFFFFFF),
640 device_type: self.device_type,
641 connected: AtomicBool::new(self.auto_connect),
642 current_reading: RwLock::new(reading),
643 device_info: RwLock::new(MockDevice::default_info(&self.name)),
644 history: RwLock::new(Vec::new()),
645 interval: RwLock::new(MeasurementInterval::FiveMinutes),
646 calibration: RwLock::new(CalibrationData::default()),
647 battery: RwLock::new(self.battery),
648 rssi: AtomicI16::new(-50),
649 read_count: AtomicU32::new(0),
650 should_fail: AtomicBool::new(false),
651 fail_message: RwLock::new("Mock failure".to_string()),
652 read_latency_ms: AtomicU64::new(0),
653 connect_latency_ms: AtomicU64::new(0),
654 fail_count: AtomicU32::new(0),
655 remaining_failures: AtomicU32::new(0),
656 }
657 }
658}
659
660#[cfg(test)]
705mod tests {
706 use super::*;
707 use crate::traits::AranetDevice;
708
709 #[tokio::test]
710 async fn test_mock_device_connect() {
711 let device = MockDevice::new("Test", DeviceType::Aranet4);
712 assert!(!device.is_connected_sync());
713
714 device.connect().await.unwrap();
715 assert!(device.is_connected_sync());
716
717 device.disconnect().await.unwrap();
718 assert!(!device.is_connected_sync());
719 }
720
721 #[tokio::test]
722 async fn test_mock_device_read() {
723 let device = MockDeviceBuilder::new().co2(1200).temperature(25.0).build();
724
725 let reading = device.read_current().await.unwrap();
726 assert_eq!(reading.co2, 1200);
727 assert!((reading.temperature - 25.0).abs() < 0.01);
728 }
729
730 #[tokio::test]
731 async fn test_mock_device_fail() {
732 let device = MockDeviceBuilder::new().build();
733 device.set_should_fail(true, Some("Test error")).await;
734
735 let result = device.read_current().await;
736 assert!(result.is_err());
737 assert!(result.unwrap_err().to_string().contains("Test error"));
738 }
739
740 #[tokio::test]
741 async fn test_mock_device_not_connected() {
742 let device = MockDeviceBuilder::new().auto_connect(false).build();
743
744 let result = device.read_current().await;
745 assert!(matches!(result, Err(Error::NotConnected)));
746 }
747
748 #[test]
749 fn test_builder_defaults() {
750 let device = MockDeviceBuilder::new().build();
751 assert!(device.is_connected_sync());
752 assert_eq!(device.device_type(), DeviceType::Aranet4);
753 }
754
755 #[tokio::test]
756 async fn test_aranet_device_trait() {
757 let device = MockDeviceBuilder::new().co2(1000).build();
758
759 async fn check_via_trait<D: AranetDevice>(d: &D) -> u16 {
761 d.read_current().await.unwrap().co2
762 }
763
764 assert_eq!(check_via_trait(&device).await, 1000);
765 }
766
767 #[tokio::test]
768 async fn test_mock_device_read_battery() {
769 let device = MockDeviceBuilder::new().battery(75).build();
770 let battery = device.read_battery().await.unwrap();
771 assert_eq!(battery, 75);
772 }
773
774 #[tokio::test]
775 async fn test_mock_device_read_rssi() {
776 let device = MockDeviceBuilder::new().build();
777 device.set_rssi(-65);
778 let rssi = device.read_rssi().await.unwrap();
779 assert_eq!(rssi, -65);
780 }
781
782 #[tokio::test]
783 async fn test_mock_device_read_device_info() {
784 let device = MockDeviceBuilder::new().name("Test Device").build();
785 let info = device.read_device_info().await.unwrap();
786 assert_eq!(info.name, "Test Device");
787 assert_eq!(info.manufacturer, "SAF Tehnika");
788 }
789
790 #[tokio::test]
791 async fn test_mock_device_history() {
792 let device = MockDeviceBuilder::new().build();
793
794 let history = device.download_history().await.unwrap();
796 assert!(history.is_empty());
797
798 let records = vec![
800 HistoryRecord {
801 timestamp: time::OffsetDateTime::now_utc(),
802 co2: 800,
803 temperature: 22.5,
804 pressure: 1013.2,
805 humidity: 50,
806 radon: None,
807 radiation_rate: None,
808 radiation_total: None,
809 },
810 HistoryRecord {
811 timestamp: time::OffsetDateTime::now_utc(),
812 co2: 850,
813 temperature: 23.0,
814 pressure: 1013.5,
815 humidity: 48,
816 radon: None,
817 radiation_rate: None,
818 radiation_total: None,
819 },
820 ];
821 device.add_history(records).await;
822
823 let history = device.download_history().await.unwrap();
824 assert_eq!(history.len(), 2);
825 assert_eq!(history[0].co2, 800);
826 assert_eq!(history[1].co2, 850);
827 }
828
829 #[tokio::test]
830 async fn test_mock_device_history_with_options() {
831 let device = MockDeviceBuilder::new().build();
832
833 let records: Vec<HistoryRecord> = (0..5)
835 .map(|i| HistoryRecord {
836 timestamp: time::OffsetDateTime::now_utc(),
837 co2: 800 + i as u16 * 10,
838 temperature: 22.0,
839 pressure: 1013.0,
840 humidity: 50,
841 radon: None,
842 radiation_rate: None,
843 radiation_total: None,
844 })
845 .collect();
846 device.add_history(records).await;
847
848 let options = HistoryOptions {
850 start_index: Some(1),
851 end_index: Some(4),
852 ..Default::default()
853 };
854 let history = device.download_history_with_options(options).await.unwrap();
855 assert_eq!(history.len(), 3);
856 assert_eq!(history[0].co2, 810); assert_eq!(history[2].co2, 830); }
859
860 #[tokio::test]
861 async fn test_mock_device_interval() {
862 let device = MockDeviceBuilder::new().build();
863
864 let interval = device.get_interval().await.unwrap();
865 assert_eq!(interval, MeasurementInterval::FiveMinutes);
866
867 device
868 .set_interval(MeasurementInterval::TenMinutes)
869 .await
870 .unwrap();
871 let interval = device.get_interval().await.unwrap();
872 assert_eq!(interval, MeasurementInterval::TenMinutes);
873 }
874
875 #[tokio::test]
876 async fn test_mock_device_calibration() {
877 let device = MockDeviceBuilder::new().build();
878 let calibration = device.get_calibration().await.unwrap();
879 assert!(calibration.co2_offset.is_some() || calibration.co2_offset.is_none());
881 }
882
883 #[tokio::test]
884 async fn test_mock_device_read_count() {
885 let device = MockDeviceBuilder::new().build();
886 assert_eq!(device.read_count(), 0);
887
888 device.read_current().await.unwrap();
889 assert_eq!(device.read_count(), 1);
890
891 device.read_current().await.unwrap();
892 device.read_current().await.unwrap();
893 assert_eq!(device.read_count(), 3);
894
895 device.reset_read_count();
896 assert_eq!(device.read_count(), 0);
897 }
898
899 #[tokio::test]
900 async fn test_mock_device_transient_failures() {
901 let device = MockDeviceBuilder::new().build();
902 device.set_transient_failures(2);
903
904 assert!(device.read_current().await.is_err());
906 assert!(device.read_current().await.is_err());
907
908 assert!(device.read_current().await.is_ok());
910 }
911
912 #[tokio::test]
913 async fn test_mock_device_set_values() {
914 let device = MockDeviceBuilder::new().build();
915
916 device.set_co2(1500).await;
917 device.set_temperature(30.0).await;
918 device.set_battery(50).await;
919
920 let reading = device.read_current().await.unwrap();
921 assert_eq!(reading.co2, 1500);
922 assert!((reading.temperature - 30.0).abs() < 0.01);
923 assert_eq!(reading.battery, 50);
924 }
925
926 #[tokio::test]
927 async fn test_mock_device_history_info() {
928 let device = MockDeviceBuilder::new().build();
929
930 let records: Vec<HistoryRecord> = (0..10)
932 .map(|_| HistoryRecord {
933 timestamp: time::OffsetDateTime::now_utc(),
934 co2: 800,
935 temperature: 22.0,
936 pressure: 1013.0,
937 humidity: 50,
938 radon: None,
939 radiation_rate: None,
940 radiation_total: None,
941 })
942 .collect();
943 device.add_history(records).await;
944
945 let info = device.get_history_info().await.unwrap();
946 assert_eq!(info.total_readings, 10);
947 assert_eq!(info.interval_seconds, 300); }
949
950 #[tokio::test]
951 async fn test_mock_device_debug() {
952 let device = MockDevice::new("Debug Test", DeviceType::Aranet4);
953 let debug_str = format!("{:?}", device);
954 assert!(debug_str.contains("MockDevice"));
955 assert!(debug_str.contains("Debug Test"));
956 assert!(debug_str.contains("Aranet4"));
957 }
958
959 #[test]
960 fn test_builder_all_options() {
961 let device = MockDeviceBuilder::new()
962 .name("Custom Device")
963 .device_type(DeviceType::Aranet2)
964 .co2(0)
965 .temperature(18.5)
966 .pressure(1020.0)
967 .humidity(65)
968 .battery(90)
969 .status(Status::Yellow)
970 .auto_connect(false)
971 .build();
972
973 assert_eq!(device.name(), "Custom Device");
974 assert_eq!(device.device_type(), DeviceType::Aranet2);
975 assert!(!device.is_connected_sync());
976 }
977
978 #[tokio::test]
979 async fn test_trait_methods_match_direct_methods() {
980 let device = MockDeviceBuilder::new()
981 .name("Trait Test")
982 .co2(999)
983 .battery(77)
984 .build();
985 device.set_rssi(-55);
986
987 let trait_device: &dyn AranetDevice = &device;
989
990 assert_eq!(trait_device.name(), Some("Trait Test"));
991 assert_eq!(trait_device.device_type(), Some(DeviceType::Aranet4));
992 assert!(trait_device.is_connected().await);
993
994 let reading = trait_device.read_current().await.unwrap();
995 assert_eq!(reading.co2, 999);
996
997 let battery = trait_device.read_battery().await.unwrap();
998 assert_eq!(battery, 77);
999
1000 let rssi = trait_device.read_rssi().await.unwrap();
1001 assert_eq!(rssi, -55);
1002 }
1003}