#![allow(dead_code)]
use umbral::db::{Alias, DatabaseRouter, RouteContext};
use umbral::migrate::ModelMeta;
#[derive(Debug, Clone, sqlx::FromRow, serde::Serialize, serde::Deserialize, umbral::orm::Model)]
#[umbral(table = "rwu_widget")]
pub struct Widget {
pub id: i64,
pub slug: String,
pub label: String,
}
struct SplitRouter;
impl DatabaseRouter for SplitRouter {
fn db_for_read(&self, _m: &ModelMeta, _c: &RouteContext) -> Alias {
Alias::new("replica")
}
fn db_for_write(&self, _m: &ModelMeta, _c: &RouteContext) -> Alias {
Alias::new("default")
}
}
async fn make_pool() -> sqlx::SqlitePool {
let pool = umbral_core::db::connect_sqlite("sqlite::memory:")
.await
.expect("in-memory sqlite");
sqlx::query(
"CREATE TABLE rwu_widget (\
id INTEGER PRIMARY KEY AUTOINCREMENT,\
slug TEXT NOT NULL UNIQUE,\
label TEXT NOT NULL\
)",
)
.execute(&pool)
.await
.expect("create rwu_widget");
pool
}
#[tokio::test(flavor = "multi_thread")]
async fn upsert_existence_probe_uses_write_db_not_replica() {
let default = make_pool().await;
let replica = make_pool().await;
sqlx::query("INSERT INTO rwu_widget (slug, label) VALUES ('alpha', 'Alpha')")
.execute(&default)
.await
.expect("seed write db");
umbral::App::builder()
.settings(umbral::Settings::from_env().expect("settings"))
.database("default", default.clone())
.database("replica", replica)
.router(SplitRouter)
.model::<Widget>()
.build()
.expect("App::build");
let (row, created) = Widget::objects()
.get_or_create(
widget::SLUG.eq("alpha"),
Widget {
id: 0,
slug: "alpha".into(),
label: "Dup".into(),
},
)
.await
.expect("get_or_create");
assert!(
!created,
"must find the existing row on the write DB, not create a duplicate"
);
assert_eq!(row.slug, "alpha");
let count: i64 = sqlx::query_scalar("SELECT COUNT(*) FROM rwu_widget")
.fetch_one(&default)
.await
.expect("count");
assert_eq!(count, 1, "no duplicate inserted on the write DB");
let (updated, created2) = Widget::objects()
.update_or_create(
widget::SLUG.eq("alpha"),
Widget {
id: 0,
slug: "alpha".into(),
label: "Updated".into(),
},
)
.await
.expect("update_or_create");
assert!(
!created2,
"must update the existing row, not insert a new one"
);
assert_eq!(
updated.label, "Updated",
"re-fetch must read the updated row from the write DB"
);
let count2: i64 = sqlx::query_scalar("SELECT COUNT(*) FROM rwu_widget")
.fetch_one(&default)
.await
.expect("count");
assert_eq!(count2, 1, "still exactly one row after update_or_create");
}