use async_trait::async_trait;
use kanban_domain::command_store::CommandStore;
use kanban_domain::data_store::DataStore;
use kanban_domain::{InMemoryStore, KanbanResult};
use uuid::Uuid;
#[async_trait]
pub trait KanbanBackend: DataStore + CommandStore + Send + Sync {
fn as_data_store(&self) -> &dyn DataStore;
async fn flush(&self) -> KanbanResult<()> {
Ok(())
}
async fn reload(&self) -> KanbanResult<()> {
Ok(())
}
fn needs_flush(&self) -> bool {
false
}
fn needs_save_worker(&self) -> bool {
false
}
fn instance_id(&self) -> Uuid {
Uuid::nil()
}
}
impl KanbanBackend for InMemoryStore {
fn as_data_store(&self) -> &dyn DataStore {
self
}
}
#[cfg(test)]
mod tests {
use super::*;
use kanban_domain::InMemoryStore;
#[test]
fn test_kanban_backend_is_object_safe() {
let store = InMemoryStore::new();
let _: &dyn KanbanBackend = &store;
}
#[test]
fn test_as_data_store_returns_data_store_ref() {
let store = InMemoryStore::new();
let backend: &dyn KanbanBackend = &store;
let _: &dyn DataStore = backend.as_data_store();
}
#[test]
fn test_in_memory_store_needs_flush_returns_false() {
let store = InMemoryStore::new();
assert!(!store.needs_flush());
}
#[test]
fn test_in_memory_store_needs_save_worker_returns_false() {
let store = InMemoryStore::new();
assert!(!store.needs_save_worker());
}
#[tokio::test]
async fn test_in_memory_store_flush_is_noop() {
let store = InMemoryStore::new();
store.flush().await.expect("flush should be a no-op");
}
#[tokio::test]
async fn test_in_memory_store_reload_is_noop() {
let store = InMemoryStore::new();
store.reload().await.expect("reload should be a no-op");
}
#[cfg(feature = "sqlite")]
mod sqlite_backend_tests {
use kanban_domain::{Board, DataStore};
use crate::backend::KanbanBackend;
use crate::sqlite_backend::SqliteBackend;
#[tokio::test(flavor = "multi_thread")]
async fn test_sqlite_backend_needs_flush_returns_false() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("t.sqlite3");
let backend = SqliteBackend::open(path.to_str().unwrap()).await.unwrap();
assert!(!backend.needs_flush());
}
#[tokio::test(flavor = "multi_thread")]
async fn test_sqlite_backend_flush_executes_wal_checkpoint() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("t.sqlite3");
let backend = SqliteBackend::open(path.to_str().unwrap()).await.unwrap();
backend.upsert_board(Board::new("B".into(), None)).unwrap();
backend
.flush()
.await
.expect("WAL checkpoint should not error");
}
#[tokio::test(flavor = "multi_thread")]
async fn test_sqlite_backend_reload_is_noop() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("t.sqlite3");
let backend = SqliteBackend::open(path.to_str().unwrap()).await.unwrap();
backend.upsert_board(Board::new("A".into(), None)).unwrap();
backend.reload().await.unwrap();
let boards = backend.list_boards().unwrap();
assert_eq!(boards.len(), 1);
assert_eq!(boards[0].name, "A");
}
}
}