1pub mod error;
22pub mod types;
23pub mod uuid;
24
25pub use error::{ParseError, ParseResult};
26pub use types::{
27 CurrentReading, CurrentReadingBuilder, DeviceInfo, DeviceInfoBuilder, DeviceType,
28 HistoryRecord, HistoryRecordBuilder, MIN_CURRENT_READING_BYTES, Status,
29};
30
31pub use uuid as ble;
34#[doc(hidden)]
35pub use uuid as uuids;
36
37#[cfg(test)]
38mod tests {
39 use super::*;
40
41 #[test]
44 fn test_parse_current_reading_from_valid_bytes() {
45 let bytes: [u8; 13] = [
55 0x20, 0x03, 0xC2, 0x01, 0x94, 0x27, 45, 85, 1, 0x2C, 0x01, 0x78, 0x00, ];
64
65 let reading = CurrentReading::from_bytes(&bytes).unwrap();
66
67 assert_eq!(reading.co2, 800);
68 assert!((reading.temperature - 22.5).abs() < 0.01);
69 assert!((reading.pressure - 1013.2).abs() < 0.1);
70 assert_eq!(reading.humidity, 45);
71 assert_eq!(reading.battery, 85);
72 assert_eq!(reading.status, Status::Green);
73 assert_eq!(reading.interval, 300);
74 assert_eq!(reading.age, 120);
75 }
76
77 #[test]
78 fn test_parse_current_reading_from_insufficient_bytes() {
79 let bytes: [u8; 10] = [0; 10]; let result = CurrentReading::from_bytes(&bytes);
82
83 assert!(result.is_err());
84 let err = result.unwrap_err();
85 assert_eq!(
86 err,
87 ParseError::InsufficientBytes {
88 expected: 13,
89 actual: 10
90 }
91 );
92 assert!(err.to_string().contains("expected 13"));
93 assert!(err.to_string().contains("got 10"));
94 }
95
96 #[test]
97 fn test_parse_current_reading_zero_bytes() {
98 let bytes: [u8; 0] = [];
99
100 let result = CurrentReading::from_bytes(&bytes);
101 assert!(result.is_err());
102 }
103
104 #[test]
105 fn test_parse_current_reading_all_zeros() {
106 let bytes: [u8; 13] = [0; 13];
107
108 let reading = CurrentReading::from_bytes(&bytes).unwrap();
109 assert_eq!(reading.co2, 0);
110 assert!((reading.temperature - 0.0).abs() < 0.01);
111 assert!((reading.pressure - 0.0).abs() < 0.1);
112 assert_eq!(reading.humidity, 0);
113 assert_eq!(reading.battery, 0);
114 assert_eq!(reading.status, Status::Error);
115 assert_eq!(reading.interval, 0);
116 assert_eq!(reading.age, 0);
117 }
118
119 #[test]
120 fn test_parse_current_reading_max_values() {
121 let bytes: [u8; 13] = [
122 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 3, 0xFF, 0xFF, 0xFF, 0xFF, ];
131
132 let reading = CurrentReading::from_bytes(&bytes).unwrap();
133 assert_eq!(reading.co2, 65535);
134 assert!((reading.temperature - 3276.75).abs() < 0.01); assert!((reading.pressure - 6553.5).abs() < 0.1); assert_eq!(reading.humidity, 255);
137 assert_eq!(reading.battery, 255);
138 assert_eq!(reading.interval, 65535);
139 assert_eq!(reading.age, 65535);
140 }
141
142 #[test]
143 fn test_parse_current_reading_high_co2_red_status() {
144 let bytes: [u8; 13] = [
146 0xD0, 0x07, 0xC2, 0x01, 0x94, 0x27, 50, 80, 3, 0x2C, 0x01, 0x78, 0x00,
151 ];
152
153 let reading = CurrentReading::from_bytes(&bytes).unwrap();
154 assert_eq!(reading.co2, 2000);
155 assert_eq!(reading.status, Status::Red);
156 }
157
158 #[test]
159 fn test_parse_current_reading_moderate_co2_yellow_status() {
160 let bytes: [u8; 13] = [
162 0xB0, 0x04, 0xC2, 0x01, 0x94, 0x27, 50, 80, 2, 0x2C, 0x01, 0x78, 0x00,
165 ];
166
167 let reading = CurrentReading::from_bytes(&bytes).unwrap();
168 assert_eq!(reading.co2, 1200);
169 assert_eq!(reading.status, Status::Yellow);
170 }
171
172 #[test]
173 fn test_parse_current_reading_extra_bytes_ignored() {
174 let bytes: [u8; 16] = [
176 0x20, 0x03, 0xC2, 0x01, 0x94, 0x27, 45, 85, 1, 0x2C, 0x01, 0x78, 0x00, 0xAA, 0xBB, 0xCC,
177 ];
178
179 let reading = CurrentReading::from_bytes(&bytes).unwrap();
180 assert_eq!(reading.co2, 800);
181 }
182
183 #[test]
186 fn test_status_from_u8() {
187 assert_eq!(Status::from(0), Status::Error);
188 assert_eq!(Status::from(1), Status::Green);
189 assert_eq!(Status::from(2), Status::Yellow);
190 assert_eq!(Status::from(3), Status::Red);
191 assert_eq!(Status::from(4), Status::Error);
193 assert_eq!(Status::from(255), Status::Error);
194 }
195
196 #[test]
197 fn test_status_repr_values() {
198 assert_eq!(Status::Error as u8, 0);
199 assert_eq!(Status::Green as u8, 1);
200 assert_eq!(Status::Yellow as u8, 2);
201 assert_eq!(Status::Red as u8, 3);
202 }
203
204 #[test]
205 fn test_status_debug() {
206 assert_eq!(format!("{:?}", Status::Green), "Green");
207 assert_eq!(format!("{:?}", Status::Yellow), "Yellow");
208 assert_eq!(format!("{:?}", Status::Red), "Red");
209 assert_eq!(format!("{:?}", Status::Error), "Error");
210 }
211
212 #[test]
213 fn test_status_clone() {
214 let status = Status::Green;
215 let cloned = status.clone();
216 assert_eq!(status, cloned);
217 }
218
219 #[test]
220 fn test_status_copy() {
221 let status = Status::Red;
222 let copied = status; assert_eq!(status, copied); }
225
226 #[test]
229 fn test_device_type_values() {
230 assert_eq!(DeviceType::Aranet4 as u8, 0xF1);
231 assert_eq!(DeviceType::Aranet2 as u8, 0xF2);
232 assert_eq!(DeviceType::AranetRadon as u8, 0xF3);
233 assert_eq!(DeviceType::AranetRadiation as u8, 0xF4);
234 }
235
236 #[test]
237 fn test_device_type_debug() {
238 assert_eq!(format!("{:?}", DeviceType::Aranet4), "Aranet4");
239 assert_eq!(format!("{:?}", DeviceType::Aranet2), "Aranet2");
240 assert_eq!(format!("{:?}", DeviceType::AranetRadon), "AranetRadon");
241 assert_eq!(
242 format!("{:?}", DeviceType::AranetRadiation),
243 "AranetRadiation"
244 );
245 }
246
247 #[test]
248 fn test_device_type_clone() {
249 let device_type = DeviceType::Aranet4;
250 let cloned = device_type.clone();
251 assert_eq!(device_type, cloned);
252 }
253
254 #[test]
255 fn test_device_type_try_from_u8() {
256 assert_eq!(DeviceType::try_from(0xF1), Ok(DeviceType::Aranet4));
257 assert_eq!(DeviceType::try_from(0xF2), Ok(DeviceType::Aranet2));
258 assert_eq!(DeviceType::try_from(0xF3), Ok(DeviceType::AranetRadon));
259 assert_eq!(DeviceType::try_from(0xF4), Ok(DeviceType::AranetRadiation));
260 }
261
262 #[test]
263 fn test_device_type_try_from_u8_invalid() {
264 let result = DeviceType::try_from(0x00);
265 assert!(result.is_err());
266 assert_eq!(result.unwrap_err(), ParseError::UnknownDeviceType(0x00));
267
268 let result = DeviceType::try_from(0xFF);
269 assert!(result.is_err());
270 assert_eq!(result.unwrap_err(), ParseError::UnknownDeviceType(0xFF));
271 }
272
273 #[test]
274 fn test_device_type_display() {
275 assert_eq!(format!("{}", DeviceType::Aranet4), "Aranet4");
276 assert_eq!(format!("{}", DeviceType::Aranet2), "Aranet2");
277 assert_eq!(format!("{}", DeviceType::AranetRadon), "Aranet Radon");
278 assert_eq!(
279 format!("{}", DeviceType::AranetRadiation),
280 "Aranet Radiation"
281 );
282 }
283
284 #[test]
285 fn test_device_type_hash() {
286 use std::collections::HashSet;
287 let mut set = HashSet::new();
288 set.insert(DeviceType::Aranet4);
289 set.insert(DeviceType::Aranet2);
290 set.insert(DeviceType::Aranet4); assert_eq!(set.len(), 2);
292 assert!(set.contains(&DeviceType::Aranet4));
293 assert!(set.contains(&DeviceType::Aranet2));
294 }
295
296 #[test]
297 fn test_status_display() {
298 assert_eq!(format!("{}", Status::Error), "Error");
299 assert_eq!(format!("{}", Status::Green), "Good");
300 assert_eq!(format!("{}", Status::Yellow), "Moderate");
301 assert_eq!(format!("{}", Status::Red), "High");
302 }
303
304 #[test]
305 fn test_status_hash() {
306 use std::collections::HashSet;
307 let mut set = HashSet::new();
308 set.insert(Status::Green);
309 set.insert(Status::Yellow);
310 set.insert(Status::Green); assert_eq!(set.len(), 2);
312 assert!(set.contains(&Status::Green));
313 assert!(set.contains(&Status::Yellow));
314 }
315
316 #[test]
319 fn test_device_info_creation() {
320 let info = types::DeviceInfo {
321 name: "Aranet4 12345".to_string(),
322 model: "Aranet4".to_string(),
323 serial: "12345".to_string(),
324 firmware: "v1.2.0".to_string(),
325 hardware: "1.0".to_string(),
326 software: "1.2.0".to_string(),
327 manufacturer: "SAF Tehnika".to_string(),
328 };
329
330 assert_eq!(info.name, "Aranet4 12345");
331 assert_eq!(info.serial, "12345");
332 assert_eq!(info.manufacturer, "SAF Tehnika");
333 }
334
335 #[test]
336 fn test_device_info_clone() {
337 let info = types::DeviceInfo {
338 name: "Test".to_string(),
339 model: "Model".to_string(),
340 serial: "123".to_string(),
341 firmware: "1.0".to_string(),
342 hardware: "1.0".to_string(),
343 software: "1.0".to_string(),
344 manufacturer: "Mfg".to_string(),
345 };
346
347 let cloned = info.clone();
348 assert_eq!(cloned.name, info.name);
349 assert_eq!(cloned.serial, info.serial);
350 }
351
352 #[test]
353 fn test_device_info_debug() {
354 let info = types::DeviceInfo {
355 name: "Aranet4".to_string(),
356 model: "".to_string(),
357 serial: "".to_string(),
358 firmware: "".to_string(),
359 hardware: "".to_string(),
360 software: "".to_string(),
361 manufacturer: "".to_string(),
362 };
363
364 let debug_str = format!("{:?}", info);
365 assert!(debug_str.contains("Aranet4"));
366 }
367
368 #[test]
369 fn test_device_info_default() {
370 let info = types::DeviceInfo::default();
371 assert_eq!(info.name, "");
372 assert_eq!(info.model, "");
373 assert_eq!(info.serial, "");
374 assert_eq!(info.firmware, "");
375 assert_eq!(info.hardware, "");
376 assert_eq!(info.software, "");
377 assert_eq!(info.manufacturer, "");
378 }
379
380 #[test]
381 fn test_device_info_equality() {
382 let info1 = types::DeviceInfo {
383 name: "Test".to_string(),
384 model: "Model".to_string(),
385 serial: "123".to_string(),
386 firmware: "1.0".to_string(),
387 hardware: "1.0".to_string(),
388 software: "1.0".to_string(),
389 manufacturer: "Mfg".to_string(),
390 };
391 let info2 = info1.clone();
392 let info3 = types::DeviceInfo {
393 name: "Different".to_string(),
394 ..info1.clone()
395 };
396 assert_eq!(info1, info2);
397 assert_ne!(info1, info3);
398 }
399
400 #[test]
403 fn test_history_record_creation() {
404 use time::OffsetDateTime;
405
406 let record = types::HistoryRecord {
407 timestamp: OffsetDateTime::UNIX_EPOCH,
408 co2: 800,
409 temperature: 22.5,
410 pressure: 1013.2,
411 humidity: 45,
412 radon: None,
413 radiation_rate: None,
414 radiation_total: None,
415 };
416
417 assert_eq!(record.co2, 800);
418 assert!((record.temperature - 22.5).abs() < 0.01);
419 assert!((record.pressure - 1013.2).abs() < 0.1);
420 assert_eq!(record.humidity, 45);
421 assert!(record.radon.is_none());
422 assert!(record.radiation_rate.is_none());
423 assert!(record.radiation_total.is_none());
424 }
425
426 #[test]
427 fn test_history_record_clone() {
428 use time::OffsetDateTime;
429
430 let record = types::HistoryRecord {
431 timestamp: OffsetDateTime::UNIX_EPOCH,
432 co2: 500,
433 temperature: 20.0,
434 pressure: 1000.0,
435 humidity: 50,
436 radon: Some(100),
437 radiation_rate: Some(0.15),
438 radiation_total: Some(1.5),
439 };
440
441 let cloned = record.clone();
442 assert_eq!(cloned.co2, record.co2);
443 assert_eq!(cloned.humidity, record.humidity);
444 assert_eq!(cloned.radon, Some(100));
445 assert_eq!(cloned.radiation_rate, Some(0.15));
446 assert_eq!(cloned.radiation_total, Some(1.5));
447 }
448
449 #[test]
450 fn test_history_record_equality() {
451 use time::OffsetDateTime;
452
453 let record1 = types::HistoryRecord {
454 timestamp: OffsetDateTime::UNIX_EPOCH,
455 co2: 800,
456 temperature: 22.5,
457 pressure: 1013.2,
458 humidity: 45,
459 radon: None,
460 radiation_rate: None,
461 radiation_total: None,
462 };
463 let record2 = record1.clone();
464 assert_eq!(record1, record2);
465 }
466
467 #[test]
468 fn test_current_reading_equality() {
469 let reading1 = CurrentReading {
470 co2: 800,
471 temperature: 22.5,
472 pressure: 1013.2,
473 humidity: 45,
474 battery: 85,
475 status: Status::Green,
476 interval: 300,
477 age: 120,
478 captured_at: None,
479 radon: None,
480 radiation_rate: None,
481 radiation_total: None,
482 radon_avg_24h: None,
483 radon_avg_7d: None,
484 radon_avg_30d: None,
485 };
486 let reading2 = reading1.clone();
487 assert_eq!(reading1, reading2);
488 }
489
490 #[test]
491 fn test_min_current_reading_bytes_const() {
492 assert_eq!(MIN_CURRENT_READING_BYTES, 13);
493 let bytes = [0u8; MIN_CURRENT_READING_BYTES];
495 assert!(CurrentReading::from_bytes(&bytes).is_ok());
496 let short_bytes = [0u8; MIN_CURRENT_READING_BYTES - 1];
498 assert!(CurrentReading::from_bytes(&short_bytes).is_err());
499 }
500
501 #[test]
504 fn test_parse_error_display() {
505 let err = ParseError::invalid_value("test message");
506 assert_eq!(err.to_string(), "Invalid value: test message");
507 }
508
509 #[test]
510 fn test_parse_error_insufficient_bytes() {
511 let err = ParseError::InsufficientBytes {
512 expected: 13,
513 actual: 5,
514 };
515 assert_eq!(err.to_string(), "Insufficient bytes: expected 13, got 5");
516 }
517
518 #[test]
519 fn test_parse_error_unknown_device_type() {
520 let err = ParseError::UnknownDeviceType(0xAB);
521 assert_eq!(err.to_string(), "Unknown device type: 0xAB");
522 }
523
524 #[test]
525 fn test_parse_error_invalid_value() {
526 let err = ParseError::InvalidValue("bad value".to_string());
527 assert_eq!(err.to_string(), "Invalid value: bad value");
528 }
529
530 #[test]
531 fn test_parse_error_debug() {
532 let err = ParseError::invalid_value("debug test");
533 let debug_str = format!("{:?}", err);
534 assert!(debug_str.contains("InvalidValue"));
535 assert!(debug_str.contains("debug test"));
536 }
537
538 #[test]
539 fn test_parse_error_equality() {
540 let err1 = ParseError::InsufficientBytes {
541 expected: 10,
542 actual: 5,
543 };
544 let err2 = ParseError::InsufficientBytes {
545 expected: 10,
546 actual: 5,
547 };
548 let err3 = ParseError::InsufficientBytes {
549 expected: 10,
550 actual: 6,
551 };
552 assert_eq!(err1, err2);
553 assert_ne!(err1, err3);
554 }
555
556 #[test]
559 fn test_current_reading_serialization() {
560 let reading = CurrentReading {
561 co2: 800,
562 temperature: 22.5,
563 pressure: 1013.2,
564 humidity: 45,
565 battery: 85,
566 status: Status::Green,
567 interval: 300,
568 age: 120,
569 captured_at: None,
570 radon: None,
571 radiation_rate: None,
572 radiation_total: None,
573 radon_avg_24h: None,
574 radon_avg_7d: None,
575 radon_avg_30d: None,
576 };
577
578 let json = serde_json::to_string(&reading).unwrap();
579 assert!(json.contains("\"co2\":800"));
580 assert!(json.contains("\"humidity\":45"));
581 }
582
583 #[test]
584 fn test_current_reading_deserialization() {
585 let json = r#"{"co2":800,"temperature":22.5,"pressure":1013.2,"humidity":45,"battery":85,"status":"Green","interval":300,"age":120,"radon":null,"radiation_rate":null,"radiation_total":null}"#;
586
587 let reading: CurrentReading = serde_json::from_str(json).unwrap();
588 assert_eq!(reading.co2, 800);
589 assert_eq!(reading.status, Status::Green);
590 }
591
592 #[test]
593 fn test_status_serialization() {
594 assert_eq!(serde_json::to_string(&Status::Green).unwrap(), "\"Green\"");
595 assert_eq!(
596 serde_json::to_string(&Status::Yellow).unwrap(),
597 "\"Yellow\""
598 );
599 assert_eq!(serde_json::to_string(&Status::Red).unwrap(), "\"Red\"");
600 assert_eq!(serde_json::to_string(&Status::Error).unwrap(), "\"Error\"");
601 }
602
603 #[test]
604 fn test_device_type_serialization() {
605 assert_eq!(
606 serde_json::to_string(&DeviceType::Aranet4).unwrap(),
607 "\"Aranet4\""
608 );
609 assert_eq!(
610 serde_json::to_string(&DeviceType::AranetRadon).unwrap(),
611 "\"AranetRadon\""
612 );
613 }
614
615 #[test]
616 fn test_device_info_serialization_roundtrip() {
617 let info = types::DeviceInfo {
618 name: "Test Device".to_string(),
619 model: "Model X".to_string(),
620 serial: "SN12345".to_string(),
621 firmware: "1.2.3".to_string(),
622 hardware: "2.0".to_string(),
623 software: "3.0".to_string(),
624 manufacturer: "Acme Corp".to_string(),
625 };
626
627 let json = serde_json::to_string(&info).unwrap();
628 let deserialized: types::DeviceInfo = serde_json::from_str(&json).unwrap();
629
630 assert_eq!(deserialized.name, info.name);
631 assert_eq!(deserialized.serial, info.serial);
632 assert_eq!(deserialized.manufacturer, info.manufacturer);
633 }
634
635 #[test]
638 fn test_status_ordering() {
639 assert!(Status::Error < Status::Green);
641 assert!(Status::Green < Status::Yellow);
642 assert!(Status::Yellow < Status::Red);
643
644 assert!(Status::Red > Status::Yellow);
646 assert!(Status::Yellow >= Status::Yellow);
647 assert!(Status::Green <= Status::Yellow);
648 }
649
650 #[test]
651 fn test_device_type_readings_characteristic() {
652 use crate::ble;
653
654 assert_eq!(
656 DeviceType::Aranet4.readings_characteristic(),
657 ble::CURRENT_READINGS_DETAIL
658 );
659
660 assert_eq!(
662 DeviceType::Aranet2.readings_characteristic(),
663 ble::CURRENT_READINGS_DETAIL_ALT
664 );
665 assert_eq!(
666 DeviceType::AranetRadon.readings_characteristic(),
667 ble::CURRENT_READINGS_DETAIL_ALT
668 );
669 assert_eq!(
670 DeviceType::AranetRadiation.readings_characteristic(),
671 ble::CURRENT_READINGS_DETAIL_ALT
672 );
673 }
674
675 #[test]
676 fn test_device_type_from_name_word_boundary() {
677 assert_eq!(
679 DeviceType::from_name("Aranet4 12345"),
680 Some(DeviceType::Aranet4)
681 );
682 assert_eq!(
683 DeviceType::from_name("My Aranet4"),
684 Some(DeviceType::Aranet4)
685 );
686
687 assert_eq!(DeviceType::from_name("ARANET4"), Some(DeviceType::Aranet4));
689 assert_eq!(DeviceType::from_name("aranet2"), Some(DeviceType::Aranet2));
690
691 assert_eq!(
693 DeviceType::from_name("AranetRn+ 306B8"),
694 Some(DeviceType::AranetRadon)
695 );
696 assert_eq!(
697 DeviceType::from_name("aranetrn+ 12345"),
698 Some(DeviceType::AranetRadon)
699 );
700 }
701
702 #[test]
703 fn test_byte_size_constants() {
704 assert_eq!(MIN_CURRENT_READING_BYTES, 13);
705 assert_eq!(types::MIN_ARANET2_READING_BYTES, 7);
706 assert_eq!(types::MIN_RADON_READING_BYTES, 15);
707 assert_eq!(types::MIN_RADON_GATT_READING_BYTES, 18);
708 assert_eq!(types::MIN_RADIATION_READING_BYTES, 28);
709 }
710
711 #[test]
712 fn test_from_bytes_aranet2() {
713 let data = [
715 0x90, 0x01, 0x32, 0x55, 0x01, 0x2C, 0x01, ];
721
722 let reading = CurrentReading::from_bytes_aranet2(&data).unwrap();
723 assert_eq!(reading.co2, 0); assert!((reading.temperature - 20.0).abs() < 0.1);
725 assert_eq!(reading.humidity, 50);
726 assert_eq!(reading.battery, 85);
727 assert_eq!(reading.status, Status::Green);
728 assert_eq!(reading.interval, 300);
729 assert_eq!(reading.pressure, 0.0); }
731
732 #[test]
733 fn test_from_bytes_aranet2_insufficient() {
734 let data = [0u8; 6]; let result = CurrentReading::from_bytes_aranet2(&data);
736 assert!(result.is_err());
737 }
738
739 #[test]
740 fn test_from_bytes_for_device() {
741 let aranet4_data = [0u8; 13];
743 let result = CurrentReading::from_bytes_for_device(&aranet4_data, DeviceType::Aranet4);
744 assert!(result.is_ok());
745
746 let aranet2_data = [0u8; 7];
747 let result = CurrentReading::from_bytes_for_device(&aranet2_data, DeviceType::Aranet2);
748 assert!(result.is_ok());
749 }
750
751 #[test]
752 fn test_builder_with_captured_at() {
753 use time::OffsetDateTime;
754
755 let now = OffsetDateTime::now_utc();
756 let reading = CurrentReading::builder()
757 .co2(800)
758 .temperature(22.5)
759 .captured_at(now)
760 .build();
761
762 assert_eq!(reading.co2, 800);
763 assert_eq!(reading.captured_at, Some(now));
764 }
765
766 #[test]
767 fn test_builder_try_build_valid() {
768 let result = CurrentReading::builder()
769 .co2(800)
770 .temperature(22.5)
771 .pressure(1013.0)
772 .humidity(50)
773 .battery(85)
774 .try_build();
775
776 assert!(result.is_ok());
777 }
778
779 #[test]
780 fn test_builder_try_build_invalid_humidity() {
781 let result = CurrentReading::builder()
782 .humidity(150) .try_build();
784
785 assert!(result.is_err());
786 let err = result.unwrap_err();
787 assert!(err.to_string().contains("humidity"));
788 }
789
790 #[test]
791 fn test_builder_try_build_invalid_battery() {
792 let result = CurrentReading::builder()
793 .battery(120) .try_build();
795
796 assert!(result.is_err());
797 let err = result.unwrap_err();
798 assert!(err.to_string().contains("battery"));
799 }
800
801 #[test]
802 fn test_builder_try_build_invalid_temperature() {
803 let result = CurrentReading::builder()
804 .temperature(-50.0) .try_build();
806
807 assert!(result.is_err());
808 let err = result.unwrap_err();
809 assert!(err.to_string().contains("temperature"));
810 }
811
812 #[test]
813 fn test_builder_try_build_invalid_pressure() {
814 let result = CurrentReading::builder()
815 .temperature(22.0) .pressure(500.0) .try_build();
818
819 assert!(result.is_err());
820 let err = result.unwrap_err();
821 assert!(err.to_string().contains("pressure"));
822 }
823
824 #[test]
825 fn test_with_captured_at() {
826 use time::OffsetDateTime;
827
828 let reading = CurrentReading::builder().age(60).build();
829
830 let now = OffsetDateTime::now_utc();
831 let reading_with_time = reading.with_captured_at(now);
832
833 assert!(reading_with_time.captured_at.is_some());
834 let captured = reading_with_time.captured_at.unwrap();
836 let expected = now - time::Duration::seconds(60);
837 assert!((captured - expected).whole_seconds().abs() < 2);
838 }
839
840 #[test]
841 fn test_parse_error_invalid_value_helper() {
842 let err = ParseError::invalid_value("test error");
843 assert_eq!(err.to_string(), "Invalid value: test error");
844 }
845}