use super::*;
use crate::core::config::e2e::CallConfig;
use crate::core::ir::{FieldDef, FunctionDef, TypeDef, TypeRef};
fn docs_fixture(assertions: serde_json::Value) -> Fixture {
serde_json::from_value(serde_json::json!({
"id": "sample_fixture",
"description": "Sample fixture",
"input": {"html": "<p>Hello World</p>"},
"assertions": assertions,
"docs": {"topic": "smoke", "stem": "sample_fixture"}
}))
.expect("fixture must parse")
}
const CONFIG_ONLY_LEAF: &str = "document_structure";
fn config() -> E2eConfig {
E2eConfig {
call: CallConfig {
function: "convert".into(),
result_var: "result".into(),
..CallConfig::default()
},
result_fields: ["document".to_string(), CONFIG_ONLY_LEAF.to_string()]
.into_iter()
.collect(),
fields_method_calls: [format!("document.{CONFIG_ONLY_LEAF}")].into_iter().collect(),
..E2eConfig::default()
}
}
fn field(name: &str, ty: TypeRef, optional: bool) -> FieldDef {
FieldDef {
name: name.to_string(),
ty,
optional,
..FieldDef::default()
}
}
fn convert_returning(type_name: &str) -> Vec<FunctionDef> {
vec![FunctionDef {
name: "convert".to_string(),
return_type: TypeRef::Named(type_name.to_string()),
..FunctionDef::default()
}]
}
fn type_defs() -> Vec<TypeDef> {
vec![
TypeDef {
name: "ConversionResult".to_string(),
fields: vec![field(
"document",
TypeRef::Named("ExtractedDocument".to_string()),
false,
)],
..TypeDef::default()
},
TypeDef {
name: "ExtractedDocument".to_string(),
fields: vec![
field("nodes", TypeRef::String, false),
field("payload", TypeRef::Json, false),
],
..TypeDef::default()
},
]
}
fn shown_expressions(field_path: &str) -> Vec<String> {
resolve(
&docs_fixture(serde_json::json!([{"type": "equals", "field": field_path, "value": "Hello"}])),
&config(),
"python",
&type_defs(),
&[],
&convert_returning("ConversionResult"),
)
.into_iter()
.map(|operation| operation.expression)
.collect()
}
#[test]
fn a_config_only_name_hung_off_a_declared_field_derives_no_accessor() {
assert_eq!(
shown_expressions(&format!("document.{CONFIG_ONLY_LEAF}")),
Vec::<String>::new(),
"`{CONFIG_ONLY_LEAF}` is declared by alef.toml, not by ExtractedDocument"
);
}
#[test]
fn a_nested_field_the_result_type_declares_still_derives_its_accessor() {
assert_eq!(shown_expressions("document.nodes"), vec!["result.document.nodes"]);
}
#[test]
fn a_path_walking_out_of_the_ir_struct_graph_still_derives_its_accessor() {
assert_eq!(
shown_expressions("document.payload.anything"),
vec!["result.document.payload.anything"]
);
}
#[test]
fn the_availability_oracles_disagree_on_purpose_for_a_nested_name_the_result_type_lacks() {
let e2e_config = config();
let resolver = build_resolver(
&e2e_config,
&e2e_config.call,
"python",
&type_defs(),
&[],
&convert_returning("ConversionResult"),
);
let absent = format!("document.{CONFIG_ONLY_LEAF}");
assert!(
resolver.is_valid_for_result(&absent),
"a hand-authored assertion on `{absent}` must still render"
);
assert_eq!(
resolver.result_field_oracle_knows(&absent),
Some(false),
"an inferred accessor for `{absent}` must be refused"
);
assert!(resolver.is_valid_for_result("document.nodes"));
assert_eq!(resolver.result_field_oracle_knows("document.nodes"), Some(true));
assert_eq!(
resolver.result_field_oracle_knows("document.payload.anything"),
Some(true)
);
}
#[test]
fn only_a_consumer_declared_path_names_the_config_key_that_declares_it() {
let e2e_config = config();
let resolver = build_resolver(
&e2e_config,
&e2e_config.call,
"python",
&type_defs(),
&[],
&convert_returning("ConversionResult"),
);
assert_eq!(
resolver.declaring_config_key(&format!("document.{CONFIG_ONLY_LEAF}")),
Some("fields_method_calls")
);
assert_eq!(resolver.declaring_config_key("document.nodes"), None);
assert_eq!(resolver.declaring_config_key("rate_limit.min_duration_ms"), None);
}