#![cfg(all(
feature = "rusqlite",
not(feature = "libsql"),
not(feature = "postgres")
))]
use toolu_orm_connection::DbConnection;
use toolu_orm_connection::rusqlite_impl::RusqliteConnection;
use toolu_orm_core::value::Value;
#[path = "fixtures/rusqlite_rows.rs"]
pub mod rusqlite_rows;
use rusqlite_rows::{LabelRow, ScalarRow};
async fn scalar(
conn: &RusqliteConnection,
sql: &str,
) -> Result<i64, toolu_orm_connection::DbError> {
let rows: Vec<ScalarRow> = conn.query_map(sql, vec![]).await?;
rows
.first()
.map(|row| row.value)
.ok_or_else(|| toolu_orm_connection::DbError::Query(format!("no row for `{sql}`")))
}
#[tokio::test]
async fn execute_batch_creates_table() -> Result<(), toolu_orm_connection::DbError> {
let conn = RusqliteConnection::open_in_memory().await?;
conn
.execute_batch("CREATE TABLE test_batch (id INTEGER PRIMARY KEY, name TEXT NOT NULL)")
.await?;
Ok(())
}
#[tokio::test]
async fn execute_sql_inserts_row() -> Result<(), toolu_orm_connection::DbError> {
let conn = RusqliteConnection::open_in_memory().await?;
conn
.execute_batch("CREATE TABLE test_insert (id INTEGER PRIMARY KEY, name TEXT NOT NULL)")
.await?;
let affected = conn
.execute_sql(
"INSERT INTO test_insert (id, name) VALUES (?1, ?2)",
vec![Value::Integer(1), Value::Text("alice".to_owned())],
)
.await?;
assert_eq!(affected, 1);
Ok(())
}
#[tokio::test]
async fn query_map_returns_rows() -> Result<(), toolu_orm_connection::DbError> {
let conn = RusqliteConnection::open_in_memory().await?;
conn
.execute_batch("CREATE TABLE test_query (count INTEGER NOT NULL)")
.await?;
conn
.execute_sql(
"INSERT INTO test_query (count) VALUES (?1)",
vec![Value::Integer(42)],
)
.await?;
let rows: Vec<ScalarRow> = conn
.query_map("SELECT count FROM test_query", vec![])
.await?;
assert_eq!(rows.len(), 1);
let row = rows
.first()
.ok_or_else(|| toolu_orm_connection::DbError::Query("missing row".to_owned()))?;
assert_eq!(row.value, 42);
Ok(())
}
#[tokio::test]
async fn execute_sql_returns_error_on_bad_sql() -> Result<(), toolu_orm_connection::DbError> {
let conn = RusqliteConnection::open_in_memory().await?;
let result = conn
.execute_sql(
"INSERT INTO nonexistent (x) VALUES (?1)",
vec![Value::Integer(1)],
)
.await;
assert!(result.is_err());
Ok(())
}
#[tokio::test]
async fn from_connection_adopts_existing_database() -> Result<(), Box<dyn std::error::Error>> {
let raw = rusqlite::Connection::open_in_memory()?;
raw.execute_batch(
"CREATE TABLE adopted (id INTEGER PRIMARY KEY, label TEXT NOT NULL);
INSERT INTO adopted (id, label) VALUES (7, 'pre-existing');",
)?;
let conn = RusqliteConnection::from_connection(raw);
let rows: Vec<LabelRow> = conn
.query_map("SELECT label FROM adopted WHERE id = 7", vec![])
.await?;
assert_eq!(rows.len(), 1);
assert_eq!(
rows.first().map(|row| row.label.as_str()),
Some("pre-existing")
);
Ok(())
}
#[tokio::test]
async fn from_connection_preserves_connection_pragmas() -> Result<(), Box<dyn std::error::Error>> {
let configured = rusqlite::Connection::open_in_memory()?;
configured.execute_batch("PRAGMA foreign_keys = OFF;")?;
let configured = RusqliteConnection::from_connection(configured);
let control = RusqliteConnection::from_connection(rusqlite::Connection::open_in_memory()?);
assert_eq!(scalar(&configured, "PRAGMA foreign_keys").await?, 0);
assert_eq!(scalar(&control, "PRAGMA foreign_keys").await?, 1);
Ok(())
}
struct TempDbPath(String);
impl TempDbPath {
fn new() -> Result<Self, std::time::SystemTimeError> {
let nanos = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)?
.as_nanos();
let path =
std::env::temp_dir().join(format!("toolu-orm-open-{}-{nanos}.db", std::process::id()));
Ok(Self(path.to_string_lossy().into_owned()))
}
fn as_str(&self) -> &str {
&self.0
}
}
impl Drop for TempDbPath {
fn drop(&mut self) {
match std::fs::remove_file(&self.0) {
Ok(()) => {},
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {},
Err(e) => eprintln!("could not remove the temp database {}: {e}", self.0),
}
}
}
#[tokio::test]
async fn open_persists_to_a_file() -> Result<(), Box<dyn std::error::Error>> {
let path = TempDbPath::new()?;
let writer = RusqliteConnection::open(path.as_str()).await?;
writer
.execute_batch("CREATE TABLE persisted (count INTEGER NOT NULL)")
.await?;
writer
.execute_sql(
"INSERT INTO persisted (count) VALUES (?1)",
vec![Value::Integer(99)],
)
.await?;
drop(writer);
let reader = RusqliteConnection::open(path.as_str()).await?;
assert_eq!(scalar(&reader, "SELECT count FROM persisted").await?, 99);
Ok(())
}