use super::*;
#[derive(Debug, Clone, toasty::Embed)]
struct Seo {
title: String,
description: String,
}
#[derive(Debug, Clone, toasty::Embed)]
struct Credit {
author: String,
}
#[derive(Debug, Clone, toasty::Embed)]
struct Poster {
url: String,
credit: Credit,
}
#[derive(Debug, Clone, toasty::Embed)]
enum Media {
#[column(variant = 1)]
Image { url: String, alt: String },
#[column(variant = 2)]
Video { video_url: String, poster: Poster },
}
#[derive(Debug, Clone, toasty::Embed)]
enum Publication {
#[column(variant = 1)]
Scheduled {
#[shared(timestamp)]
scheduled_at: String,
scheduled_for: String,
},
#[column(variant = 2)]
Published {
#[shared(timestamp)]
published_at: String,
canonical_url: String,
},
}
#[derive(Debug, Clone, toasty::Embed)]
struct Wrapper {
label: String,
inner: Media,
}
#[derive(Debug, Clone, toasty::Embed)]
struct Stats {
word_count: i64,
read_minutes: i64,
}
#[derive(Debug, Clone, toasty::Model)]
struct LensPost {
#[key]
#[auto]
id: uuid::Uuid,
title: String,
seo: Seo,
media: Media,
publication: Publication,
wrapper: Wrapper,
#[document]
stats: Stats,
}
#[derive(Debug, Clone, toasty::Model)]
struct Impostor {
#[key]
#[auto]
id: uuid::Uuid,
seo: Seo,
wrapper: Wrapper,
}
fn enum_at<M: toasty::schema::Model, T>(path: impl Into<Path<M, T>>) -> Option<EnumShape> {
resolve_enum(&path.into()).expect("the model compiles")
}
fn leaf<M: toasty::schema::Model, T>(path: Path<M, T>) -> Result<LeafField, DeclarationErrorKind> {
resolve::<M>(&ModelPath::of(&path))
}
#[test]
fn a_plain_leaf_resolves_through_the_app_field() {
let leaf = leaf(LensPost::fields().title()).unwrap();
assert_eq!(leaf.name, "title");
assert_eq!(leaf.label, "Title");
assert!(
!leaf.nullable,
"a required top-level column is not storage-nullable"
);
}
#[test]
fn a_leaf_in_an_embedded_struct_resolves_to_its_flattened_column() {
let leaf = leaf(LensPost::fields().seo().title()).unwrap();
assert_eq!(leaf.name, "seo_title");
assert_eq!(
leaf.label, "Seo title",
"a label reads the storage name as prose"
);
assert!(
leaf.nullable,
"an embedded leaf is never required by default"
);
}
#[test]
fn a_leaf_in_a_struct_nested_in_an_enum_variant_resolves_to_one_column() {
assert_eq!(
leaf(LensPost::fields().media().image().url()).unwrap().name,
"media_url"
);
assert_eq!(
leaf(LensPost::fields().media().video().poster().url())
.unwrap()
.name,
"media_poster_url"
);
let leaf = leaf(
LensPost::fields()
.media()
.video()
.poster()
.credit()
.author(),
)
.unwrap();
assert_eq!(leaf.name, "media_poster_credit_author");
assert_eq!(leaf.label, "Media poster credit author");
assert!(leaf.nullable);
}
#[test]
fn a_variant_rooted_path_through_an_embedded_struct_resolves() {
assert_eq!(
leaf(LensPost::fields().wrapper().inner().image().url())
.unwrap()
.name,
"wrapper_inner_url"
);
assert_eq!(
leaf(LensPost::fields().wrapper().inner().video().video_url())
.unwrap()
.name,
"wrapper_inner_video_url"
);
}
#[test]
fn a_shared_column_resolves_for_every_variant_that_declares_it() {
assert_eq!(
leaf(LensPost::fields().publication().scheduled().scheduled_at())
.unwrap()
.name,
"publication_timestamp"
);
assert_eq!(
leaf(LensPost::fields().publication().published().published_at())
.unwrap()
.name,
"publication_timestamp"
);
assert_eq!(
leaf(LensPost::fields().publication().published().canonical_url())
.unwrap()
.name,
"publication_canonical_url"
);
}
#[test]
fn a_document_leaf_resolves_to_the_document_column() {
let leaf = leaf(LensPost::fields().stats().word_count()).unwrap();
assert_eq!(leaf.name, "stats");
assert_eq!(leaf.label, "Stats");
assert!(
leaf.nullable,
"a document leaf is never required by default either"
);
}
#[test]
fn an_embedded_enum_resolves_its_discriminant_and_variants() {
let shape = enum_at(LensPost::fields().publication()).expect("an embedded enum resolves");
assert_eq!(shape.discriminant, "publication");
let variants: Vec<_> = shape
.variants
.iter()
.map(|(value, label)| (value.as_str(), label.english()))
.collect();
assert_eq!(
variants,
[
("1", "Scheduled".to_string()),
("2", "Published".to_string()),
]
);
assert_eq!(
shape.variants[0].1,
crate::Text::option("publication", "scheduled"),
"a variant reads the message `option-{{enum}}.{{variant}}`"
);
}
#[test]
fn an_enum_inside_a_struct_resolves_through_its_path() {
let shape = enum_at(LensPost::fields().wrapper().inner()).expect("the nested enum resolves");
assert_eq!(shape.discriminant, "wrapper_inner");
}
#[test]
fn anything_but_an_enum_resolves_to_nothing() {
assert!(
enum_at(LensPost::fields().seo()).is_none(),
"a struct has no variant"
);
assert!(
enum_at(LensPost::fields().title()).is_none(),
"a primitive field is a leaf"
);
assert!(
enum_at(LensPost::fields().stats()).is_none(),
"a #[document] stores as one column"
);
assert!(
enum_at(LensPost::fields().media().video().video_url()).is_none(),
"a variant-rooted path names one variant, not the value"
);
}
#[test]
fn an_embedded_value_under_another_model_resolves_against_that_model() {
assert_eq!(
leaf(Impostor::fields().seo().title()).unwrap().name,
"seo_title"
);
assert_eq!(
leaf(Impostor::fields().wrapper().inner().image().url())
.unwrap()
.name,
"wrapper_inner_url"
);
assert_eq!(
enum_at(Impostor::fields().wrapper().inner())
.expect("the nested enum resolves")
.discriminant,
"wrapper_inner"
);
}
#[derive(Debug, Clone, toasty::Model)]
struct Oversized {
#[key]
#[auto]
id: uuid::Uuid,
#[column(type = varchar(2000000000))]
body: String,
seo: Seo,
media: Media,
}
#[test]
fn an_uncompiled_model_refuses_its_embedded_paths() {
let Err(DeclarationErrorKind::Uncompiled { error, .. }) =
leaf(Oversized::fields().seo().title())
else {
panic!("an embedded path of an uncompiled model is refused");
};
for driver in ["PostgreSQL", "SQLite", "MySQL", "DynamoDB"] {
assert!(error.contains(driver), "{error}");
}
assert!(matches!(
resolve_enum(&Oversized::fields().media().into()),
Err(DeclarationErrorKind::Uncompiled { .. })
));
assert_eq!(
leaf(Oversized::fields().body()).unwrap().name,
"body",
"a single field reads its own model, compiled or not"
);
}