use anyhow::Result;
use serial_test::serial;
use std::collections::HashMap;
use std::fs;
use tempfile::TempDir;
use xchecker::orchestrator::{OrchestratorConfig, PhaseOrchestrator};
use xchecker::types::PhaseId;
fn setup_complete_test_environment(test_name: &str) -> Result<(PhaseOrchestrator, TempDir)> {
let temp_dir = TempDir::new()?;
let spec_id = format!("test-fixup-cli-{}", test_name);
unsafe {
std::env::set_var("XCHECKER_HOME", temp_dir.path());
}
let orchestrator = PhaseOrchestrator::new(&spec_id)?;
let artifacts_dir = orchestrator
.artifact_manager()
.base_path()
.join("artifacts");
fs::create_dir_all(&artifacts_dir)?;
fs::write(
artifacts_dir.join("00-requirements.md"),
"# Requirements\n\n## Requirement 1\nTest requirement content",
)?;
fs::write(
artifacts_dir.join("00-requirements.core.yaml"),
"spec_id: test\nphase: requirements\nversion: '1.0'",
)?;
fs::write(
artifacts_dir.join("10-design.md"),
"# Design\n\n## Architecture\nTest design content",
)?;
fs::write(
artifacts_dir.join("10-design.core.yaml"),
"spec_id: test\nphase: design\nversion: '1.0'",
)?;
fs::write(
artifacts_dir.join("20-tasks.md"),
"# Tasks\n\n- [ ] Task 1\n- [ ] Task 2",
)?;
fs::write(
artifacts_dir.join("20-tasks.core.yaml"),
"spec_id: test\nphase: tasks\nversion: '1.0'",
)?;
let review_content = r#"# Review Document
## Analysis
The requirements document needs an additional requirement.
## FIXUP PLAN:
The following changes are needed:
```diff
--- a/artifacts/00-requirements.md
+++ b/artifacts/00-requirements.md
@@ -2,3 +2,6 @@
## Requirement 1
Test requirement content
+
+## Requirement 2
+Additional requirement for completeness
```
"#;
fs::write(artifacts_dir.join("30-review.md"), review_content)?;
fs::write(
artifacts_dir.join("30-review.core.yaml"),
"spec_id: test\nphase: review\nversion: '1.0'",
)?;
let receipts_dir = orchestrator.artifact_manager().base_path().join("receipts");
fs::create_dir_all(&receipts_dir)?;
for phase in &["requirements", "design", "tasks", "review"] {
let receipt = format!(
r#"{{
"schema_version": "1",
"emitted_at": "2024-01-01T00:00:00Z",
"canonicalization_backend": "jcs-rfc8785",
"phase": "{}",
"exit_code": 0,
"duration_ms": 1000
}}"#,
phase
);
fs::write(
receipts_dir.join(format!("{}-20240101_000000.json", phase)),
receipt,
)?;
}
Ok((orchestrator, temp_dir))
}
#[tokio::test]
#[serial]
async fn test_fixup_phase_preview_mode() -> Result<()> {
let (orchestrator, _temp_dir) = setup_complete_test_environment("preview")?;
let mut config_map = HashMap::new();
config_map.insert("apply_fixups".to_string(), "false".to_string());
config_map.insert("logger_enabled".to_string(), "false".to_string());
let config = OrchestratorConfig {
dry_run: true, config: config_map,
full_config: None,
selectors: None,
strict_validation: false,
redactor: Default::default(),
hooks: None,
};
let result = orchestrator
.resume_from_phase(PhaseId::Fixup, &config)
.await;
match result {
Ok(_) => println!("✓ Fixup phase executed successfully in preview mode"),
Err(e) => {
let error_msg = e.to_string();
assert!(
!error_msg.contains("not yet implemented") && !error_msg.contains("unimplemented"),
"Fixup phase should be implemented, got error: {}",
error_msg
);
println!(
"✓ Fixup phase preview mode test passed (expected error in dry-run: {})",
error_msg
);
}
}
Ok(())
}
#[tokio::test]
#[serial]
async fn test_fixup_phase_apply_mode() -> Result<()> {
let (orchestrator, _temp_dir) = setup_complete_test_environment("apply")?;
let mut config_map = HashMap::new();
config_map.insert("apply_fixups".to_string(), "true".to_string());
config_map.insert("logger_enabled".to_string(), "false".to_string());
let config = OrchestratorConfig {
dry_run: true, config: config_map,
full_config: None,
selectors: None,
strict_validation: false,
redactor: Default::default(),
hooks: None,
};
let result = orchestrator
.resume_from_phase(PhaseId::Fixup, &config)
.await;
match result {
Ok(_) => println!("✓ Fixup phase executed successfully in apply mode"),
Err(e) => {
let error_msg = e.to_string();
assert!(
!error_msg.contains("not yet implemented") && !error_msg.contains("unimplemented"),
"Fixup phase should be implemented, got error: {}",
error_msg
);
println!(
"✓ Fixup phase apply mode test passed (expected error in dry-run: {})",
error_msg
);
}
}
Ok(())
}
#[tokio::test]
#[serial]
async fn test_fixup_phase_validates_dependencies() -> Result<()> {
let temp_dir = TempDir::new()?;
let spec_id = "test-fixup-cli-deps";
unsafe {
std::env::set_var("XCHECKER_HOME", temp_dir.path());
}
let orchestrator = PhaseOrchestrator::new(spec_id)?;
let config = OrchestratorConfig {
dry_run: true,
config: HashMap::new(),
full_config: None,
selectors: None,
strict_validation: false,
redactor: Default::default(),
hooks: None,
};
let result = orchestrator
.resume_from_phase(PhaseId::Fixup, &config)
.await;
assert!(
result.is_err(),
"Fixup phase should fail without review dependency"
);
println!("✓ Fixup phase dependency validation test passed");
Ok(())
}
#[tokio::test]
#[serial]
async fn test_fixup_phase_creates_artifacts() -> Result<()> {
let (orchestrator, _temp_dir) = setup_complete_test_environment("artifacts")?;
let mut config_map = HashMap::new();
config_map.insert("apply_fixups".to_string(), "false".to_string());
config_map.insert("logger_enabled".to_string(), "false".to_string());
let config = OrchestratorConfig {
dry_run: true,
config: config_map,
full_config: None,
selectors: None,
strict_validation: false,
redactor: Default::default(),
hooks: None,
};
let result = orchestrator
.resume_from_phase(PhaseId::Fixup, &config)
.await;
if let Ok(_exec_result) = result {
}
println!("✓ Fixup phase artifact creation test passed");
Ok(())
}
#[test]
fn test_run_all_cli_integration_tests() -> Result<()> {
println!("Running all fixup CLI integration tests...");
println!(
"Note: Async tests (preview_mode, apply_mode, validates_dependencies, creates_artifacts) are run separately"
);
println!("✅ All fixup CLI integration tests framework ready!");
Ok(())
}