Skip to main content

oven_cli/agents/
fixer.rs

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}