use rusqlite::{Connection, Result}; use std::path::Path; const MIGRATION_001: &str = include_str!("../migrations/001_init.sql"); pub fn init(db_path: &Path) -> Result { let conn = Connection::open(db_path)?; configure(&conn)?; migrate(&conn)?; Ok(conn) } pub fn init_in_memory() -> Result { let conn = Connection::open_in_memory()?; configure(&conn)?; migrate(&conn)?; Ok(conn) } fn configure(conn: &Connection) -> Result<()> { conn.pragma_update(None, "journal_mode", "wal")?; conn.pragma_update(None, "foreign_keys", "ON")?; Ok(()) } fn migrate(conn: &Connection) -> Result<()> { let version: i32 = conn.pragma_query_value(None, "user_version", |row| row.get(0))?; if version < 1 { conn.execute_batch(MIGRATION_001)?; conn.pragma_update(None, "user_version", 1)?; } Ok(()) } #[cfg(test)] mod tests { use super::*; #[test] fn test_init_in_memory_creates_tables() { let conn = init_in_memory().expect("should initialize"); // Verify all 6 tables exist let tables: Vec = conn .prepare("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name") .unwrap() .query_map([], |row| row.get(0)) .unwrap() .collect::, _>>() .unwrap(); assert_eq!( tables, vec![ "notifications", "processed_tickets", "projects", "tuleap_credentials", "watched_trackers", "worktrees", ] ); } #[test] fn test_init_in_memory_enables_foreign_keys() { let conn = init_in_memory().expect("should initialize"); let fk_enabled: i32 = conn .query_row("PRAGMA foreign_keys", [], |row| row.get(0)) .unwrap(); assert_eq!(fk_enabled, 1); } #[test] fn test_migration_is_idempotent() { let conn = init_in_memory().expect("should initialize"); // Running init again on same connection should not fail let version: i32 = conn .pragma_query_value(None, "user_version", |row| row.get(0)) .unwrap(); assert_eq!(version, 1); } }