use anyhow::Result;
use camino::Utf8PathBuf;
use std::collections::HashMap;
use std::fs;
use tempfile::TempDir;
use xchecker::packet::{ContentSelector, PacketBuilder};
use xchecker::phase::{Phase, PhaseContext};
use xchecker::phases::RequirementsPhase;
use xchecker::types::Priority;
#[test]
fn test_problem_statement_file_priority() -> Result<()> {
let temp_dir = TempDir::new()?;
let base_path = Utf8PathBuf::try_from(temp_dir.path().to_path_buf())?;
let source_dir = base_path.join("source");
fs::create_dir_all(&source_dir)?;
fs::write(
source_dir.join("00-problem-statement.md"),
"# Problem Statement\n\nBuild a REST API for user management.",
)?;
fs::write(base_path.join("README.md"), "# Project README")?;
fs::write(base_path.join("config.toml"), "# Config file")?;
let selector = ContentSelector::new()?;
let files = selector.select_files(&base_path)?;
let problem_file = files
.iter()
.find(|f| f.path.to_string().contains("problem-statement"));
assert!(
problem_file.is_some(),
"Problem statement file should be selected"
);
let problem_file = problem_file.unwrap();
assert_eq!(
problem_file.priority,
Priority::High,
"Problem statement should have High priority"
);
Ok(())
}
#[test]
fn test_problem_statement_in_packet_content() -> Result<()> {
let temp_dir = TempDir::new()?;
let base_path = Utf8PathBuf::try_from(temp_dir.path().to_path_buf())?;
let source_dir = base_path.join("source");
let context_dir = base_path.join("context");
fs::create_dir_all(&source_dir)?;
fs::create_dir_all(&context_dir)?;
let problem_text =
"Build a REST API for user management with authentication and CRUD operations.";
fs::write(
source_dir.join("00-problem-statement.md"),
format!("# Problem Statement\n\n{problem_text}"),
)?;
let mut builder = PacketBuilder::new()?;
let packet = builder.build_packet(&base_path, "requirements", &context_dir, None)?;
assert!(
packet.content.contains(problem_text),
"Packet should contain problem statement text"
);
Ok(())
}
#[test]
fn test_prompt_includes_problem_statement() {
let temp_dir = TempDir::new().unwrap();
let spec_dir = temp_dir.path().to_path_buf();
let problem_text = "Build a REST API for user management with authentication.";
let mut config = HashMap::new();
config.insert("problem_statement".to_string(), problem_text.to_string());
let ctx = PhaseContext {
spec_id: "test-spec".to_string(),
spec_dir,
config,
artifacts: vec![],
selectors: None,
strict_validation: false,
redactor: Default::default(),
};
let phase = RequirementsPhase::new();
let prompt = phase.prompt(&ctx);
assert!(
prompt.contains(problem_text),
"Prompt should contain problem statement text"
);
assert!(
prompt.contains("# Problem Statement"),
"Prompt should have Problem Statement section"
);
}
#[test]
fn test_prompt_fallback_without_problem_statement() {
let temp_dir = TempDir::new().unwrap();
let spec_dir = temp_dir.path().to_path_buf();
let ctx = PhaseContext {
spec_id: "test-spec".to_string(),
spec_dir,
config: HashMap::new(),
artifacts: vec![],
selectors: None,
strict_validation: false,
redactor: Default::default(),
};
let phase = RequirementsPhase::new();
let prompt = phase.prompt(&ctx);
assert!(
prompt.contains("No explicit problem statement was provided"),
"Prompt should have fallback text when no problem statement"
);
}
#[test]
fn test_problem_statement_naming_patterns() -> Result<()> {
let temp_dir = TempDir::new()?;
let base_path = Utf8PathBuf::try_from(temp_dir.path().to_path_buf())?;
let patterns = vec![
"problem-statement.md",
"00-problem-statement.md",
"my-problem-statement.txt",
];
for pattern in patterns {
for entry in fs::read_dir(&base_path)? {
let entry = entry?;
if entry.path().is_file() {
fs::remove_file(entry.path())?;
}
}
fs::write(base_path.join(pattern), format!("# Problem: {pattern}"))?;
let selector = ContentSelector::new()?;
let files = selector.select_files(&base_path)?;
let found = files
.iter()
.any(|f| f.path.to_string().contains("problem-statement"));
assert!(
found,
"Pattern '{pattern}' should be detected as problem statement"
);
}
Ok(())
}
#[test]
fn test_prompt_includes_spec_id() {
let temp_dir = TempDir::new().unwrap();
let spec_dir = temp_dir.path().to_path_buf();
let ctx = PhaseContext {
spec_id: "my-test-spec-123".to_string(),
spec_dir,
config: HashMap::new(),
artifacts: vec![],
selectors: None,
strict_validation: false,
redactor: Default::default(),
};
let phase = RequirementsPhase::new();
let prompt = phase.prompt(&ctx);
assert!(
prompt.contains("my-test-spec-123"),
"Prompt should contain spec ID"
);
}