use std::collections::{HashMap, HashSet};
use super::assertions::render_assertion;
use crate::e2e::field_access::FieldResolver;
use crate::e2e::fixture::Assertion;
fn collection_resolver(field: &str) -> FieldResolver {
FieldResolver::new(
&HashMap::new(),
&HashSet::new(),
&HashSet::new(),
&HashSet::from([field.to_string()]),
&HashSet::new(),
)
}
fn render_contains(field: &str, expected: &str) -> String {
let assertion = Assertion {
assertion_type: "contains".to_string(),
field: Some(field.to_string()),
value: Some(serde_json::json!(expected)),
..Default::default()
};
let resolver = collection_resolver(field);
let mut out = String::new();
render_assertion(
&mut out,
&assertion,
"result",
"sample",
"sample",
false,
&[],
&resolver,
false,
false,
false,
false,
false,
None,
);
out
}
fn collection_item_matches(item: &serde_json::Value, expected: &str) -> bool {
match item {
serde_json::Value::String(text) => text.contains(expected),
serde_json::Value::Object(fields) => {
["kind", "name", "source", "alias", "text", "signature"]
.iter()
.any(|key| {
fields
.get(*key)
.and_then(serde_json::Value::as_str)
.is_some_and(|text| text.contains(expected))
})
|| item.to_string().contains(expected)
}
_ => false,
}
}
#[test]
fn collection_contains_matches_the_intended_substring_semantics() {
let function_item = serde_json::json!({"kind": "Function", "name": "main"});
let cases: [(&str, &serde_json::Value, &str, bool); 4] = [
("match on a non-name key ('kind')", &function_item, "Function", true),
("match on the 'name' key", &function_item, "main", true),
("substring match, not whole-value equality", &function_item, "unc", true),
("genuine non-match fails", &function_item, "Class", false),
];
for (description, item, expected, want) in cases {
let got = collection_item_matches(item, expected);
assert_eq!(got, want, "case '{description}': item={item}, expected={expected}");
}
}
#[test]
fn generated_predicate_checks_every_key_with_substring_not_equality() {
let rendered = render_contains("structure", "Function");
assert!(
rendered.contains("fields.get(*key)"),
"generated predicate must look up each key via the shared `fields.get(*key)` lookup, got: {rendered}"
);
for key in ["kind", "name", "source", "alias", "text", "signature"] {
assert!(
rendered.contains(&format!("\"{key}\"")),
"generated predicate must list the '{key}' key in its key set, got: {rendered}"
);
}
assert!(
rendered.contains(".contains(r#\"Function\"#)") || rendered.contains(".contains(\"Function\")"),
"generated predicate must use substring `.contains`, got: {rendered}"
);
assert!(
!rendered.contains("== r#\"Function\"#") && !rendered.contains("== \"Function\""),
"generated predicate must not use whole-value equality, got: {rendered}"
);
}
#[test]
fn bug_report_fixture_shape_emits_parseable_rust() {
let body = render_contains("structure", "Function");
let unit = format!("fn generated() {{\n{body}}}\n");
syn::parse_file(&unit).unwrap_or_else(|error| panic!("must emit parseable Rust: {error}\n{unit}"));
}