use crate::core::config::ResolvedCrateConfig;
use crate::core::ir::{FieldDef, FunctionDef, TypeDef, TypeRef};
use crate::e2e::config::E2eConfig;
use crate::e2e::fixture::{Assertion, Fixture};
use super::test_method::render_test_method;
fn sample_lib_type_defs() -> Vec<TypeDef> {
vec![
TypeDef {
name: "SampleResult".to_string(),
fields: vec![FieldDef {
name: "entries".to_string(),
ty: TypeRef::Vec(Box::new(TypeRef::Named("Entry".to_string()))),
..FieldDef::default()
}],
..TypeDef::default()
},
TypeDef {
name: "Entry".to_string(),
fields: vec![
FieldDef {
name: "sections".to_string(),
ty: TypeRef::Vec(Box::new(TypeRef::Named("Section".to_string()))),
optional: true,
..FieldDef::default()
},
FieldDef {
name: "tags".to_string(),
ty: TypeRef::Vec(Box::new(TypeRef::String)),
optional: false,
..FieldDef::default()
},
],
..TypeDef::default()
},
TypeDef {
name: "Section".to_string(),
fields: vec![FieldDef {
name: "label".to_string(),
ty: TypeRef::String,
..FieldDef::default()
}],
..TypeDef::default()
},
]
}
fn sample_lib_functions() -> Vec<FunctionDef> {
vec![FunctionDef {
name: "get_report".to_string(),
return_type: TypeRef::Named("SampleResult".to_string()),
..FunctionDef::default()
}]
}
fn make_assertion(field: &str, value: serde_json::Value) -> Assertion {
Assertion {
assertion_type: "equals".to_string(),
field: Some(field.to_string()),
value: Some(value),
..Assertion::default()
}
}
fn render(assertions: Vec<Assertion>) -> String {
let fixture = Fixture {
id: "get_report_sections".into(),
description: "sample_lib report with optional nested sections".into(),
assertions,
..Fixture::default()
};
let mut e2e_config = E2eConfig::default();
e2e_config.call.function = "get_report".into();
e2e_config.call.result_var = "result".into();
let mut out = String::new();
render_test_method(
&mut out,
&fixture,
"Facade",
"",
"",
&[],
None,
false,
&e2e_config,
&std::collections::HashMap::new(),
false,
&ResolvedCrateConfig::default(),
&sample_lib_type_defs(),
&[],
&sample_lib_functions(),
)
.expect("render_test_method succeeds");
out
}
#[test]
fn nested_optional_segment_field_safe_calls_before_indexing() {
let out = render(vec![make_assertion(
"entries[0].sections[0].label",
serde_json::Value::String("alpha".to_string()),
)]);
assert!(
out.contains("sections()?.first()"),
"must safe-call the Option<List<Section>> before indexing; got:\n{out}"
);
assert!(
!out.contains("sections().first()"),
"must not emit the un-guarded Option<List<T>> index; got:\n{out}"
);
}
#[test]
fn nested_optional_segment_field_safe_calls_before_size() {
let out = render(vec![make_assertion(
"entries[0].sections.length",
serde_json::Value::Number(serde_json::Number::from(1u64)),
)]);
assert!(
out.contains("sections()?.size"),
"must safe-call the Option<List<Section>> before .size; got:\n{out}"
);
assert!(
!out.contains("sections().size"),
"must not emit .size directly against the nullable list; got:\n{out}"
);
}
#[test]
fn non_optional_sibling_segment_field_stays_plain() {
let out = render(vec![
make_assertion("entries[0].tags[0]", serde_json::Value::String("beta".to_string())),
make_assertion(
"entries[0].tags.length",
serde_json::Value::Number(serde_json::Number::from(1u64)),
),
]);
assert!(
out.contains("tags().first()") && !out.contains("tags()?.first()"),
"a non-optional Vec<T> field must index plainly, with no safe-call; got:\n{out}"
);
assert!(
out.contains("tags().size") && !out.contains("tags()?.size"),
"a non-optional Vec<T> field must read .size plainly, with no safe-call; got:\n{out}"
);
}