use proserpina::{
AgentId, EchoAgent, InteractionGraph, Message, MessageKind, Persona, Report, Runner, Severity,
Subject, Topology, Transcript,
};
#[test]
fn severity_is_exhaustive_with_four_levels() {
let levels = [
Severity::Info,
Severity::Minor,
Severity::Major,
Severity::Blocker,
];
assert_eq!(levels.len(), 4);
assert!(Severity::Blocker > Severity::Major);
assert!(Severity::Major > Severity::Minor);
assert!(Severity::Minor > Severity::Info);
}
#[test]
fn report_synthesizes_one_finding_per_critique() {
let graph = InteractionGraph::from(Topology::parallel(vec![
AgentId::new("critic-a"),
AgentId::new("critic-b"),
]));
let mut runner = Runner::new(graph)
.with_agent(EchoAgent::new(
AgentId::new("critic-a"),
Persona::new("Critic A"),
))
.with_agent(EchoAgent::new(
AgentId::new("critic-b"),
Persona::new("Critic B"),
));
let transcript = runner
.execute(&Subject::from_markdown(
"# Plan\n\nDo the thing.",
"plan.md",
))
.expect("echo run never fails");
let report = Report::from_transcript(&transcript);
assert_eq!(report.findings().len(), 2);
let authors: Vec<&str> = report
.findings()
.iter()
.map(|f| f.author().map(|a| a.as_str()).unwrap_or(""))
.collect();
assert_eq!(authors, vec!["critic-a", "critic-b"]);
for finding in report.findings() {
assert_eq!(finding.summary(), "# Plan\n\nDo the thing.");
assert_eq!(finding.severity(), &Severity::Major);
}
}
#[test]
fn report_from_manual_transcript_skips_non_critiques() {
let mut transcript = Transcript::new();
transcript.push(Message::new(
AgentId::new("system"),
None,
MessageKind::Prompt,
"prompt that must not become a finding",
));
transcript.push(Message::new(
AgentId::new("critic-a"),
None,
MessageKind::Critique,
"a real finding",
));
transcript.push(Message::new(
AgentId::new("critic-b"),
None,
MessageKind::Question,
"a question, not a finding",
));
let report = Report::from_transcript(&transcript);
assert_eq!(report.findings().len(), 1);
assert_eq!(
report.findings()[0].author().map(|a| a.as_str()),
Some("critic-a")
);
assert_eq!(report.findings()[0].summary(), "a real finding");
}
#[test]
fn report_renders_to_markdown_with_title_and_per_finding_blocks() {
use proserpina::Finding;
let mut report = Report::new();
report.push_finding(
Finding::new(Severity::Blocker, "Assumptions unsupported.")
.with_supporting_critics(vec![AgentId::new("critic-a")]),
);
report.push_finding(
Finding::new(Severity::Info, "Worth a footnote.")
.with_supporting_critics(vec![AgentId::new("critic-b")]),
);
let md = report.to_markdown();
assert!(md.starts_with("# Critique Report"));
assert!(md.contains("1. [blocker] Assumptions unsupported."));
assert!(md.contains("2. [info] Worth a footnote."));
assert!(md.contains("critic-a"));
assert!(md.contains("critic-b"));
}
#[test]
fn empty_report_renders_no_findings_message() {
let report = Report::new();
let md = report.to_markdown();
assert!(md.contains("# Critique Report"));
assert!(md.contains("No findings."));
}