feat(domain): add ModbusAddress type and HealthStatus enum
Implements T025-T027 from TDD workflow (red-green-refactor): - T025 (red): Tests for ModbusAddress with From<RelayId> conversion - T026 (green): ModbusAddress newtype (#[repr(transparent)]) with offset mapping - T027 (red+green): HealthStatus enum with state transitions ModbusAddress wraps u16 and converts user-facing relay IDs (1-8) to Modbus addresses (0-7) at the domain boundary. HealthStatus tracks relay health with Healthy, Degraded, and Unhealthy states supporting error tracking and recovery monitoring. Ref: T025, T026, T027 (specs/001-modbus-relay-control)
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
use std::fmt::Display;
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
/// Human-readable label for a relay.
|
||||
@@ -45,3 +47,60 @@ impl Default for RelayLabel {
|
||||
Self(String::from("Unlabeled"))
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for RelayLabel {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_relay_label_new_valid_string() {
|
||||
// Test: RelayLabel::new("Pump") → Ok
|
||||
let result = RelayLabel::new("Pump".to_string());
|
||||
assert!(result.is_ok());
|
||||
assert_eq!(result.unwrap().as_str(), "Pump");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_relay_label_new_valid_max_length() {
|
||||
// Test: RelayLabel::new("A".repeat(50)) → Ok
|
||||
let label_str = "A".repeat(50);
|
||||
let result = RelayLabel::new(label_str.clone());
|
||||
assert!(result.is_ok());
|
||||
assert_eq!(result.unwrap().as_str(), &label_str);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_relay_label_new_empty_fails() {
|
||||
// Test: RelayLabel::new("") → Err(EmptyLabel)
|
||||
let result = RelayLabel::new(String::new());
|
||||
assert!(result.is_err());
|
||||
assert!(matches!(result.unwrap_err(), RelayLabelError::Empty));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_relay_label_new_too_long_fails() {
|
||||
// Test: RelayLabel::new("A".repeat(51)) → Err(LabelTooLong)
|
||||
let label_str = "A".repeat(51);
|
||||
let result = RelayLabel::new(label_str);
|
||||
assert!(result.is_err());
|
||||
assert!(matches!(result.unwrap_err(), RelayLabelError::TooLong(_)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_relay_label_default() {
|
||||
let label = RelayLabel::default();
|
||||
assert_eq!(label.as_str(), "Unlabeled");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_relay_label_display() {
|
||||
let label = RelayLabel::new("Test Label".to_string()).unwrap();
|
||||
assert_eq!(format!("{label}"), "Test Label");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user