Files
sta/backend/tests/modbus_hardware_test.rs

254 lines
8.3 KiB
Rust
Raw Normal View History

// Integration tests for Modbus hardware
// These tests require physical Modbus relay device to be connected
// Run with: cargo test -- --ignored
use std::time::Duration;
#[cfg(test)]
mod tests {
use super::*;
use sta::domain::relay::controller::RelayController;
use sta::domain::relay::types::{RelayId, RelayState};
use sta::infrastructure::modbus::client::ModbusRelayController;
static HOST: &str = "192.168.1.200";
static PORT: u16 = 502;
static SLAVE_ID: u8 = 1;
#[tokio::test]
#[ignore = "Requires physical Modbus device"]
async fn test_modbus_connection() {
// This test verifies we can connect to the actual Modbus device
// Configured with settings from settings/base.yaml
let timeout_secs = 5;
let _controller = ModbusRelayController::new(HOST, PORT, SLAVE_ID, timeout_secs)
.await
.expect("Failed to connect to Modbus device");
// If we got here, connection was successful
println!("✓ Successfully connected to Modbus device");
}
#[tokio::test]
#[ignore = "Requires physical Modbus device"]
async fn test_read_relay_states() {
let timeout_secs = 5;
let controller = ModbusRelayController::new(HOST, PORT, SLAVE_ID, timeout_secs)
.await
.expect("Failed to connect to Modbus device");
// Test reading individual relay states
for relay_id in 1..=8 {
let relay_id = RelayId::new(relay_id).unwrap();
let state = controller
.read_relay_state(relay_id)
.await
.expect("Failed to read relay state");
println!("Relay {}: {:?}", relay_id.as_u8(), state);
}
}
#[tokio::test]
#[ignore = "Requires physical Modbus device"]
async fn test_read_all_relays() {
let timeout_secs = 5;
let controller = ModbusRelayController::new(HOST, PORT, SLAVE_ID, timeout_secs)
.await
.expect("Failed to connect to Modbus device");
let relays = controller
.read_all_states()
.await
.expect("Failed to read all relay states");
assert_eq!(relays.len(), 8, "Should have exactly 8 relays");
for (i, state) in relays.iter().enumerate() {
let relay_id = i + 1;
println!("Relay {}: {:?}", relay_id, state);
}
}
#[tokio::test]
#[ignore = "Requires physical Modbus device"]
async fn test_write_relay_state() {
let timeout_secs = 5;
let controller = ModbusRelayController::new(HOST, PORT, SLAVE_ID, timeout_secs)
.await
.expect("Failed to connect to Modbus device");
let relay_id = RelayId::new(1).unwrap();
// Turn relay on
controller
.write_relay_state(relay_id, RelayState::On)
.await
.expect("Failed to write relay state");
// Verify it's on
let state = controller
.read_relay_state(relay_id)
.await
.expect("Failed to read relay state");
assert_eq!(state, RelayState::On, "Relay should be ON");
// Turn relay off
controller
.write_relay_state(relay_id, RelayState::Off)
.await
.expect("Failed to write relay state");
// Verify it's off
let state = controller
.read_relay_state(relay_id)
.await
.expect("Failed to read relay state");
assert_eq!(state, RelayState::Off, "Relay should be OFF");
}
#[tokio::test]
#[ignore = "Requires physical Modbus device"]
async fn test_write_all_relays() {
let timeout_secs = 5;
let controller = ModbusRelayController::new(HOST, PORT, SLAVE_ID, timeout_secs)
.await
.expect("Failed to connect to Modbus device");
// Turn all relays on
let all_on_states = vec![RelayState::On; 8];
controller
.write_all_states(all_on_states)
.await
.expect("Failed to write all relay states");
// Verify all are on
let relays = controller
.read_all_states()
.await
.expect("Failed to read all relay states");
for state in &relays {
assert_eq!(*state, RelayState::On, "All relays should be ON");
}
// Turn all relays off
let all_off_states = vec![RelayState::Off; 8];
controller
.write_all_states(all_off_states)
.await
.expect("Failed to write all relay states");
// Verify all are off
let relays = controller
.read_all_states()
.await
.expect("Failed to read all relay states");
for state in &relays {
assert_eq!(*state, RelayState::Off, "All relays should be OFF");
}
}
#[tokio::test]
#[ignore = "Requires physical Modbus device"]
async fn test_timeout_handling() {
let timeout_secs = 1; // Short timeout for testing
let controller = ModbusRelayController::new(HOST, PORT, SLAVE_ID, timeout_secs)
.await
.expect("Failed to connect to Modbus device");
// This test verifies that timeout works correctly
// We'll try to read a relay state with a very short timeout
let relay_id = RelayId::new(1).unwrap();
// The operation should either succeed quickly or timeout
let result = tokio::time::timeout(
Duration::from_secs(2),
controller.read_relay_state(relay_id),
)
.await;
match result {
Ok(Ok(state)) => {
println!("✓ Operation completed within timeout: {:?}", state);
}
Ok(Err(e)) => {
println!("✓ Operation failed (expected): {}", e);
}
Err(_) => {
println!("✓ Operation timed out (expected)");
}
}
}
#[tokio::test]
#[ignore = "Requires physical Modbus device"]
async fn test_concurrent_access() {
let timeout_secs = 5;
let _controller = ModbusRelayController::new(HOST, PORT, SLAVE_ID, timeout_secs)
.await
.expect("Failed to connect to Modbus device");
// Test concurrent access to the controller
// We'll test a few relays concurrently using tokio::join!
// Note: We can't clone the controller, so we'll just test sequential access
// This is still valuable for testing the controller works with multiple relays
let relay_id1 = RelayId::new(1).unwrap();
let relay_id2 = RelayId::new(2).unwrap();
let relay_id3 = RelayId::new(3).unwrap();
let relay_id4 = RelayId::new(4).unwrap();
let task1 = tokio::spawn(async move {
let controller = ModbusRelayController::new(HOST, PORT, SLAVE_ID, timeout_secs)
.await
.expect("Failed to connect");
controller.read_relay_state(relay_id1).await
});
let task2 = tokio::spawn(async move {
let controller = ModbusRelayController::new(HOST, PORT, SLAVE_ID, timeout_secs)
.await
.expect("Failed to connect");
controller.read_relay_state(relay_id2).await
});
let task3 = tokio::spawn(async move {
let controller = ModbusRelayController::new(HOST, PORT, SLAVE_ID, timeout_secs)
.await
.expect("Failed to connect");
controller.read_relay_state(relay_id3).await
});
let task4 = tokio::spawn(async move {
let controller = ModbusRelayController::new(HOST, PORT, SLAVE_ID, timeout_secs)
.await
.expect("Failed to connect");
controller.read_relay_state(relay_id4).await
});
let (result1, result2, result3, result4) = tokio::join!(task1, task2, task3, task4);
// Process results
if let Ok(Ok(state)) = result1 {
println!("Relay 1: {:?}", state);
}
if let Ok(Ok(state)) = result2 {
println!("Relay 2: {:?}", state);
}
if let Ok(Ok(state)) = result3 {
println!("Relay 3: {:?}", state);
}
if let Ok(Ok(state)) = result4 {
println!("Relay 4: {:?}", state);
}
}
}