pub mod cover;
pub mod matchers;
pub mod model;
use crate::errors::diagnostic::Diagnostic;
use crate::storage::Store;
pub struct EpisodeGraph {
pub episode_id: String,
pub steps: Vec<crate::storage::rows::StepRow>,
pub tool_calls: Vec<crate::storage::rows::ToolCallRow>,
}
pub struct AssertionOutcome {
pub diagnostics: Vec<Diagnostic>,
pub not_exercised: Vec<cover::AssertionCover>,
}
pub fn verify_assertions(
store: &Store,
run_id: i64,
test_id: &str,
assertions: &[model::TraceAssertion],
) -> anyhow::Result<Vec<Diagnostic>> {
verify_assertions_with_meta(store, run_id, test_id, assertions, &serde_json::Value::Null)
.map(|o| o.diagnostics)
}
pub fn verify_assertions_with_meta(
store: &Store,
run_id: i64,
test_id: &str,
assertions: &[model::TraceAssertion],
meta: &serde_json::Value,
) -> anyhow::Result<AssertionOutcome> {
let finish = |graph: &EpisodeGraph| -> anyhow::Result<AssertionOutcome> {
let tools = cover::ToolAvailability::observe(meta, graph);
Ok(AssertionOutcome {
diagnostics: matchers::evaluate(graph, assertions)?,
not_exercised: cover::evaluate_cover(graph, &tools, assertions),
})
};
let graph_res = store.get_episode_graph(run_id, test_id);
match graph_res {
Ok(graph) => finish(&graph),
Err(e) => {
#[expect(
clippy::wildcard_enum_match_arm,
reason = "an assertion kind with no test-input field cannot be a unit test; a new kind that carries one must be named above or its unit-test form is not recognised"
)]
let is_unit_test = assertions.iter().all(|a| match a {
model::TraceAssertion::ArgsValid { test_args, .. } => test_args.is_some(),
model::TraceAssertion::SequenceValid {
test_trace,
test_trace_raw,
..
} => test_trace.is_some() || test_trace_raw.is_some(),
model::TraceAssertion::ToolBlocklist {
test_tool_calls, ..
} => test_tool_calls.is_some(),
_ => false,
});
if is_unit_test {
let dummy = EpisodeGraph {
episode_id: "unit_test_mock".into(),
steps: vec![],
tool_calls: vec![],
};
return finish(&dummy);
}
if e.to_string().contains("E_TRACE_EPISODE_MISSING") {
match store.get_latest_episode_graph_by_test_id(test_id) {
Ok(latest_graph) => return finish(&latest_graph),
Err(fallback_err) => {
return Err(anyhow::anyhow!("E_TRACE_EPISODE_MISSING: Primary query failed ({}), Fallback failed: {}", e, fallback_err));
}
}
}
Err(e)
}
}
}