use diesel::prelude::*;
use diesel::result::Error;
use diesel_libsql::LibSqlConnection;
diesel::table! {
users (id) {
id -> Integer,
name -> Text,
}
}
diesel::table! {
typed_data (id) {
id -> Integer,
int_val -> Integer,
bigint_val -> BigInt,
real_val -> Double,
text_val -> Text,
blob_val -> Binary,
bool_val -> Bool,
}
}
diesel::table! {
nullable_data (id) {
id -> Integer,
text_val -> Nullable<Text>,
small_val -> Nullable<SmallInt>,
float_val -> Nullable<Float>,
}
}
diesel::table! {
items (id) {
id -> Integer,
title -> Text,
}
}
#[test]
fn test_in_memory_crud() {
let mut conn = LibSqlConnection::establish(":memory:").expect("Failed to connect");
diesel::sql_query(
"CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL)",
)
.execute(&mut conn)
.expect("Failed to create table");
diesel::sql_query("INSERT INTO users (name) VALUES ('alice')")
.execute(&mut conn)
.expect("Failed to insert");
let results: Vec<(i32, String)> = users::table
.select((users::id, users::name))
.load(&mut conn)
.expect("Failed to select");
assert_eq!(results.len(), 1);
assert_eq!(results[0].1, "alice");
diesel::update(users::table.filter(users::name.eq("alice")))
.set(users::name.eq("bob"))
.execute(&mut conn)
.expect("Failed to update");
let results: Vec<(i32, String)> = users::table
.select((users::id, users::name))
.load(&mut conn)
.expect("Failed to select after update");
assert_eq!(results.len(), 1);
assert_eq!(results[0].1, "bob");
diesel::delete(users::table.filter(users::name.eq("bob")))
.execute(&mut conn)
.expect("Failed to delete");
let results: Vec<(i32, String)> = users::table
.select((users::id, users::name))
.load(&mut conn)
.expect("Failed to select after delete");
assert!(results.is_empty());
}
#[test]
fn test_transaction_rollback() {
let mut conn = LibSqlConnection::establish(":memory:").expect("Failed to connect");
diesel::sql_query(
"CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL)",
)
.execute(&mut conn)
.expect("Failed to create table");
let result: Result<(), Error> = conn.transaction::<_, Error, _>(|conn| {
diesel::sql_query("INSERT INTO users (name) VALUES ('should_not_exist')")
.execute(conn)
.expect("Failed to insert in transaction");
let count: Vec<(i32, String)> = users::table
.select((users::id, users::name))
.load(conn)
.expect("Failed to select in transaction");
assert_eq!(count.len(), 1);
Err(Error::RollbackTransaction)
});
assert!(result.is_err());
let results: Vec<(i32, String)> = users::table
.select((users::id, users::name))
.load(&mut conn)
.expect("Failed to select after rollback");
assert!(results.is_empty());
}
#[test]
fn test_file_persistence() {
let dir = tempfile::tempdir().expect("Failed to create temp dir");
let db_path = dir.path().join("test.db");
let db_url = db_path.to_str().unwrap();
{
let mut conn = LibSqlConnection::establish(db_url).expect("Failed to connect");
diesel::sql_query(
"CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL)",
)
.execute(&mut conn)
.expect("Failed to create table");
diesel::sql_query("INSERT INTO users (name) VALUES ('persistent_alice')")
.execute(&mut conn)
.expect("Failed to insert");
}
{
let mut conn = LibSqlConnection::establish(db_url).expect("Failed to reconnect");
let results: Vec<(i32, String)> = users::table
.select((users::id, users::name))
.load(&mut conn)
.expect("Failed to select after reconnect");
assert_eq!(results.len(), 1);
assert_eq!(results[0].1, "persistent_alice");
}
}
#[test]
fn test_multiple_types() {
let mut conn = LibSqlConnection::establish(":memory:").expect("Failed to connect");
diesel::sql_query(
"CREATE TABLE typed_data (
id INTEGER PRIMARY KEY AUTOINCREMENT,
int_val INTEGER NOT NULL,
bigint_val INTEGER NOT NULL,
real_val REAL NOT NULL,
text_val TEXT NOT NULL,
blob_val BLOB NOT NULL,
bool_val INTEGER NOT NULL
)",
)
.execute(&mut conn)
.expect("Failed to create table");
diesel::sql_query(
"INSERT INTO typed_data (int_val, bigint_val, real_val, text_val, blob_val, bool_val)
VALUES (42, 9999999999, 3.14, 'hello world', X'DEADBEEF', 1)",
)
.execute(&mut conn)
.expect("Failed to insert typed data");
let results: Vec<(i32, i32, i64, f64, String, Vec<u8>, bool)> = typed_data::table
.select((
typed_data::id,
typed_data::int_val,
typed_data::bigint_val,
typed_data::real_val,
typed_data::text_val,
typed_data::blob_val,
typed_data::bool_val,
))
.load(&mut conn)
.expect("Failed to load typed data");
assert_eq!(results.len(), 1);
let row = &results[0];
assert_eq!(row.1, 42); assert_eq!(row.2, 9999999999i64); assert!((row.3 - 3.14).abs() < 0.001); assert_eq!(row.4, "hello world"); assert_eq!(row.5, vec![0xDE, 0xAD, 0xBE, 0xEF]); assert!(row.6); }
#[test]
fn test_nullable_types() {
let mut conn = LibSqlConnection::establish(":memory:").expect("Failed to connect");
diesel::sql_query(
"CREATE TABLE nullable_data (
id INTEGER PRIMARY KEY AUTOINCREMENT,
text_val TEXT,
small_val INTEGER,
float_val REAL
)",
)
.execute(&mut conn)
.expect("Failed to create table");
diesel::sql_query(
"INSERT INTO nullable_data (text_val, small_val, float_val) VALUES ('hello', 42, 2.5)",
)
.execute(&mut conn)
.expect("Failed to insert row with values");
diesel::sql_query(
"INSERT INTO nullable_data (text_val, small_val, float_val) VALUES (NULL, NULL, NULL)",
)
.execute(&mut conn)
.expect("Failed to insert row with NULLs");
let results: Vec<(i32, Option<String>, Option<i16>, Option<f32>)> = nullable_data::table
.select((
nullable_data::id,
nullable_data::text_val,
nullable_data::small_val,
nullable_data::float_val,
))
.order(nullable_data::id.asc())
.load(&mut conn)
.expect("Failed to load nullable data");
assert_eq!(results.len(), 2);
assert_eq!(results[0].1, Some("hello".to_string()));
assert_eq!(results[0].2, Some(42i16));
assert!((results[0].3.unwrap() - 2.5f32).abs() < 0.001);
assert_eq!(results[1].1, None);
assert_eq!(results[1].2, None);
assert_eq!(results[1].3, None);
}
#[test]
fn test_typed_inserts_i16_f32() {
let mut conn = LibSqlConnection::establish(":memory:").expect("Failed to connect");
diesel::sql_query(
"CREATE TABLE nullable_data (
id INTEGER PRIMARY KEY AUTOINCREMENT,
text_val TEXT,
small_val INTEGER,
float_val REAL
)",
)
.execute(&mut conn)
.expect("Failed to create table");
diesel::insert_into(nullable_data::table)
.values((
nullable_data::text_val.eq(Some("typed")),
nullable_data::small_val.eq(Some(127i16)),
nullable_data::float_val.eq(Some(1.5f32)),
))
.execute(&mut conn)
.expect("Failed to typed insert");
let results: Vec<(i32, Option<String>, Option<i16>, Option<f32>)> = nullable_data::table
.select((
nullable_data::id,
nullable_data::text_val,
nullable_data::small_val,
nullable_data::float_val,
))
.load(&mut conn)
.expect("Failed to load");
assert_eq!(results.len(), 1);
assert_eq!(results[0].1, Some("typed".to_string()));
assert_eq!(results[0].2, Some(127i16));
assert!((results[0].3.unwrap() - 1.5f32).abs() < 0.001);
}
#[test]
fn test_alter_column() {
let mut conn = LibSqlConnection::establish(":memory:").expect("Failed to connect");
diesel::sql_query("CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT)")
.execute(&mut conn)
.expect("Failed to create table");
conn.alter_column("users", "name", "name TEXT NOT NULL DEFAULT 'unknown'")
.expect("Failed to alter column");
diesel::sql_query("INSERT INTO users DEFAULT VALUES")
.execute(&mut conn)
.expect("Failed to insert with default");
let results: Vec<(i32, String)> = users::table
.select((users::id, users::name))
.load(&mut conn)
.expect("Failed to select");
assert_eq!(results.len(), 1);
assert_eq!(results[0].1, "unknown");
conn.alter_column("users", "name", "name TEXT NOT NULL")
.expect("Failed to alter column again");
diesel::sql_query("INSERT INTO users (name) VALUES ('alice')")
.execute(&mut conn)
.expect("Failed to insert after second alter");
let results: Vec<(i32, String)> = users::table
.select((users::id, users::name))
.order(users::id.asc())
.load(&mut conn)
.expect("Failed to select");
assert_eq!(results.len(), 2);
assert_eq!(results[1].1, "alice");
}
#[test]
fn test_migrations() {
use diesel_migrations::{embed_migrations, HarnessWithOutput, MigrationHarness};
const MIGRATIONS: diesel_migrations::EmbeddedMigrations = embed_migrations!("tests/migrations");
let mut conn = LibSqlConnection::establish(":memory:").expect("Failed to connect");
let mut harness = HarnessWithOutput::write_to_stdout(&mut conn);
let applied = harness
.run_pending_migrations(MIGRATIONS)
.expect("Failed to run migrations");
assert_eq!(applied.len(), 1);
drop(harness);
diesel::sql_query("INSERT INTO items (title) VALUES ('test item')")
.execute(&mut conn)
.expect("Failed to insert into items");
let results: Vec<(i32, String)> = items::table
.select((items::id, items::title))
.load(&mut conn)
.expect("Failed to select items");
assert_eq!(results.len(), 1);
assert_eq!(results[0].1, "test item");
assert!(!conn
.has_pending_migration(MIGRATIONS)
.expect("Failed to check pending"));
let mut harness = HarnessWithOutput::write_to_stdout(&mut conn);
harness
.revert_last_migration(MIGRATIONS)
.expect("Failed to revert migration");
drop(harness);
let result = diesel::sql_query("SELECT * FROM items").execute(&mut conn);
assert!(result.is_err());
}
#[test]
fn test_immediate_transaction() {
let mut conn = LibSqlConnection::establish(":memory:").expect("Failed to connect");
diesel::sql_query(
"CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL)",
)
.execute(&mut conn)
.expect("Failed to create table");
let result: Result<(), Error> = conn.immediate_transaction(|conn| {
diesel::sql_query("INSERT INTO users (name) VALUES ('immediate_alice')")
.execute(conn)
.expect("Failed to insert");
Ok(())
});
assert!(result.is_ok());
let results: Vec<(i32, String)> = users::table
.select((users::id, users::name))
.load(&mut conn)
.expect("Failed to select");
assert_eq!(results.len(), 1);
assert_eq!(results[0].1, "immediate_alice");
}
#[test]
fn test_exclusive_transaction() {
let mut conn = LibSqlConnection::establish(":memory:").expect("Failed to connect");
diesel::sql_query(
"CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL)",
)
.execute(&mut conn)
.expect("Failed to create table");
let result: Result<(), Error> = conn.exclusive_transaction(|conn| {
diesel::sql_query("INSERT INTO users (name) VALUES ('exclusive_bob')")
.execute(conn)
.expect("Failed to insert");
Ok(())
});
assert!(result.is_ok());
let results: Vec<(i32, String)> = users::table
.select((users::id, users::name))
.load(&mut conn)
.expect("Failed to select");
assert_eq!(results.len(), 1);
assert_eq!(results[0].1, "exclusive_bob");
}
#[test]
fn test_last_insert_rowid() {
let mut conn = LibSqlConnection::establish(":memory:").expect("Failed to connect");
diesel::sql_query(
"CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL)",
)
.execute(&mut conn)
.expect("Failed to create table");
diesel::sql_query("INSERT INTO users (name) VALUES ('alice')")
.execute(&mut conn)
.expect("Failed to insert");
let rowid1 = conn.last_insert_rowid();
assert!(rowid1 > 0, "Expected rowid > 0, got {}", rowid1);
diesel::sql_query("INSERT INTO users (name) VALUES ('bob')")
.execute(&mut conn)
.expect("Failed to insert");
let rowid2 = conn.last_insert_rowid();
assert!(
rowid2 > rowid1,
"Expected rowid2 ({}) > rowid1 ({})",
rowid2,
rowid1
);
}
diesel::table! {
all_types (id) {
id -> Integer,
bool_val -> Bool,
small_val -> SmallInt,
int_val -> Integer,
big_val -> BigInt,
float_val -> Float,
double_val -> Double,
text_val -> Text,
blob_val -> Binary,
}
}
#[test]
fn test_typed_inserts_all_types() {
let mut conn = LibSqlConnection::establish(":memory:").expect("Failed to connect");
diesel::sql_query(
"CREATE TABLE all_types (
id INTEGER PRIMARY KEY AUTOINCREMENT,
bool_val INTEGER NOT NULL,
small_val INTEGER NOT NULL,
int_val INTEGER NOT NULL,
big_val INTEGER NOT NULL,
float_val REAL NOT NULL,
double_val REAL NOT NULL,
text_val TEXT NOT NULL,
blob_val BLOB NOT NULL
)",
)
.execute(&mut conn)
.expect("Failed to create table");
diesel::insert_into(all_types::table)
.values((
all_types::bool_val.eq(true),
all_types::small_val.eq(255i16),
all_types::int_val.eq(42i32),
all_types::big_val.eq(9_999_999_999i64),
all_types::float_val.eq(1.5f32),
all_types::double_val.eq(3.14159f64),
all_types::text_val.eq("hello world"),
all_types::blob_val.eq(vec![0xDE, 0xAD, 0xBE, 0xEF]),
))
.execute(&mut conn)
.expect("Failed to typed insert all types");
diesel::insert_into(all_types::table)
.values((
all_types::bool_val.eq(false),
all_types::small_val.eq(-1i16),
all_types::int_val.eq(-100i32),
all_types::big_val.eq(0i64),
all_types::float_val.eq(0.0f32),
all_types::double_val.eq(-1.0f64),
all_types::text_val.eq(""),
all_types::blob_val.eq(vec![]),
))
.execute(&mut conn)
.expect("Failed to typed insert second row");
let results: Vec<(i32, bool, i16, i32, i64, f32, f64, String, Vec<u8>)> = all_types::table
.select((
all_types::id,
all_types::bool_val,
all_types::small_val,
all_types::int_val,
all_types::big_val,
all_types::float_val,
all_types::double_val,
all_types::text_val,
all_types::blob_val,
))
.order(all_types::id.asc())
.load(&mut conn)
.expect("Failed to load all types");
assert_eq!(results.len(), 2);
assert!(results[0].1); assert_eq!(results[0].2, 255i16);
assert_eq!(results[0].3, 42i32);
assert_eq!(results[0].4, 9_999_999_999i64);
assert!((results[0].5 - 1.5f32).abs() < 0.001);
assert!((results[0].6 - 3.14159f64).abs() < 0.00001);
assert_eq!(results[0].7, "hello world");
assert_eq!(results[0].8, vec![0xDE, 0xAD, 0xBE, 0xEF]);
assert!(!results[1].1); assert_eq!(results[1].2, -1i16);
assert_eq!(results[1].3, -100i32);
assert_eq!(results[1].4, 0i64);
assert!((results[1].5 - 0.0f32).abs() < 0.001);
assert!((results[1].6 - (-1.0f64)).abs() < 0.00001);
assert_eq!(results[1].7, "");
assert!(results[1].8.is_empty());
}
diesel::table! {
limit_test (id) {
id -> Integer,
val -> Text,
}
}
#[test]
fn test_query_limit() {
let mut conn = LibSqlConnection::establish(":memory:").expect("Failed to connect");
diesel::sql_query(
"CREATE TABLE limit_test (id INTEGER PRIMARY KEY AUTOINCREMENT, val TEXT NOT NULL)",
)
.execute(&mut conn)
.expect("Failed to create table");
for i in 0..10 {
diesel::insert_into(limit_test::table)
.values(limit_test::val.eq(format!("item_{}", i)))
.execute(&mut conn)
.expect("Failed to insert");
}
let results: Vec<(i32, String)> = limit_test::table
.select((limit_test::id, limit_test::val))
.order(limit_test::id.asc())
.limit(5)
.load(&mut conn)
.expect("Failed to load with limit");
assert_eq!(results.len(), 5);
assert_eq!(results[0].1, "item_0");
assert_eq!(results[4].1, "item_4");
}
#[test]
fn test_query_offset_only() {
let mut conn = LibSqlConnection::establish(":memory:").expect("Failed to connect");
diesel::sql_query(
"CREATE TABLE limit_test (id INTEGER PRIMARY KEY AUTOINCREMENT, val TEXT NOT NULL)",
)
.execute(&mut conn)
.expect("Failed to create table");
for i in 0..10 {
diesel::insert_into(limit_test::table)
.values(limit_test::val.eq(format!("item_{}", i)))
.execute(&mut conn)
.expect("Failed to insert");
}
let results: Vec<(i32, String)> = limit_test::table
.select((limit_test::id, limit_test::val))
.order(limit_test::id.asc())
.offset(7)
.load(&mut conn)
.expect("Failed to load with offset only");
assert_eq!(results.len(), 3);
assert_eq!(results[0].1, "item_7");
assert_eq!(results[2].1, "item_9");
}
#[test]
fn test_query_limit_and_offset() {
let mut conn = LibSqlConnection::establish(":memory:").expect("Failed to connect");
diesel::sql_query(
"CREATE TABLE limit_test (id INTEGER PRIMARY KEY AUTOINCREMENT, val TEXT NOT NULL)",
)
.execute(&mut conn)
.expect("Failed to create table");
for i in 0..10 {
diesel::insert_into(limit_test::table)
.values(limit_test::val.eq(format!("item_{}", i)))
.execute(&mut conn)
.expect("Failed to insert");
}
let results: Vec<(i32, String)> = limit_test::table
.select((limit_test::id, limit_test::val))
.order(limit_test::id.asc())
.limit(3)
.offset(2)
.load(&mut conn)
.expect("Failed to load with limit+offset");
assert_eq!(results.len(), 3);
assert_eq!(results[0].1, "item_2");
assert_eq!(results[2].1, "item_4");
}
#[test]
fn test_insert_returning() {
let mut conn = LibSqlConnection::establish(":memory:").expect("Failed to connect");
diesel::sql_query(
"CREATE TABLE limit_test (id INTEGER PRIMARY KEY AUTOINCREMENT, val TEXT NOT NULL)",
)
.execute(&mut conn)
.expect("Failed to create table");
let result: (i32, String) = diesel::insert_into(limit_test::table)
.values(limit_test::val.eq("returning_test"))
.returning((limit_test::id, limit_test::val))
.get_result(&mut conn)
.expect("Failed to insert returning");
assert_eq!(result.0, 1);
assert_eq!(result.1, "returning_test");
let result2: (i32, String) = diesel::insert_into(limit_test::table)
.values(limit_test::val.eq("second"))
.returning((limit_test::id, limit_test::val))
.get_result(&mut conn)
.expect("Failed to insert returning second");
assert_eq!(result2.0, 2);
assert_eq!(result2.1, "second");
}
#[test]
fn test_boxed_query_limit_offset() {
let mut conn = LibSqlConnection::establish(":memory:").expect("Failed to connect");
diesel::sql_query(
"CREATE TABLE limit_test (id INTEGER PRIMARY KEY AUTOINCREMENT, val TEXT NOT NULL)",
)
.execute(&mut conn)
.expect("Failed to create table");
for i in 0..10 {
diesel::insert_into(limit_test::table)
.values(limit_test::val.eq(format!("item_{}", i)))
.execute(&mut conn)
.expect("Failed to insert");
}
let results: Vec<(i32, String)> = limit_test::table
.select((limit_test::id, limit_test::val))
.order(limit_test::id.asc())
.limit(3)
.offset(2)
.into_boxed()
.load(&mut conn)
.expect("Failed to load boxed limit+offset");
assert_eq!(results.len(), 3);
assert_eq!(results[0].1, "item_2");
let results: Vec<(i32, String)> = limit_test::table
.select((limit_test::id, limit_test::val))
.order(limit_test::id.asc())
.limit(2)
.into_boxed()
.load(&mut conn)
.expect("Failed to load boxed limit only");
assert_eq!(results.len(), 2);
let results: Vec<(i32, String)> = limit_test::table
.select((limit_test::id, limit_test::val))
.order(limit_test::id.asc())
.offset(8)
.into_boxed()
.load(&mut conn)
.expect("Failed to load boxed offset only");
assert_eq!(results.len(), 2);
let results: Vec<(i32, String)> = limit_test::table
.select((limit_test::id, limit_test::val))
.into_boxed()
.load(&mut conn)
.expect("Failed to load boxed no limit");
assert_eq!(results.len(), 10);
}
#[test]
fn test_sql_query_named_columns() {
use diesel::sql_types::{Integer, Nullable, Text};
#[derive(QueryableByName, Debug)]
struct NamedRow {
#[diesel(sql_type = Integer)]
id: i32,
#[diesel(sql_type = Text)]
name: String,
#[diesel(sql_type = Nullable<Text>)]
bio: Option<String>,
}
let mut conn = LibSqlConnection::establish(":memory:").expect("Failed to connect");
diesel::sql_query(
"CREATE TABLE named_test (id INTEGER PRIMARY KEY, name TEXT NOT NULL, bio TEXT)",
)
.execute(&mut conn)
.expect("Failed to create table");
diesel::sql_query("INSERT INTO named_test (id, name, bio) VALUES (1, 'alice', 'dev')")
.execute(&mut conn)
.expect("Failed to insert");
diesel::sql_query("INSERT INTO named_test (id, name, bio) VALUES (2, 'bob', NULL)")
.execute(&mut conn)
.expect("Failed to insert");
let results: Vec<NamedRow> =
diesel::sql_query("SELECT id, name, bio FROM named_test ORDER BY id")
.load(&mut conn)
.expect("Failed to load named");
assert_eq!(results.len(), 2);
assert_eq!(results[0].id, 1);
assert_eq!(results[0].name, "alice");
assert_eq!(results[0].bio, Some("dev".to_string()));
assert_eq!(results[1].id, 2);
assert_eq!(results[1].name, "bob");
assert_eq!(results[1].bio, None);
}
diesel::table! {
int_as_double (id) {
id -> Integer,
int_col -> Integer,
double_col -> Double,
}
}
#[test]
fn test_integer_read_as_double() {
let mut conn = LibSqlConnection::establish(":memory:").expect("Failed to connect");
diesel::sql_query(
"CREATE TABLE int_as_double (
id INTEGER PRIMARY KEY AUTOINCREMENT,
int_col INTEGER NOT NULL,
double_col REAL NOT NULL
)",
)
.execute(&mut conn)
.expect("Failed to create table");
diesel::sql_query("INSERT INTO int_as_double (int_col, double_col) VALUES (42, 42)")
.execute(&mut conn)
.expect("Failed to insert");
let results: Vec<(i32, i32, f64)> = int_as_double::table
.select((
int_as_double::id,
int_as_double::int_col,
int_as_double::double_col,
))
.load(&mut conn)
.expect("Failed to load");
assert_eq!(results.len(), 1);
assert_eq!(results[0].1, 42);
assert!((results[0].2 - 42.0).abs() < 0.001);
}
#[test]
fn test_bad_sql_error() {
use diesel::connection::SimpleConnection;
let mut conn = LibSqlConnection::establish(":memory:").expect("Failed to connect");
let result = diesel::sql_query("THIS IS NOT VALID SQL").execute(&mut conn);
assert!(result.is_err());
let result = diesel::sql_query("SELECT * FROM nonexistent_table").execute(&mut conn);
assert!(result.is_err());
let result = conn.batch_execute("INVALID SQL BATCH");
assert!(result.is_err());
diesel::table! {
ghost_table (id) {
id -> Integer,
val -> Text,
}
}
let result: Result<Vec<(i32, String)>, _> = ghost_table::table
.select((ghost_table::id, ghost_table::val))
.load(&mut conn);
assert!(result.is_err());
}
#[test]
fn test_set_prepared_statement_cache_size() {
use diesel::connection::CacheSize;
let mut conn = LibSqlConnection::establish(":memory:").expect("Failed to connect");
conn.set_prepared_statement_cache_size(CacheSize::Unbounded);
conn.set_prepared_statement_cache_size(CacheSize::Disabled);
use diesel::connection::SimpleConnection;
conn.batch_execute("SELECT 1")
.expect("Connection should still work");
}
#[test]
fn test_moveable_bind_collector_via_cache() {
let mut conn = LibSqlConnection::establish(":memory:").expect("Failed to connect");
diesel::sql_query(
"CREATE TABLE cache_test (id INTEGER PRIMARY KEY AUTOINCREMENT, val TEXT NOT NULL, num INTEGER NOT NULL)",
)
.execute(&mut conn)
.expect("Failed to create table");
diesel::table! {
cache_test (id) {
id -> Integer,
val -> Text,
num -> Integer,
}
}
for i in 0..10 {
diesel::insert_into(cache_test::table)
.values((
cache_test::val.eq(format!("item_{}", i)),
cache_test::num.eq(i),
))
.execute(&mut conn)
.expect("Failed to insert");
}
for i in 0..10 {
let results: Vec<(i32, String, i32)> = cache_test::table
.select((cache_test::id, cache_test::val, cache_test::num))
.filter(cache_test::num.eq(i))
.load(&mut conn)
.expect("Failed to load");
assert_eq!(results.len(), 1);
assert_eq!(results[0].2, i);
}
}
#[test]
fn test_immediate_transaction_rollback() {
let mut conn = LibSqlConnection::establish(":memory:").expect("Failed to connect");
diesel::sql_query(
"CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL)",
)
.execute(&mut conn)
.expect("Failed to create table");
let result: Result<(), Error> = conn.immediate_transaction(|conn| {
diesel::sql_query("INSERT INTO users (name) VALUES ('should_vanish')")
.execute(conn)
.expect("Failed to insert");
Err(Error::RollbackTransaction)
});
assert!(result.is_err());
let results: Vec<(i32, String)> = users::table
.select((users::id, users::name))
.load(&mut conn)
.expect("Failed to select");
assert!(results.is_empty());
}
#[test]
fn test_exclusive_transaction_rollback() {
let mut conn = LibSqlConnection::establish(":memory:").expect("Failed to connect");
diesel::sql_query(
"CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL)",
)
.execute(&mut conn)
.expect("Failed to create table");
let result: Result<(), Error> = conn.exclusive_transaction(|conn| {
diesel::sql_query("INSERT INTO users (name) VALUES ('should_vanish')")
.execute(conn)
.expect("Failed to insert");
Err(Error::RollbackTransaction)
});
assert!(result.is_err());
let results: Vec<(i32, String)> = users::table
.select((users::id, users::name))
.load(&mut conn)
.expect("Failed to select");
assert!(results.is_empty());
}
#[test]
fn test_typed_insert_nulls() {
let mut conn = LibSqlConnection::establish(":memory:").expect("Failed to connect");
diesel::sql_query(
"CREATE TABLE nullable_data (
id INTEGER PRIMARY KEY AUTOINCREMENT,
text_val TEXT,
small_val INTEGER,
float_val REAL
)",
)
.execute(&mut conn)
.expect("Failed to create table");
diesel::insert_into(nullable_data::table)
.values((
nullable_data::text_val.eq(None::<String>),
nullable_data::small_val.eq(None::<i16>),
nullable_data::float_val.eq(None::<f32>),
))
.execute(&mut conn)
.expect("Failed to typed insert nulls");
let results: Vec<(i32, Option<String>, Option<i16>, Option<f32>)> = nullable_data::table
.select((
nullable_data::id,
nullable_data::text_val,
nullable_data::small_val,
nullable_data::float_val,
))
.load(&mut conn)
.expect("Failed to load");
assert_eq!(results.len(), 1);
assert_eq!(results[0].1, None);
assert_eq!(results[0].2, None);
assert_eq!(results[0].3, None);
}
#[test]
fn test_sync_non_replica_noop() {
let mut conn = LibSqlConnection::establish(":memory:").expect("Failed to connect");
conn.sync().expect("sync on non-replica should be no-op");
}
#[test]
fn test_update_returning() {
let mut conn = LibSqlConnection::establish(":memory:").expect("Failed to connect");
diesel::sql_query(
"CREATE TABLE limit_test (id INTEGER PRIMARY KEY AUTOINCREMENT, val TEXT NOT NULL)",
)
.execute(&mut conn)
.expect("Failed to create table");
diesel::insert_into(limit_test::table)
.values(limit_test::val.eq("original"))
.execute(&mut conn)
.expect("Failed to insert");
let result: (i32, String) = diesel::update(limit_test::table.filter(limit_test::id.eq(1)))
.set(limit_test::val.eq("updated"))
.returning((limit_test::id, limit_test::val))
.get_result(&mut conn)
.expect("Failed to update returning");
assert_eq!(result.0, 1);
assert_eq!(result.1, "updated");
}
#[test]
fn test_delete_returning() {
let mut conn = LibSqlConnection::establish(":memory:").expect("Failed to connect");
diesel::sql_query(
"CREATE TABLE limit_test (id INTEGER PRIMARY KEY AUTOINCREMENT, val TEXT NOT NULL)",
)
.execute(&mut conn)
.expect("Failed to create table");
diesel::insert_into(limit_test::table)
.values(limit_test::val.eq("to_delete"))
.execute(&mut conn)
.expect("Failed to insert");
let result: (i32, String) = diesel::delete(limit_test::table.filter(limit_test::id.eq(1)))
.returning((limit_test::id, limit_test::val))
.get_result(&mut conn)
.expect("Failed to delete returning");
assert_eq!(result.0, 1);
assert_eq!(result.1, "to_delete");
let count: Vec<(i32, String)> = limit_test::table
.select((limit_test::id, limit_test::val))
.load(&mut conn)
.expect("Failed to select");
assert!(count.is_empty());
}
#[test]
fn test_remote_url_with_auth_token_query_param() {
let result = LibSqlConnection::establish("libsql://fake-host.example.com?authToken=test-token");
if let Ok(mut conn) = result {
let query_result = diesel::sql_query("SELECT 1").execute(&mut conn);
assert!(query_result.is_err());
}
}
#[test]
fn test_remote_url_with_ampersand_auth_token() {
let result =
LibSqlConnection::establish("libsql://fake-host.example.com?foo=bar&authToken=my-token");
if let Ok(mut conn) = result {
let query_result = diesel::sql_query("SELECT 1").execute(&mut conn);
assert!(query_result.is_err());
}
}
#[test]
fn test_remote_url_with_auth_token_and_trailing_params() {
let result = LibSqlConnection::establish(
"libsql://fake-host.example.com?authToken=my-token&other=value",
);
if let Ok(mut conn) = result {
let query_result = diesel::sql_query("SELECT 1").execute(&mut conn);
assert!(query_result.is_err());
}
}
#[test]
fn test_remote_url_with_empty_auth_token() {
let result = LibSqlConnection::establish("libsql://fake-host.example.com?authToken=");
assert!(result.is_err());
}
#[test]
fn test_remote_url_no_auth_token() {
std::env::remove_var("LIBSQL_AUTH_TOKEN");
let result = LibSqlConnection::establish("libsql://fake-host.example.com");
assert!(result.is_err());
}
#[test]
fn test_remote_url_auth_from_env() {
std::env::set_var("LIBSQL_AUTH_TOKEN", "env-token-value");
let result = LibSqlConnection::establish("libsql://fake-host.example.com");
std::env::remove_var("LIBSQL_AUTH_TOKEN");
if let Ok(mut conn) = result {
let query_result = diesel::sql_query("SELECT 1").execute(&mut conn);
assert!(query_result.is_err());
}
}
#[test]
fn test_remote_url_https() {
let result = LibSqlConnection::establish("https://fake-host.example.com?authToken=test-token");
if let Ok(mut conn) = result {
let query_result = diesel::sql_query("SELECT 1").execute(&mut conn);
assert!(query_result.is_err());
}
}
#[test]
fn test_remote_url_http() {
let result = LibSqlConnection::establish("http://fake-host.example.com?authToken=test-token");
if let Ok(mut conn) = result {
let query_result = diesel::sql_query("SELECT 1").execute(&mut conn);
assert!(query_result.is_err());
}
}
#[test]
fn test_remote_url_ampersand_empty_auth_token() {
let result = LibSqlConnection::establish("libsql://fake-host.example.com?foo=bar&authToken=");
assert!(result.is_err());
}
#[test]
fn test_remote_url_ampersand_auth_token_trailing() {
let result = LibSqlConnection::establish(
"libsql://fake-host.example.com?foo=bar&authToken=my-token&baz=qux",
);
if let Ok(mut conn) = result {
let query_result = diesel::sql_query("SELECT 1").execute(&mut conn);
assert!(query_result.is_err());
}
}
#[test]
fn test_replica_builder_construction() {
let builder = LibSqlConnection::replica_builder(
"/tmp/test_replica.db",
"libsql://fake.example.com",
"fake-token",
)
.sync_interval(std::time::Duration::from_secs(60))
.read_your_writes(false);
let result = builder.establish();
assert!(result.is_err());
}
#[test]
fn test_replica_builder_defaults() {
let builder = LibSqlConnection::replica_builder(
"/tmp/test_replica2.db",
"libsql://fake.example.com",
"fake-token",
);
let result = builder.establish();
assert!(result.is_err());
}
#[test]
fn test_establish_inside_tokio_runtime() {
let rt = tokio::runtime::Runtime::new().expect("Failed to create runtime");
rt.block_on(async {
tokio::task::block_in_place(|| {
let mut conn = LibSqlConnection::establish(":memory:").expect("Failed to connect");
diesel::sql_query("CREATE TABLE rt_test (id INTEGER PRIMARY KEY)")
.execute(&mut conn)
.expect("Failed to create table");
});
});
}
#[test]
#[cfg(feature = "encryption")]
fn test_establish_encrypted() {
let dir = tempfile::tempdir().expect("Failed to create temp dir");
let db_path = dir.path().join("encrypted.db");
let db_url = db_path.to_str().unwrap();
let key: Vec<u8> = (0..32).collect();
{
let mut conn =
LibSqlConnection::establish_encrypted(db_url, key.clone()).expect("Failed to connect");
diesel::sql_query(
"CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL)",
)
.execute(&mut conn)
.expect("Failed to create table");
diesel::sql_query("INSERT INTO users (name) VALUES ('secret_alice')")
.execute(&mut conn)
.expect("Failed to insert");
}
{
let mut conn = LibSqlConnection::establish_encrypted(db_url, key.clone())
.expect("Failed to reconnect");
let results: Vec<(i32, String)> = users::table
.select((users::id, users::name))
.load(&mut conn)
.expect("Failed to select after reconnect");
assert_eq!(results.len(), 1);
assert_eq!(results[0].1, "secret_alice");
}
{
let wrong_key: Vec<u8> = (32..64).collect();
let result = LibSqlConnection::establish_encrypted(db_url, wrong_key);
if let Ok(mut conn) = result {
let query_result: Result<Vec<(i32, String)>, _> = users::table
.select((users::id, users::name))
.load(&mut conn);
assert!(
query_result.is_err(),
"Expected error when reading with wrong key"
);
}
}
}
#[test]
#[cfg(feature = "r2d2")]
fn test_connection_pool() {
use diesel::connection::SimpleConnection;
use diesel_libsql::r2d2::LibSqlConnectionManager;
let manager = LibSqlConnectionManager::new("file::memory:?cache=shared");
let pool = r2d2::Pool::builder()
.max_size(4)
.build(manager)
.expect("Failed to create pool");
{
let mut conn = pool.get().expect("Failed to get setup connection");
conn.batch_execute(
"CREATE TABLE IF NOT EXISTS pool_test (id INTEGER PRIMARY KEY, val TEXT)",
)
.expect("Failed to create table");
}
let handles: Vec<_> = (0..4)
.map(|i| {
let pool = pool.clone();
std::thread::spawn(move || {
let mut conn = pool.get().expect("Failed to get connection in thread");
diesel::sql_query(format!(
"INSERT INTO pool_test (id, val) VALUES ({}, 'thread_{}')",
i, i
))
.execute(&mut *conn)
.expect("Failed to insert in thread");
conn.batch_execute("SELECT 1")
.expect("SELECT 1 failed in thread");
})
})
.collect();
for handle in handles {
handle.join().expect("Thread panicked");
}
let mut conn = pool.get().expect("Failed to get final connection");
conn.batch_execute("SELECT 1")
.expect("Final connection check failed");
}