#![cfg(all(feature = "sqlite", feature = "test-support"))]
use autumn_web::config::DatabaseConfig;
use autumn_web::db::{Db, RuntimeConnection, TxOptions, create_pool};
use autumn_web::reexports::{diesel, diesel_async};
use diesel::sql_types::BigInt;
use diesel_async::RunQueryDsl as _;
use diesel_async::pooled_connection::deadpool::Pool;
use scoped_futures::ScopedFutureExt as _;
type SqlitePool = Pool<RuntimeConnection>;
#[derive(diesel::QueryableByName)]
struct RowCount {
#[diesel(sql_type = BigInt)]
n: i64,
}
async fn row_count(pool: &SqlitePool) -> i64 {
let mut conn = pool.get().await.expect("checkout a sqlite connection");
diesel::sql_query("SELECT COUNT(*) AS n FROM t")
.get_result::<RowCount>(&mut *conn)
.await
.expect("count rows")
.n
}
#[tokio::test]
async fn sqlite_read_only_tx_is_rejected_and_read_write_still_commits() {
let tmp = tempfile::TempDir::new().expect("temp dir");
let db_path = tmp.path().join("rotx.db");
let url = format!("sqlite://{}", db_path.display());
let config = DatabaseConfig {
url: Some(url),
primary_pool_size: Some(4),
..Default::default()
};
let pool: SqlitePool = create_pool(&config)
.expect("sqlite pool builds via build_sqlite_pool")
.expect("a url is configured");
{
let mut conn = pool.get().await.expect("checkout a sqlite connection");
diesel::sql_query("CREATE TABLE t (id INTEGER PRIMARY KEY)")
.execute(&mut *conn)
.await
.expect("create table t");
}
{
let mut db = Db::connect_for_test(&pool).await.expect("db checkout");
let result: Result<(), autumn_web::AutumnError> = db
.tx_with(TxOptions::default().read_only(), move |conn| {
async move {
diesel::sql_query("INSERT INTO t (id) VALUES (1)")
.execute(conn)
.await?;
Ok::<_, autumn_web::AutumnError>(())
}
.scope_boxed()
})
.await;
let err = result.expect_err("read-only tx under sqlite must be rejected");
let message = err.to_string();
assert!(
message.contains("read-only") && message.contains("SQLite"),
"expected an unsupported read-only-options error naming SQLite, got: {message}"
);
}
assert_eq!(
row_count(&pool).await,
0,
"a rejected read-only transaction must not commit a write"
);
{
let mut db = Db::connect_for_test(&pool).await.expect("db checkout");
db.tx_with(TxOptions::default(), move |conn| {
async move {
diesel::sql_query("INSERT INTO t (id) VALUES (1)")
.execute(conn)
.await?;
Ok::<_, autumn_web::AutumnError>(())
}
.scope_boxed()
})
.await
.expect("a read-write sqlite transaction commits its write");
}
assert_eq!(
row_count(&pool).await,
1,
"a read-write transaction must commit its insert"
);
}