use crate::prelude::*;
#[driver_test]
pub async fn data_carrying_enum_schema(test: &mut Test) {
#[allow(dead_code)]
#[derive(toasty::Embed)]
enum ContactInfo {
#[column(variant = 1)]
Email { address: String },
#[column(variant = 2)]
Phone { number: String },
}
let db = test.setup_db(models!(ContactInfo)).await;
let schema = db.schema();
assert_struct!(schema.app.models, #{
ContactInfo::id(): toasty::schema::app::Model::EmbeddedEnum({
name.upper_camel_case(): "ContactInfo",
variants: [
{
name.upper_camel_case(): "Email",
discriminant: toasty_core::stmt::Value::I64(1),
..
},
{
name.upper_camel_case(): "Phone",
discriminant: toasty_core::stmt::Value::I64(2),
..
},
],
fields: [
{ id.index: 0, name.app: Some("address") },
{ id.index: 1, name.app: Some("number") },
],
}),
});
}
#[driver_test]
pub async fn mixed_enum_schema(test: &mut Test) {
#[allow(dead_code)]
#[derive(toasty::Embed)]
enum Status {
#[column(variant = 1)]
Pending,
#[column(variant = 2)]
Failed { reason: String },
#[column(variant = 3)]
Done,
}
let db = test.setup_db(models!(Status)).await;
let schema = db.schema();
assert_struct!(schema.app.models, #{
Status::id(): toasty::schema::app::Model::EmbeddedEnum({
variants: [
{
name.upper_camel_case(): "Pending",
discriminant: toasty_core::stmt::Value::I64(1),
..
},
{
name.upper_camel_case(): "Failed",
discriminant: toasty_core::stmt::Value::I64(2),
..
},
{
name.upper_camel_case(): "Done",
discriminant: toasty_core::stmt::Value::I64(3),
..
},
],
fields: [
{ id.index: 0, name.app: Some("reason") },
],
}),
});
}
#[driver_test]
pub async fn data_carrying_enum_db_schema(test: &mut Test) {
#[allow(dead_code)]
#[derive(toasty::Embed)]
enum ContactInfo {
#[column(variant = 1)]
Email { address: String },
#[column(variant = 2)]
Phone { number: String },
}
#[derive(toasty::Model)]
struct User {
#[key]
id: String,
#[allow(dead_code)]
contact: ContactInfo,
}
let db = test.setup_db(models!(User, ContactInfo)).await;
let schema = db.schema();
assert_struct!(schema.db.tables, [
{
name: =~ r"users$",
columns: [
{ name: "id" },
{ name: "contact", nullable: false },
{ name: "contact_address", nullable: true },
{ name: "contact_number", nullable: true },
],
},
]);
}
#[driver_test]
pub async fn data_variant_roundtrip(test: &mut Test) -> Result<()> {
#[derive(Debug, PartialEq, toasty::Embed)]
enum ContactInfo {
#[column(variant = 1)]
Email { address: String },
#[column(variant = 2)]
Phone { number: String },
}
#[derive(Debug, toasty::Model)]
struct User {
#[key]
#[auto]
id: uuid::Uuid,
name: String,
contact: ContactInfo,
}
let mut db = test.setup_db(models!(User, ContactInfo)).await;
let alice = User::create()
.name("Alice")
.contact(ContactInfo::Email {
address: "alice@example.com".to_string(),
})
.exec(&mut db)
.await?;
let bob = User::create()
.name("Bob")
.contact(ContactInfo::Phone {
number: "555-1234".to_string(),
})
.exec(&mut db)
.await?;
let found_alice = User::get_by_id(&mut db, &alice.id).await?;
assert_eq!(
found_alice.contact,
ContactInfo::Email {
address: "alice@example.com".to_string()
}
);
let found_bob = User::get_by_id(&mut db, &bob.id).await?;
assert_eq!(
found_bob.contact,
ContactInfo::Phone {
number: "555-1234".to_string()
}
);
alice.delete().exec(&mut db).await?;
bob.delete().exec(&mut db).await?;
Ok(())
}
#[driver_test]
pub async fn mixed_enum_roundtrip(test: &mut Test) -> Result<()> {
#[derive(Debug, PartialEq, toasty::Embed)]
enum Status {
#[column(variant = 1)]
Pending,
#[column(variant = 2)]
Failed { reason: String },
#[column(variant = 3)]
Done,
}
#[derive(Debug, toasty::Model)]
struct Task {
#[key]
#[auto]
id: uuid::Uuid,
title: String,
status: Status,
}
let mut db = test.setup_db(models!(Task, Status)).await;
let pending = Task::create()
.title("Pending task")
.status(Status::Pending)
.exec(&mut db)
.await?;
let failed = Task::create()
.title("Failed task")
.status(Status::Failed {
reason: "out of memory".to_string(),
})
.exec(&mut db)
.await?;
let done = Task::create()
.title("Done task")
.status(Status::Done)
.exec(&mut db)
.await?;
let found_pending = Task::get_by_id(&mut db, &pending.id).await?;
assert_eq!(found_pending.status, Status::Pending);
let found_failed = Task::get_by_id(&mut db, &failed.id).await?;
assert_eq!(
found_failed.status,
Status::Failed {
reason: "out of memory".to_string()
}
);
let found_done = Task::get_by_id(&mut db, &done.id).await?;
assert_eq!(found_done.status, Status::Done);
Ok(())
}
#[driver_test]
pub async fn data_variant_with_uuid_field(test: &mut Test) -> Result<()> {
#[derive(Debug, PartialEq, toasty::Embed)]
enum OrderRef {
#[column(variant = 1)]
Internal { id: uuid::Uuid },
#[column(variant = 2)]
External { code: String },
}
#[derive(Debug, toasty::Model)]
struct Order {
#[key]
#[auto]
id: uuid::Uuid,
order_ref: OrderRef,
}
let mut db = test.setup_db(models!(Order, OrderRef)).await;
let internal_id = uuid::Uuid::new_v4();
let o1 = Order::create()
.order_ref(OrderRef::Internal { id: internal_id })
.exec(&mut db)
.await?;
let o2 = Order::create()
.order_ref(OrderRef::External {
code: "EXT-001".to_string(),
})
.exec(&mut db)
.await?;
let found_o1 = Order::get_by_id(&mut db, &o1.id).await?;
assert_eq!(found_o1.order_ref, OrderRef::Internal { id: internal_id });
let found_o2 = Order::get_by_id(&mut db, &o2.id).await?;
assert_eq!(
found_o2.order_ref,
OrderRef::External {
code: "EXT-001".to_string()
}
);
Ok(())
}
#[driver_test]
pub async fn data_variant_with_jiff_timestamp(test: &mut Test) -> Result<()> {
#[derive(Debug, PartialEq, toasty::Embed)]
enum EventTime {
#[column(variant = 1)]
Scheduled { at: jiff::Timestamp },
#[column(variant = 2)]
Unscheduled,
}
#[derive(Debug, toasty::Model)]
struct Event {
#[key]
#[auto]
id: uuid::Uuid,
name: String,
time: EventTime,
}
let mut db = test.setup_db(models!(Event, EventTime)).await;
let ts = jiff::Timestamp::from_second(1_700_000_000).unwrap();
let scheduled = Event::create()
.name("launch")
.time(EventTime::Scheduled { at: ts })
.exec(&mut db)
.await?;
let unscheduled = Event::create()
.name("tbd")
.time(EventTime::Unscheduled)
.exec(&mut db)
.await?;
let found_scheduled = Event::get_by_id(&mut db, &scheduled.id).await?;
assert_eq!(found_scheduled.time, EventTime::Scheduled { at: ts });
let found_unscheduled = Event::get_by_id(&mut db, &unscheduled.id).await?;
assert_eq!(found_unscheduled.time, EventTime::Unscheduled);
Ok(())
}
#[driver_test]
pub async fn struct_in_data_variant(test: &mut Test) -> Result<()> {
#[derive(Debug, PartialEq, toasty::Embed)]
struct Address {
street: String,
city: String,
}
#[derive(Debug, PartialEq, toasty::Embed)]
enum Destination {
#[column(variant = 1)]
Digital { email: String },
#[column(variant = 2)]
Physical { address: Address },
}
#[derive(Debug, toasty::Model)]
struct Shipment {
#[key]
#[auto]
id: uuid::Uuid,
destination: Destination,
}
let mut db = test.setup_db(models!(Shipment, Destination, Address)).await;
let digital = Shipment::create()
.destination(Destination::Digital {
email: "user@example.com".to_string(),
})
.exec(&mut db)
.await?;
let physical = Shipment::create()
.destination(Destination::Physical {
address: Address {
street: "123 Main St".to_string(),
city: "Seattle".to_string(),
},
})
.exec(&mut db)
.await?;
let found_digital = Shipment::get_by_id(&mut db, &digital.id).await?;
assert_eq!(
found_digital.destination,
Destination::Digital {
email: "user@example.com".to_string()
}
);
let found_physical = Shipment::get_by_id(&mut db, &physical.id).await?;
assert_eq!(
found_physical.destination,
Destination::Physical {
address: Address {
street: "123 Main St".to_string(),
city: "Seattle".to_string(),
},
}
);
Ok(())
}
#[driver_test]
pub async fn enum_in_enum_roundtrip(test: &mut Test) -> Result<()> {
#[derive(Debug, PartialEq, toasty::Embed)]
enum Channel {
#[column(variant = 1)]
Email,
#[column(variant = 2)]
Sms,
}
#[derive(Debug, PartialEq, toasty::Embed)]
enum Notification {
#[column(variant = 1)]
Send { channel: Channel, message: String },
#[column(variant = 2)]
Suppress,
}
#[derive(Debug, toasty::Model)]
struct Alert {
#[key]
#[auto]
id: uuid::Uuid,
notification: Notification,
}
let mut db = test.setup_db(models!(Alert, Notification, Channel)).await;
let a1 = Alert::create()
.notification(Notification::Send {
channel: Channel::Email,
message: "hello".to_string(),
})
.exec(&mut db)
.await?;
let a2 = Alert::create()
.notification(Notification::Send {
channel: Channel::Sms,
message: "world".to_string(),
})
.exec(&mut db)
.await?;
let a3 = Alert::create()
.notification(Notification::Suppress)
.exec(&mut db)
.await?;
let found_a1 = Alert::get_by_id(&mut db, &a1.id).await?;
assert_eq!(
found_a1.notification,
Notification::Send {
channel: Channel::Email,
message: "hello".to_string(),
}
);
let found_a2 = Alert::get_by_id(&mut db, &a2.id).await?;
assert_eq!(
found_a2.notification,
Notification::Send {
channel: Channel::Sms,
message: "world".to_string(),
}
);
let found_a3 = Alert::get_by_id(&mut db, &a3.id).await?;
assert_eq!(found_a3.notification, Notification::Suppress);
Ok(())
}
#[driver_test]
pub async fn global_field_indices(test: &mut Test) {
#[allow(dead_code)]
#[derive(toasty::Embed)]
enum Event {
#[column(variant = 1)]
Login { user_id: String, ip: String },
#[column(variant = 2)]
Purchase { item_id: String, amount: i64 },
}
let db = test.setup_db(models!(Event)).await;
let schema = db.schema();
assert_struct!(schema.app.models, #{
Event::id(): toasty::schema::app::Model::EmbeddedEnum({
fields: [
{ id.index: 0, name.app: Some("user_id") },
{ id.index: 1, name.app: Some("ip") },
{ id.index: 2, name.app: Some("item_id") },
{ id.index: 3, name.app: Some("amount") },
],
}),
});
}