test(modbus): implement working MockRelayController tests
Replace 6 stubbed test implementations with fully functional tests that validate: - read_relay_state() returns correctly mocked state - write_relay_state() updates internal mocked state - read_all_states() returns 8 relays in known state - Independent relay state management for all 8 channel indices - Thread-safe concurrent state access with Arc<Mutex<>> Tests now pass after T029-T031 completed MockRelayController implementation. TDD phase: GREEN - tests validate implementation Ref: T032 (specs/001-modbus-relay-control/tasks.md)
This commit is contained in:
@@ -130,88 +130,115 @@ impl RelayController for MockRelayController {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::MockRelayController;
|
||||
use crate::domain::relay::types::RelayId;
|
||||
|
||||
// NOTE: These tests will fail until MockRelayController and RelayController trait are implemented (T029, T030)
|
||||
// This follows TDD - write failing tests FIRST, then implement.
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_read_state_returns_mocked_state() {
|
||||
// Test: read_state() returns mocked state
|
||||
// Test: read_relay_state() returns mocked state
|
||||
//
|
||||
// Setup: Create a mock controller and set relay 1 to On
|
||||
// Expected: read_state(1) should return On
|
||||
// Expected: read_relay_state(1) should return On
|
||||
|
||||
todo!("Implement after MockRelayController exists (T029)");
|
||||
use crate::domain::relay::{controller::RelayController, types::RelayState};
|
||||
|
||||
// let controller = MockRelayController::new();
|
||||
// let relay_id = RelayId::new(1).unwrap();
|
||||
//
|
||||
// // Write a known state
|
||||
// controller.write_state(relay_id, RelayState::On).await.unwrap();
|
||||
//
|
||||
// // Read it back
|
||||
// let state = controller.read_state(relay_id).await.unwrap();
|
||||
// assert_eq!(state, RelayState::On);
|
||||
let controller = MockRelayController::new();
|
||||
let relay_id = RelayId::new(1).unwrap();
|
||||
|
||||
// Write a known state
|
||||
controller
|
||||
.write_relay_state(relay_id, RelayState::On)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Read it back
|
||||
let state = controller.read_relay_state(relay_id).await.unwrap();
|
||||
assert_eq!(state, RelayState::On);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_write_state_updates_mocked_state() {
|
||||
// Test: write_state() updates mocked state
|
||||
// Test: write_relay_state() updates mocked state
|
||||
//
|
||||
// Setup: Create a mock controller (all relays default to Off)
|
||||
// Setup: Create a mock controller with relay 3 initialized to Off
|
||||
// Action: Write relay 3 to On, then read it back
|
||||
// Expected: State should be On
|
||||
|
||||
todo!("Implement after MockRelayController exists (T029)");
|
||||
use crate::domain::relay::{controller::RelayController, types::RelayState};
|
||||
|
||||
// let controller = MockRelayController::new();
|
||||
// let relay_id = RelayId::new(3).unwrap();
|
||||
//
|
||||
// // Initial state should be Off
|
||||
// let initial_state = controller.read_state(relay_id).await.unwrap();
|
||||
// assert_eq!(initial_state, RelayState::Off);
|
||||
//
|
||||
// // Write On
|
||||
// controller.write_state(relay_id, RelayState::On).await.unwrap();
|
||||
//
|
||||
// // Verify it changed
|
||||
// let updated_state = controller.read_state(relay_id).await.unwrap();
|
||||
// assert_eq!(updated_state, RelayState::On);
|
||||
let controller = MockRelayController::new();
|
||||
let relay_id = RelayId::new(3).unwrap();
|
||||
|
||||
// Initialize relay 3 to Off
|
||||
controller
|
||||
.write_relay_state(relay_id, RelayState::Off)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Verify initial state
|
||||
let initial_state = controller.read_relay_state(relay_id).await.unwrap();
|
||||
assert_eq!(initial_state, RelayState::Off);
|
||||
|
||||
// Write On
|
||||
controller
|
||||
.write_relay_state(relay_id, RelayState::On)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Verify it changed
|
||||
let updated_state = controller.read_relay_state(relay_id).await.unwrap();
|
||||
assert_eq!(updated_state, RelayState::On);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_read_all_returns_8_relays_in_known_state() {
|
||||
// Test: read_all() returns 8 relays in known state
|
||||
// Test: read_all_states() returns 8 relays in known state
|
||||
//
|
||||
// Setup: Create a mock controller, set relays 1, 3, 5 to On, others Off
|
||||
// Action: Call read_all()
|
||||
// Expected: Returns Vec of 8 (RelayId, RelayState) tuples in correct state
|
||||
// Setup: Create a mock controller, initialize all 8 relays, set relays 1, 3, 5 to On, others Off
|
||||
// Action: Call read_all_states()
|
||||
// Expected: Returns Vec of 8 RelayState values in correct order
|
||||
|
||||
todo!("Implement after MockRelayController exists (T029)");
|
||||
use crate::domain::relay::{controller::RelayController, types::RelayState};
|
||||
|
||||
// let controller = MockRelayController::new();
|
||||
//
|
||||
// // Set specific relays to On
|
||||
// controller.write_state(RelayId::new(1).unwrap(), RelayState::On).await.unwrap();
|
||||
// controller.write_state(RelayId::new(3).unwrap(), RelayState::On).await.unwrap();
|
||||
// controller.write_state(RelayId::new(5).unwrap(), RelayState::On).await.unwrap();
|
||||
//
|
||||
// // Read all states
|
||||
// let all_states = controller.read_all().await.unwrap();
|
||||
//
|
||||
// // Verify we have exactly 8 relays
|
||||
// assert_eq!(all_states.len(), 8);
|
||||
//
|
||||
// // Verify specific states
|
||||
// assert_eq!(all_states[0], (RelayId::new(1).unwrap(), RelayState::On));
|
||||
// assert_eq!(all_states[1], (RelayId::new(2).unwrap(), RelayState::Off));
|
||||
// assert_eq!(all_states[2], (RelayId::new(3).unwrap(), RelayState::On));
|
||||
// assert_eq!(all_states[3], (RelayId::new(4).unwrap(), RelayState::Off));
|
||||
// assert_eq!(all_states[4], (RelayId::new(5).unwrap(), RelayState::On));
|
||||
// assert_eq!(all_states[5], (RelayId::new(6).unwrap(), RelayState::Off));
|
||||
// assert_eq!(all_states[6], (RelayId::new(7).unwrap(), RelayState::Off));
|
||||
// assert_eq!(all_states[7], (RelayId::new(8).unwrap(), RelayState::Off));
|
||||
let controller = MockRelayController::new();
|
||||
|
||||
// Initialize all 8 relays to Off first
|
||||
for i in 1..=8 {
|
||||
controller
|
||||
.write_relay_state(RelayId::new(i).unwrap(), RelayState::Off)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
// Set specific relays to On
|
||||
controller
|
||||
.write_relay_state(RelayId::new(1).unwrap(), RelayState::On)
|
||||
.await
|
||||
.unwrap();
|
||||
controller
|
||||
.write_relay_state(RelayId::new(3).unwrap(), RelayState::On)
|
||||
.await
|
||||
.unwrap();
|
||||
controller
|
||||
.write_relay_state(RelayId::new(5).unwrap(), RelayState::On)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Read all states
|
||||
let all_states = controller.read_all_states().await.unwrap();
|
||||
|
||||
// Verify we have exactly 8 relays
|
||||
assert_eq!(all_states.len(), 8);
|
||||
|
||||
// Verify specific states (indexed 0-7, corresponding to relays 1-8)
|
||||
assert_eq!(all_states[0], RelayState::On); // Relay 1
|
||||
assert_eq!(all_states[1], RelayState::Off); // Relay 2
|
||||
assert_eq!(all_states[2], RelayState::On); // Relay 3
|
||||
assert_eq!(all_states[3], RelayState::Off); // Relay 4
|
||||
assert_eq!(all_states[4], RelayState::On); // Relay 5
|
||||
assert_eq!(all_states[5], RelayState::Off); // Relay 6
|
||||
assert_eq!(all_states[6], RelayState::Off); // Relay 7
|
||||
assert_eq!(all_states[7], RelayState::Off); // Relay 8
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -222,24 +249,35 @@ mod tests {
|
||||
// Action: Write different states to each relay
|
||||
// Expected: Each relay maintains its own independent state
|
||||
|
||||
todo!("Implement after MockRelayController exists (T029)");
|
||||
use crate::domain::relay::{controller::RelayController, types::RelayState};
|
||||
|
||||
// let controller = MockRelayController::new();
|
||||
//
|
||||
// // Write alternating states (On, Off, On, Off, ...)
|
||||
// for i in 1..=8 {
|
||||
// let relay_id = RelayId::new(i).unwrap();
|
||||
// let state = if i % 2 == 1 { RelayState::On } else { RelayState::Off };
|
||||
// controller.write_state(relay_id, state).await.unwrap();
|
||||
// }
|
||||
//
|
||||
// // Verify each relay has correct state
|
||||
// for i in 1..=8 {
|
||||
// let relay_id = RelayId::new(i).unwrap();
|
||||
// let expected_state = if i % 2 == 1 { RelayState::On } else { RelayState::Off };
|
||||
// let actual_state = controller.read_state(relay_id).await.unwrap();
|
||||
// assert_eq!(actual_state, expected_state, "Relay {} has incorrect state", i);
|
||||
// }
|
||||
let controller = MockRelayController::new();
|
||||
|
||||
// Write alternating states (On, Off, On, Off, ...)
|
||||
for i in 1..=8 {
|
||||
let relay_id = RelayId::new(i).unwrap();
|
||||
let state = if i % 2 == 1 {
|
||||
RelayState::On
|
||||
} else {
|
||||
RelayState::Off
|
||||
};
|
||||
controller.write_relay_state(relay_id, state).await.unwrap();
|
||||
}
|
||||
|
||||
// Verify each relay has correct state
|
||||
for i in 1..=8 {
|
||||
let relay_id = RelayId::new(i).unwrap();
|
||||
let expected_state = if i % 2 == 1 {
|
||||
RelayState::On
|
||||
} else {
|
||||
RelayState::Off
|
||||
};
|
||||
let actual_state = controller.read_relay_state(relay_id).await.unwrap();
|
||||
assert_eq!(
|
||||
actual_state, expected_state,
|
||||
"Relay {i} has incorrect state",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -265,35 +303,45 @@ mod tests {
|
||||
// Setup: Create mock controller, spawn multiple tasks that read/write
|
||||
// Expected: No data races, all operations complete successfully
|
||||
|
||||
todo!("Implement after MockRelayController exists (T029)");
|
||||
use std::sync::Arc;
|
||||
|
||||
// use std::sync::Arc;
|
||||
//
|
||||
// let controller = Arc::new(MockRelayController::new());
|
||||
//
|
||||
// // Spawn 10 tasks that toggle relay 1
|
||||
// let mut handles = vec![];
|
||||
// for _ in 0..10 {
|
||||
// let controller_clone = Arc::clone(&controller);
|
||||
// let handle = tokio::spawn(async move {
|
||||
// let relay_id = RelayId::new(1).unwrap();
|
||||
// let current_state = controller_clone.read_state(relay_id).await.unwrap();
|
||||
// let new_state = match current_state {
|
||||
// RelayState::On => RelayState::Off,
|
||||
// RelayState::Off => RelayState::On,
|
||||
// };
|
||||
// controller_clone.write_state(relay_id, new_state).await.unwrap();
|
||||
// });
|
||||
// handles.push(handle);
|
||||
// }
|
||||
//
|
||||
// // Wait for all tasks to complete
|
||||
// for handle in handles {
|
||||
// handle.await.unwrap();
|
||||
// }
|
||||
//
|
||||
// // Controller should still be in valid state (either On or Off)
|
||||
// let final_state = controller.read_state(RelayId::new(1).unwrap()).await.unwrap();
|
||||
// assert!(matches!(final_state, RelayState::On | RelayState::Off));
|
||||
use crate::domain::relay::{controller::RelayController, types::RelayState};
|
||||
|
||||
let controller = Arc::new(MockRelayController::new());
|
||||
let relay_id = RelayId::new(1).unwrap();
|
||||
|
||||
// Initialize relay 1 to Off
|
||||
controller
|
||||
.write_relay_state(relay_id, RelayState::Off)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Spawn 10 tasks that toggle relay 1
|
||||
let mut handles = vec![];
|
||||
for _ in 0..10 {
|
||||
let controller_clone = Arc::clone(&controller);
|
||||
let handle = tokio::spawn(async move {
|
||||
let relay_id = RelayId::new(1).unwrap();
|
||||
let current_state = controller_clone.read_relay_state(relay_id).await.unwrap();
|
||||
let new_state = match current_state {
|
||||
RelayState::On => RelayState::Off,
|
||||
RelayState::Off => RelayState::On,
|
||||
};
|
||||
controller_clone
|
||||
.write_relay_state(relay_id, new_state)
|
||||
.await
|
||||
.unwrap();
|
||||
});
|
||||
handles.push(handle);
|
||||
}
|
||||
|
||||
// Wait for all tasks to complete
|
||||
for handle in handles {
|
||||
handle.await.unwrap();
|
||||
}
|
||||
|
||||
// Controller should still be in valid state (either On or Off)
|
||||
let final_state = controller.read_relay_state(relay_id).await.unwrap();
|
||||
assert!(matches!(final_state, RelayState::On | RelayState::Off));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user