use rusqlite::{Connection, Result}; use std::path::Path; const MIGRATION_001: &str = include_str!("../migrations/001_init.sql"); const MIGRATION_002: &str = include_str!("../migrations/002_add_last_polled.sql"); const MIGRATION_003: &str = include_str!("../migrations/003_add_agents.sql"); const MIGRATION_004: &str = include_str!("../migrations/004_default_agents.sql"); pub fn init(db_path: &Path) -> Result { let conn = Connection::open(db_path)?; configure(&conn)?; migrate(&conn)?; Ok(conn) } #[cfg(test)] 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)?; } if version < 2 { conn.execute_batch(MIGRATION_002)?; conn.pragma_update(None, "user_version", 2)?; } if version < 3 { conn.execute_batch(MIGRATION_003)?; conn.pragma_update(None, "user_version", 3)?; } if version < 4 { conn.execute_batch(MIGRATION_004)?; conn.pragma_update(None, "user_version", 4)?; } 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 7 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![ "agents", "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, 4); } #[test] fn test_default_agents_are_seeded() { let conn = init_in_memory().expect("should initialize"); let analyst_defaults: i32 = conn .query_row( "SELECT COUNT(*) FROM agents WHERE role = 'analyst' AND is_default = 1", [], |row| row.get(0), ) .unwrap(); let developer_defaults: i32 = conn .query_row( "SELECT COUNT(*) FROM agents WHERE role = 'developer' AND is_default = 1", [], |row| row.get(0), ) .unwrap(); assert_eq!(analyst_defaults, 1); assert_eq!(developer_defaults, 1); } }