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        }
38    }
39
40    fn sample_findings() -> Vec<ReviewFinding> {
41        vec![ReviewFinding {
42            id: 1,
43            agent_run_id: 1,
44            severity: "critical".to_string(),
45            category: "bug".to_string(),
46            file_path: Some("src/main.rs".to_string()),
47            line_number: Some(10),
48            message: "null pointer".to_string(),
49            resolved: false,
50        }]
51    }
52
53    #[test]
54    fn prompt_includes_findings() {
55        let prompt = build_prompt(&sample_context(), &sample_findings()).unwrap();
56        assert!(prompt.contains("fixer agent"));
57        assert!(prompt.contains("[critical] bug"));
58        assert!(prompt.contains("src/main.rs:10"));
59        assert!(prompt.contains("null pointer"));
60        assert!(prompt.contains("cargo test"));
61        assert!(prompt.contains("<review_findings>"));
62    }
63
64    #[test]
65    fn prompt_includes_scope_discipline() {
66        let prompt = build_prompt(&sample_context(), &sample_findings()).unwrap();
67        assert!(prompt.contains("Scope Discipline"));
68        assert!(prompt.contains("Do NOT refactor code that wasn't flagged"));
69    }
70
71    #[test]
72    fn prompt_includes_verification_section() {
73        let prompt = build_prompt(&sample_context(), &sample_findings()).unwrap();
74        assert!(prompt.contains("Verification"));
75        assert!(prompt.contains("git diff main --stat"));
76    }
77
78    #[test]
79    fn prompt_includes_skip_guidance() {
80        let prompt = build_prompt(&sample_context(), &sample_findings()).unwrap();
81        assert!(prompt.contains("Handling Unclear Findings"));
82        assert!(prompt.contains("Skip it"));
83    }
84}