use std::path::Path;
use reposix_core::audit;
use rusqlite::Connection;
use crate::error::ApiError;
pub const ISSUES_SQL: &str = "\
CREATE TABLE IF NOT EXISTS issues (
project TEXT NOT NULL,
id INTEGER NOT NULL,
title TEXT NOT NULL,
status TEXT NOT NULL,
assignee TEXT,
labels TEXT NOT NULL DEFAULT '[]',
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
version INTEGER NOT NULL DEFAULT 1,
body TEXT NOT NULL DEFAULT '',
PRIMARY KEY(project, id)
);
";
pub fn open_db(path: &Path, ephemeral: bool) -> Result<Connection, ApiError> {
let conn = if ephemeral || path == Path::new(":memory:") {
Connection::open_in_memory()?
} else {
Connection::open(path)?
};
let is_memory = ephemeral || path == Path::new(":memory:");
if !is_memory {
let _: String = conn.query_row("PRAGMA journal_mode=WAL", [], |r| r.get(0))?;
conn.pragma_update(None, "synchronous", "NORMAL")?;
conn.pragma_update(None, "busy_timeout", 5000_i64)?;
}
conn.execute_batch(ISSUES_SQL)?;
audit::load_schema(&conn).map_err(|e| ApiError::Internal(e.to_string()))?;
Ok(conn)
}
#[cfg(test)]
mod tests {
use super::{open_db, Path};
#[test]
fn open_db_in_memory_succeeds() {
let conn = open_db(Path::new(":memory:"), true).expect("open");
let count: i64 = conn
.query_row(
"SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='issues'",
[],
|r| r.get(0),
)
.expect("issues table present");
assert_eq!(count, 1);
}
#[test]
fn open_db_is_idempotent_on_repeated_schema_load() {
let conn = open_db(Path::new(":memory:"), true).expect("first");
reposix_core::audit::load_schema(&conn).expect("second load_schema");
conn.execute_batch(super::ISSUES_SQL).expect("re-run ddl");
}
#[test]
fn open_db_installs_audit_triggers() {
let conn = open_db(Path::new(":memory:"), true).expect("open");
let triggers: Vec<String> = {
let mut stmt = conn
.prepare(
"SELECT name FROM sqlite_master WHERE type='trigger' \
AND tbl_name='audit_events' ORDER BY name",
)
.expect("prepare");
stmt.query_map([], |r| r.get::<_, String>(0))
.expect("query_map")
.map(std::result::Result::unwrap)
.collect()
};
assert_eq!(triggers, vec!["audit_no_delete", "audit_no_update"]);
}
}