use super::*;
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};
const FORMAT_METADATA_VARIANTS: &[&str] = &[
"pdf",
"docx",
"excel",
"email",
"pptx",
"archive",
"image",
"xml",
"text",
"html",
"ocr",
"csv",
"bibtex",
"citation",
"fiction_book",
"dbf",
"jats",
"epub",
"pst",
"audio",
"code",
];
#[derive(Clone, Copy)]
enum Unwrap {
Panic,
Error,
}
const WILDCARD_MISSING_FIELD_ERROR: &str = "error.WildcardElementFieldMissing";
fn json_path_expr(result_var: &str, field_path: &str, field_resolver: &FieldResolver) -> String {
json_path_expr_with(result_var, field_path, Unwrap::Panic, field_resolver)
}
fn json_path_expr_with(result_var: &str, field_path: &str, unwrap: Unwrap, field_resolver: &FieldResolver) -> String {
let segments: Vec<&str> = field_path.split('.').collect();
let mut expr = result_var.to_string();
let mut prev_seg: Option<&str> = None;
for seg in &segments {
if prev_seg == Some("format") && FORMAT_METADATA_VARIANTS.contains(seg) {
prev_seg = Some(seg);
continue;
}
if let Some(key) = seg.strip_suffix("[]") {
expr = format!("{}.array.items[0]", json_get(&expr, key, unwrap, field_resolver));
} else if let Some(bracket_pos) = seg.find('[') {
if let Some(end_pos) = seg.find(']')
&& end_pos > bracket_pos + 1
&& end_pos == seg.len() - 1
{
let key = &seg[..bracket_pos];
let idx = &seg[bracket_pos + 1..end_pos];
if idx.chars().all(|c| c.is_ascii_digit()) {
expr = format!("{}.array.items[{idx}]", json_get(&expr, key, unwrap, field_resolver));
prev_seg = Some(seg);
continue;
}
expr = json_get(
&json_get(&expr, key, unwrap, field_resolver),
idx,
unwrap,
field_resolver,
);
prev_seg = Some(seg);
continue;
}
expr = json_get(&expr, seg, unwrap, field_resolver);
} else {
expr = json_get(&expr, seg, unwrap, field_resolver);
}
prev_seg = Some(seg);
}
expr
}
fn json_get(expr: &str, key: &str, unwrap: Unwrap, field_resolver: &FieldResolver) -> String {
if field_resolver.is_wire_optional_key(key) {
return format!("({expr}.object.get(\"{key}\") orelse .null)");
}
match unwrap {
Unwrap::Panic => format!("{expr}.object.get(\"{key}\").?"),
Unwrap::Error => format!("({expr}.object.get(\"{key}\") orelse return {WILDCARD_MISSING_FIELD_ERROR})"),
}
}
fn split_wildcard(field_path: &str) -> Option<(&str, &str)> {
let pos = field_path.find("[].")?;
let array_root = &field_path[..pos];
let element_sub_path = &field_path[pos + 3..];
if array_root.is_empty() || element_sub_path.is_empty() {
return None;
}
Some((array_root, element_sub_path))
}
fn render_wildcard_json_assertion(
out: &mut String,
assertion: &Assertion,
result_var: &str,
array_root: &str,
element_sub_path: &str,
is_length_access: bool,
field_resolver: &FieldResolver,
) {
let field_name = assertion.field.as_deref().unwrap_or_default();
if let Some(line) = nested_wildcard_skip_line(" ", "//", field_name, element_sub_path) {
let _ = writeln!(out, "{line}");
return;
}
let element_expr = json_path_expr_with("_wce", element_sub_path, Unwrap::Error, field_resolver);
let body = render_json_assertion_template(assertion, &element_expr, is_length_access, false);
if body.trim().is_empty() {
let atype = &assertion.assertion_type;
let _ = writeln!(
out,
" // skipped: assertion '{atype}' on array-wildcard field '{field_name}' not supported in zig"
);
return;
}
let array_expr = json_path_expr(result_var, array_root, field_resolver);
let _ = writeln!(out, " {{");
let _ = writeln!(out, " const _WildcardCheck = struct {{");
let _ = writeln!(out, " fn check(_wce: std.json.Value) anyerror!void {{");
for line in body.lines() {
if line.trim().is_empty() {
out.push('\n');
} else {
let _ = writeln!(out, " {line}");
}
}
let _ = writeln!(out, " }}");
let _ = writeln!(out, " }};");
let _ = writeln!(out, " var _wc_found = false;");
let _ = writeln!(out, " for ({array_expr}.array.items) |_wc_item| {{");
let _ = writeln!(out, " _WildcardCheck.check(_wc_item) catch continue;");
let _ = writeln!(out, " _wc_found = true;");
let _ = writeln!(out, " break;");
let _ = writeln!(out, " }}");
let _ = writeln!(
out,
" try testing.expect(_wc_found); // no element of '{array_root}' matched"
);
let _ = writeln!(out, " }}");
}
fn emit_zig_chunks_predicate(
out: &mut String,
result_var: &str,
assertion_type: &str,
chunk_field_accessor: &str,
field_name: &str,
require_non_empty_string: bool,
) {
let _ = writeln!(out, " {{");
let _ = writeln!(out, " const _chunks_opt = {result_var}.object.get(\"chunks\");");
let _ = writeln!(out, " var _all: bool = true;");
let _ = writeln!(out, " if (_chunks_opt) |_chunks_val| {{");
let _ = writeln!(out, " if (_chunks_val == .array) {{");
let _ = writeln!(
out,
" if (_chunks_val.array.items.len == 0) _all = false;"
);
let _ = writeln!(out, " for (_chunks_val.array.items) |c| {{");
let _ = writeln!(out, " if (c != .object) {{ _all = false; break; }}");
let _ = writeln!(out, " const _v = {chunk_field_accessor};");
if require_non_empty_string {
let _ = writeln!(
out,
" if (_v == null or _v.? != .string or _v.?.string.len == 0) {{ _all = false; break; }}"
);
} else {
let _ = writeln!(
out,
" if (_v == null or _v.? == .null) {{ _all = false; break; }}"
);
}
let _ = writeln!(out, " }}");
let _ = writeln!(out, " }} else {{ _all = false; }}");
let _ = writeln!(out, " }} else {{ _all = false; }}");
match assertion_type {
"is_true" => {
let _ = writeln!(out, " try testing.expect(_all);");
}
"is_false" => {
let _ = writeln!(out, " try testing.expect(!_all);");
}
_ => {
let _ = writeln!(
out,
" // skipped: unsupported assertion type on synthetic field '{field_name}'"
);
}
}
let _ = writeln!(out, " }}");
}
fn emit_zig_chunks_heading_context_predicate(
out: &mut String,
result_var: &str,
assertion_type: &str,
field_name: &str,
only_first: bool,
) {
let _ = writeln!(out, " {{");
let _ = writeln!(out, " const _chunks_opt = {result_var}.object.get(\"chunks\");");
let _ = writeln!(out, " var _all: bool = true;");
let _ = writeln!(out, " if (_chunks_opt) |_chunks_val| {{");
let _ = writeln!(out, " if (_chunks_val == .array) {{");
let _ = writeln!(
out,
" if (_chunks_val.array.items.len == 0) _all = false;"
);
let _ = writeln!(out, " for (_chunks_val.array.items) |c| {{");
let _ = writeln!(out, " if (c != .object) {{ _all = false; break; }}");
let _ = writeln!(out, " var _has_heading = false;");
let _ = writeln!(out, " if (c.object.get(\"metadata\")) |_meta| {{");
let _ = writeln!(out, " if (_meta == .object) {{");
let _ = writeln!(
out,
" if (_meta.object.get(\"heading_context\")) |_hc| {{"
);
let _ = writeln!(
out,
" if (_hc != .null) {{ _has_heading = true; }}"
);
let _ = writeln!(out, " }}");
let _ = writeln!(out, " }}");
let _ = writeln!(out, " }}");
let _ = writeln!(out, " if (!_has_heading) {{ _all = false; break; }}");
if only_first {
let _ = writeln!(out, " break;");
}
let _ = writeln!(out, " }}");
let _ = writeln!(out, " }} else {{ _all = false; }}");
let _ = writeln!(out, " }} else {{ _all = false; }}");
match assertion_type {
"is_true" => {
let _ = writeln!(out, " try testing.expect(_all);");
}
"is_false" => {
let _ = writeln!(out, " try testing.expect(!_all);");
}
_ => {
let _ = writeln!(
out,
" // skipped: unsupported assertion type on synthetic field '{field_name}'"
);
}
}
let _ = writeln!(out, " }}");
}
pub(super) fn render_json_assertion(
out: &mut String,
assertion: &Assertion,
result_var: &str,
field_resolver: &FieldResolver,
uses_streaming: bool,
) {
if let Some(f) = &assertion.field
&& uses_streaming
&& !f.is_empty()
&& is_streaming_virtual_field(f)
{
if let Some(expr) = StreamingFieldResolver::accessor(f, "zig", "chunks") {
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, " try testing.expect({expr}.len >= {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, " try testing.expectEqual(@as(usize, {n}), {expr}.len);");
} else {
let _ = writeln!(out, "{}", value_skip());
}
}
"equals" => {
if let Some(serde_json::Value::String(s)) = &assertion.value {
let escaped = escape_zig(s);
let _ = writeln!(out, " try testing.expectEqualStrings(\"{escaped}\", {expr});");
} else if let Some(v) = &assertion.value {
let zig_val = json_to_zig(v);
let _ = writeln!(out, " try testing.expectEqual({zig_val}, {expr});");
} else {
let _ = writeln!(out, "{}", value_skip());
}
}
"not_empty" => {
let _ = writeln!(out, " try testing.expect({expr}.len > 0);");
}
"is_true" => {
let _ = writeln!(out, " try testing.expect({expr});");
}
"is_false" => {
let _ = writeln!(out, " try testing.expect(!{expr});");
}
_ => {
let _ = writeln!(
out,
"{}",
streaming_assertion_type_skip_line(" ", "//", f, &assertion.assertion_type)
);
}
}
} else {
let _ = writeln!(
out,
" // skipped: {}",
FieldSkip::StreamingAssertionOnUnsupportedField.message(f)
);
}
return;
}
if let Some(f) = &assertion.field
&& f == "embeddings"
&& !field_resolver.has_explicit_field("embeddings")
{
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, " try testing.expect({result_var}.array.items.len >= {n});");
}
return;
}
"count_equals" => {
if let Some(n) = assertion.value.as_ref().and_then(|v| v.as_u64()) {
let _ = writeln!(
out,
" try testing.expectEqual(@as(usize, {n}), {result_var}.array.items.len);"
);
}
return;
}
"not_empty" => {
let _ = writeln!(out, " try testing.expect({result_var}.array.items.len > 0);");
return;
}
"is_empty" => {
let _ = writeln!(
out,
" try testing.expectEqual(@as(usize, 0), {result_var}.array.items.len);"
);
return;
}
_ => {}
}
}
if let Some(f) = &assertion.field {
match f.as_str() {
"chunks_have_content" => {
emit_zig_chunks_predicate(
out,
result_var,
assertion.assertion_type.as_str(),
"c.object.get(\"content\")",
"chunks_have_content",
true,
);
return;
}
"chunks_have_heading_context" => {
emit_zig_chunks_heading_context_predicate(
out,
result_var,
assertion.assertion_type.as_str(),
"chunks_have_heading_context",
false,
);
return;
}
"first_chunk_starts_with_heading" => {
emit_zig_chunks_heading_context_predicate(
out,
result_var,
assertion.assertion_type.as_str(),
"first_chunk_starts_with_heading",
true,
);
return;
}
"chunks_have_embeddings" => {
emit_zig_chunks_predicate(
out,
result_var,
assertion.assertion_type.as_str(),
"c.object.get(\"embedding\")",
"chunks_have_embeddings",
false,
);
return;
}
"keywords" | "keywords_count" => {
let _ = writeln!(
out,
" // skipped: {}",
FieldSkip::NotAvailableOnJsonStructResult.message(f)
);
return;
}
_ => {}
}
}
if 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 matches!(assertion.assertion_type.as_str(), "not_error" | "error") {
return;
}
let raw_field_path = assertion.field.as_deref().unwrap_or("").trim();
let field_path = if raw_field_path.is_empty() {
raw_field_path.to_string()
} else {
field_resolver.resolve(raw_field_path).to_string()
};
let field_path = field_path.trim();
let (field_path_for_expr, is_length_access) = if let Some(parent) = field_path.strip_suffix(".length") {
(parent, true)
} else {
(field_path, false)
};
if let Some((array_root, element_sub_path)) = split_wildcard(field_path_for_expr) {
render_wildcard_json_assertion(
out,
assertion,
result_var,
array_root,
element_sub_path,
is_length_access,
field_resolver,
);
return;
}
let field_expr = if field_path_for_expr.is_empty() {
result_var.to_string()
} else {
json_path_expr(result_var, field_path_for_expr, field_resolver)
};
let field_is_optional = !field_path_for_expr.is_empty() && field_resolver.is_optional(field_path_for_expr);
if field_path_for_expr == "metadata.format"
&& matches!(
assertion.assertion_type.as_str(),
"equals" | "contains" | "not_empty" | "is_empty" | "starts_with" | "ends_with"
)
{
let base = json_path_expr(result_var, field_path_for_expr, field_resolver);
let _ = writeln!(out, " {{");
let _ = writeln!(out, " const _fmt_obj = {base}.object;");
let _ = writeln!(out, " const _fmt_type = _fmt_obj.get(\"format_type\").?.string;");
let _ = writeln!(
out,
" const _fmt_display: []const u8 = if (std.mem.eql(u8, _fmt_type, \"image\")) _fmt_obj.get(\"format\").?.string else _fmt_type;"
);
match assertion.assertion_type.as_str() {
"equals" => {
if let Some(serde_json::Value::String(s)) = &assertion.value {
let escaped = escape_zig(s);
let _ = writeln!(
out,
" try testing.expectEqualStrings(\"{escaped}\", std.mem.trim(u8, _fmt_display, \" \\n\\r\\t\"));"
);
}
}
"contains" => {
if let Some(serde_json::Value::String(s)) = &assertion.value {
let escaped = escape_zig(s);
let _ = writeln!(
out,
" try testing.expect(std.mem.indexOf(u8, _fmt_display, \"{escaped}\") != null);"
);
}
}
"starts_with" => {
if let Some(serde_json::Value::String(s)) = &assertion.value {
let escaped = escape_zig(s);
let _ = writeln!(
out,
" try testing.expect(std.mem.startsWith(u8, _fmt_display, \"{escaped}\"));"
);
}
}
"ends_with" => {
if let Some(serde_json::Value::String(s)) = &assertion.value {
let escaped = escape_zig(s);
let _ = writeln!(
out,
" try testing.expect(std.mem.endsWith(u8, _fmt_display, \"{escaped}\"));"
);
}
}
"not_empty" => {
let _ = writeln!(out, " try testing.expect(_fmt_display.len > 0);");
}
"is_empty" => {
let _ = writeln!(out, " try testing.expectEqual(@as(usize, 0), _fmt_display.len);");
}
_ => {}
}
let _ = writeln!(out, " }}");
return;
}
out.push_str(&render_json_assertion_template(
assertion,
&field_expr,
is_length_access,
field_is_optional,
));
}
fn render_json_assertion_template(
assertion: &Assertion,
field_expr: &str,
is_length_access: bool,
field_is_optional: bool,
) -> String {
let zig_val = match &assertion.value {
Some(serde_json::Value::String(s)) => format!("\"{}\"", escape_zig(s)),
_ => String::new(),
};
let is_string_val = matches!(&assertion.value, Some(serde_json::Value::String(_)));
let is_bool_val = matches!(&assertion.value, Some(serde_json::Value::Bool(_)));
let bool_val = match &assertion.value {
Some(serde_json::Value::Bool(b)) if *b => "true",
_ => "false",
};
let is_null_val = matches!(&assertion.value, Some(serde_json::Value::Null));
let n = assertion.value.as_ref().map(json_to_zig).unwrap_or_default();
let has_n = assertion.value.as_ref().is_some_and(|v| v.is_number() || v.is_u64());
let is_float_val = matches!(&assertion.value, Some(serde_json::Value::Number(n)) if !n.is_i64() && !n.is_u64());
let n_as_i64 = if has_n {
format!("@as(i64, {})", n)
} else {
String::new()
};
let n_as_usize = if has_n {
if n.starts_with('-') {
format!("@as(i64, {})", n)
} else {
format!("@as(usize, {})", n)
}
} else {
String::new()
};
let n_as_f64 = if is_float_val {
format!("@as(f64, {})", n)
} else {
String::new()
};
let values_list: Vec<String> = assertion
.values
.as_deref()
.unwrap_or_default()
.iter()
.filter_map(|v| {
if let serde_json::Value::String(s) = v {
Some(format!("\"{}\"", escape_zig(s)))
} else {
None
}
})
.collect();
crate::e2e::template_env::render(
"zig/json_assertion.jinja",
minijinja::context! {
assertion_type => assertion.assertion_type.as_str(),
field_expr => field_expr,
field_is_optional => field_is_optional,
is_length_access => is_length_access,
zig_val => zig_val,
is_string_val => is_string_val,
is_bool_val => is_bool_val,
bool_val => bool_val,
is_null_val => is_null_val,
n => n,
n_as_i64 => n_as_i64,
n_as_usize => n_as_usize,
n_as_f64 => n_as_f64,
has_n => has_n,
is_float_val => is_float_val,
values_list => values_list,
},
)
}
pub(super) fn assertion_emits_code(assertion: &Assertion, field_resolver: &FieldResolver) -> bool {
if let Some(f) = &assertion.field {
if !f.is_empty() && is_streaming_virtual_field(f) {
} else if !f.is_empty() && !field_resolver.is_valid_for_result(f) {
return false;
}
}
matches!(
assertion.assertion_type.as_str(),
"equals"
| "contains"
| "contains_all"
| "not_contains"
| "not_empty"
| "is_empty"
| "starts_with"
| "ends_with"
| "min_length"
| "max_length"
| "count_min"
| "count_equals"
| "is_true"
| "is_false"
| "greater_than"
| "less_than"
| "greater_than_or_equal"
| "less_than_or_equal"
| "contains_any"
)
}
pub(super) fn render_assertion(
out: &mut String,
assertion: &Assertion,
result_var: &str,
field_resolver: &FieldResolver,
result_is_option: bool,
result_is_simple: bool,
) {
let bare_result_is_option = result_is_option && assertion.field.as_deref().filter(|f| !f.is_empty()).is_none();
if bare_result_is_option {
match assertion.assertion_type.as_str() {
"is_empty" => {
let _ = writeln!(out, " try testing.expect({result_var} == null);");
return;
}
"not_empty" => {
let _ = writeln!(out, " try testing.expect({result_var} != null);");
return;
}
"not_error" => {
let _ = writeln!(out, " // not_error: covered by try propagation");
return;
}
"equals" => {
if let Some(expected) = &assertion.value {
let zig_val = json_to_zig(expected);
let _ = writeln!(out, " try testing.expectEqualStrings({zig_val}, {result_var}.?);");
return;
}
}
_ => {}
}
}
if let Some(f) = &assertion.field
&& f == "embeddings"
&& !field_resolver.has_explicit_field(f)
{
match assertion.assertion_type.as_str() {
"count_min" | "count_equals" | "not_empty" | "is_empty" => {
let _ = writeln!(out, " {{");
let _ = writeln!(
out,
" var _eparse = try std.json.parseFromSlice(std.json.Value, std.heap.c_allocator, {result_var}, .{{}});"
);
let _ = writeln!(out, " defer _eparse.deinit();");
let _ = writeln!(out, " const _embeddings_len = _eparse.value.array.items.len;");
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, " try testing.expect(_embeddings_len >= {n});");
}
}
"count_equals" => {
if let Some(n) = assertion.value.as_ref().and_then(|v| v.as_u64()) {
let _ = writeln!(
out,
" try testing.expectEqual(@as(usize, {n}), _embeddings_len);"
);
}
}
"not_empty" => {
let _ = writeln!(out, " try testing.expect(_embeddings_len > 0);");
}
"is_empty" => {
let _ = writeln!(out, " try testing.expectEqual(@as(usize, 0), _embeddings_len);");
}
_ => {}
}
let _ = writeln!(out, " }}");
return;
}
_ => {}
}
}
if result_is_simple && let Some(f) = &assertion.field {
let f_lower = f.to_lowercase();
if !f.is_empty()
&& f_lower != "content"
&& (f_lower.starts_with("metadata") || f_lower.starts_with("document") || f_lower.starts_with("structure"))
{
let _ = writeln!(
out,
" // skipped: {}",
FieldSkip::NotAvailableWhenResultIsSimple.message(f)
);
return;
}
}
if let Some(f) = &assertion.field
&& f == "result"
&& !field_resolver.has_explicit_field(f)
{
match assertion.assertion_type.as_str() {
"contains" => {
if let Some(expected) = &assertion.value {
let zig_val = json_to_zig(expected);
let _ = writeln!(
out,
" try testing.expect(std.mem.indexOf(u8, {result_var}, {zig_val}) != null);"
);
return;
}
}
"not_contains" => {
if let Some(expected) = &assertion.value {
let zig_val = json_to_zig(expected);
let _ = writeln!(
out,
" try testing.expect(std.mem.indexOf(u8, {result_var}, {zig_val}) == null);"
);
return;
}
}
"equals" => {
if let Some(expected) = &assertion.value {
let zig_val = json_to_zig(expected);
let _ = writeln!(out, " try testing.expectEqualStrings({zig_val}, {result_var});");
return;
}
}
"not_empty" => {
let _ = writeln!(out, " try testing.expect({result_var}.len > 0);");
return;
}
"is_empty" => {
let _ = writeln!(out, " try testing.expectEqual(@as(usize, 0), {result_var}.len);");
return;
}
_ => {}
}
}
if let Some(f) = &assertion.field
&& !f.is_empty()
&& !field_resolver.is_valid_for_result(f)
{
let _ = writeln!(
out,
" // skipped: {}",
FieldSkip::NotAvailableOnResultType.message(f)
);
return;
}
let field_is_enum = assertion
.field
.as_deref()
.filter(|f| !f.is_empty())
.is_some_and(|f| field_resolver.is_enum(f));
if field_is_enum && assertion.assertion_type == "equals" {
let f = assertion.field.as_deref().unwrap_or("");
let _ = writeln!(
out,
" // skipped: {}",
FieldSkip::EnumEqualsNotSupportedOnZigTypedResult.message(f)
);
return;
}
let field_expr = match &assertion.field {
_ if result_is_simple => result_var.to_string(),
Some(f) if !f.is_empty() => field_resolver.accessor(f, "zig", result_var),
_ => result_var.to_string(),
};
match assertion.assertion_type.as_str() {
"equals" => {
if let Some(expected) = &assertion.value {
let zig_val = json_to_zig(expected);
let _ = writeln!(out, " try testing.expectEqual({zig_val}, {field_expr});");
}
}
"contains" => {
if let Some(expected) = &assertion.value {
let zig_val = json_to_zig(expected);
let _ = writeln!(
out,
" try testing.expect(std.mem.indexOf(u8, {field_expr}, {zig_val}) != null);"
);
}
}
"contains_all" => {
if let Some(values) = &assertion.values {
for val in values {
let zig_val = json_to_zig(val);
let _ = writeln!(
out,
" try testing.expect(std.mem.indexOf(u8, {field_expr}, {zig_val}) != null);"
);
}
}
}
"not_contains" => {
if let Some(expected) = &assertion.value {
let zig_val = json_to_zig(expected);
let _ = writeln!(
out,
" try testing.expect(std.mem.indexOf(u8, {field_expr}, {zig_val}) == null);"
);
} else if let Some(values) = &assertion.values {
for val in values {
let zig_val = json_to_zig(val);
let _ = writeln!(
out,
" try testing.expect(std.mem.indexOf(u8, {field_expr}, {zig_val}) == null);"
);
}
}
}
"not_empty" => {
let _ = writeln!(out, " try testing.expect({field_expr}.len > 0);");
}
"is_empty" => {
let _ = writeln!(out, " try testing.expect({field_expr}.len == 0);");
}
"starts_with" => {
if let Some(expected) = &assertion.value {
let zig_val = json_to_zig(expected);
let _ = writeln!(
out,
" try testing.expect(std.mem.startsWith(u8, {field_expr}, {zig_val}));"
);
}
}
"ends_with" => {
if let Some(expected) = &assertion.value {
let zig_val = json_to_zig(expected);
let _ = writeln!(
out,
" try testing.expect(std.mem.endsWith(u8, {field_expr}, {zig_val}));"
);
}
}
"min_length" => {
if let Some(val) = &assertion.value
&& let Some(n) = val.as_u64()
{
let _ = writeln!(out, " try testing.expect({field_expr}.len >= {n});");
}
}
"max_length" => {
if let Some(val) = &assertion.value
&& let Some(n) = val.as_u64()
{
let _ = writeln!(out, " try testing.expect({field_expr}.len <= {n});");
}
}
"count_min" => {
if let Some(val) = &assertion.value
&& let Some(n) = val.as_u64()
{
let _ = writeln!(out, " try testing.expect({field_expr}.len >= {n});");
}
}
"count_equals" => {
if let Some(val) = &assertion.value
&& let Some(n) = val.as_u64()
{
let has_field = assertion.field.as_deref().is_some_and(|f| !f.is_empty());
if has_field {
let _ = writeln!(out, " try testing.expectEqual(@as(usize, {n}), {field_expr}.len);");
} else {
let _ = writeln!(out, " {{");
let _ = writeln!(
out,
" var _cparse = try std.json.parseFromSlice(std.json.Value, std.heap.c_allocator, {field_expr}, .{{}});"
);
let _ = writeln!(out, " defer _cparse.deinit();");
let _ = writeln!(
out,
" try testing.expectEqual(@as(usize, {n}), _cparse.value.array.items.len);"
);
let _ = writeln!(out, " }}");
}
}
}
"is_true" => {
if let Some(optional_expr) = field_expr.strip_suffix(".?") {
let _ = writeln!(out, " try testing.expect({optional_expr} != null);");
} else {
let _ = writeln!(out, " try testing.expect({field_expr});");
}
}
"is_false" => {
if let Some(optional_expr) = field_expr.strip_suffix(".?") {
let _ = writeln!(out, " try testing.expect({optional_expr} == null);");
} else {
let _ = writeln!(out, " try testing.expect(!{field_expr});");
}
}
"not_error" => {
}
"error" => {
}
"greater_than" => {
if let Some(val) = &assertion.value {
let is_negative = matches!(val, serde_json::Value::Number(n) if n.as_i64().is_some_and(|i| i < 0));
if !is_negative {
let zig_val = json_to_zig(val);
let _ = writeln!(out, " try testing.expect({field_expr} > {zig_val});");
}
}
}
"less_than" => {
if let Some(val) = &assertion.value {
let zig_val = json_to_zig(val);
let _ = writeln!(out, " try testing.expect({field_expr} < {zig_val});");
}
}
"greater_than_or_equal" => {
if let Some(val) = &assertion.value {
let is_negative = matches!(val, serde_json::Value::Number(n) if n.as_i64().is_some_and(|i| i < 0));
if !is_negative {
let zig_val = json_to_zig(val);
let _ = writeln!(out, " try testing.expect({field_expr} >= {zig_val});");
}
}
}
"less_than_or_equal" => {
if let Some(val) = &assertion.value {
let zig_val = json_to_zig(val);
let _ = writeln!(out, " try testing.expect({field_expr} <= {zig_val});");
}
}
"contains_any" => {
if let Some(values) = &assertion.values {
let string_values: Vec<String> = values
.iter()
.filter_map(|v| {
if let serde_json::Value::String(s) = v {
Some(format!(
"std.mem.indexOf(u8, {field_expr}, \"{}\") != null",
escape_zig(s)
))
} else {
None
}
})
.collect();
if !string_values.is_empty() {
let condition = string_values.join(" or\n ");
let _ = writeln!(out, " try testing.expect(\n {condition}\n );");
}
}
}
"matches_regex" => {
let _ = writeln!(out, " // regex match not yet implemented for Zig");
}
"method_result" => {
let _ = writeln!(out, " // method_result assertions not yet implemented for Zig");
}
other => {
panic!("Zig e2e generator: unsupported assertion type: {other}");
}
}
}
pub(super) fn json_to_zig(value: &serde_json::Value) -> String {
match value {
serde_json::Value::String(s) => format!("\"{}\"", escape_zig(s)),
serde_json::Value::Bool(b) => b.to_string(),
serde_json::Value::Number(n) => n.to_string(),
serde_json::Value::Null => "null".to_string(),
serde_json::Value::Array(arr) => {
let items: Vec<String> = arr.iter().map(json_to_zig).collect();
format!("&.{{{}}}", items.join(", "))
}
serde_json::Value::Object(_) => {
let json_str = serde_json::to_string(value).unwrap_or_default();
format!("\"{}\"", escape_zig(&json_str))
}
}
}
#[cfg(test)]
mod wildcard_tests {
use super::*;
use std::collections::HashMap;
fn resolver() -> FieldResolver {
FieldResolver::new(
&HashMap::new(),
&HashSet::new(),
&HashSet::new(),
&HashSet::new(),
&HashSet::new(),
)
}
fn assertion(assertion_type: &str, field: &str, value: serde_json::Value) -> Assertion {
Assertion {
assertion_type: assertion_type.into(),
field: Some(field.into()),
value: Some(value),
..Assertion::default()
}
}
fn render(assertion: &Assertion) -> String {
let mut out = String::new();
render_json_assertion(&mut out, assertion, "result", &resolver(), false);
out
}
#[test]
fn wildcard_field_should_emit_a_loop_over_every_element() {
let rendered = render(&assertion("contains", "links[].url", serde_json::json!("example.com")));
assert!(
rendered.contains("for (result.object.get(\"links\").?.array.items) |_wc_item|"),
"expected a loop over every element, got:\n{rendered}"
);
assert!(
rendered.contains("try testing.expect(_wc_found);"),
"expected the any-element flag to be asserted, got:\n{rendered}"
);
}
#[test]
fn wildcard_field_should_not_pin_the_assertion_to_element_zero() {
let rendered = render(&assertion("contains", "links[].url", serde_json::json!("example.com")));
assert!(
!rendered.contains("items[0]"),
"wildcard must not lower to element 0, got:\n{rendered}"
);
assert!(
rendered.contains("catch continue;"),
"a non-matching element must advance the loop, not fail the test, got:\n{rendered}"
);
}
#[test]
fn wildcard_element_accessor_should_error_instead_of_panicking_on_a_missing_key() {
let rendered = render(&assertion("contains", "links[].url", serde_json::json!("example.com")));
assert!(
rendered.contains("orelse return error.WildcardElementFieldMissing"),
"an element without the key must be skipped, not abort the test, got:\n{rendered}"
);
}
#[test]
fn explicit_numeric_index_should_still_lower_to_that_index() {
let rendered = render(&assertion(
"contains",
"results[0].url",
serde_json::json!("example.com"),
));
assert!(
rendered.contains("result.object.get(\"results\").?.array.items[0].object.get(\"url\").?"),
"explicit index accessor changed, got:\n{rendered}"
);
assert!(
!rendered.contains("_wc_found"),
"explicit index must not take the wildcard path, got:\n{rendered}"
);
}
#[test]
fn plain_field_path_should_be_unchanged() {
let rendered = render(&assertion("equals", "metadata.title", serde_json::json!("Hello")));
assert!(
rendered.contains("result.object.get(\"metadata\").?.object.get(\"title\").?.string"),
"plain accessor changed, got:\n{rendered}"
);
assert!(!rendered.contains("_wc_found"), "got:\n{rendered}");
}
#[test]
fn nested_wildcard_should_emit_a_visible_skip_rather_than_a_wrong_check() {
let rendered = render(&assertion(
"contains",
"pages[].links[].url",
serde_json::json!("example.com"),
));
assert!(
rendered.contains("// skipped: nested array-wildcard field 'pages[].links[].url'"),
"expected a visible skip, got:\n{rendered}"
);
assert!(!rendered.contains("items[0]"), "got:\n{rendered}");
assert!(!rendered.contains("testing.expect"), "got:\n{rendered}");
}
#[test]
fn wildcard_length_assertion_should_measure_the_element_array() {
let rendered = render(&assertion("count_min", "links[].tags.length", serde_json::json!(2)));
assert!(
rendered.contains("_wce.object.get(\"tags\")") || rendered.contains("(_wce.object.get(\"tags\")"),
"element accessor must be rooted at the loop element, got:\n{rendered}"
);
assert!(rendered.contains("_wc_found"), "got:\n{rendered}");
}
#[test]
fn split_wildcard_should_ignore_a_trailing_bracket_with_no_element_field() {
assert_eq!(split_wildcard("links[].url"), Some(("links", "url")));
assert_eq!(split_wildcard("links[]"), None);
assert_eq!(split_wildcard("results[0].url"), None);
assert_eq!(split_wildcard("metadata.title"), None);
}
}
#[cfg(test)]
mod chunks_heading_context_tests {
use super::*;
use std::collections::HashMap;
fn resolver() -> FieldResolver {
FieldResolver::new(
&HashMap::new(),
&HashSet::new(),
&HashSet::new(),
&HashSet::new(),
&HashSet::new(),
)
}
fn render(field: &str, assertion_type: &str) -> String {
let assertion = Assertion {
assertion_type: assertion_type.into(),
field: Some(field.into()),
..Assertion::default()
};
let mut out = String::new();
render_json_assertion(&mut out, &assertion, "result", &resolver(), false);
out
}
#[test]
fn chunks_have_heading_context_reads_the_real_field_not_a_content_proxy() {
let rendered = render("chunks_have_heading_context", "is_true");
assert!(
!rendered.contains("skipped"),
"must not skip a reachable field, got:\n{rendered}"
);
assert!(
rendered.contains("\"metadata\"") && rendered.contains("\"heading_context\""),
"must read the real metadata.heading_context field, got:\n{rendered}"
);
assert!(
!rendered.contains("\"content\""),
"must not fall back to a content-shape proxy, got:\n{rendered}"
);
assert!(
rendered.contains("try testing.expect(_all);"),
"is_true must assert the aggregate flag, got:\n{rendered}"
);
}
#[test]
fn chunks_have_heading_context_is_false_negates_the_aggregate_flag() {
let rendered = render("chunks_have_heading_context", "is_false");
assert!(rendered.contains("try testing.expect(!_all);"), "got:\n{rendered}");
}
#[test]
fn chunks_have_heading_context_defaults_to_false_absent_explicit_proof() {
let rendered = render("chunks_have_heading_context", "is_true");
assert_eq!(
rendered.matches("_has_heading = true").count(),
1,
"exactly one line may prove heading context present, got:\n{rendered}"
);
assert!(
rendered.contains("var _has_heading = false;"),
"must default to false, got:\n{rendered}"
);
}
#[test]
fn first_chunk_starts_with_heading_only_inspects_the_first_chunk() {
let rendered = render("first_chunk_starts_with_heading", "is_true");
assert!(
!rendered.contains("skipped"),
"must not skip a reachable field, got:\n{rendered}"
);
assert!(
rendered.contains("\"heading_context\""),
"must read the real field, got:\n{rendered}"
);
assert!(
!rendered.contains("startsWith") && !rendered.contains("\"#\""),
"must not fall back to a markdown-heading content-prefix proxy, got:\n{rendered}"
);
assert_eq!(
rendered.matches("break;").count(),
3,
"expected exactly one extra unconditional break restricting the loop to element 0, got:\n{rendered}"
);
}
#[test]
fn chunks_have_heading_context_inspects_every_chunk_not_only_the_first() {
let rendered = render("chunks_have_heading_context", "is_true");
assert_eq!(
rendered.matches("break;").count(),
2,
"must not carry the element-0-only break, got:\n{rendered}"
);
}
}