feat(infra): add SqliteAppSettingsRepository
This commit is contained in:
83
k-tv-backend/infra/src/app_settings_repository.rs
Normal file
83
k-tv-backend/infra/src/app_settings_repository.rs
Normal file
@@ -0,0 +1,83 @@
|
||||
//! SQLite implementation of IAppSettingsRepository.
|
||||
|
||||
use async_trait::async_trait;
|
||||
use sqlx::SqlitePool;
|
||||
use domain::{DomainError, DomainResult, IAppSettingsRepository};
|
||||
|
||||
pub struct SqliteAppSettingsRepository {
|
||||
pool: SqlitePool,
|
||||
}
|
||||
|
||||
impl SqliteAppSettingsRepository {
|
||||
pub fn new(pool: SqlitePool) -> Self {
|
||||
Self { pool }
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl IAppSettingsRepository for SqliteAppSettingsRepository {
|
||||
async fn get(&self, key: &str) -> DomainResult<Option<String>> {
|
||||
sqlx::query_scalar::<_, String>("SELECT value FROM app_settings WHERE key = ?")
|
||||
.bind(key)
|
||||
.fetch_optional(&self.pool)
|
||||
.await
|
||||
.map_err(|e| DomainError::InfrastructureError(e.to_string()))
|
||||
}
|
||||
|
||||
async fn set(&self, key: &str, value: &str) -> DomainResult<()> {
|
||||
sqlx::query("INSERT OR REPLACE INTO app_settings (key, value) VALUES (?, ?)")
|
||||
.bind(key)
|
||||
.bind(value)
|
||||
.execute(&self.pool)
|
||||
.await
|
||||
.map(|_| ())
|
||||
.map_err(|e| DomainError::InfrastructureError(e.to_string()))
|
||||
}
|
||||
|
||||
async fn get_all(&self) -> DomainResult<Vec<(String, String)>> {
|
||||
sqlx::query_as::<_, (String, String)>("SELECT key, value FROM app_settings ORDER BY key")
|
||||
.fetch_all(&self.pool)
|
||||
.await
|
||||
.map_err(|e| DomainError::InfrastructureError(e.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use sqlx::SqlitePool;
|
||||
use domain::IAppSettingsRepository;
|
||||
|
||||
async fn setup() -> SqlitePool {
|
||||
let pool = SqlitePool::connect(":memory:").await.unwrap();
|
||||
sqlx::query(
|
||||
"CREATE TABLE app_settings (key TEXT PRIMARY KEY, value TEXT NOT NULL)"
|
||||
).execute(&pool).await.unwrap();
|
||||
sqlx::query("INSERT INTO app_settings VALUES ('library_sync_interval_hours', '6')")
|
||||
.execute(&pool).await.unwrap();
|
||||
pool
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn get_returns_seeded_value() {
|
||||
let repo = SqliteAppSettingsRepository::new(setup().await);
|
||||
let val = repo.get("library_sync_interval_hours").await.unwrap();
|
||||
assert_eq!(val, Some("6".to_string()));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn set_then_get() {
|
||||
let repo = SqliteAppSettingsRepository::new(setup().await);
|
||||
repo.set("library_sync_interval_hours", "12").await.unwrap();
|
||||
let val = repo.get("library_sync_interval_hours").await.unwrap();
|
||||
assert_eq!(val, Some("12".to_string()));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn get_all_returns_all_keys() {
|
||||
let repo = SqliteAppSettingsRepository::new(setup().await);
|
||||
let all = repo.get_all().await.unwrap();
|
||||
assert!(!all.is_empty());
|
||||
assert!(all.iter().any(|(k, _)| k == "library_sync_interval_hours"));
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ pub mod db;
|
||||
pub mod factory;
|
||||
pub mod jellyfin;
|
||||
pub mod provider_registry;
|
||||
mod app_settings_repository;
|
||||
mod activity_log_repository;
|
||||
mod channel_repository;
|
||||
mod library_repository;
|
||||
@@ -33,6 +34,8 @@ pub mod local_files;
|
||||
pub use db::run_migrations;
|
||||
pub use provider_registry::ProviderRegistry;
|
||||
|
||||
#[cfg(feature = "sqlite")]
|
||||
pub use app_settings_repository::SqliteAppSettingsRepository;
|
||||
#[cfg(feature = "sqlite")]
|
||||
pub use activity_log_repository::SqliteActivityLogRepository;
|
||||
#[cfg(feature = "sqlite")]
|
||||
|
||||
Reference in New Issue
Block a user