1use anyhow::{Context, Result};
2use askama::Template;
3
4use super::AgentContext;
5use crate::db::ReviewFinding;
6
7#[derive(Template)]
8#[template(path = "fixer.txt")]
9struct FixerPrompt<'a> {
10 ctx: &'a AgentContext,
11 findings: &'a [ReviewFinding],
12}
13
14pub fn build_prompt(ctx: &AgentContext, findings: &[ReviewFinding]) -> Result<String> {
15 let tmpl = FixerPrompt { ctx, findings };
16 tmpl.render().context("rendering fixer template")
17}
18
19#[cfg(test)]
20mod tests {
21 use super::*;
22 use crate::agents::AgentContext;
23
24 fn sample_context() -> AgentContext {
25 AgentContext {
26 issue_number: 42,
27 issue_title: "Fix bug".to_string(),
28 issue_body: "details".to_string(),
29 branch: "oven/issue-42-abc".to_string(),
30 pr_number: None,
31 test_command: Some("cargo test".to_string()),
32 lint_command: None,
33 review_findings: None,
34 cycle: 1,
35 target_repo: None,
36 issue_source: "github".to_string(),
37 base_branch: "main".to_string(),
38 }
39 }
40
41 fn sample_findings() -> Vec<ReviewFinding> {
42 vec![ReviewFinding {
43 id: 1,
44 agent_run_id: 1,
45 severity: "critical".to_string(),
46 category: "bug".to_string(),
47 file_path: Some("src/main.rs".to_string()),
48 line_number: Some(10),
49 message: "null pointer".to_string(),
50 resolved: false,
51 dispute_reason: None,
52 }]
53 }
54
55 #[test]
56 fn prompt_includes_findings() {
57 let prompt = build_prompt(&sample_context(), &sample_findings()).unwrap();
58 assert!(prompt.contains("fixer agent"));
59 assert!(prompt.contains("[critical] bug"));
60 assert!(prompt.contains("src/main.rs:10"));
61 assert!(prompt.contains("null pointer"));
62 assert!(prompt.contains("cargo test"));
63 assert!(prompt.contains("<review_findings>"));
64 }
65
66 #[test]
67 fn prompt_includes_scope_discipline() {
68 let prompt = build_prompt(&sample_context(), &sample_findings()).unwrap();
69 assert!(prompt.contains("Scope Discipline"));
70 assert!(prompt.contains("Do NOT refactor code that wasn't flagged"));
71 }
72
73 #[test]
74 fn prompt_includes_verification_section() {
75 let prompt = build_prompt(&sample_context(), &sample_findings()).unwrap();
76 assert!(prompt.contains("Verification"));
77 assert!(prompt.contains("git diff main --stat"));
78 }
79
80 #[test]
81 fn prompt_includes_skip_guidance() {
82 let prompt = build_prompt(&sample_context(), &sample_findings()).unwrap();
83 assert!(prompt.contains("Handling Unclear Findings"));
84 assert!(prompt.contains("Skip it"));
85 }
86}