use crate::e2e::codegen::assertion_type_skip::{
streaming_assertion_type_skip_line, streaming_assertion_value_skip_line,
};
use crate::e2e::codegen::field_skip::{FieldSkip, nested_wildcard_skip_line};
use crate::e2e::escape::escape_elixir;
use crate::e2e::field_access::FieldResolver;
use crate::e2e::fixture::Assertion;
use std::collections::{HashMap, HashSet};
use std::fmt::Write as FmtWrite;
use super::values::json_to_elixir;
pub(super) fn is_numeric_expr(field_expr: &str) -> bool {
field_expr.starts_with("length(")
}
#[allow(clippy::too_many_arguments)]
pub(super) fn render_assertion(
out: &mut String,
assertion: &Assertion,
result_var: &str,
field_resolver: &FieldResolver,
module_path: &str,
fields_enum: &HashSet<String>,
per_call_enum_fields: &HashMap<String, String>,
result_is_simple: bool,
is_streaming: bool,
) {
if let Some(f) = &assertion.field {
match f.as_str() {
"chunks_have_content" => {
let pred =
format!("Enum.all?({result_var}.chunks || [], fn c -> c.content != nil and c.content != \"\" end)");
match assertion.assertion_type.as_str() {
"is_true" => {
let _ = writeln!(out, " assert {pred}");
}
"is_false" => {
let _ = writeln!(out, " refute {pred}");
}
other => {
panic!("Elixir e2e generator: unsupported assertion type '{other}' on synthetic field '{f}'");
}
}
return;
}
"chunks_have_embeddings" => {
let pred = format!(
"Enum.all?({result_var}.chunks || [], fn c -> c.embedding != nil and c.embedding != [] end)"
);
match assertion.assertion_type.as_str() {
"is_true" => {
let _ = writeln!(out, " assert {pred}");
}
"is_false" => {
let _ = writeln!(out, " refute {pred}");
}
other => {
panic!("Elixir e2e generator: unsupported assertion type '{other}' on synthetic field '{f}'");
}
}
return;
}
"chunks_have_heading_context" => {
let pred = format!(
"Enum.all?({result_var}.chunks || [], fn c -> c.metadata != nil and c.metadata.heading_context != nil end)"
);
match assertion.assertion_type.as_str() {
"is_true" => {
let _ = writeln!(out, " assert {pred}");
}
"is_false" => {
let _ = writeln!(out, " refute {pred}");
}
other => {
panic!("Elixir e2e generator: unsupported assertion type '{other}' on synthetic field '{f}'");
}
}
return;
}
"first_chunk_starts_with_heading" => {
let expr = format!(
"case List.first({result_var}.chunks || []) do
c when is_map(c) -> c.metadata != nil and c.metadata.heading_context != nil
_ -> false
end"
);
match assertion.assertion_type.as_str() {
"is_true" => {
let _ = writeln!(out, " assert ({expr})");
}
"is_false" => {
let _ = writeln!(out, " refute ({expr})");
}
other => {
panic!("Elixir e2e generator: unsupported assertion type '{other}' on synthetic field '{f}'");
}
}
return;
}
"embeddings" => {
match assertion.assertion_type.as_str() {
"count_equals" => {
if let Some(val) = &assertion.value {
let ex_val = json_to_elixir(val);
let _ = writeln!(out, " assert length({result_var}) == {ex_val}");
}
}
"count_min" => {
if let Some(val) = &assertion.value {
let ex_val = json_to_elixir(val);
let _ = writeln!(out, " assert length({result_var}) >= {ex_val}");
}
}
"not_empty" => {
let _ = writeln!(out, " assert {result_var} != []");
}
"is_empty" => {
let _ = writeln!(out, " assert {result_var} == []");
}
other => {
panic!(
"Elixir e2e generator: unsupported assertion type '{other}' on synthetic field 'embeddings'"
);
}
}
return;
}
"embedding_dimensions" => {
let expr = format!("(if {result_var} == [], do: 0, else: length(hd({result_var})))");
match assertion.assertion_type.as_str() {
"equals" => {
if let Some(val) = &assertion.value {
let ex_val = json_to_elixir(val);
let _ = writeln!(out, " assert {expr} == {ex_val}");
}
}
"greater_than" => {
if let Some(val) = &assertion.value {
let ex_val = json_to_elixir(val);
let _ = writeln!(out, " assert {expr} > {ex_val}");
}
}
other => {
panic!(
"Elixir e2e generator: unsupported assertion type '{other}' on synthetic field 'embedding_dimensions'"
);
}
}
return;
}
"embeddings_valid" | "embeddings_finite" | "embeddings_non_zero" | "embeddings_normalized" => {
let pred = match f.as_str() {
"embeddings_valid" => {
format!("Enum.all?({result_var}, fn e -> e != [] end)")
}
"embeddings_finite" => {
format!("Enum.all?({result_var}, fn e -> Enum.all?(e, fn v -> is_float(v) and v == v end) end)")
}
"embeddings_non_zero" => {
format!("Enum.all?({result_var}, fn e -> Enum.any?(e, fn v -> v != 0.0 end) end)")
}
"embeddings_normalized" => {
format!(
"Enum.all?({result_var}, fn e -> n = Enum.reduce(e, 0.0, fn v, acc -> acc + v * v end); abs(n - 1.0) < 1.0e-3 end)"
)
}
_ => unreachable!(),
};
match assertion.assertion_type.as_str() {
"is_true" => {
let _ = writeln!(out, " assert {pred}");
}
"is_false" => {
let _ = writeln!(out, " refute {pred}");
}
other => {
panic!("Elixir e2e generator: unsupported assertion type '{other}' on synthetic field '{f}'");
}
}
return;
}
"keywords" | "keywords_count" => {
let _ = writeln!(
out,
" # skipped: {}",
FieldSkip::NotAvailableOnElixirResultType.message(f)
);
return;
}
_ => {}
}
}
if is_streaming
&& let Some(f) = &assertion.field
&& !f.is_empty()
&& crate::e2e::codegen::streaming_assertions::is_streaming_virtual_field(f)
{
if let Some(expr) =
crate::e2e::codegen::streaming_assertions::StreamingFieldResolver::accessor(f, "elixir", result_var)
{
let value_skip = || streaming_assertion_value_skip_line(" ", "#", f, &assertion.assertion_type);
match assertion.assertion_type.as_str() {
"count_min" => {
if let Some(n) = assertion.value.as_ref().and_then(|v| v.as_u64()) {
let _ = writeln!(out, " assert length({expr}) >= {n}");
} else {
let _ = writeln!(out, "{}", value_skip());
}
}
"count_equals" => {
if let Some(n) = assertion.value.as_ref().and_then(|v| v.as_u64()) {
let _ = writeln!(out, " assert length({expr}) == {n}");
} else {
let _ = writeln!(out, "{}", value_skip());
}
}
"equals" => {
if let Some(serde_json::Value::String(s)) = &assertion.value {
let escaped = escape_elixir(s);
let _ = writeln!(out, " assert {expr} == \"{escaped}\"");
} else if let Some(n) = assertion.value.as_ref().and_then(|v| v.as_u64()) {
let _ = writeln!(out, " assert {expr} == {n}");
} else {
let _ = writeln!(out, "{}", value_skip());
}
}
"not_empty" => {
let _ = writeln!(out, " assert {expr} not in [nil, \"\", [], %{{}}]");
}
"is_empty" => {
let _ = writeln!(out, " assert {expr} == []");
}
"is_true" => {
let _ = writeln!(out, " assert {expr}");
}
"is_false" => {
let _ = writeln!(out, " refute {expr}");
}
"greater_than" => {
if let Some(n) = assertion.value.as_ref().and_then(|v| v.as_u64()) {
let _ = writeln!(out, " assert {expr} > {n}");
} else {
let _ = writeln!(out, "{}", value_skip());
}
}
"greater_than_or_equal" => {
if let Some(n) = assertion.value.as_ref().and_then(|v| v.as_u64()) {
let _ = writeln!(out, " assert {expr} >= {n}");
} else {
let _ = writeln!(out, "{}", value_skip());
}
}
"contains" => {
if let Some(serde_json::Value::String(s)) = &assertion.value {
let escaped = escape_elixir(s);
let _ = writeln!(out, " assert String.contains?({expr}, \"{escaped}\")");
} else {
let _ = writeln!(out, "{}", value_skip());
}
}
_ => {
let _ = writeln!(
out,
"{}",
streaming_assertion_type_skip_line(" ", "#", f, &assertion.assertion_type)
);
}
}
} else {
let _ = writeln!(
out,
" # skipped: {}",
FieldSkip::StreamingAssertionOnUnsupportedField.message(f)
);
}
return;
}
if !result_is_simple
&& let Some(f) = &assertion.field
&& !f.is_empty()
&& !field_resolver.is_valid_for_result(f)
{
let _ = writeln!(
out,
" # skipped: {}",
FieldSkip::NotAvailableOnResultType.message(f)
);
return;
}
if !result_is_simple
&& let Some(f) = assertion.field.as_deref()
&& let Some((array_part, elem_part)) = field_resolver.wildcard_split(f)
{
if let Some(line) = nested_wildcard_skip_line(" ", "#", f, &elem_part) {
let _ = writeln!(out, "{line}");
return;
}
let array_accessor = if array_part.is_empty() {
result_var.to_string()
} else {
field_resolver.accessor(&array_part, "elixir", result_var)
};
let elem_accessor = if elem_part.is_empty() {
"e".to_string()
} else {
field_resolver.accessor(&elem_part, "elixir", "e")
};
render_wildcard_assertion(out, assertion, &array_accessor, &elem_accessor, f);
return;
}
let field_expr = if result_is_simple {
result_var.to_string()
} else {
match &assertion.field {
Some(f) if !f.is_empty() => field_resolver.accessor(f, "elixir", result_var),
_ => result_var.to_string(),
}
};
let is_numeric = is_numeric_expr(&field_expr);
let field_is_enum = assertion.field.as_deref().filter(|f| !f.is_empty()).is_some_and(|f| {
let resolved = field_resolver.resolve(f);
fields_enum.contains(f)
|| fields_enum.contains(resolved)
|| per_call_enum_fields.contains_key(f)
|| per_call_enum_fields.contains_key(resolved)
});
let field_is_format_metadata = assertion
.field
.as_deref()
.filter(|f| !f.is_empty())
.is_some_and(|f| f == "metadata.format" || f.ends_with(".metadata.format"));
let field_is_display_as_text = assertion
.field
.as_deref()
.filter(|f| !f.is_empty())
.is_some_and(|f| field_resolver.is_display_as_text(f));
let coerced_field_expr = if field_is_format_metadata {
format!("alef_e2e_format_to_string({field_expr})")
} else if field_is_display_as_text {
format!("(({field_expr} && {field_expr}.text) || \"\")")
} else if field_is_enum {
format!("to_string({field_expr})")
} else {
field_expr.clone()
};
let field_is_array = assertion
.field
.as_deref()
.filter(|f| !f.is_empty())
.is_some_and(|f| field_resolver.is_array(field_resolver.resolve(f)));
match assertion.assertion_type.as_str() {
"equals" => {
if let Some(expected) = &assertion.value {
let elixir_val = json_to_elixir(expected);
let is_string_expected = expected.is_string();
if is_string_expected && !is_numeric {
let _ = writeln!(out, " assert {coerced_field_expr} == {elixir_val}");
} else if field_is_enum {
let _ = writeln!(out, " assert {coerced_field_expr} == {elixir_val}");
} else {
let _ = writeln!(out, " assert {field_expr} == {elixir_val}");
}
}
}
"contains" => {
if let Some(expected) = &assertion.value {
let elixir_val = json_to_elixir(expected);
if field_is_array && expected.is_string() {
let _ = writeln!(
out,
" assert Enum.any?({field_expr}, fn item -> Enum.any?(alef_e2e_item_texts(item), &String.contains?(&1, {elixir_val})) end)"
);
} else {
let _ = writeln!(
out,
" assert String.contains?(to_string({field_expr}), {elixir_val})"
);
}
}
}
"contains_all" => {
if let Some(values) = &assertion.values {
for val in values {
let elixir_val = json_to_elixir(val);
if field_is_array && val.is_string() {
let _ = writeln!(
out,
" assert Enum.any?({field_expr}, fn item -> Enum.any?(alef_e2e_item_texts(item), &String.contains?(&1, {elixir_val})) end)"
);
} else {
let _ = writeln!(
out,
" assert String.contains?(to_string({field_expr}), {elixir_val})"
);
}
}
}
}
"not_contains" => {
for expected in assertion.expected_values() {
let elixir_val = json_to_elixir(expected);
if field_is_array && expected.is_string() {
let _ = writeln!(
out,
" refute Enum.any?({field_expr}, fn item -> Enum.any?(alef_e2e_item_texts(item), &String.contains?(&1, {elixir_val})) end)"
);
} else {
let _ = writeln!(
out,
" refute String.contains?(to_string({field_expr}), {elixir_val})"
);
}
}
}
"not_empty" => {
let _ = writeln!(out, " assert {field_expr} not in [nil, \"\", [], %{{}}]");
}
"is_empty" => {
if is_numeric {
let _ = writeln!(out, " assert {field_expr} == 0");
} else {
let _ = writeln!(out, " assert is_nil({field_expr}) or {coerced_field_expr} == \"\"");
}
}
"contains_any" => {
if let Some(values) = &assertion.values {
let items: Vec<String> = values.iter().map(json_to_elixir).collect();
let list_str = items.join(", ");
let _ = writeln!(
out,
" assert Enum.any?([{list_str}], fn v -> String.contains?(to_string({field_expr}), v) end)"
);
}
}
"greater_than" => {
if let Some(val) = &assertion.value {
let elixir_val = json_to_elixir(val);
let _ = writeln!(out, " assert {field_expr} > {elixir_val}");
}
}
"less_than" => {
if let Some(val) = &assertion.value {
let elixir_val = json_to_elixir(val);
let _ = writeln!(out, " assert {field_expr} < {elixir_val}");
}
}
"greater_than_or_equal" => {
if let Some(val) = &assertion.value {
let elixir_val = json_to_elixir(val);
let _ = writeln!(out, " assert {field_expr} >= {elixir_val}");
}
}
"less_than_or_equal" => {
if let Some(val) = &assertion.value {
let elixir_val = json_to_elixir(val);
let _ = writeln!(out, " assert {field_expr} <= {elixir_val}");
}
}
"starts_with" => {
if let Some(expected) = &assertion.value {
let elixir_val = json_to_elixir(expected);
let _ = writeln!(out, " assert String.starts_with?({field_expr}, {elixir_val})");
}
}
"ends_with" => {
if let Some(expected) = &assertion.value {
let elixir_val = json_to_elixir(expected);
let _ = writeln!(out, " assert String.ends_with?({field_expr}, {elixir_val})");
}
}
"min_length" => {
if let Some(val) = &assertion.value
&& let Some(n) = val.as_u64()
{
let _ = writeln!(
out,
" assert (is_binary({field_expr}) && byte_size({field_expr}) >= {n}) || (is_list({field_expr}) && length({field_expr}) >= {n})"
);
}
}
"max_length" => {
if let Some(val) = &assertion.value
&& let Some(n) = val.as_u64()
{
let _ = writeln!(
out,
" assert (is_binary({field_expr}) && byte_size({field_expr}) <= {n}) || (is_list({field_expr}) && length({field_expr}) <= {n})"
);
}
}
"count_min" => {
if let Some(val) = &assertion.value
&& let Some(n) = val.as_u64()
{
let _ = writeln!(out, " assert length({field_expr}) >= {n}");
}
}
"count_equals" => {
if let Some(val) = &assertion.value
&& let Some(n) = val.as_u64()
{
let _ = writeln!(out, " assert length({field_expr}) == {n}");
}
}
"is_true" => {
let field_is_optional = assertion
.field
.as_ref()
.is_some_and(|f| !f.is_empty() && field_resolver.is_optional(f));
if field_is_optional {
let _ = writeln!(out, " refute is_nil({field_expr})");
} else {
let _ = writeln!(out, " assert {field_expr} == true");
}
}
"is_false" => {
let field_is_optional = assertion
.field
.as_ref()
.is_some_and(|f| !f.is_empty() && field_resolver.is_optional(f));
if field_is_optional {
let _ = writeln!(out, " assert is_nil({field_expr})");
} else {
let _ = writeln!(out, " assert {field_expr} == false");
}
}
"method_result" => {
if let Some(method_name) = &assertion.method {
let call_expr = build_elixir_method_call(result_var, method_name, assertion.args.as_ref(), module_path);
let check = assertion.check.as_deref().unwrap_or("is_true");
match check {
"equals" => {
if let Some(val) = &assertion.value {
let elixir_val = json_to_elixir(val);
let _ = writeln!(out, " assert {call_expr} == {elixir_val}");
}
}
"is_true" => {
let _ = writeln!(out, " assert {call_expr} == true");
}
"is_false" => {
let _ = writeln!(out, " assert {call_expr} == false");
}
"greater_than_or_equal" => {
if let Some(val) = &assertion.value {
let n = val.as_u64().unwrap_or(0);
let _ = writeln!(out, " assert {call_expr} >= {n}");
}
}
"count_min" => {
if let Some(val) = &assertion.value {
let n = val.as_u64().unwrap_or(0);
let _ = writeln!(out, " assert length({call_expr}) >= {n}");
}
}
"contains" => {
if let Some(val) = &assertion.value {
let elixir_val = json_to_elixir(val);
let _ = writeln!(out, " assert String.contains?({call_expr}, {elixir_val})");
}
}
"is_error" => {
let _ = writeln!(out, " assert_raise RuntimeError, fn -> {call_expr} end");
}
other_check => {
panic!("Elixir e2e generator: unsupported method_result check type: {other_check}");
}
}
} else {
panic!("Elixir e2e generator: method_result assertion missing 'method' field");
}
}
"matches_regex" => {
if let Some(expected) = &assertion.value {
let elixir_val = json_to_elixir(expected);
let _ = writeln!(out, " assert Regex.match?(~r/{elixir_val}/, {field_expr})");
}
}
"not_error" => {
let _ = writeln!(out, " refute is_nil({result_var})");
}
"error" => {}
other => {
panic!("Elixir e2e generator: unsupported assertion type: {other}");
}
}
}
pub(super) fn build_elixir_method_call(
result_var: &str,
method_name: &str,
args: Option<&serde_json::Value>,
module_path: &str,
) -> String {
match method_name {
"root_child_count" => format!("{module_path}.root_child_count({result_var})"),
"has_error_nodes" => format!("{module_path}.tree_has_error_nodes({result_var})"),
"error_count" | "tree_error_count" => format!("{module_path}.tree_error_count({result_var})"),
"tree_to_sexp" => format!("{module_path}.tree_to_sexp({result_var})"),
"contains_node_type" => {
let node_type = args
.and_then(|a| a.get("node_type"))
.and_then(|v| v.as_str())
.unwrap_or("");
format!("{module_path}.tree_contains_node_type({result_var}, \"{node_type}\")")
}
"find_nodes_by_type" => {
let node_type = args
.and_then(|a| a.get("node_type"))
.and_then(|v| v.as_str())
.unwrap_or("");
format!("{module_path}.find_nodes_by_type({result_var}, \"{node_type}\")")
}
"run_query" => {
let query_source = args
.and_then(|a| a.get("query_source"))
.and_then(|v| v.as_str())
.unwrap_or("");
let language = args
.and_then(|a| a.get("language"))
.and_then(|v| v.as_str())
.unwrap_or("");
format!("{module_path}.run_query({result_var}, \"{language}\", \"{query_source}\", source)")
}
_ => format!("{module_path}.{method_name}({result_var})"),
}
}
fn render_wildcard_assertion(
out: &mut String,
assertion: &Assertion,
array_accessor: &str,
elem_accessor: &str,
field: &str,
) {
let guarded = format!("({array_accessor} || [])");
let any_expr = |elixir_val: &str| {
format!("Enum.any?({guarded}, fn e -> String.contains?(to_string({elem_accessor}), {elixir_val}) end)")
};
match assertion.assertion_type.as_str() {
"contains" => {
if let Some(val) = &assertion.value {
let elixir_val = json_to_elixir(val);
let _ = writeln!(out, " assert {}", any_expr(&elixir_val));
}
}
"contains_all" => {
if let Some(values) = &assertion.values {
for val in values {
let elixir_val = json_to_elixir(val);
let _ = writeln!(out, " assert {}", any_expr(&elixir_val));
}
}
}
"not_contains" => {
for val in assertion.expected_values() {
let elixir_val = json_to_elixir(val);
let _ = writeln!(out, " refute {}", any_expr(&elixir_val));
}
}
"not_empty" => {
let _ = writeln!(
out,
" assert Enum.any?({guarded}, fn e -> to_string({elem_accessor}) != \"\" end)"
);
}
other => {
let _ = writeln!(
out,
" # skipped: unsupported traversal assertion '{other}' on '{field}'"
);
}
}
}
#[cfg(test)]
mod tests {
use crate::e2e::field_access::FieldResolver;
use std::collections::{HashMap, HashSet};
use super::render_assertion;
use crate::e2e::codegen::assertion_type_skip::{AssertionTypeSkip, streaming_assertion_type_skip_line};
use crate::e2e::fixture::Assertion;
fn empty_resolver() -> FieldResolver {
FieldResolver::new(
&HashMap::new(),
&HashSet::new(),
&HashSet::new(),
&HashSet::new(),
&HashSet::new(),
)
}
#[test]
fn elixir_ir_reachable_field_absent_from_result_fields_is_not_skipped() {
let reachable: HashSet<String> = ["data".to_string()].into_iter().collect();
let resolver = FieldResolver::new(
&HashMap::new(),
&HashSet::new(),
&HashSet::new(),
&HashSet::new(),
&HashSet::new(),
)
.with_ir_fields(reachable, HashSet::new(), HashSet::new());
let assertion = Assertion {
assertion_type: "equals".to_string(),
field: Some("data".to_string()),
value: Some(serde_json::Value::String("hello".to_string())),
..Default::default()
};
let mut out = String::new();
render_assertion(
&mut out,
&assertion,
"result",
&resolver,
"Sample",
&HashSet::new(),
&HashMap::new(),
false,
false,
);
assert!(!out.contains("skipped"), "got: {out}");
}
#[test]
fn first_chunk_starts_with_heading_asserts_the_real_field_not_a_content_proxy() {
let assertion = Assertion {
assertion_type: "is_true".to_string(),
field: Some("first_chunk_starts_with_heading".to_string()),
..Default::default()
};
let mut out = String::new();
render_assertion(
&mut out,
&assertion,
"result",
&empty_resolver(),
"Sample",
&HashSet::new(),
&HashMap::new(),
false,
false,
);
assert!(
out.contains("c.metadata.heading_context != nil"),
"must read the real field, got: {out}"
);
assert!(
!out.contains("starts_with?") && !out.contains("\"#\""),
"must not fall back to a content-prefix proxy, got: {out}"
);
}
#[test]
fn elixir_ir_excluded_field_present_in_result_fields_is_still_skipped() {
let result_fields: HashSet<String> = ["internal_diagnostics".to_string()].into_iter().collect();
let excluded: HashSet<String> = ["internal_diagnostics".to_string()].into_iter().collect();
let resolver = FieldResolver::new(
&HashMap::new(),
&HashSet::new(),
&result_fields,
&HashSet::new(),
&HashSet::new(),
)
.with_ir_fields(HashSet::new(), excluded, HashSet::new());
let assertion = Assertion {
assertion_type: "equals".to_string(),
field: Some("internal_diagnostics".to_string()),
value: Some(serde_json::Value::String("hello".to_string())),
..Default::default()
};
let mut out = String::new();
render_assertion(
&mut out,
&assertion,
"result",
&resolver,
"Sample",
&HashSet::new(),
&HashMap::new(),
false,
false,
);
assert!(out.contains("skipped"), "got: {out}");
}
#[test]
fn render_assertion_equals_string_compares_exactly_without_trim() {
let resolver = empty_resolver();
let assertion = Assertion {
assertion_type: "equals".to_string(),
field: None,
value: Some(serde_json::Value::String("hello\n".into())),
..Default::default()
};
let mut out = String::new();
render_assertion(
&mut out,
&assertion,
"result",
&resolver,
"Sample",
&HashSet::new(),
&HashMap::new(),
true,
false,
);
assert!(
!out.contains("String.trim("),
"equals must not trim either side; got: {out}"
);
assert!(out.contains("assert result =="), "got: {out}");
}
#[test]
fn render_assertion_is_empty_does_not_trim_actual_value() {
let resolver = empty_resolver();
let assertion = Assertion {
assertion_type: "is_empty".to_string(),
field: None,
value: None,
..Default::default()
};
let mut out = String::new();
render_assertion(
&mut out,
&assertion,
"result",
&resolver,
"Sample",
&HashSet::new(),
&HashMap::new(),
true,
false,
);
assert!(
!out.contains("String.trim("),
"is_empty must not trim the actual value; got: {out}"
);
assert_eq!(
out, " assert is_nil(result) or result == \"\"\n",
"emitted is_empty check drifted: {out}"
);
}
#[test]
fn render_assertion_equals_still_discriminates_trailing_whitespace() {
let render_for = |value: &str| {
let resolver = empty_resolver();
let assertion = Assertion {
assertion_type: "equals".to_string(),
field: None,
value: Some(serde_json::Value::String(value.into())),
..Default::default()
};
let mut out = String::new();
render_assertion(
&mut out,
&assertion,
"result",
&resolver,
"Sample",
&HashSet::new(),
&HashMap::new(),
true,
false,
);
out
};
let emitted = render_for("hello\n");
assert_eq!(
emitted, " assert result == \"hello\\n\"\n",
"emitted assertion drifted: {emitted}"
);
assert_ne!(
emitted,
render_for("hello"),
"trailing newline must still change the emitted assertion"
);
}
fn render_not_empty(field: Option<&str>, is_streaming: bool) -> String {
let assertion = Assertion {
assertion_type: "not_empty".to_string(),
field: field.map(str::to_string),
..Default::default()
};
let mut out = String::new();
render_assertion(
&mut out,
&assertion,
"result",
&empty_resolver(),
"Sample",
&HashSet::new(),
&HashMap::new(),
false,
is_streaming,
);
out
}
#[test]
fn not_empty_rejects_every_empty_shape_for_values_and_streams() {
assert_eq!(
render_not_empty(None, false).trim(),
"assert result not in [nil, \"\", [], %{}]"
);
assert_eq!(
render_not_empty(Some("chunks"), true).trim(),
"assert result not in [nil, \"\", [], %{}]"
);
}
#[test]
fn display_as_text_field_accessor_emits_text_property_access() {
let mut display_fields = HashSet::new();
display_fields.insert("content".to_string());
let resolver = FieldResolver::new(
&HashMap::new(),
&HashSet::new(),
&HashSet::new(),
&HashSet::new(),
&HashSet::new(),
)
.with_display_as_text_fields(display_fields);
assert!(
resolver.is_display_as_text("content"),
"resolver should recognize 'content' as display_as_text"
);
let field_expr = "result.content";
let field_is_display_as_text = resolver.is_display_as_text("content");
assert!(
field_is_display_as_text,
"field_is_display_as_text should be true for 'content'"
);
let coerced = if field_is_display_as_text {
format!("(({field_expr} && {field_expr}.text) || \"\")")
} else {
field_expr.to_string()
};
assert_eq!(
coerced, "((result.content && result.content.text) || \"\")",
"display_as_text field should emit nil-guarded .text accessor"
);
}
#[test]
fn non_display_as_text_field_accessor_uses_bare_expression() {
let resolver = FieldResolver::new(
&HashMap::new(),
&HashSet::new(),
&HashSet::new(),
&HashSet::new(),
&HashSet::new(),
)
.with_display_as_text_fields(HashSet::new());
let field_expr = "result.content";
let field_is_display_as_text = resolver.is_display_as_text("content");
assert!(
!field_is_display_as_text,
"field_is_display_as_text should be false when not in display_as_text set"
);
let coerced = if field_is_display_as_text {
format!("(({field_expr} && {field_expr}.text) || \"\")")
} else {
field_expr.to_string()
};
assert_eq!(
coerced, "result.content",
"non-display_as_text field should use bare expression"
);
}
#[test]
fn not_error_emits_a_real_refute_is_nil_and_consumes_the_binding() {
let resolver = empty_resolver();
let assertion = Assertion {
assertion_type: "not_error".to_string(),
field: None,
value: None,
..Default::default()
};
let mut out = String::new();
render_assertion(
&mut out,
&assertion,
"result",
&resolver,
"Sample",
&HashSet::new(),
&HashMap::new(),
false,
false,
);
assert_eq!(out, " refute is_nil(result)\n");
}
#[test]
fn not_error_asserts_on_whatever_variable_the_caller_passes() {
let resolver = empty_resolver();
let assertion = Assertion {
assertion_type: "not_error".to_string(),
field: None,
value: None,
..Default::default()
};
let mut out = String::new();
render_assertion(
&mut out,
&assertion,
"chunks",
&resolver,
"Sample",
&HashSet::new(),
&HashMap::new(),
false,
true,
);
assert_eq!(out, " refute is_nil(chunks)\n");
}
#[test]
#[should_panic(expected = "unsupported assertion type 'bogus_type' on synthetic field 'chunks_have_content'")]
fn elixir_synthetic_field_unsupported_type_fails_loudly() {
let resolver = empty_resolver();
let assertion = Assertion {
assertion_type: "bogus_type".to_string(),
field: Some("chunks_have_content".to_string()),
..Default::default()
};
let mut out = String::new();
render_assertion(
&mut out,
&assertion,
"result",
&resolver,
"Sample",
&HashSet::new(),
&HashMap::new(),
false,
false,
);
}
#[test]
fn elixir_synthetic_chunks_have_content_supported_type_renders_assertion() {
let resolver = empty_resolver();
let assertion = Assertion {
assertion_type: "is_true".to_string(),
field: Some("chunks_have_content".to_string()),
..Default::default()
};
let mut out = String::new();
render_assertion(
&mut out,
&assertion,
"result",
&resolver,
"Sample",
&HashSet::new(),
&HashMap::new(),
false,
false,
);
assert_eq!(
out,
" assert Enum.all?(result.chunks || [], fn c -> c.content != nil and c.content != \"\" end)\n"
);
}
#[test]
#[should_panic(expected = "unsupported assertion type 'bogus_type' on synthetic field 'embeddings'")]
fn elixir_synthetic_embeddings_unsupported_type_fails_loudly() {
let resolver = empty_resolver();
let assertion = Assertion {
assertion_type: "bogus_type".to_string(),
field: Some("embeddings".to_string()),
..Default::default()
};
let mut out = String::new();
render_assertion(
&mut out,
&assertion,
"result",
&resolver,
"Sample",
&HashSet::new(),
&HashMap::new(),
false,
false,
);
}
#[test]
fn elixir_synthetic_embeddings_supported_type_renders_assertion() {
let resolver = empty_resolver();
let assertion = Assertion {
assertion_type: "not_empty".to_string(),
field: Some("embeddings".to_string()),
..Default::default()
};
let mut out = String::new();
render_assertion(
&mut out,
&assertion,
"result",
&resolver,
"Sample",
&HashSet::new(),
&HashMap::new(),
false,
false,
);
assert_eq!(out, " assert result != []\n");
}
#[test]
fn elixir_streaming_virtual_field_unsupported_type_is_counted_not_fatal() {
let resolver = empty_resolver();
let assertion = Assertion {
assertion_type: "bogus_type".to_string(),
field: Some("chunks".to_string()),
..Default::default()
};
let mut out = String::new();
render_assertion(
&mut out,
&assertion,
"result",
&resolver,
"Sample",
&HashSet::new(),
&HashMap::new(),
false,
true,
);
assert_eq!(
out.trim_end(),
streaming_assertion_type_skip_line(" ", "#", "chunks", "bogus_type"),
"the gap must be recorded on the registered wording, not dropped: {out}"
);
assert_eq!(
AssertionTypeSkip::extract_classified(out.trim_end()),
Some(("bogus_type", AssertionTypeSkip::StreamingAssertionTypeNotSupported)),
"an emitted marker the type funnel cannot recognise is uncounted, which is the \
silent drop wearing a comment: {out}"
);
}
#[test]
fn elixir_streaming_virtual_field_supported_type_renders_assertion() {
let resolver = empty_resolver();
let assertion = Assertion {
assertion_type: "count_min".to_string(),
field: Some("chunks".to_string()),
value: Some(serde_json::Value::from(1)),
..Default::default()
};
let mut out = String::new();
render_assertion(
&mut out,
&assertion,
"result",
&resolver,
"Sample",
&HashSet::new(),
&HashMap::new(),
false,
true,
);
assert_eq!(out, " assert length(result) >= 1\n");
}
}
#[cfg(test)]
mod wildcard_tests {
use super::render_assertion;
use crate::e2e::field_access::FieldResolver;
use crate::e2e::fixture::Assertion;
use std::collections::{HashMap, HashSet};
fn array_resolver(field: &str) -> FieldResolver {
let result_fields: HashSet<String> = [field.to_string()].into_iter().collect();
let array_fields: HashSet<String> = [field.to_string()].into_iter().collect();
FieldResolver::new(
&HashMap::new(),
&HashSet::new(),
&result_fields,
&array_fields,
&HashSet::new(),
)
}
fn render(assertion: &Assertion, resolver: &FieldResolver) -> String {
let mut out = String::new();
render_assertion(
&mut out,
assertion,
"result",
resolver,
"Sample",
&HashSet::new(),
&HashMap::new(),
false,
false,
);
out
}
fn contains_on(field: &str) -> Assertion {
Assertion {
assertion_type: "contains".to_string(),
field: Some(field.to_string()),
value: Some(serde_json::Value::String("beta".to_string())),
..Default::default()
}
}
#[test]
fn elixir_wildcard_contains_quantifies_over_every_element() {
let out = render(&contains_on("items[].name"), &array_resolver("items"));
assert_eq!(
out,
" assert Enum.any?((result.items || []), fn e -> String.contains?(to_string(e.name), \"beta\") end)\n",
"got: {out}"
);
}
#[test]
fn elixir_explicit_index_still_lowers_to_enum_at() {
let out = render(&contains_on("items[0].name"), &array_resolver("items"));
assert!(out.contains("Enum.at(result.items, 0).name"), "got: {out}");
assert!(!out.contains("Enum.any?"), "got: {out}");
}
#[test]
fn elixir_wildcard_match_in_a_non_first_element_is_not_pinned_to_element_zero() {
let mut assertion = contains_on("items[].name");
assertion.value = Some(serde_json::Value::String("only-in-element-1".to_string()));
let out = render(&assertion, &array_resolver("items"));
assert!(!out.contains("Enum.at("), "index-pinned lookup survived: {out}");
assert!(out.contains("Enum.any?((result.items || [])"), "got: {out}");
assert!(out.contains("to_string(e.name)"), "got: {out}");
}
#[test]
fn nested_wildcard_should_emit_a_visible_skip_rather_than_an_index_zero_check() {
let out = render(&contains_on("pages[].links[].url"), &array_resolver("pages"));
assert_eq!(
out, " # skipped: nested array-wildcard field 'pages[].links[].url' not supported\n",
"got: {out}"
);
}
}
#[cfg(test)]
mod skip_marker_tests {
use super::render_assertion;
use crate::e2e::codegen::assertion_type_skip::AssertionTypeSkip;
use crate::e2e::codegen::field_skip::FieldSkip;
use crate::e2e::codegen::{SkipVerdict, fail_on_unavailable_field_markers, take_skip_records};
use crate::e2e::field_access::FieldResolver;
use crate::e2e::fixture::Assertion;
use std::collections::{HashMap, HashSet};
fn render_streaming(assertion_type: &str, field: &str, value: Option<serde_json::Value>) -> String {
let resolver = FieldResolver::new(
&HashMap::new(),
&HashSet::new(),
&HashSet::new(),
&HashSet::new(),
&HashSet::new(),
);
let assertion = Assertion {
assertion_type: assertion_type.to_string(),
field: Some(field.to_string()),
value,
..Assertion::default()
};
let mut out = String::new();
render_assertion(
&mut out,
&assertion,
"result",
&resolver,
"Sample",
&HashSet::new(),
&HashMap::new(),
false,
true,
);
out
}
fn field_verdicts(body: &str) -> Vec<SkipVerdict> {
let _ = take_skip_records();
fail_on_unavailable_field_markers(body, "elixir", "stream_smoke", &[]);
take_skip_records().into_iter().map(|record| record.verdict).collect()
}
#[test]
fn the_streaming_harness_renders_a_real_assertion_when_the_accessor_resolves() {
let out = render_streaming("count_min", "chunks", Some(serde_json::json!(2)));
assert!(out.contains("assert length("), "got: {out}");
assert!(
field_verdicts(&out).is_empty(),
"a live assertion records no skip: {out}"
);
}
#[test]
fn a_streaming_field_with_no_accessor_emits_a_counted_hash_comment() {
let out = render_streaming("is_true", "stream.has_page_event", None);
assert!(out.trim_start().starts_with('#'), "elixir comments start with #: {out}");
assert_eq!(
FieldSkip::extract_classified(out.trim_end()),
Some(("stream.has_page_event", FieldSkip::StreamingAssertionOnUnsupportedField)),
"got: {out}"
);
assert_eq!(field_verdicts(&out), vec![SkipVerdict::AwaitingGeneratorSupport]);
}
#[test]
fn an_unrenderable_streaming_assertion_type_is_counted_rather_than_panicking() {
let out = render_streaming("matches_regex", "chunks", None);
assert!(out.trim_start().starts_with('#'), "got: {out}");
assert_eq!(
AssertionTypeSkip::extract_classified(out.trim_end()),
Some(("matches_regex", AssertionTypeSkip::StreamingAssertionTypeNotSupported)),
"got: {out}"
);
}
}