feat(infrastructure): add dependency injection factories with TDD stubs

- Add relay controller factory with retry/fallback logic (T039a stub)
- Add label repository factory with mock/SQLite selection (T039b stub)
- Include comprehensive test suites for expected factory behavior
- Update module exports to expose factory functions
This commit is contained in:
2026-01-23 20:46:48 +01:00
parent 0b7636c80c
commit aaf82e3a5c
5 changed files with 383 additions and 3 deletions
@@ -0,0 +1,177 @@
//! Factory module for creating relay label repository instances.
//!
//! This module provides factory functions for creating relay label repositories
//! with appropriate implementations based on configuration.
use std::sync::Arc;
use crate::domain::relay::repository::{RelayLabelRepository, RepositoryError};
/// Creates a relay label repository based on configuration.
///
/// # Parameters
///
/// - `db_path`: Path to SQLite database file (e.g., "relays.db" or ":memory:")
/// - `use_mock`: If true, returns `MockRelayLabelRepository` for testing
///
/// # Returns
///
/// - `Ok(Arc<dyn RelayLabelRepository>)` on success
/// - `Err(RepositoryError)` if database connection fails or path is invalid
///
/// # Errors
///
/// Returns `RepositoryError` if:
/// - Database path is invalid or inaccessible
/// - SQLite connection fails
/// - Database schema migration fails
pub fn create_label_repository(
_db_path: &str,
_use_mock: bool,
) -> Result<Arc<dyn RelayLabelRepository>, RepositoryError> {
// TODO: Implement in T039b
unimplemented!("T039b: create_label_repository factory not yet implemented")
}
#[cfg(test)]
mod tests {
use super::*;
use crate::domain::relay::types::{RelayId, RelayLabel};
// T039b: Test 1 - use_mock=true returns MockLabelRepository
#[tokio::test]
async fn test_create_label_repository_with_mock_flag() {
// GIVEN: use_mock=true (db_path is ignored in mock mode)
let db_path = ":memory:";
// WHEN: create_label_repository is called with use_mock=true
let result = create_label_repository(db_path, true);
// THEN: Should return MockLabelRepository successfully
assert!(
result.is_ok(),
"Failed to create mock repository"
);
let repository = result.unwrap();
// Verify it's a mock by testing basic operations
// Mock repository should start empty
let relay_id = RelayId::new(1).unwrap();
let label_result = repository.get_label(relay_id).await;
assert!(
label_result.is_ok(),
"Mock repository should be immediately usable"
);
assert_eq!(
label_result.unwrap(),
None,
"Mock repository should start with no labels"
);
}
// T039b: Test 2 - use_mock=false returns SqliteRelayLabelRepository
#[tokio::test]
async fn test_create_label_repository_with_sqlite() {
// GIVEN: Valid in-memory SQLite database path
let db_path = ":memory:";
// WHEN: create_label_repository is called with use_mock=false
let result = create_label_repository(db_path, false);
// THEN: Should return SqliteRelayLabelRepository successfully
assert!(
result.is_ok(),
"Failed to create SQLite repository"
);
let repository = result.unwrap();
// Verify it's working by performing a basic operation
let relay_id = RelayId::new(1).unwrap();
let label = RelayLabel::new("Pump".to_string()).unwrap();
// Should be able to save and get labels
let save_result = repository.save_label(relay_id, label.clone()).await;
assert!(
save_result.is_ok(),
"Failed to save label on SQLite repository"
);
let get_result = repository.get_label(relay_id).await;
assert!(get_result.is_ok(), "Failed to get label");
assert_eq!(get_result.unwrap(), Some(label));
}
// T039b: Test 3 - Invalid db_path returns RepositoryError
#[test]
fn test_create_label_repository_with_invalid_path() {
// GIVEN: Invalid database path (directory that doesn't exist)
let db_path = "/nonexistent/directory/impossible/path/relays.db";
// WHEN: create_label_repository is called with use_mock=false
let result = create_label_repository(db_path, false);
// THEN: Should return RepositoryError
assert!(
result.is_err(),
"Should fail with invalid database path"
);
// Verify the error is appropriate
if let Err(error) = result {
match error {
RepositoryError::DatabaseError(_) => {
// Expected error type - test passes
}
_ => panic!("Expected DatabaseError for invalid path"),
}
}
}
// Additional test: Verify mock and SQLite repositories are independent
#[tokio::test]
async fn test_mock_and_sqlite_repositories_are_independent() {
// GIVEN: Both mock and SQLite repositories
let mock_repo = create_label_repository(":memory:", true).unwrap();
let sqlite_repo = create_label_repository(":memory:", false).unwrap();
let relay_id = RelayId::new(1).unwrap();
let label = RelayLabel::new("Test".to_string()).unwrap();
// WHEN: We save a label in the mock repository
mock_repo.save_label(relay_id, label.clone()).await.unwrap();
// THEN: The SQLite repository should not have that label
let sqlite_result = sqlite_repo.get_label(relay_id).await.unwrap();
assert_eq!(
sqlite_result, None,
"SQLite repository should be independent from mock"
);
}
// Additional test: Verify in-memory SQLite doesn't persist
#[tokio::test]
async fn test_in_memory_sqlite_does_not_persist() {
// GIVEN: An in-memory SQLite database
let relay_id = RelayId::new(1).unwrap();
let label = RelayLabel::new("Temporary".to_string()).unwrap();
// WHEN: We create a repository, save a label, and drop it
{
let repo = create_label_repository(":memory:", false).unwrap();
repo.save_label(relay_id, label.clone()).await.unwrap();
} // repo is dropped here
// AND: We create a new in-memory repository
let new_repo = create_label_repository(":memory:", false).unwrap();
// THEN: The label should not exist in the new repository
let result = new_repo.get_label(relay_id).await.unwrap();
assert_eq!(
result, None,
"In-memory database should not persist across instances"
);
}
}
@@ -3,6 +3,11 @@
//! This module contains the concrete implementations of repository traits
//! for data persistence, including SQLite-based storage for relay labels.
pub mod entities;
/// Factory functions for creating relay label repositories.
pub mod factory;
/// Mock repository implementation for testing.
pub mod label_repository;
@@ -12,5 +17,3 @@ pub mod label_repository_tests;
/// `SQLite` repository implementation for relay labels.
pub mod sqlite_repository;
pub mod entities;