Add complete SQLite implementation of RelayLabelRepository trait with
all CRUD operations (get_label, save_label, delete_label, get_all_labels).
Key changes:
- Create infrastructure entities module with RelayLabelRecord struct
- Implement TryFrom traits for converting between database records and domain types
- Add From<sqlx::Error> and From<RelayLabelError> for RepositoryError
- Write comprehensive functional tests covering all repository operations
- Verify proper handling of edge cases (empty results, overwrites, max length)
TDD phase: GREEN - All repository trait tests now passing with SQLite implementation
Ref: T036 (specs/001-modbus-relay-control/tasks.md)
Add HealthMonitor service for tracking system health status with
comprehensive state transition logic and thread-safe operations.
Includes 16 unit tests covering all functionality including concurrent
access scenarios.
Add optional Modbus hardware integration tests with 7 test cases for
real device testing. Tests are marked as ignored and can be run with
running 21 tests
test infrastructure::modbus::client::tests::t025c_write_single_coil_timeout_tests::test_write_single_coil_returns_error_on_failure ... FAILED
test infrastructure::modbus::client::tests::t025c_write_single_coil_timeout_tests::test_write_single_coil_returns_timeout_on_slow_device ... FAILED
test infrastructure::modbus::client::tests::t025b_read_coils_timeout_tests::test_read_coils_returns_timeout_on_slow_response ... FAILED
test infrastructure::modbus::client::tests::t025b_read_coils_timeout_tests::test_read_coils_returns_modbus_exception_on_protocol_error ... FAILED
test infrastructure::modbus::client::tests::t025d_read_relay_state_tests::test_read_state_returns_on_when_coil_is_true ... FAILED
test infrastructure::modbus::client::tests::t025d_read_relay_state_tests::test_read_state_propagates_controller_error ... FAILED
test infrastructure::modbus::client::tests::t025d_read_relay_state_tests::test_read_state_returns_off_when_coil_is_false ... FAILED
test infrastructure::modbus::client::tests::t025b_read_coils_timeout_tests::test_read_coils_returns_connection_error_on_io_error ... FAILED
test infrastructure::modbus::client::tests::t025a_connection_setup_tests::test_new_with_valid_config_connects_successfully ... ok
test infrastructure::modbus::client::tests::t025a_connection_setup_tests::test_new_stores_correct_timeout_duration ... ok
test infrastructure::modbus::client::tests::t025b_read_coils_timeout_tests::test_read_coils_returns_coil_values_on_success ... ok
test infrastructure::modbus::client::tests::write_all_states_validation_tests::test_write_all_states_with_9_states_returns_invalid_input ... ok
test infrastructure::modbus::client::tests::write_all_states_validation_tests::test_write_all_states_with_empty_vector_returns_invalid_input ... ok
test infrastructure::modbus::client::tests::t025e_write_relay_state_tests::test_write_state_can_toggle_relay_multiple_times ... ok
test infrastructure::modbus::client::tests::write_all_states_validation_tests::test_write_all_states_with_8_states_succeeds ... ok
test infrastructure::modbus::client::tests::t025c_write_single_coil_timeout_tests::test_write_single_coil_succeeds_for_valid_write ... ok
test infrastructure::modbus::client::tests::t025e_write_relay_state_tests::test_write_state_off_writes_false_to_coil ... FAILED
test infrastructure::modbus::client::tests::t025d_read_relay_state_tests::test_read_state_correctly_maps_relay_id_to_modbus_address ... ok
test infrastructure::modbus::client::tests::write_all_states_validation_tests::test_write_all_states_with_7_states_returns_invalid_input ... ok
test infrastructure::modbus::client::tests::t025e_write_relay_state_tests::test_write_state_on_writes_true_to_coil ... ok
test infrastructure::modbus::client::tests::t025e_write_relay_state_tests::test_write_state_correctly_maps_relay_id_to_modbus_address ... ok
failures:
---- infrastructure::modbus::client::tests::t025c_write_single_coil_timeout_tests::test_write_single_coil_returns_error_on_failure stdout ----
thread 'infrastructure::modbus::client::tests::t025c_write_single_coil_timeout_tests::test_write_single_coil_returns_error_on_failure' (1157113) panicked at backend/src/infrastructure/modbus/client_test.rs:320:14:
Failed to connect: ConnectionError("Connection refused (os error 111)")
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
---- infrastructure::modbus::client::tests::t025c_write_single_coil_timeout_tests::test_write_single_coil_returns_timeout_on_slow_device stdout ----
thread 'infrastructure::modbus::client::tests::t025c_write_single_coil_timeout_tests::test_write_single_coil_returns_timeout_on_slow_device' (1157114) panicked at backend/src/infrastructure/modbus/client_test.rs:293:14:
Failed to connect: ConnectionError("Connection refused (os error 111)")
---- infrastructure::modbus::client::tests::t025b_read_coils_timeout_tests::test_read_coils_returns_timeout_on_slow_response stdout ----
thread 'infrastructure::modbus::client::tests::t025b_read_coils_timeout_tests::test_read_coils_returns_timeout_on_slow_response' (1157112) panicked at backend/src/infrastructure/modbus/client_test.rs:176:14:
Failed to connect: ConnectionError("Connection refused (os error 111)")
---- infrastructure::modbus::client::tests::t025b_read_coils_timeout_tests::test_read_coils_returns_modbus_exception_on_protocol_error stdout ----
thread 'infrastructure::modbus::client::tests::t025b_read_coils_timeout_tests::test_read_coils_returns_modbus_exception_on_protocol_error' (1157111) panicked at backend/src/infrastructure/modbus/client_test.rs:227:14:
Failed to connect: ConnectionError("Connection refused (os error 111)")
---- infrastructure::modbus::client::tests::t025d_read_relay_state_tests::test_read_state_returns_on_when_coil_is_true stdout ----
thread 'infrastructure::modbus::client::tests::t025d_read_relay_state_tests::test_read_state_returns_on_when_coil_is_true' (1157119) panicked at backend/src/infrastructure/modbus/client_test.rs:354:14:
Failed to connect to test server: ConnectionError("Connection refused (os error 111)")
---- infrastructure::modbus::client::tests::t025d_read_relay_state_tests::test_read_state_propagates_controller_error stdout ----
thread 'infrastructure::modbus::client::tests::t025d_read_relay_state_tests::test_read_state_propagates_controller_error' (1157117) panicked at backend/src/infrastructure/modbus/client_test.rs:396:14:
Failed to connect to test server: ConnectionError("Connection refused (os error 111)")
---- infrastructure::modbus::client::tests::t025d_read_relay_state_tests::test_read_state_returns_off_when_coil_is_false stdout ----
thread 'infrastructure::modbus::client::tests::t025d_read_relay_state_tests::test_read_state_returns_off_when_coil_is_false' (1157118) panicked at backend/src/infrastructure/modbus/client_test.rs:375:14:
Failed to connect to test server: ConnectionError("Connection refused (os error 111)")
---- infrastructure::modbus::client::tests::t025b_read_coils_timeout_tests::test_read_coils_returns_connection_error_on_io_error stdout ----
thread 'infrastructure::modbus::client::tests::t025b_read_coils_timeout_tests::test_read_coils_returns_connection_error_on_io_error' (1157110) panicked at backend/src/infrastructure/modbus/client_test.rs:202:14:
Failed to connect: ConnectionError("Connection refused (os error 111)")
---- infrastructure::modbus::client::tests::t025e_write_relay_state_tests::test_write_state_off_writes_false_to_coil stdout ----
thread 'infrastructure::modbus::client::tests::t025e_write_relay_state_tests::test_write_state_off_writes_false_to_coil' (1157122) panicked at backend/src/infrastructure/modbus/client_test.rs:508:9:
assertion `left == right` failed: Relay should be Off after writing Off state
left: On
right: Off
failures:
infrastructure::modbus::client::tests::t025b_read_coils_timeout_tests::test_read_coils_returns_connection_error_on_io_error
infrastructure::modbus::client::tests::t025b_read_coils_timeout_tests::test_read_coils_returns_modbus_exception_on_protocol_error
infrastructure::modbus::client::tests::t025b_read_coils_timeout_tests::test_read_coils_returns_timeout_on_slow_response
infrastructure::modbus::client::tests::t025c_write_single_coil_timeout_tests::test_write_single_coil_returns_error_on_failure
infrastructure::modbus::client::tests::t025c_write_single_coil_timeout_tests::test_write_single_coil_returns_timeout_on_slow_device
infrastructure::modbus::client::tests::t025d_read_relay_state_tests::test_read_state_propagates_controller_error
infrastructure::modbus::client::tests::t025d_read_relay_state_tests::test_read_state_returns_off_when_coil_is_false
infrastructure::modbus::client::tests::t025d_read_relay_state_tests::test_read_state_returns_on_when_coil_is_true
infrastructure::modbus::client::tests::t025e_write_relay_state_tests::test_write_state_off_writes_false_to_coil
test result: FAILED. 12 passed; 9 failed; 0 ignored; 0 measured; 128 filtered out; finished in 3.27s.
Ref: T034, T039, T040 (specs/001-modbus-relay-control/tasks.org)
Add reusable test suite with 18 test functions covering get_label(),
save_label(), delete_label(), and get_all_labels() methods. Tests
verify contract compliance for any repository implementation.
Added delete_label() method to trait interface and implemented it in
MockRelayLabelRepository to support complete CRUD operations.
TDD phase: RED - Tests written before SQLite implementation (T036)
Ref: T035 (specs/001-modbus-relay-control/tasks.md)
Add real Modbus TCP communication through ModbusRelayController:
- T025a: Connection setup with Arc<Mutex<Context>> and configurable timeout
- T025b: read_coils_with_timeout() helper wrapping tokio::time::timeout
- T025c: write_single_coil_with_timeout() with nested Result handling
- T025d: RelayController::read_relay_state() using timeout helper
- T025e: RelayController::write_relay_state() with state conversion
- Additional: Complete RelayController trait with all required methods
- Domain support: RelayId::to_modbus_address(), RelayState conversion helpers
Implements hexagonal architecture with infrastructure layer properly
depending on domain types. Includes structured logging at key operations.
TDD phase: green (implementation following test stubs from T023-T024)
Ref: T025a-T025e (specs/001-modbus-relay-control/tasks.md)
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)
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)
Implemented the Relay aggregate as the primary domain entity for relay
control operations. Added RelayLabel newtype for validated human-readable
relay labels.
Relay aggregate features:
- Construction with id, state, and optional label
- State control methods: toggle(), turn_on(), turn_off()
- Accessor methods: id(), state(), label()
- All methods use const where possible for compile-time optimization
RelayLabel newtype features:
- Validation: non-empty, max 50 characters
- Smart constructor with Result-based error handling
- Default implementation: "Unlabeled"
- Transparent representation for zero-cost abstraction
Additional changes:
- Made RelayId derive Copy for ergonomic value semantics
- All public APIs include documentation and #[must_use] attributes
TDD phase: GREEN - Tests pass for Relay aggregate (T021 tests now pass)
Ref: T022, T024 (specs/001-modbus-relay-control/tasks.md)
Created test suite for Relay entity covering construction, state toggling,
and explicit state control methods. Tests intentionally fail as Relay
struct is not yet implemented.
Tests cover:
- Relay::new() with id, state, and optional label
- toggle() flipping state between On/Off
- turn_on() setting state to On
- turn_off() setting state to Off
TDD phase: RED - Tests written, implementation pending (T022)
Ref: T021 (specs/001-modbus-relay-control/tasks.md)
Tests verify serialization and deserialization of RelayState enum with
"on" and "off" states. Red phase of TDD - tests define expected behavior
before implementation.
Ref: T019 (specs/001-modbus-relay-control/tasks.md)
Implement smart constructor that validates relay IDs are within valid
range (1-8 for 8-channel relay controller). Add accessor method as_u8()
for safe access to inner value. Add comprehensive documentation to satisfy
clippy requirements.
TDD green phase: Tests from T017 now pass.
Ref: T018 (specs/001-modbus-relay-control/tasks.md)
Replace Cors::new() with Cors::from(value.settings.cors.clone()) in the
From<Application> for RunnableApplication implementation to use CORS
settings from configuration instead of hardcoded defaults.
Changes:
- Use From<CorsSettings> for Cors trait to build CORS middleware
- Add unit test verifying CORS middleware uses settings
- Maintain correct middleware order: RateLimit → CORS → Data
Ref: T015
Implement From<CorsSettings> for Cors trait to configure CORS middleware
with production-ready security validation.
- Move CorsSettings to backend/src/settings/cors.rs module
- Validate wildcard + credentials constraint (browser security policy)
- Configure allowed methods, headers, credentials, and max_age
- Add structured logging for CORS configuration
- Move tests from settings/mod.rs and startup.rs to cors module
Ref: T014
Add failing test cases for the CORS configuration builder function.
Tests verify correct initialization of CorsSettings with allowed origins,
credentials, and max age configuration. These tests fail until build_cors()
is implemented in the green phase.
Ref: T013 (specs/001-modbus-relay-control)
Set up CORS policy to allow requests from frontend development server and
update development.yaml with proper frontend origin URL configuration.
Ref: T011 (specs/001-modbus-relay-control)
Implements CorsSettings struct with validation and deserialization support
for configuring Cross-Origin Resource Sharing in the application settings.
Ref: T010 (specs/001-modbus-relay-control)
Convert project from single backend to monorepo structure with separate
frontend (Vue 3 + TypeScript + Vite) and backend directories. Updates
all configuration files and build system to support both workspaces.
Ref: T007 (specs/001-modbus-relay-control)