use std::collections::{HashMap, HashSet};
use super::assertion_streaming::try_render_streaming_virtual_field_assertion;
use crate::e2e::field_access::FieldResolver;
use crate::e2e::fixture::Assertion;
fn optional_resolver(field: &str, array_fields: &[&str]) -> FieldResolver {
FieldResolver::new(
&HashMap::new(),
&HashSet::from([field.to_string()]),
&HashSet::new(),
&array_fields
.iter()
.map(ToString::to_string)
.collect::<HashSet<String>>(),
&HashSet::new(),
)
}
fn render(field: &str, assertion_type: &str, array_fields: &[&str]) -> String {
let assertion = Assertion {
assertion_type: assertion_type.to_string(),
field: Some(field.to_string()),
value: Some(serde_json::json!(1)),
..Default::default()
};
let resolver = optional_resolver(field, array_fields);
let mut out = String::new();
let handled = try_render_streaming_virtual_field_assertion(&mut out, &assertion, "sample_dep", &resolver, None);
assert!(
handled,
"'{field}' must be handled as a streaming-virtual field, got: {out}"
);
out
}
#[test]
fn should_not_append_as_ref_to_nested_flat_map_tool_calls_chain_when_field_is_declared_optional() {
let out = render("tool_calls", "not_empty", &[]);
assert!(
out.contains("flat_map") && out.contains("collect::<Vec<_>>()"),
"expected the flattened tool_calls chain, got: {out}"
);
assert!(
!out.contains("collect::<Vec<_>>().as_ref()"),
"`Vec<T>` implements `AsRef` for more than one target, so a bare `.as_ref()` on the \
collected chain is an unannotatable `AsRef<T>` call (E0282) in the generated test; \
got: {out}"
);
assert!(
out.contains("!chunks.iter().flat_map"),
"the collected chain is a `Vec`, so emptiness must be checked with `!expr.is_empty()`; \
got: {out}"
);
}
#[test]
fn should_not_append_as_ref_to_collected_tool_calls_chain_for_count_min() {
let out = render("tool_calls", "count_min", &[]);
assert!(
!out.contains(".as_ref().map_or(0, |v| v.len())"),
"the collected chain is a `Vec`, so its length is `.len()`, not an Option projection; \
got: {out}"
);
assert!(
out.contains("collect::<Vec<_>>().len()"),
"expected `.collect::<Vec<_>>().len()`, got: {out}"
);
}
#[test]
fn should_not_append_as_ref_to_unwrap_or_default_finish_reason_string() {
let out = render("finish_reason", "not_empty", &[]);
assert!(
!out.contains(".unwrap_or_default().as_ref()"),
"`String` implements `AsRef` for several targets, so appending `.as_ref()` to the \
finish_reason accessor is unannotatable (E0282); got: {out}"
);
assert!(
out.contains("!chunks.last()") && out.contains(".unwrap_or_default().is_empty()"),
"expected a direct `!expr.is_empty()` on the `String` accessor, got: {out}"
);
}
#[test]
fn should_still_append_as_ref_when_accessor_passes_the_declared_optional_local_through() {
let out = render("chunks", "not_empty", &["chunks"]);
assert!(
out.contains("chunks.as_ref().is_some_and(|v| !v.is_empty())"),
"the passthrough accessor keeps the declared `Option` wrapper, so the borrow still needs \
`.as_ref()` before `is_some_and`; got: {out}"
);
}
#[test]
fn should_still_append_as_ref_on_passthrough_local_for_is_empty() {
let out = render("chunks", "is_empty", &["chunks"]);
assert!(
out.contains("chunks.as_ref().is_none_or(|v| v.is_empty())"),
"the passthrough accessor keeps the declared `Option` wrapper; got: {out}"
);
}