use super::test_method::render_test_method;
use crate::core::config::ResolvedCrateConfig;
use crate::core::ir::{EnumDef, EnumVariant, FieldDef, FunctionDef, PrimitiveType, TypeDef, TypeRef};
use crate::e2e::codegen::field_skip::FieldSkip;
use crate::e2e::config::{CallConfig, E2eConfig};
use crate::e2e::fixture::{Assertion, Fixture};
fn stage_output_enum() -> EnumDef {
EnumDef {
name: "StageOutput".to_string(),
variants: vec![EnumVariant {
name: "Text".to_string(),
fields: vec![FieldDef {
name: "0".to_string(),
ty: TypeRef::String,
..FieldDef::default()
}],
is_tuple: true,
..EnumVariant::default()
}],
serde_untagged: true,
..EnumDef::default()
}
}
fn stage_status_enum() -> EnumDef {
EnumDef {
name: "StageStatus".to_string(),
variants: vec![EnumVariant {
name: "Queued".to_string(),
..EnumVariant::default()
}],
..EnumDef::default()
}
}
fn field(name: &str, ty: TypeRef, optional: bool) -> FieldDef {
FieldDef {
name: name.to_string(),
ty,
optional,
..FieldDef::default()
}
}
fn union_ir() -> (Vec<TypeDef>, Vec<EnumDef>, Vec<FunctionDef>) {
let type_defs = vec![
TypeDef {
name: "UnionResult".to_string(),
fields: vec![
field(
"summary",
TypeRef::Optional(Box::new(TypeRef::Named("StageOutput".to_string()))),
true,
),
field("payload", TypeRef::Named("StageOutput".to_string()), false),
field("status", TypeRef::Named("StageStatus".to_string()), false),
field(
"stages",
TypeRef::Vec(Box::new(TypeRef::Named("Stage".to_string()))),
false,
),
field("title", TypeRef::String, false),
field("attempts", TypeRef::Primitive(PrimitiveType::U32), false),
field("tags", TypeRef::Vec(Box::new(TypeRef::String)), false),
field("flag", TypeRef::Primitive(PrimitiveType::Bool), false),
],
..TypeDef::default()
},
TypeDef {
name: "Stage".to_string(),
fields: vec![
field("payload", TypeRef::Named("StageOutput".to_string()), false),
field("label", TypeRef::String, false),
],
..TypeDef::default()
},
];
let enums = vec![stage_output_enum(), stage_status_enum()];
let functions = vec![FunctionDef {
name: "read_union".to_string(),
return_type: TypeRef::Named("UnionResult".to_string()),
..FunctionDef::default()
}];
(type_defs, enums, functions)
}
fn fixture(id: &str, assertion: Assertion) -> 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],
visitor: None,
args: vec![],
assertion_recipes: vec![],
}
}
fn assertion(assertion_type: &str, field_path: &str, value: Option<serde_json::Value>) -> Assertion {
Assertion {
assertion_type: assertion_type.to_string(),
field: Some(field_path.to_string()),
value,
..Assertion::default()
}
}
fn text(value: &str) -> Option<serde_json::Value> {
Some(serde_json::Value::String(value.to_string()))
}
fn number(value: u64) -> Option<serde_json::Value> {
Some(serde_json::Value::Number(value.into()))
}
fn render(assertion: Assertion, fields_display_as_text: &[&str]) -> String {
let (type_defs, enums, functions) = union_ir();
let e2e_config = E2eConfig {
call: CallConfig {
function: "read_union".to_string(),
result_var: "result".to_string(),
..CallConfig::default()
},
fields_display_as_text: fields_display_as_text.iter().map(|s| s.to_string()).collect(),
..Default::default()
};
let mut out = String::new();
render_test_method(
&mut out,
&fixture("union_family", assertion),
"SampleClass",
"",
"",
&[],
None,
false,
&e2e_config,
&std::collections::HashMap::new(),
false,
&[],
&ResolvedCrateConfig::default(),
&type_defs,
&enums,
&functions,
&[],
);
out
}
fn assert_skipped(rendered: &str, field_path: &str, fragment: &str) {
let line = rendered
.lines()
.find(|line| line.contains("skipped:"))
.unwrap_or_else(|| panic!("expected a skip line for '{field_path}', got:\n{rendered}"));
assert_eq!(
FieldSkip::extract_classified(line),
Some((field_path, FieldSkip::PayloadUnionHasNoScalarWireAccessor)),
"the skip must be registered, not just commented; got: {line}"
);
assert!(
!rendered.contains(fragment),
"'{fragment}' must not be emitted for a payload-union leaf, got:\n{rendered}"
);
}
fn assert_emitted(rendered: &str, fragment: &str) {
assert!(
rendered.contains(fragment),
"expected '{fragment}' to still be emitted, got:\n{rendered}"
);
assert!(
!rendered.contains("payload-carrying union"),
"this shape must not be refused as a payload union, got:\n{rendered}"
);
}
#[test]
fn regex_on_a_payload_union_is_skipped() {
let out = render(assertion("matches_regex", "payload", text("^ok.*$")), &[]);
assert_skipped(&out, "payload", "result.payload().matches(");
}
#[test]
fn regex_on_an_optional_payload_union_is_skipped() {
let out = render(assertion("matches_regex", "summary", text("^ok.*$")), &[]);
assert_skipped(&out, "summary", ".matches(");
}
#[test]
fn regex_on_a_string_field_still_emits() {
let out = render(assertion("matches_regex", "title", text("^ok.*$")), &[]);
assert_emitted(&out, ".matches(");
}
#[test]
fn length_on_a_payload_union_is_skipped() {
let out = render(assertion("min_length", "payload", number(3)), &[]);
assert_skipped(&out, "payload", "result.payload().length()");
}
#[test]
fn length_on_a_string_field_still_emits() {
let out = render(assertion("min_length", "title", number(3)), &[]);
assert_emitted(&out, ".length() >= 3");
}
#[test]
fn count_on_an_optional_payload_union_is_skipped() {
let out = render(assertion("count_min", "summary", number(1)), &[]);
assert_skipped(&out, "summary", ".size()");
}
#[test]
fn count_on_a_collection_field_still_emits() {
let out = render(assertion("count_min", "tags", number(1)), &[]);
assert_emitted(&out, ".size() >= 1");
}
#[test]
fn numeric_comparison_on_a_payload_union_is_skipped() {
let out = render(assertion("greater_than", "payload", number(1)), &[]);
assert_skipped(&out, "payload", "result.payload() > 1");
}
#[test]
fn numeric_comparison_on_a_numeric_field_still_emits() {
let out = render(assertion("greater_than", "attempts", number(1)), &[]);
assert_emitted(&out, "result.attempts() > 1");
}
#[test]
fn equality_on_a_payload_union_is_skipped() {
let out = render(assertion("equals", "payload", text("ok")), &[]);
assert_skipped(&out, "payload", "assertEquals");
}
#[test]
fn equality_on_an_optional_payload_union_is_skipped() {
let out = render(assertion("equals", "summary", text("ok")), &[]);
assert_skipped(&out, "summary", "assertEquals");
}
#[test]
fn equality_on_a_fieldless_enum_field_still_emits() {
let out = render(assertion("equals", "status", text("Queued")), &[]);
assert_emitted(&out, "result.status().getValue()");
}
#[test]
fn string_containment_on_an_optional_payload_union_is_skipped() {
let out = render(assertion("contains", "summary", text("ok")), &[]);
assert_skipped(&out, "summary", ".contains(");
}
#[test]
fn string_containment_on_a_string_field_still_emits() {
let out = render(assertion("contains", "title", text("ok")), &[]);
assert_emitted(&out, ".contains(");
}
#[test]
fn boolean_on_a_non_optional_payload_union_is_skipped() {
let out = render(assertion("is_true", "payload", None), &[]);
assert_skipped(&out, "payload", "assertTrue(result.payload()");
}
#[test]
fn boolean_on_an_optional_payload_union_still_emits_a_presence_check() {
let out = render(assertion("is_true", "summary", None), &[]);
assert_emitted(&out, "java.util.Optional.ofNullable(result.summary()).isPresent()");
}
#[test]
fn boolean_on_a_bool_field_still_emits() {
let out = render(assertion("is_true", "flag", None), &[]);
assert_emitted(&out, "assertTrue(result.flag()");
}
#[test]
fn presence_on_an_optional_payload_union_still_emits() {
let out = render(assertion("not_empty", "summary", None), &[]);
assert_emitted(&out, "java.util.Optional.ofNullable(result.summary())");
}
#[test]
fn presence_on_a_non_optional_payload_union_is_skipped() {
let out = render(assertion("not_empty", "payload", None), &[]);
assert_skipped(&out, "payload", "result.payload().isEmpty()");
}
#[test]
fn a_display_as_text_union_field_still_emits_through_the_text_accessor() {
let out = render(assertion("equals", "summary", text("ok")), &["summary"]);
assert_emitted(&out, ".map(v -> v.text()).orElse(\"\")");
}
#[test]
fn string_containment_on_a_display_as_text_union_still_emits() {
let out = render(assertion("contains", "summary", text("ok")), &["summary"]);
assert_emitted(&out, ".map(v -> v.text()).orElse(\"\").contains(");
}
#[test]
fn length_on_a_display_as_text_union_still_emits() {
let out = render(assertion("min_length", "summary", number(3)), &["summary"]);
assert_emitted(&out, ".map(v -> v.text()).orElse(\"\").length() >= 3");
}
#[test]
fn numeric_comparison_on_a_display_as_text_union_is_skipped() {
let out = render(assertion("greater_than", "summary", number(1)), &["summary"]);
assert_skipped(&out, "summary", ".orElse(\"\") > 1");
}
#[test]
fn count_on_a_display_as_text_union_is_skipped() {
let out = render(assertion("count_min", "summary", number(1)), &["summary"]);
assert_skipped(&out, "summary", ".size()");
}
#[test]
fn regex_on_a_display_as_text_union_is_skipped() {
let out = render(assertion("matches_regex", "summary", text("^ok$")), &["summary"]);
assert_skipped(&out, "summary", ".matches(");
}
#[test]
fn numeric_valued_equality_on_a_display_as_text_union_is_skipped() {
let out = render(assertion("equals", "summary", number(1)), &["summary"]);
assert_skipped(&out, "summary", "assertEquals");
}
#[test]
fn boolean_on_a_display_as_text_union_still_emits_a_presence_check() {
let out = render(assertion("is_true", "summary", None), &["summary"]);
assert_emitted(&out, "java.util.Optional.ofNullable(result.summary()).isPresent()");
}
#[test]
fn wildcard_containment_on_a_payload_union_leaf_is_skipped() {
let out = render(assertion("contains", "stages[].payload", text("ok")), &[]);
assert_skipped(&out, "stages[].payload", "anyMatch");
}
#[test]
fn wildcard_presence_on_a_payload_union_leaf_is_skipped() {
let out = render(assertion("not_empty", "stages[].payload", None), &[]);
assert_skipped(&out, "stages[].payload", "anyMatch");
}
#[test]
fn wildcard_containment_on_a_string_leaf_still_emits() {
let out = render(assertion("contains", "stages[].label", text("ok")), &[]);
assert_emitted(&out, "result.stages().stream().anyMatch(");
assert!(
out.contains(".label()).contains("),
"the lambda body must address the element's own field, got:\n{out}"
);
}
#[test]
fn wildcard_presence_on_a_string_leaf_still_emits() {
let out = render(assertion("not_empty", "stages[].label", None), &[]);
assert_emitted(&out, "result.stages().stream().anyMatch(");
}