use std::collections::HashMap;
use super::*;
use crate::{schema::Source, test_support::DummyUser};
fn enumeration(values: &[&str]) -> EmbeddedBuilder {
let variants: Vec<(String, String)> = values
.iter()
.map(|value| (value.to_string(), format!("Variant {value}")))
.collect();
EmbeddedBuilder {
resolver: FieldResolver::default(),
fields: vec![Field::discriminant("kind".to_string(), variants.clone())],
shape: Shape::Enum(EnumNode {
key: "kind".to_string(),
discriminant: 0,
shared: Vec::new(),
variants: variants
.into_iter()
.map(|(value, _)| Variant {
value,
members: Vec::new(),
})
.collect(),
}),
variant: None,
}
}
fn two_variants() -> Schema {
let mut builder = enumeration(&["1", "2"]);
builder.variant();
builder.shared(Field::text(DummyUser::fields().name()), false);
builder.leaf(Field::text(DummyUser::fields().email()).email(), true);
builder.variant();
builder.shared(Field::text(DummyUser::fields().name()), false);
builder.leaf(Field::text(DummyUser::fields().id()), true);
builder.finish()
}
fn map(pairs: &[(&str, &str)]) -> HashMap<String, String> {
pairs
.iter()
.map(|(k, v)| (k.to_string(), v.to_string()))
.collect()
}
#[tokio::test]
async fn a_hidden_variant_group_is_not_checked() {
let cx = crate::test_support::cx();
let schema = Schema::new(two_variants());
let values = map(&[
("kind", "2"),
("email", "not-an-email"),
("id", "0f8fad5b-d9cb-469f-a165-70867728950e"),
]);
let errors = schema.checked(&cx, &values).await;
assert!(
!errors.contains_key("email"),
"a hidden variant's field must not block the submit, got {errors:?}"
);
let mut named = values.clone();
named.insert("kind".to_string(), "1".to_string());
assert!(
schema.checked(&cx, &named).await.contains_key("email"),
"the named variant's field must still check"
);
let mut unnamed = values.clone();
unnamed.insert("kind".to_string(), String::new());
assert!(
schema.checked(&cx, &unnamed).await.contains_key("email"),
"an unnamed submission checks every variant's fields"
);
}
#[test]
fn a_submission_hides_the_variants_it_does_not_name() {
let schema = Schema::new(two_variants());
let hidden = |pairs: &[(&str, &str)]| {
let mut keys: Vec<String> = schema.hidden_fields(&map(pairs)).into_iter().collect();
keys.sort();
keys
};
assert_eq!(hidden(&[("kind", "1")]), ["id"]);
assert_eq!(hidden(&[("kind", "2")]), ["email"]);
assert!(
hidden(&[("kind", "")]).is_empty(),
"a submission naming no variant hides nothing"
);
}
#[test]
fn a_shared_column_is_one_field_and_one_key() {
let schema = two_variants();
assert_eq!(
schema.fields().map(Field::name).collect::<Vec<_>>(),
["kind", "name", "email", "id"]
);
assert_eq!(
schema.embedded_root().keys(),
["kind", "name", "email", "id"],
"the discriminant first, then every column once"
);
}
#[test]
fn the_variant_a_submission_reads_as() {
let schema = two_variants();
let node = schema.embedded_root();
let index = |pairs: &[(&str, &str)]| node.variant_index(&map(pairs));
assert_eq!(index(&[("kind", "2"), ("email", "a@b.co")]), Ok(1));
assert_eq!(index(&[("id", "x")]), Ok(1), "the payload names variant 2");
assert_eq!(
index(&[("name", "shared")]),
Ok(0),
"a shared column selects no variant, so the first one reads"
);
let refused = index(&[("kind", "9")]).expect_err("an undeclared variant");
assert_eq!(refused[0].key, "kind");
}
#[test]
fn an_embedded_node_keeps_its_slots_when_appended() {
let schema = Schema::new((Field::text(DummyUser::fields().email()), {
let mut builder = enumeration(&["1"]);
builder.variant();
builder.leaf(Field::text(DummyUser::fields().name()), false);
builder.finish()
}));
assert_eq!(
schema.fields().map(Field::name).collect::<Vec<_>>(),
["email", "kind", "name"]
);
let mut slots = Vec::new();
let Node::Embedded(node) = &schema.nodes[1] else {
panic!("the second node is the embedded value");
};
node.visit_fields(&mut |index| slots.push(schema.fields[index].name()));
assert_eq!(slots, ["kind", "name"], "the node reads its own fields");
}
#[tokio::test]
async fn a_view_renders_a_shared_column_only_for_a_variant_declaring_it() {
let cx = crate::test_support::cx();
let mut builder = enumeration(&["1", "2", "3"]);
builder.variant();
builder.variant();
builder.shared(
Field::text(DummyUser::fields().name()).label("Stamp"),
false,
);
builder.variant();
builder.shared(
Field::text(DummyUser::fields().name()).label("Stamp"),
false,
);
let schema = Schema::new(builder.finish());
let render = |values: HashMap<String, String>| {
let schema = &schema;
let cx = &cx;
async move {
schema
.render(cx, Source::view(&values))
.await
.unwrap()
.single()
.await
.unwrap()
.render(cx)
}
};
let unit = render(map(&[("kind", "1")])).await;
assert!(
unit.contains("Variant 1"),
"the stored variant is named: {unit}"
);
assert!(
!unit.contains("Stamp") && !unit.contains("(missing)"),
"a unit variant declares no shared column: {unit}"
);
let stamped = render(map(&[("kind", "3"), ("name", "noon")])).await;
assert!(
stamped.contains("Stamp") && stamped.contains("noon"),
"{stamped}"
);
}