#![allow(dead_code, private_interfaces)]
use chrono::{DateTime, NaiveDate, NaiveTime, TimeZone, Utc};
use serde_json::json;
use sqlx::SqlitePool;
use tokio::sync::OnceCell;
use uuid::Uuid;
#[derive(
Debug, Clone, PartialEq, sqlx::FromRow, serde::Serialize, serde::Deserialize, umbral::orm::Model,
)]
#[umbral(table = "roundtrip_sqlite_all")]
struct Sweep {
id: i64,
f_small: i16,
f_int: i32,
f_big: i64,
f_real: f32,
f_double: f64,
f_bool: bool,
f_text: String,
f_date: NaiveDate,
f_time: NaiveTime,
f_ts: DateTime<Utc>,
f_uuid: Uuid,
f_json: serde_json::Value,
f_bytes: Vec<u8>,
f_opt_int: Option<i32>,
f_opt_text: Option<String>,
}
static BOOT: OnceCell<SqlitePool> = OnceCell::const_new();
async fn boot() {
BOOT.get_or_init(|| async {
let pool = umbral::db::connect_sqlite("sqlite::memory:")
.await
.expect("in-memory sqlite");
let mut settings = umbral::Settings::from_env().expect("settings");
settings.database_url = "sqlite::memory:".to_string();
umbral::App::builder()
.settings(settings)
.database("default", pool.clone())
.model::<Sweep>()
.build_deferred()
.expect("App::build_deferred");
umbral_core::migrate::create_tables_for_tests()
.await
.expect("create the test schema");
pool
})
.await;
}
fn sample() -> Sweep {
Sweep {
id: 0,
f_small: i16::MIN,
f_int: i32::MAX,
f_big: i64::MIN,
f_real: 1.5, f_double: 2.25, f_bool: true,
f_text: "üñîçødé — with a 'quote' and a \\ backslash".to_string(),
f_date: NaiveDate::from_ymd_opt(2024, 2, 29).unwrap(), f_time: NaiveTime::from_hms_micro_opt(23, 59, 58, 123_456).unwrap(),
f_ts: Utc.with_ymd_and_hms(2026, 8, 16, 12, 34, 56).unwrap(),
f_uuid: Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap(),
f_json: json!({"nested": {"a": [1, 2, 3], "b": null}, "s": "x"}),
f_bytes: vec![0u8, 255, 1, 128, 0, 42],
f_opt_int: Some(-7),
f_opt_text: None,
}
}
#[tokio::test]
async fn every_portable_type_round_trips_through_the_orm() {
boot().await;
let created = Sweep::objects()
.create(sample())
.await
.expect("create a row with every portable field type");
let fetched = Sweep::objects()
.get(sweep::ID.eq(created.id))
.await
.expect("read the row back by primary key");
assert_eq!(
fetched,
Sweep {
id: created.id,
..sample()
},
"a stored row must round-trip unchanged across all SQLite-portable types",
);
}
#[tokio::test]
async fn nullable_columns_round_trip_a_present_value() {
boot().await;
let mut input = sample();
input.f_opt_int = None;
input.f_opt_text = Some("present".to_string());
let created = Sweep::objects().create(input).await.expect("create");
let fetched = Sweep::objects()
.get(sweep::ID.eq(created.id))
.await
.expect("read back");
assert_eq!(fetched.f_opt_int, None);
assert_eq!(fetched.f_opt_text.as_deref(), Some("present"));
}