use rusqlite::{Connection, Result as SqlResult};
use crate::migrations;
pub fn open(path: &str) -> Result<Connection, crate::SqliteStoreError> {
let conn = Connection::open(path)?;
apply_pragmas(&conn)?;
migrations::apply_migrations(&conn)?;
Ok(conn)
}
pub fn open_in_memory() -> Result<Connection, crate::SqliteStoreError> {
let conn = Connection::open_in_memory()?;
apply_pragmas(&conn)?;
migrations::apply_migrations(&conn)?;
Ok(conn)
}
fn apply_pragmas(conn: &Connection) -> SqlResult<()> {
conn.execute_batch(
"PRAGMA journal_mode = WAL;\
PRAGMA synchronous = FULL;\
PRAGMA foreign_keys = ON;",
)?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
fn pragma_str(conn: &Connection, pragma: &str) -> String {
conn.query_row(
&format!("PRAGMA {pragma}"),
[],
|row| row.get::<_, String>(0),
)
.unwrap_or_else(|_| String::new())
}
fn pragma_int(conn: &Connection, pragma: &str) -> i64 {
conn.query_row(
&format!("PRAGMA {pragma}"),
[],
|row| row.get::<_, i64>(0),
)
.unwrap_or(-1)
}
#[test]
fn in_memory_synchronous_is_full() {
let conn = open_in_memory().expect("in-memory open should succeed");
let sync_val = pragma_int(&conn, "synchronous");
assert_eq!(sync_val, 2, "synchronous must be FULL (2) on in-memory connection");
}
#[test]
fn in_memory_foreign_keys_is_on() {
let conn = open_in_memory().expect("in-memory open should succeed");
let fk_val = pragma_int(&conn, "foreign_keys");
assert_eq!(fk_val, 1, "foreign_keys must be ON (1) on in-memory connection");
}
#[test]
fn in_memory_journal_mode_is_memory_not_wal() {
let conn = open_in_memory().expect("in-memory open should succeed");
let mode = pragma_str(&conn, "journal_mode");
assert_eq!(
mode, "memory",
"in-memory SQLite must use journal_mode=memory (WAL not supported on :memory:)"
);
}
#[test]
fn file_backed_journal_mode_is_wal() {
let dir = tempfile::tempdir().expect("tempdir should create");
let path = dir.path().join("test_wal.db");
let path_str = path.to_str().unwrap();
let conn = open(path_str).expect("file-backed open should succeed");
let mode = pragma_str(&conn, "journal_mode");
assert_eq!(mode, "wal", "file-backed connection must be WAL");
drop(conn);
let _ = fs::remove_file(path.with_extension("db-wal"));
let _ = fs::remove_file(path.with_extension("db-shm"));
}
#[test]
fn file_backed_synchronous_is_full() {
let dir = tempfile::tempdir().expect("tempdir should create");
let path = dir.path().join("test_sync.db");
let conn = open(path.to_str().unwrap()).expect("file-backed open should succeed");
let sync_val = pragma_int(&conn, "synchronous");
assert_eq!(sync_val, 2, "synchronous must be FULL (2) on file-backed connection");
}
#[test]
fn file_backed_foreign_keys_is_on() {
let dir = tempfile::tempdir().expect("tempdir should create");
let path = dir.path().join("test_fk.db");
let conn = open(path.to_str().unwrap()).expect("file-backed open should succeed");
let fk_val = pragma_int(&conn, "foreign_keys");
assert_eq!(fk_val, 1, "foreign_keys must be ON (1) on file-backed connection");
}
#[test]
fn open_in_memory_runs_migrations() {
let conn = open_in_memory().expect("in-memory open should succeed");
let count: i64 = conn
.query_row(
"SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='claims'",
[],
|r| r.get(0),
)
.unwrap();
assert_eq!(count, 1, "claims table must exist after open_in_memory");
}
}