use car_eventlog::tool_receipts::{verify_tool_claims_windowed, ToolClaim, ToolReceipt};
pub fn verify(
claims_json: &str,
receipts_json: &str,
window_complete: bool,
) -> Result<String, String> {
let claims: Vec<ToolClaim> = crate::from_json("claims", claims_json)?;
let receipts: Vec<ToolReceipt> = crate::from_json("receipts", receipts_json)?;
let report = verify_tool_claims_windowed(&claims, &receipts, window_complete);
crate::to_json(&report)
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::Value;
#[test]
fn fabricated_round_trips() {
let claims = r#"[{"kind":"invoked","tool":"search"}]"#;
let out = verify(claims, "[]", true).unwrap();
let v: Value = serde_json::from_str(&out).unwrap();
assert_eq!(v["grounded"], false);
assert_eq!(v["hallucinations"][0]["kind"], "fabricated_tool_reference");
}
#[test]
fn count_misstatement_round_trips() {
let claims = r#"[{"kind":"count","tool":"search","count":12}]"#;
let receipts = r#"[{"tool":"search","ok":true,"result_count":3}]"#;
let out = verify(claims, receipts, true).unwrap();
let v: Value = serde_json::from_str(&out).unwrap();
assert_eq!(v["hallucinations"][0]["kind"], "count_misstatement");
}
#[test]
fn grounded_round_trips() {
let claims = r#"[{"kind":"absence","tool":"search"}]"#;
let receipts = r#"[{"tool":"search","ok":true,"result_count":0}]"#;
let out = verify(claims, receipts, true).unwrap();
let v: Value = serde_json::from_str(&out).unwrap();
assert_eq!(v["grounded"], true);
}
#[test]
fn evicted_window_round_trips_ungroundable() {
let claims = r#"[{"kind":"invoked","tool":"search"}]"#;
let out = verify(claims, "[]", false).unwrap();
let v: Value = serde_json::from_str(&out).unwrap();
assert_eq!(v["grounded"], true);
assert!(v["hallucinations"].as_array().unwrap().is_empty());
assert_eq!(v["ungroundable"][0]["tool"], "search");
}
#[test]
fn invalid_json_errors() {
assert!(verify("nope", "[]", true).is_err());
}
}