#![cfg(all(
feature = "rusqlite",
not(feature = "libsql"),
not(feature = "postgres")
))]
use std::rc::Rc;
use toolu_orm_connection::{DbConnection, DbConnectionBlocking, DbError};
use toolu_orm_core::error::DbCoreError;
use toolu_orm_core::row::FromRow;
use toolu_orm_core::value::Value;
#[path = "fixtures/blocking_conn.rs"]
pub mod blocking_conn;
#[path = "fixtures/rusqlite_rows.rs"]
pub mod rusqlite_rows;
use blocking_conn::{open_in_memory, scalar};
use rusqlite_rows::LabelRow;
#[test]
fn blocking_roundtrip_without_a_runtime() -> Result<(), Box<dyn std::error::Error>> {
let conn = open_in_memory()?;
DbConnectionBlocking::execute_batch(
&conn,
"CREATE TABLE blocking_smoke (id INTEGER PRIMARY KEY, label TEXT NOT NULL)",
)?;
let affected = DbConnectionBlocking::execute_sql(
&conn,
"INSERT INTO blocking_smoke (id, label) VALUES (?1, ?2)",
vec![Value::Integer(1), Value::Text("no-runtime".to_owned())],
)?;
assert_eq!(affected, 1);
let rows: Vec<LabelRow> =
DbConnectionBlocking::query_map(&conn, "SELECT label FROM blocking_smoke", vec![])?;
assert_eq!(rows.len(), 1);
assert_eq!(
rows.first().map(|row| row.label.as_str()),
Some("no-runtime")
);
assert_eq!(scalar(&conn, "SELECT COUNT(*) FROM blocking_smoke")?, 1);
Ok(())
}
struct RcRow {
label: Rc<String>,
}
impl FromRow for RcRow {
const REQUIRED_COLUMNS: &'static [&'static str] = &["label"];
fn from_row(row: &rusqlite::Row<'_>) -> Result<Self, DbCoreError> {
let label: String = row
.get(0)
.map_err(|e| DbCoreError::RowMapping(e.to_string()))?;
Ok(Self {
label: Rc::new(label),
})
}
}
#[test]
fn query_map_accepts_a_non_send_row() -> Result<(), Box<dyn std::error::Error>> {
let conn = open_in_memory()?;
DbConnectionBlocking::execute_batch(
&conn,
"CREATE TABLE non_send (label TEXT NOT NULL);
INSERT INTO non_send (label) VALUES ('not-send');",
)?;
let rows: Vec<RcRow> =
DbConnectionBlocking::query_map(&conn, "SELECT label FROM non_send", vec![])?;
assert_eq!(rows.len(), 1);
assert_eq!(rows.first().map(|row| row.label.as_str()), Some("not-send"));
Ok(())
}
#[test]
fn bad_sql_returns_a_query_error() -> Result<(), Box<dyn std::error::Error>> {
let conn = open_in_memory()?;
let error = DbConnectionBlocking::execute_sql(
&conn,
"INSERT INTO nonexistent (x) VALUES (?1)",
vec![Value::Integer(1)],
)
.err()
.ok_or("the statement should have failed")?;
assert!(
matches!(&error, DbError::Query(message) if message.contains("no such table")),
"expected a DbError::Query naming the missing table, got {error:?}"
);
Ok(())
}
struct RejectingRow;
impl FromRow for RejectingRow {
const REQUIRED_COLUMNS: &'static [&'static str] = &["label"];
fn from_row(_row: &rusqlite::Row<'_>) -> Result<Self, DbCoreError> {
Err(DbCoreError::RowMapping(
"label is not a colour name".to_owned(),
))
}
}
#[test]
fn decode_failure_returns_a_row_mapping_error() -> Result<(), Box<dyn std::error::Error>> {
let conn = open_in_memory()?;
DbConnectionBlocking::execute_batch(
&conn,
"CREATE TABLE rejecting (label TEXT NOT NULL);
INSERT INTO rejecting (label) VALUES ('not-a-colour');",
)?;
let error =
DbConnectionBlocking::query_map::<RejectingRow>(&conn, "SELECT label FROM rejecting", vec![])
.err()
.ok_or("the decode should have failed")?;
assert!(
matches!(&error, DbError::RowMapping(message) if message.contains("not a colour name")),
"expected the FromRow message inside DbError::RowMapping, got {error:?}"
);
Ok(())
}
#[tokio::test]
async fn async_and_blocking_share_one_connection() -> Result<(), Box<dyn std::error::Error>> {
let conn = open_in_memory()?;
DbConnectionBlocking::execute_batch(&conn, "CREATE TABLE shared (label TEXT NOT NULL)")?;
DbConnectionBlocking::execute_sql(
&conn,
"INSERT INTO shared (label) VALUES (?1)",
vec![Value::Text("written-blocking".to_owned())],
)?;
let seen_by_async: Vec<LabelRow> =
DbConnection::query_map(&conn, "SELECT label FROM shared", vec![]).await?;
assert_eq!(
seen_by_async.first().map(|row| row.label.as_str()),
Some("written-blocking")
);
DbConnection::execute_sql(
&conn,
"INSERT INTO shared (label) VALUES (?1)",
vec![Value::Text("written-async".to_owned())],
)
.await?;
let seen_by_blocking: Vec<LabelRow> = DbConnectionBlocking::query_map(
&conn,
"SELECT label FROM shared WHERE label = 'written-async'",
vec![],
)?;
assert_eq!(
seen_by_blocking.first().map(|row| row.label.as_str()),
Some("written-async")
);
assert_eq!(scalar(&conn, "SELECT COUNT(*) FROM shared")?, 2);
Ok(())
}