use super::test_method::render_test_method;
use crate::backends::java::gen_bindings::test_only_gen_record_type;
use crate::core::config::{JavaBuilderMode, ResolvedCrateConfig};
use crate::core::ir::{DefaultValue, FieldDef, FunctionDef, TypeDef, TypeRef};
use crate::e2e::config::{CallConfig, E2eConfig};
use crate::e2e::fixture::{Assertion, Fixture};
use ahash::AHashSet;
use std::collections::HashSet;
fn data_field() -> FieldDef {
FieldDef {
name: "data".to_string(),
ty: TypeRef::String,
optional: true,
..FieldDef::default()
}
}
fn required_collection_with_skip_if_field() -> FieldDef {
FieldDef {
name: "imports".to_string(),
ty: TypeRef::Vec(Box::new(TypeRef::String)),
optional: false,
default: Some("/* serde(default) */".to_string()),
typed_default: Some(DefaultValue::Empty),
serde_skip_serializing_if: true,
serde_skip: false,
..FieldDef::default()
}
}
fn genuinely_optional_collection_field() -> FieldDef {
FieldDef {
name: "imports".to_string(),
ty: TypeRef::Vec(Box::new(TypeRef::String)),
optional: true,
..FieldDef::default()
}
}
fn process_result_type(imports_field: FieldDef) -> TypeDef {
TypeDef {
name: "ProcessResult".to_string(),
has_serde: true,
fields: vec![data_field(), imports_field],
..TypeDef::default()
}
}
fn render_binding(typ: &TypeDef) -> String {
test_only_gen_record_type(
"dev.sample.bindings",
typ,
&AHashSet::new(),
&AHashSet::new(),
"",
&[],
"",
JavaBuilderMode::Always,
&ahash::AHashMap::new(),
&AHashSet::new(),
&HashSet::new(),
)
}
fn equals_null_fixture(id: &str, field: &str) -> Fixture {
Fixture {
docs: None,
requirements: Vec::new(),
id: id.to_string(),
category: None,
description: "test".to_string(),
tags: vec![],
skip: None,
env: None,
setup: Vec::new(),
call: None,
input: serde_json::Value::Null,
mock_response: None,
source: String::new(),
http: None,
asyncapi: None,
websocket: None,
preserve_input_urls: false,
assertions: vec![Assertion {
assertion_type: "equals".to_string(),
field: Some(field.to_string()),
value: Some(serde_json::Value::Null),
..Assertion::default()
}],
visitor: None,
args: vec![],
assertion_recipes: vec![],
}
}
fn render_test(typ: &TypeDef, fixture: &Fixture) -> String {
let type_defs = vec![typ.clone()];
let functions = vec![FunctionDef {
name: "process".to_string(),
return_type: TypeRef::Named("ProcessResult".to_string()),
..FunctionDef::default()
}];
let call = CallConfig {
function: "process".to_string(),
result_var: "result".to_string(),
..CallConfig::default()
};
let e2e_config = E2eConfig {
call,
..Default::default()
};
let config = ResolvedCrateConfig::default();
let mut out = String::new();
render_test_method(
&mut out,
fixture,
"SampleClass",
"",
"",
&[],
None,
false,
&e2e_config,
&std::collections::HashMap::new(),
false,
&[],
&config,
&type_defs,
&[],
&functions,
&[],
);
out
}
#[test]
fn required_collection_field_test_and_binding_agree_absent_means_empty_not_null() {
let typ = process_result_type(required_collection_with_skip_if_field());
let binding = render_binding(&typ);
assert!(
binding.contains("private List<String> imports = List.of();"),
"expected the binding's Builder to default a non-optional, serde-defaulted Vec field \
to List.of(), got:\n{binding}"
);
let fixture = equals_null_fixture("imports_equals_null", "imports");
let test = render_test(&typ, &fixture);
assert!(
!test.contains("assertEquals(null,"),
"the test emitter must not assert a field equals null when the binding it is testing \
can never produce null for that field, got:\n{test}"
);
assert!(
test.contains(".isEmpty()"),
"expected the test to assert emptiness (matching the binding's List.of() default) \
instead, got:\n{test}"
);
}
#[test]
fn genuinely_optional_collection_field_keeps_null_equality() {
let typ = process_result_type(genuinely_optional_collection_field());
let binding = render_binding(&typ);
assert!(
!binding.contains("private List<String> imports = List.of();"),
"a genuinely optional (Option<Vec<T>>) field's Builder default must stay null (no \
eager initializer), got:\n{binding}"
);
let fixture = equals_null_fixture("imports_equals_null_optional", "imports");
let test = render_test(&typ, &fixture);
assert!(
test.contains("assertEquals(null,"),
"a genuinely optional collection field's equals-null assertion must be left as a \
literal null comparison -- the binding really can produce null here, got:\n{test}"
);
}
#[test]
fn non_collection_optional_field_is_unaffected() {
let typ = process_result_type(required_collection_with_skip_if_field());
let fixture = equals_null_fixture("data_equals_null", "data");
let out = render_test(&typ, &fixture);
assert!(
out.contains("data"),
"expected an assertion referencing the data field to be rendered at all, got:\n{out}"
);
assert!(
!out.contains("binding never returns null for a non-optional collection"),
"the new collection-defaulting branch must never fire for a non-collection field -- \
`data` is `TypeRef::String`, not a Vec/Map, so `is_collection_root` must answer false \
and this fixture must render through the pre-existing, untouched code path, got:\n{out}"
);
}