use crate::prelude::*;
use std::{rc::Rc, sync::Arc};
use toasty::schema::db;
use toasty_core::{
driver::Operation,
stmt::{Assignment, ExprSet, InsertTarget, Statement, Value},
};
macro_rules! num_ty_test_body {
($test:expr, $ty:ty, $test_values:expr) => {{
#[derive(Debug, toasty::Model)]
#[allow(dead_code)]
struct Item {
#[key]
#[auto]
id: uuid::Uuid,
val: $ty,
}
let test = $test;
let mut db = test.setup_db(models!(Item)).await;
let mut test_values: Vec<$ty> = (*$test_values).to_vec();
if <$ty>::MIN == 0 {
if let Some(max_unsigned) = test.capability().storage_types.max_unsigned_integer {
test_values.retain(|&val| {
let val_as_u64 = val as u64;
val_as_u64 <= max_unsigned
});
}
}
test.log().clear();
for &val in &test_values {
let created = Item::create().val(val).exec(&mut db).await?;
let (op, _resp) = test.log().pop();
let sql = test.capability().sql;
let val_pat = if sql { ArgOr::Arg(1) } else { ArgOr::Value(val) };
assert_struct!(op, Operation::QuerySql({
stmt: Statement::Insert({
target: InsertTarget::Table({
table: == table_id(&mut db, "items"),
columns: == columns(&mut db, "items", &["id", "val"]),
}),
source.body: ExprSet::Values({
rows: [=~ (Any, val_pat)],
}),
}),
}));
if sql {
assert_struct!(op, Operation::QuerySql({
params[1].value: =~ val,
}));
}
let read = Item::get_by_id(&mut db, &created.id).await?;
assert_eq!(read.val, val, "Round-trip failed for: {}", val);
test.log().clear();
}
let mut created_records = Vec::new();
for &val in &test_values {
let created = Item::create().val(val).exec(&mut db).await?;
created_records.push((created.id, val));
test.log().clear();
}
for (id, expected_val) in created_records {
let read = Item::get_by_id(&mut db, &id).await?;
assert_eq!(
read.val, expected_val,
"Multiple records test failed for: {}",
expected_val
);
test.log().clear();
}
if !test_values.is_empty() {
let mut record = Item::create().val(test_values[0]).exec(&mut db).await?;
test.log().clear();
for &val in &test_values {
record.update().val(val).exec(&mut db).await?;
let (op, _resp) = test.log().pop();
if test.capability().sql {
assert_struct!(op, Operation::QuerySql({
stmt: Statement::Update({
assignments: #{ [1]: Assignment::Set(_)},
}),
}));
} else {
assert_struct!(op, Operation::UpdateByKey({
assignments: #{ [1]: Assignment::Set(_)},
}));
}
let read = Item::get_by_id(&mut db, &record.id).await?;
assert_eq!(read.val, val, "Update chain failed for: {}", val);
record.val = val;
test.log().clear();
}
}
Ok(())
}};
}
#[driver_test]
pub async fn ty_i8(test: &mut Test) -> Result<()> {
num_ty_test_body!(test, i8, &[i8::MIN, -100, -1, 0, 1, 63, 100, i8::MAX])
}
#[driver_test]
pub async fn ty_i16(test: &mut Test) -> Result<()> {
num_ty_test_body!(
test,
i16,
&[i16::MIN, -10000, -1, 0, 1, 10000, 16383, i16::MAX]
)
}
#[driver_test]
pub async fn ty_i32(test: &mut Test) -> Result<()> {
num_ty_test_body!(
test,
i32,
&[i32::MIN, -1000000, -1, 0, 1, 1000000, 1073741823, i32::MAX]
)
}
#[driver_test]
pub async fn ty_i64(test: &mut Test) -> Result<()> {
num_ty_test_body!(
test,
i64,
&[
i64::MIN,
-1000000000000,
-1,
0,
1,
1000000000000,
4611686018427387903,
i64::MAX
]
)
}
#[driver_test]
pub async fn ty_isize(test: &mut Test) -> Result<()> {
num_ty_test_body!(
test,
isize,
&[
isize::MIN,
-1000000000000,
-1,
0,
1,
1000000000000,
4611686018427387903,
isize::MAX
]
)
}
#[driver_test]
pub async fn ty_u8(test: &mut Test) -> Result<()> {
num_ty_test_body!(test, u8, &[u8::MIN, 0, 1, 100, 127, 200, u8::MAX])
}
#[driver_test]
pub async fn ty_u16(test: &mut Test) -> Result<()> {
num_ty_test_body!(test, u16, &[u16::MIN, 0, 1, 10000, 32767, 50000, u16::MAX])
}
#[driver_test]
pub async fn ty_u32(test: &mut Test) -> Result<()> {
num_ty_test_body!(
test,
u32,
&[u32::MIN, 0, 1, 1000000, 2147483647, 2000000000, u32::MAX]
)
}
#[driver_test]
pub async fn ty_u64(test: &mut Test) -> Result<()> {
num_ty_test_body!(
test,
u64,
&[
u64::MIN,
0,
1,
1000000000000,
9223372036854775807,
10000000000000000000,
u64::MAX
]
)
}
#[driver_test]
pub async fn ty_usize(test: &mut Test) -> Result<()> {
num_ty_test_body!(
test,
usize,
&[
usize::MIN,
0,
1,
1000000000000,
9223372036854775807,
10000000000000000000,
usize::MAX
]
)
}
#[driver_test]
pub async fn ty_str(test: &mut Test) -> Result<()> {
#[derive(Debug, toasty::Model)]
struct Item {
#[key]
#[auto]
id: uuid::Uuid,
val: String,
}
let mut db = test.setup_db(models!(Item)).await;
let test_values: Vec<_> = [
gen_string(0, "empty"),
gen_string(10, "ascii"),
gen_string(20, "unicode"),
gen_string(100, "mixed"),
gen_string(1_000, "ascii"),
gen_string(10_000, "mixed"),
gen_string(100_000, "ascii"),
gen_string(20, "newlines"),
gen_string(100, "spaces"),
]
.into_iter()
.filter(
|value| match test.capability().default_string_max_length() {
Some(max_len) => max_len >= value.len() as _,
None => true,
},
)
.collect();
test.log().clear();
for val in &test_values {
let created = Item::create().val((*val).clone()).exec(&mut db).await?;
let (op, _resp) = test.log().pop();
let sql = test.capability().sql;
let val_pat = if sql {
ArgOr::Arg(1)
} else {
ArgOr::Value(val.as_str())
};
assert_struct!(op, Operation::QuerySql({
stmt: Statement::Insert({
target: InsertTarget::Table({
table: == table_id(&db, "items"),
columns: == columns(&db, "items", &["id", "val"]),
}),
source.body: ExprSet::Values({
rows: [=~ (Any, val_pat)],
}),
}),
}));
if sql {
assert_struct!(op, Operation::QuerySql({
params[1].value: == val.as_str(),
}));
}
let read = Item::get_by_id(&mut db, &created.id).await?;
assert_eq!(read.val, *val);
test.log().clear();
}
let mut record = Item::create().val(&test_values[0]).exec(&mut db).await?;
test.log().clear();
for val in &test_values {
record.update().val(val).exec(&mut db).await?;
let (op, _resp) = test.log().pop();
if test.capability().sql {
assert_struct!(op, Operation::QuerySql({
stmt: Statement::Update({
assignments: #{ [1]: Assignment::Set(_)},
}),
}));
} else {
assert_struct!(op, Operation::UpdateByKey({
assignments: #{ [1]: Assignment::Set(_)},
}));
}
let read = Item::get_by_id(&mut db, &record.id).await?;
assert_eq!(read.val, *val);
test.log().clear();
}
Ok(())
}
fn gen_string(length: usize, pattern: &str) -> String {
match pattern {
"empty" => String::new(),
"ascii" => "a".repeat(length),
"unicode" => "🦀".repeat(length),
"mixed" => "test ".repeat(length / 5), "newlines" => "line\n".repeat(length / 5),
"spaces" => " ".repeat(length),
_ => pattern.repeat(length / pattern.len().max(1)),
}
}
#[driver_test]
pub async fn ty_bytes(test: &mut Test) -> Result<()> {
#[derive(Debug, toasty::Model)]
struct Item {
#[key]
#[auto]
id: uuid::Uuid,
val: Vec<u8>,
}
let mut db = test.setup_db(models!(Item)).await;
let test_values: Vec<Vec<u8>> = vec![
vec![],
vec![0],
vec![1, 2, 3],
vec![0xFF; 100],
(0..=255).collect(),
vec![0; 1000],
];
test.log().clear();
for val in &test_values {
let expected = Value::Bytes(val.clone());
let created = Item::create().val(val.clone()).exec(&mut db).await?;
let (op, _resp) = test.log().pop();
let sql = test.capability().sql;
let val_pat = if sql {
ArgOr::Arg(1)
} else {
ArgOr::Value(expected.clone())
};
assert_struct!(op, Operation::QuerySql({
stmt: Statement::Insert({
target: InsertTarget::Table({
table: == table_id(&db, "items"),
columns: == columns(&db, "items", &["id", "val"]),
}),
source.body: ExprSet::Values({
rows: [=~ (Any, val_pat)],
}),
}),
}));
if sql {
assert_struct!(op, Operation::QuerySql({
params[1].value: == expected,
}));
}
let read = Item::get_by_id(&mut db, &created.id).await?;
assert_eq!(read.val, *val);
test.log().clear();
}
let mut record = Item::create()
.val(test_values[0].clone())
.exec(&mut db)
.await?;
test.log().clear();
for val in &test_values {
record.update().val(val.clone()).exec(&mut db).await?;
let (op, _resp) = test.log().pop();
if test.capability().sql {
assert_struct!(op, Operation::QuerySql({
stmt: Statement::Update({
assignments: #{ [1]: Assignment::Set(_)},
}),
}));
} else {
assert_struct!(op, Operation::UpdateByKey({
assignments: #{ [1]: Assignment::Set(_)},
}));
}
let read = Item::get_by_id(&mut db, &record.id).await?;
assert_eq!(read.val, *val);
test.log().clear();
}
Ok(())
}
#[driver_test]
pub async fn ty_uuid(test: &mut Test) -> Result<()> {
#[derive(Debug, toasty::Model)]
struct Item {
#[key]
#[auto]
id: uuid::Uuid,
val: uuid::Uuid,
}
let mut db = test.setup_db(models!(Item)).await;
test.log().clear();
for _ in 0..16 {
let val = uuid::Uuid::new_v4();
let created = Item::create().val(val).exec(&mut db).await?;
let (op, _resp) = test.log().pop();
let expected = match &test.capability().storage_types.default_uuid_type {
db::Type::Uuid => Value::Uuid(val),
db::Type::Blob => Value::Bytes(val.as_bytes().to_vec()),
db::Type::Text | db::Type::VarChar(..) => Value::String(val.to_string()),
ty => todo!("ty={ty:#?}"),
};
let sql = test.capability().sql;
let val_pat = if sql {
ArgOr::Arg(1)
} else {
ArgOr::Value(expected.clone())
};
assert_struct!(op, Operation::QuerySql({
stmt: Statement::Insert({
target: InsertTarget::Table({
table: == table_id(&db, "items"),
columns: == columns(&db, "items", &["id", "val"]),
}),
source.body: ExprSet::Values({
rows: [=~ (Any, val_pat)],
}),
}),
}));
if sql {
assert_struct!(op, Operation::QuerySql({
params[1].value: == expected,
}));
}
let read = Item::get_by_id(&mut db, &created.id).await?;
assert_eq!(read.val, val);
test.log().clear();
}
Ok(())
}
#[driver_test]
pub async fn ty_smart_ptrs(test: &mut Test) -> Result<()> {
#[derive(Debug, toasty::Model)]
struct Item {
#[key]
#[auto]
id: uuid::Uuid,
arced: Arc<i32>,
rced: Rc<i32>,
boxed: Box<i32>,
}
let mut db = test.setup_db(models!(Item)).await;
test.log().clear();
let created = Item::create()
.arced(1i32)
.rced(2i32)
.boxed(3i32)
.exec(&mut db)
.await?;
let (op, _resp) = test.log().pop();
assert_struct!(op, Operation::QuerySql({
stmt: Statement::Insert({
target: InsertTarget::Table({
table: == table_id(&db, "items"),
columns: == columns(&db, "items", &["id", "arced", "rced", "boxed"]),
}),
source.body: ExprSet::Values({
rows: [=~ (Any, Any, Any, Any)],
}),
}),
}));
let read = Item::get_by_id(&mut db, &created.id).await?;
assert_eq!(created.id, read.id);
assert_eq!(created.arced, read.arced);
assert_eq!(created.rced, read.rced);
assert_eq!(created.boxed, read.boxed);
test.log().clear();
Ok(())
}