use crate::{
pipeline::prompt::{
ReviewContext, ReviewPrMeta, build_review_prompt, build_system_prompt,
reviewer_system_prompt,
},
voice::{VoiceConfig, principles::principles_addendum},
};
fn sample_meta() -> ReviewPrMeta {
ReviewPrMeta {
title: "Add feature".to_string(),
body: String::new(),
author: "bob".to_string(),
url: "https://github.com/acme/repo/pull/1".to_string(),
}
}
#[test]
fn build_system_prompt_stock_only() {
let vc = VoiceConfig::stock_only();
let layered = build_system_prompt(&vc);
let stock = reviewer_system_prompt();
assert_eq!(
layered, stock,
"stock_only VoiceConfig must produce identical output to reviewer_system_prompt()"
);
}
#[test]
fn build_system_prompt_with_principles_only() {
let vc = VoiceConfig {
principles: Some(principles_addendum().to_string()),
voice_addendum: None,
voice_name: None,
};
let result = build_system_prompt(&vc);
assert!(
result.starts_with("You are a senior software engineer"),
"result must start with stock base"
);
assert!(
result.contains("Review principles"),
"result must contain principles heading"
);
assert!(
result.contains(reviewer_system_prompt()),
"result must contain the full stock base"
);
}
#[test]
fn build_system_prompt_full_pipeline_ordering() {
let vc = VoiceConfig {
principles: Some("## PRINCIPLES_MARKER".to_string()),
voice_addendum: Some("## VOICE_MARKER".to_string()),
voice_name: Some("test".to_string()),
};
let result = build_system_prompt(&vc);
let stock_pos = result.find("senior software engineer").unwrap();
let principles_pos = result.find("PRINCIPLES_MARKER").unwrap();
let voice_pos = result.find("VOICE_MARKER").unwrap();
assert!(
stock_pos < principles_pos,
"stock base must precede principles"
);
assert!(
principles_pos < voice_pos,
"principles must precede voice addendum"
);
}
#[test]
fn build_review_prompt_with_voice_config_principles() {
let vc = VoiceConfig {
principles: Some("## UNIQUE_PRINCIPLES_42".to_string()),
voice_addendum: None,
voice_name: None,
};
let req = build_review_prompt(
"o",
"r",
&sample_meta(),
"+fn x() {}",
&ReviewContext::default(),
"",
"openai/gpt-5.4-mini-20260317",
&vc,
);
assert!(
req.system.contains("UNIQUE_PRINCIPLES_42"),
"LlmRequest.system must include the principles addendum"
);
assert!(
req.system.contains("You are a senior software engineer"),
"LlmRequest.system must still include the stock base"
);
}
#[test]
fn build_review_prompt_with_voice_config_full() {
let vc = VoiceConfig {
principles: Some("PRINCIPLES_TEXT_XYZ".to_string()),
voice_addendum: Some("VOICE_TEXT_XYZ".to_string()),
voice_name: Some("testvoice".to_string()),
};
let req = build_review_prompt(
"o",
"r",
&sample_meta(),
"+fn y() {}",
&ReviewContext::default(),
"",
"openai/gpt-5.4-mini-20260317",
&vc,
);
assert!(
req.system.contains("PRINCIPLES_TEXT_XYZ"),
"system must contain principles marker"
);
assert!(
req.system.contains("VOICE_TEXT_XYZ"),
"system must contain voice marker"
);
let user = &req.messages[0].content;
assert!(
!user.contains("PRINCIPLES_TEXT_XYZ"),
"user message must not contain principles (system-prompt content)"
);
assert!(
!user.contains("VOICE_TEXT_XYZ"),
"user message must not contain voice addendum (system-prompt content)"
);
}
#[test]
fn build_system_prompt_no_trailing_separator_when_no_addendum() {
let vc = VoiceConfig::stock_only();
let result = build_system_prompt(&vc);
assert!(
!result.ends_with("\n\n"),
"stock_only must not gain a trailing double-newline"
);
assert_eq!(
result.len(),
reviewer_system_prompt().len(),
"stock_only result length must match stock base length"
);
}