use super::{Severity, validate_field_classifications};
use crate::core::config::e2e::{CallConfig, E2eConfig};
use crate::core::ir::{FieldDef, TypeDef, TypeRef};
use std::collections::HashSet;
fn field(name: &str, ty: TypeRef, optional: bool) -> FieldDef {
FieldDef {
name: name.to_string(),
ty,
optional,
..FieldDef::default()
}
}
fn article_ir() -> Vec<TypeDef> {
vec![TypeDef {
name: "ArticleMetadata".to_string(),
fields: vec![
field("tags", TypeRef::Vec(Box::new(TypeRef::String)), false),
field("subtitle", TypeRef::String, true),
field("title", TypeRef::String, false),
field(
"open_graph",
TypeRef::Map(Box::new(TypeRef::String), Box::new(TypeRef::String)),
false,
),
],
..TypeDef::default()
}]
}
fn config_with(optional: &[&str], array: &[&str]) -> E2eConfig {
E2eConfig {
fields_optional: optional.iter().map(|f| (*f).to_string()).collect(),
fields_array: array.iter().map(|f| (*f).to_string()).collect(),
..E2eConfig::default()
}
}
fn errors_only(diagnostics: &[super::ValidationError]) -> Vec<&super::ValidationError> {
diagnostics
.iter()
.filter(|diag| diag.severity == Severity::Error)
.collect()
}
#[test]
fn optional_entry_naming_a_non_optional_ir_field_is_an_error_that_names_key_and_type() {
let diagnostics = validate_field_classifications(&config_with(&["metadata.article.tags"], &[]), &article_ir(), &[]);
let errors = errors_only(&diagnostics);
assert_eq!(errors.len(), 1, "expected exactly one hard error, got: {diagnostics:?}");
let message = &errors[0].message;
assert!(message.contains("[e2e].fields_optional"), "names the table: {message}");
assert!(
message.contains("`metadata.article.tags`"),
"names the entry: {message}"
);
assert!(message.contains("Vec<String>"), "names the real IR type: {message}");
assert_eq!(errors[0].file, "alef.toml");
}
#[test]
fn optional_entry_naming_a_genuinely_optional_ir_field_produces_no_error() {
let diagnostics = validate_field_classifications(&config_with(&["metadata.subtitle"], &[]), &article_ir(), &[]);
assert!(
errors_only(&diagnostics).is_empty(),
"a correct fields_optional entry must not be rejected: {diagnostics:?}"
);
}
#[test]
fn optional_entry_subscripting_a_map_field_produces_no_error() {
let diagnostics = validate_field_classifications(
&config_with(&["metadata.document.open_graph[title]"], &[]),
&article_ir(),
&[],
);
assert!(
errors_only(&diagnostics).is_empty(),
"a map key lookup is legitimately optional: {diagnostics:?}"
);
}
#[test]
fn optional_entry_subscripting_a_vec_field_produces_no_error() {
let diagnostics = validate_field_classifications(&config_with(&["tags[0]"], &[]), &article_ir(), &[]);
assert!(
errors_only(&diagnostics).is_empty(),
"a list index is legitimately optional: {diagnostics:?}"
);
}
#[test]
fn optional_entry_subscripting_a_scalar_field_is_still_an_error() {
let diagnostics = validate_field_classifications(&config_with(&["title[0]"], &[]), &article_ir(), &[]);
let errors = errors_only(&diagnostics);
assert_eq!(errors.len(), 1, "expected exactly one hard error, got: {diagnostics:?}");
assert!(
errors[0].message.contains("subscripts it"),
"got: {}",
errors[0].message
);
assert!(errors[0].message.contains("String"), "got: {}", errors[0].message);
}
#[test]
fn array_entry_subscripting_a_map_of_scalars_is_an_error() {
let diagnostics = validate_field_classifications(&config_with(&[], &["open_graph[title]"]), &article_ir(), &[]);
let errors = errors_only(&diagnostics);
assert_eq!(errors.len(), 1, "expected exactly one hard error, got: {diagnostics:?}");
assert!(errors[0].message.contains("fields_array"), "got: {}", errors[0].message);
}
#[test]
fn array_entry_naming_a_non_collection_ir_field_is_an_error() {
let diagnostics = validate_field_classifications(&config_with(&[], &["title"]), &article_ir(), &[]);
let errors = errors_only(&diagnostics);
assert_eq!(errors.len(), 1, "expected exactly one hard error, got: {diagnostics:?}");
assert!(errors[0].message.contains("fields_array"), "got: {}", errors[0].message);
assert!(errors[0].message.contains("String"), "got: {}", errors[0].message);
}
#[test]
fn array_entry_naming_a_vec_ir_field_produces_no_error() {
let diagnostics = validate_field_classifications(&config_with(&[], &["tags"]), &article_ir(), &[]);
assert!(
errors_only(&diagnostics).is_empty(),
"a correct fields_array entry must not be rejected: {diagnostics:?}"
);
}
#[test]
fn an_absent_ir_produces_no_diagnostic_at_all() {
let diagnostics = validate_field_classifications(&config_with(&["tags"], &["title"]), &[], &[]);
assert!(
diagnostics.is_empty(),
"an absent IR licenses no claim in either direction: {diagnostics:?}"
);
}
#[test]
fn an_ir_unknown_leaf_warns_rather_than_failing_generation() {
let diagnostics =
validate_field_classifications(&config_with(&["interaction.chunk_count"], &[]), &article_ir(), &[]);
assert!(
errors_only(&diagnostics).is_empty(),
"an unverifiable entry must not fail generation: {diagnostics:?}"
);
assert_eq!(diagnostics.len(), 1, "expected one warning, got: {diagnostics:?}");
assert_eq!(diagnostics[0].severity, Severity::Warning);
assert!(
diagnostics[0].message.contains("unverified") && diagnostics[0].message.contains("`chunk_count`"),
"the warning must name the leaf it could not resolve: {}",
diagnostics[0].message
);
}
#[test]
fn indexed_and_wildcard_entry_spellings_resolve_to_the_same_leaf_field() {
let indexed = validate_field_classifications(&config_with(&["article[0].tags"], &[]), &article_ir(), &[]);
let wildcard = validate_field_classifications(&config_with(&["article[].tags"], &[]), &article_ir(), &[]);
assert_eq!(errors_only(&indexed).len(), 1, "got: {indexed:?}");
assert_eq!(errors_only(&wildcard).len(), 1, "got: {wildcard:?}");
}
#[test]
fn per_call_override_tables_are_checked_and_named_in_the_diagnostic() {
let mut config = E2eConfig::default();
config.calls.insert(
"summarize".to_string(),
CallConfig {
fields_optional: HashSet::from(["tags".to_string()]),
..CallConfig::default()
},
);
let diagnostics = validate_field_classifications(&config, &article_ir(), &[]);
let errors = errors_only(&diagnostics);
assert_eq!(errors.len(), 1, "got: {diagnostics:?}");
assert!(
errors[0].message.contains("[e2e.calls.summarize].fields_optional"),
"got: {}",
errors[0].message
);
}
#[test]
fn a_same_named_field_that_agrees_on_another_type_clears_the_entry() {
let mut ir = article_ir();
ir.push(TypeDef {
name: "DraftMetadata".to_string(),
fields: vec![field("tags", TypeRef::Vec(Box::new(TypeRef::String)), true)],
..TypeDef::default()
});
let diagnostics = validate_field_classifications(&config_with(&["tags"], &[]), &ir, &[]);
assert!(
errors_only(&diagnostics).is_empty(),
"agrees-on-any-type must win, or a shared field name produces a false failure: {diagnostics:?}"
);
}
fn crossing_enums() -> Vec<crate::core::ir::EnumDef> {
use crate::core::ir::{EnumDef, EnumVariant};
vec![EnumDef {
name: "FormatInfo".to_string(),
variants: vec![EnumVariant {
name: "Variant".to_string(),
fields: vec![field("variant", TypeRef::Named("VariantDetail".to_string()), false)],
..EnumVariant::default()
}],
..EnumDef::default()
}]
}
fn crossing_type_defs() -> Vec<TypeDef> {
let mut ir = article_ir();
ir[0]
.fields
.push(field("format", TypeRef::Named("FormatInfo".to_string()), false));
ir.push(TypeDef {
name: "VariantDetail".to_string(),
fields: vec![field("detail", TypeRef::String, false)],
..TypeDef::default()
});
ir
}
fn config_with_method_calls(optional: &[&str], method_calls: &[&str]) -> E2eConfig {
E2eConfig {
fields_optional: optional.iter().map(|f| (*f).to_string()).collect(),
fields_method_calls: method_calls.iter().map(|f| (*f).to_string()).collect(),
..E2eConfig::default()
}
}
#[test]
fn a_declared_method_call_crossing_produces_no_diagnostic() {
let diagnostics = validate_field_classifications(
&config_with_method_calls(&["format.variant"], &["format.variant"]),
&crossing_type_defs(),
&crossing_enums(),
);
assert!(
diagnostics.is_empty(),
"a fields_method_calls-declared, IR-confirmed crossing must not warn or error: {diagnostics:?}"
);
}
#[test]
fn an_ir_confirmed_crossing_without_a_declared_method_call_still_warns() {
let diagnostics = validate_field_classifications(
&config_with_method_calls(&["format.variant"], &[]),
&crossing_type_defs(),
&crossing_enums(),
);
let warnings = diagnostics
.iter()
.filter(|d| d.severity == Severity::Warning)
.collect::<Vec<_>>();
assert_eq!(warnings.len(), 1, "got: {diagnostics:?}");
assert!(
warnings[0].message.contains("unverified") && warnings[0].message.contains("`variant`"),
"got: {}",
warnings[0].message
);
}
#[test]
fn a_declared_method_call_with_no_ir_enum_data_still_warns() {
let diagnostics = validate_field_classifications(
&config_with_method_calls(&["format.variant"], &["format.variant"]),
&crossing_type_defs(),
&[],
);
let warnings = diagnostics
.iter()
.filter(|d| d.severity == Severity::Warning)
.collect::<Vec<_>>();
assert_eq!(warnings.len(), 1, "got: {diagnostics:?}");
assert!(
warnings[0].message.contains("unverified"),
"got: {}",
warnings[0].message
);
}