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;
217 assert_eq!(status, cloned);
218 }
219
220 #[test]
221 fn test_status_copy() {
222 let status = Status::Red;
223 let copied = status; assert_eq!(status, copied); }
226
227 #[test]
230 fn test_device_type_values() {
231 assert_eq!(DeviceType::Aranet4 as u8, 0xF1);
232 assert_eq!(DeviceType::Aranet2 as u8, 0xF2);
233 assert_eq!(DeviceType::AranetRadon as u8, 0xF3);
234 assert_eq!(DeviceType::AranetRadiation as u8, 0xF4);
235 }
236
237 #[test]
238 fn test_device_type_debug() {
239 assert_eq!(format!("{:?}", DeviceType::Aranet4), "Aranet4");
240 assert_eq!(format!("{:?}", DeviceType::Aranet2), "Aranet2");
241 assert_eq!(format!("{:?}", DeviceType::AranetRadon), "AranetRadon");
242 assert_eq!(
243 format!("{:?}", DeviceType::AranetRadiation),
244 "AranetRadiation"
245 );
246 }
247
248 #[test]
249 fn test_device_type_clone() {
250 let device_type = DeviceType::Aranet4;
251 let cloned = device_type;
253 assert_eq!(device_type, cloned);
254 }
255
256 #[test]
257 fn test_device_type_try_from_u8() {
258 assert_eq!(DeviceType::try_from(0xF1), Ok(DeviceType::Aranet4));
259 assert_eq!(DeviceType::try_from(0xF2), Ok(DeviceType::Aranet2));
260 assert_eq!(DeviceType::try_from(0xF3), Ok(DeviceType::AranetRadon));
261 assert_eq!(DeviceType::try_from(0xF4), Ok(DeviceType::AranetRadiation));
262 }
263
264 #[test]
265 fn test_device_type_try_from_u8_invalid() {
266 let result = DeviceType::try_from(0x00);
267 assert!(result.is_err());
268 assert_eq!(result.unwrap_err(), ParseError::UnknownDeviceType(0x00));
269
270 let result = DeviceType::try_from(0xFF);
271 assert!(result.is_err());
272 assert_eq!(result.unwrap_err(), ParseError::UnknownDeviceType(0xFF));
273 }
274
275 #[test]
276 fn test_device_type_display() {
277 assert_eq!(format!("{}", DeviceType::Aranet4), "Aranet4");
278 assert_eq!(format!("{}", DeviceType::Aranet2), "Aranet2");
279 assert_eq!(format!("{}", DeviceType::AranetRadon), "Aranet Radon");
280 assert_eq!(
281 format!("{}", DeviceType::AranetRadiation),
282 "Aranet Radiation"
283 );
284 }
285
286 #[test]
287 fn test_device_type_hash() {
288 use std::collections::HashSet;
289 let mut set = HashSet::new();
290 set.insert(DeviceType::Aranet4);
291 set.insert(DeviceType::Aranet2);
292 set.insert(DeviceType::Aranet4); assert_eq!(set.len(), 2);
294 assert!(set.contains(&DeviceType::Aranet4));
295 assert!(set.contains(&DeviceType::Aranet2));
296 }
297
298 #[test]
299 fn test_status_display() {
300 assert_eq!(format!("{}", Status::Error), "Error");
301 assert_eq!(format!("{}", Status::Green), "Good");
302 assert_eq!(format!("{}", Status::Yellow), "Moderate");
303 assert_eq!(format!("{}", Status::Red), "High");
304 }
305
306 #[test]
307 fn test_status_hash() {
308 use std::collections::HashSet;
309 let mut set = HashSet::new();
310 set.insert(Status::Green);
311 set.insert(Status::Yellow);
312 set.insert(Status::Green); assert_eq!(set.len(), 2);
314 assert!(set.contains(&Status::Green));
315 assert!(set.contains(&Status::Yellow));
316 }
317
318 #[test]
321 fn test_device_info_creation() {
322 let info = types::DeviceInfo {
323 name: "Aranet4 12345".to_string(),
324 model: "Aranet4".to_string(),
325 serial: "12345".to_string(),
326 firmware: "v1.2.0".to_string(),
327 hardware: "1.0".to_string(),
328 software: "1.2.0".to_string(),
329 manufacturer: "SAF Tehnika".to_string(),
330 };
331
332 assert_eq!(info.name, "Aranet4 12345");
333 assert_eq!(info.serial, "12345");
334 assert_eq!(info.manufacturer, "SAF Tehnika");
335 }
336
337 #[test]
338 fn test_device_info_clone() {
339 let info = types::DeviceInfo {
340 name: "Test".to_string(),
341 model: "Model".to_string(),
342 serial: "123".to_string(),
343 firmware: "1.0".to_string(),
344 hardware: "1.0".to_string(),
345 software: "1.0".to_string(),
346 manufacturer: "Mfg".to_string(),
347 };
348
349 let cloned = info.clone();
350 assert_eq!(cloned.name, info.name);
351 assert_eq!(cloned.serial, info.serial);
352 }
353
354 #[test]
355 fn test_device_info_debug() {
356 let info = types::DeviceInfo {
357 name: "Aranet4".to_string(),
358 model: "".to_string(),
359 serial: "".to_string(),
360 firmware: "".to_string(),
361 hardware: "".to_string(),
362 software: "".to_string(),
363 manufacturer: "".to_string(),
364 };
365
366 let debug_str = format!("{:?}", info);
367 assert!(debug_str.contains("Aranet4"));
368 }
369
370 #[test]
371 fn test_device_info_default() {
372 let info = types::DeviceInfo::default();
373 assert_eq!(info.name, "");
374 assert_eq!(info.model, "");
375 assert_eq!(info.serial, "");
376 assert_eq!(info.firmware, "");
377 assert_eq!(info.hardware, "");
378 assert_eq!(info.software, "");
379 assert_eq!(info.manufacturer, "");
380 }
381
382 #[test]
383 fn test_device_info_equality() {
384 let info1 = types::DeviceInfo {
385 name: "Test".to_string(),
386 model: "Model".to_string(),
387 serial: "123".to_string(),
388 firmware: "1.0".to_string(),
389 hardware: "1.0".to_string(),
390 software: "1.0".to_string(),
391 manufacturer: "Mfg".to_string(),
392 };
393 let info2 = info1.clone();
394 let info3 = types::DeviceInfo {
395 name: "Different".to_string(),
396 ..info1.clone()
397 };
398 assert_eq!(info1, info2);
399 assert_ne!(info1, info3);
400 }
401
402 #[test]
405 fn test_history_record_creation() {
406 use time::OffsetDateTime;
407
408 let record = types::HistoryRecord {
409 timestamp: OffsetDateTime::UNIX_EPOCH,
410 co2: 800,
411 temperature: 22.5,
412 pressure: 1013.2,
413 humidity: 45,
414 radon: None,
415 radiation_rate: None,
416 radiation_total: None,
417 };
418
419 assert_eq!(record.co2, 800);
420 assert!((record.temperature - 22.5).abs() < 0.01);
421 assert!((record.pressure - 1013.2).abs() < 0.1);
422 assert_eq!(record.humidity, 45);
423 assert!(record.radon.is_none());
424 assert!(record.radiation_rate.is_none());
425 assert!(record.radiation_total.is_none());
426 }
427
428 #[test]
429 fn test_history_record_clone() {
430 use time::OffsetDateTime;
431
432 let record = types::HistoryRecord {
433 timestamp: OffsetDateTime::UNIX_EPOCH,
434 co2: 500,
435 temperature: 20.0,
436 pressure: 1000.0,
437 humidity: 50,
438 radon: Some(100),
439 radiation_rate: Some(0.15),
440 radiation_total: Some(1.5),
441 };
442
443 let cloned = record.clone();
444 assert_eq!(cloned.co2, record.co2);
445 assert_eq!(cloned.humidity, record.humidity);
446 assert_eq!(cloned.radon, Some(100));
447 assert_eq!(cloned.radiation_rate, Some(0.15));
448 assert_eq!(cloned.radiation_total, Some(1.5));
449 }
450
451 #[test]
452 fn test_history_record_equality() {
453 use time::OffsetDateTime;
454
455 let record1 = types::HistoryRecord {
456 timestamp: OffsetDateTime::UNIX_EPOCH,
457 co2: 800,
458 temperature: 22.5,
459 pressure: 1013.2,
460 humidity: 45,
461 radon: None,
462 radiation_rate: None,
463 radiation_total: None,
464 };
465 let record2 = record1.clone();
466 assert_eq!(record1, record2);
467 }
468
469 #[test]
470 fn test_current_reading_equality() {
471 let reading1 = CurrentReading {
472 co2: 800,
473 temperature: 22.5,
474 pressure: 1013.2,
475 humidity: 45,
476 battery: 85,
477 status: Status::Green,
478 interval: 300,
479 age: 120,
480 captured_at: None,
481 radon: None,
482 radiation_rate: None,
483 radiation_total: None,
484 radon_avg_24h: None,
485 radon_avg_7d: None,
486 radon_avg_30d: None,
487 };
488 let reading2 = reading1;
490 assert_eq!(reading1, reading2);
491 }
492
493 #[test]
494 fn test_min_current_reading_bytes_const() {
495 assert_eq!(MIN_CURRENT_READING_BYTES, 13);
496 let bytes = [0u8; MIN_CURRENT_READING_BYTES];
498 assert!(CurrentReading::from_bytes(&bytes).is_ok());
499 let short_bytes = [0u8; MIN_CURRENT_READING_BYTES - 1];
501 assert!(CurrentReading::from_bytes(&short_bytes).is_err());
502 }
503
504 #[test]
507 fn test_parse_error_display() {
508 let err = ParseError::invalid_value("test message");
509 assert_eq!(err.to_string(), "Invalid value: test message");
510 }
511
512 #[test]
513 fn test_parse_error_insufficient_bytes() {
514 let err = ParseError::InsufficientBytes {
515 expected: 13,
516 actual: 5,
517 };
518 assert_eq!(err.to_string(), "Insufficient bytes: expected 13, got 5");
519 }
520
521 #[test]
522 fn test_parse_error_unknown_device_type() {
523 let err = ParseError::UnknownDeviceType(0xAB);
524 assert_eq!(err.to_string(), "Unknown device type: 0xAB");
525 }
526
527 #[test]
528 fn test_parse_error_invalid_value() {
529 let err = ParseError::InvalidValue("bad value".to_string());
530 assert_eq!(err.to_string(), "Invalid value: bad value");
531 }
532
533 #[test]
534 fn test_parse_error_debug() {
535 let err = ParseError::invalid_value("debug test");
536 let debug_str = format!("{:?}", err);
537 assert!(debug_str.contains("InvalidValue"));
538 assert!(debug_str.contains("debug test"));
539 }
540
541 #[test]
542 fn test_parse_error_equality() {
543 let err1 = ParseError::InsufficientBytes {
544 expected: 10,
545 actual: 5,
546 };
547 let err2 = ParseError::InsufficientBytes {
548 expected: 10,
549 actual: 5,
550 };
551 let err3 = ParseError::InsufficientBytes {
552 expected: 10,
553 actual: 6,
554 };
555 assert_eq!(err1, err2);
556 assert_ne!(err1, err3);
557 }
558
559 #[test]
562 fn test_current_reading_serialization() {
563 let reading = CurrentReading {
564 co2: 800,
565 temperature: 22.5,
566 pressure: 1013.2,
567 humidity: 45,
568 battery: 85,
569 status: Status::Green,
570 interval: 300,
571 age: 120,
572 captured_at: None,
573 radon: None,
574 radiation_rate: None,
575 radiation_total: None,
576 radon_avg_24h: None,
577 radon_avg_7d: None,
578 radon_avg_30d: None,
579 };
580
581 let json = serde_json::to_string(&reading).unwrap();
582 assert!(json.contains("\"co2\":800"));
583 assert!(json.contains("\"humidity\":45"));
584 }
585
586 #[test]
587 fn test_current_reading_deserialization() {
588 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}"#;
589
590 let reading: CurrentReading = serde_json::from_str(json).unwrap();
591 assert_eq!(reading.co2, 800);
592 assert_eq!(reading.status, Status::Green);
593 }
594
595 #[test]
596 fn test_status_serialization() {
597 assert_eq!(serde_json::to_string(&Status::Green).unwrap(), "\"Green\"");
598 assert_eq!(
599 serde_json::to_string(&Status::Yellow).unwrap(),
600 "\"Yellow\""
601 );
602 assert_eq!(serde_json::to_string(&Status::Red).unwrap(), "\"Red\"");
603 assert_eq!(serde_json::to_string(&Status::Error).unwrap(), "\"Error\"");
604 }
605
606 #[test]
607 fn test_device_type_serialization() {
608 assert_eq!(
609 serde_json::to_string(&DeviceType::Aranet4).unwrap(),
610 "\"Aranet4\""
611 );
612 assert_eq!(
613 serde_json::to_string(&DeviceType::AranetRadon).unwrap(),
614 "\"AranetRadon\""
615 );
616 }
617
618 #[test]
619 fn test_device_info_serialization_roundtrip() {
620 let info = types::DeviceInfo {
621 name: "Test Device".to_string(),
622 model: "Model X".to_string(),
623 serial: "SN12345".to_string(),
624 firmware: "1.2.3".to_string(),
625 hardware: "2.0".to_string(),
626 software: "3.0".to_string(),
627 manufacturer: "Acme Corp".to_string(),
628 };
629
630 let json = serde_json::to_string(&info).unwrap();
631 let deserialized: types::DeviceInfo = serde_json::from_str(&json).unwrap();
632
633 assert_eq!(deserialized.name, info.name);
634 assert_eq!(deserialized.serial, info.serial);
635 assert_eq!(deserialized.manufacturer, info.manufacturer);
636 }
637
638 #[test]
641 fn test_status_ordering() {
642 assert!(Status::Error < Status::Green);
644 assert!(Status::Green < Status::Yellow);
645 assert!(Status::Yellow < Status::Red);
646
647 assert!(Status::Red > Status::Yellow);
649 assert!(Status::Yellow >= Status::Yellow);
650 assert!(Status::Green <= Status::Yellow);
651 }
652
653 #[test]
654 fn test_device_type_readings_characteristic() {
655 use crate::ble;
656
657 assert_eq!(
659 DeviceType::Aranet4.readings_characteristic(),
660 ble::CURRENT_READINGS_DETAIL
661 );
662
663 assert_eq!(
665 DeviceType::Aranet2.readings_characteristic(),
666 ble::CURRENT_READINGS_DETAIL_ALT
667 );
668 assert_eq!(
669 DeviceType::AranetRadon.readings_characteristic(),
670 ble::CURRENT_READINGS_DETAIL_ALT
671 );
672 assert_eq!(
673 DeviceType::AranetRadiation.readings_characteristic(),
674 ble::CURRENT_READINGS_DETAIL_ALT
675 );
676 }
677
678 #[test]
679 fn test_device_type_from_name_word_boundary() {
680 assert_eq!(
682 DeviceType::from_name("Aranet4 12345"),
683 Some(DeviceType::Aranet4)
684 );
685 assert_eq!(
686 DeviceType::from_name("My Aranet4"),
687 Some(DeviceType::Aranet4)
688 );
689
690 assert_eq!(DeviceType::from_name("ARANET4"), Some(DeviceType::Aranet4));
692 assert_eq!(DeviceType::from_name("aranet2"), Some(DeviceType::Aranet2));
693
694 assert_eq!(
696 DeviceType::from_name("AranetRn+ 306B8"),
697 Some(DeviceType::AranetRadon)
698 );
699 assert_eq!(
700 DeviceType::from_name("aranetrn+ 12345"),
701 Some(DeviceType::AranetRadon)
702 );
703 }
704
705 #[test]
706 fn test_byte_size_constants() {
707 assert_eq!(MIN_CURRENT_READING_BYTES, 13);
708 assert_eq!(types::MIN_ARANET2_READING_BYTES, 7);
709 assert_eq!(types::MIN_RADON_READING_BYTES, 15);
710 assert_eq!(types::MIN_RADON_GATT_READING_BYTES, 18);
711 assert_eq!(types::MIN_RADIATION_READING_BYTES, 28);
712 }
713
714 #[test]
715 fn test_from_bytes_aranet2() {
716 let data = [
718 0x90, 0x01, 0x32, 0x55, 0x01, 0x2C, 0x01, ];
724
725 let reading = CurrentReading::from_bytes_aranet2(&data).unwrap();
726 assert_eq!(reading.co2, 0); assert!((reading.temperature - 20.0).abs() < 0.1);
728 assert_eq!(reading.humidity, 50);
729 assert_eq!(reading.battery, 85);
730 assert_eq!(reading.status, Status::Green);
731 assert_eq!(reading.interval, 300);
732 assert_eq!(reading.pressure, 0.0); }
734
735 #[test]
736 fn test_from_bytes_aranet2_insufficient() {
737 let data = [0u8; 6]; let result = CurrentReading::from_bytes_aranet2(&data);
739 assert!(result.is_err());
740 }
741
742 #[test]
743 fn test_from_bytes_for_device() {
744 let aranet4_data = [0u8; 13];
746 let result = CurrentReading::from_bytes_for_device(&aranet4_data, DeviceType::Aranet4);
747 assert!(result.is_ok());
748
749 let aranet2_data = [0u8; 7];
750 let result = CurrentReading::from_bytes_for_device(&aranet2_data, DeviceType::Aranet2);
751 assert!(result.is_ok());
752 }
753
754 #[test]
755 fn test_builder_with_captured_at() {
756 use time::OffsetDateTime;
757
758 let now = OffsetDateTime::now_utc();
759 let reading = CurrentReading::builder()
760 .co2(800)
761 .temperature(22.5)
762 .captured_at(now)
763 .build();
764
765 assert_eq!(reading.co2, 800);
766 assert_eq!(reading.captured_at, Some(now));
767 }
768
769 #[test]
770 fn test_builder_try_build_valid() {
771 let result = CurrentReading::builder()
772 .co2(800)
773 .temperature(22.5)
774 .pressure(1013.0)
775 .humidity(50)
776 .battery(85)
777 .try_build();
778
779 assert!(result.is_ok());
780 }
781
782 #[test]
783 fn test_builder_try_build_invalid_humidity() {
784 let result = CurrentReading::builder()
785 .humidity(150) .try_build();
787
788 assert!(result.is_err());
789 let err = result.unwrap_err();
790 assert!(err.to_string().contains("humidity"));
791 }
792
793 #[test]
794 fn test_builder_try_build_invalid_battery() {
795 let result = CurrentReading::builder()
796 .battery(120) .try_build();
798
799 assert!(result.is_err());
800 let err = result.unwrap_err();
801 assert!(err.to_string().contains("battery"));
802 }
803
804 #[test]
805 fn test_builder_try_build_invalid_temperature() {
806 let result = CurrentReading::builder()
807 .temperature(-50.0) .try_build();
809
810 assert!(result.is_err());
811 let err = result.unwrap_err();
812 assert!(err.to_string().contains("temperature"));
813 }
814
815 #[test]
816 fn test_builder_try_build_invalid_pressure() {
817 let result = CurrentReading::builder()
818 .temperature(22.0) .pressure(500.0) .try_build();
821
822 assert!(result.is_err());
823 let err = result.unwrap_err();
824 assert!(err.to_string().contains("pressure"));
825 }
826
827 #[test]
828 fn test_with_captured_at() {
829 use time::OffsetDateTime;
830
831 let reading = CurrentReading::builder().age(60).build();
832
833 let now = OffsetDateTime::now_utc();
834 let reading_with_time = reading.with_captured_at(now);
835
836 assert!(reading_with_time.captured_at.is_some());
837 let captured = reading_with_time.captured_at.unwrap();
839 let expected = now - time::Duration::seconds(60);
840 assert!((captured - expected).whole_seconds().abs() < 2);
841 }
842
843 #[test]
844 fn test_parse_error_invalid_value_helper() {
845 let err = ParseError::invalid_value("test error");
846 assert_eq!(err.to_string(), "Invalid value: test error");
847 }
848}