use rusqlite::Connection;
pub const SCHEMA_DDL: &str = include_str!("schema.sql");
#[tracing::instrument]
pub fn create_schema(connection: &Connection) -> rusqlite::Result<()> {
connection.execute_batch(SCHEMA_DDL)
}
pub fn table_names() -> Vec<&'static str> {
SCHEMA_DDL
.lines()
.filter_map(|line| line.trim_start().strip_prefix("CREATE TABLE "))
.map(|rest| rest.split_whitespace().next().unwrap_or(""))
.filter(|name| !name.is_empty())
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn create_schema_succeeds_on_fresh_connection() {
let connection = Connection::open_in_memory().unwrap();
create_schema(&connection).unwrap();
}
#[test]
fn create_schema_creates_every_declared_table() {
let connection = Connection::open_in_memory().unwrap();
create_schema(&connection).unwrap();
let mut statement = connection
.prepare("SELECT name FROM sqlite_master WHERE type = 'table' ORDER BY name")
.unwrap();
let mut existing: Vec<String> = statement
.query_map([], |row| row.get::<usize, String>(0))
.unwrap()
.collect::<rusqlite::Result<_>>()
.unwrap();
existing.sort();
let mut expected: Vec<String> = table_names().iter().map(|s| s.to_string()).collect();
expected.sort();
assert_eq!(existing, expected);
assert_eq!(expected.len(), 31);
}
#[test]
fn create_schema_fails_if_called_twice() {
let connection = Connection::open_in_memory().unwrap();
create_schema(&connection).unwrap();
assert!(create_schema(&connection).is_err());
}
#[test]
fn create_schema_enforces_strict_typing() {
let connection = Connection::open_in_memory().unwrap();
create_schema(&connection).unwrap();
let result = connection.execute(
"INSERT INTO invCategories (categoryId, categoryName, published) \
VALUES (1, 'Test', 'yes')",
[],
);
assert!(result.is_err());
}
#[test]
fn create_schema_enforces_foreign_keys() {
let connection = Connection::open_in_memory().unwrap();
create_schema(&connection).unwrap();
let result = connection.execute(
"INSERT INTO invGroups (groupId, groupName, categoryId, anchorable) \
VALUES (1, 'Test', 999, 0)",
[],
);
assert!(result.is_err());
}
#[test]
fn create_schema_accepts_valid_rows_respecting_fk_order() {
let connection = Connection::open_in_memory().unwrap();
create_schema(&connection).unwrap();
connection
.execute(
"INSERT INTO invCategories (categoryId, categoryName, published) \
VALUES (1, 'Celestial', 1)",
[],
)
.unwrap();
connection
.execute(
"INSERT INTO invGroups (groupId, groupName, categoryId, anchorable) \
VALUES (10, 'Sun', 1, 0)",
[],
)
.unwrap();
let count: i64 = connection
.query_row("SELECT COUNT(*) FROM invGroups", [], |row| row.get(0))
.unwrap();
assert_eq!(count, 1);
}
}