#![cfg(feature = "test-utils")]
use anyhow::Result;
use std::fs;
use tempfile::TempDir;
use xchecker::orchestrator::{OrchestratorConfig, PhaseOrchestrator};
use xchecker::test_support;
use xchecker::types::PhaseId;
fn setup_test_environment_with_files(test_name: &str) -> (PhaseOrchestrator, TempDir) {
let temp_dir = xchecker::paths::with_isolated_home();
let spec_id = format!("test-packet-phase-{}", test_name);
let orchestrator = PhaseOrchestrator::new(&spec_id).unwrap();
let spec_dir = orchestrator.artifact_manager().base_path();
fs::write(
spec_dir.join("README.md"),
"# Test Spec\n\nThis is a test specification for packet building.",
)
.unwrap();
fs::write(
spec_dir.join("SPEC-001.md"),
"# Specification Document\n\nDetailed specification content.",
)
.unwrap();
fs::write(
spec_dir.join("config.core.yaml"),
"version: 1.0\nname: test-spec\n",
)
.unwrap();
(orchestrator, temp_dir)
}
#[tokio::test]
async fn test_packet_builder_integration() -> Result<()> {
let (orchestrator, _temp_dir) = setup_test_environment_with_files("builder-integration");
let config = OrchestratorConfig {
dry_run: true,
..Default::default()
};
let result = orchestrator.execute_requirements_phase(&config).await?;
assert!(result.success, "Requirements phase should succeed");
assert_eq!(result.exit_code, 0, "Exit code should be 0 for success");
assert!(result.receipt_path.is_some(), "Receipt should be written");
let receipt_path = result.receipt_path.unwrap();
let receipt_content = fs::read_to_string(&receipt_path)?;
assert!(
receipt_content.contains("packet_evidence") || receipt_content.contains("files"),
"Receipt should contain packet evidence"
);
Ok(())
}
#[tokio::test]
async fn test_packet_evidence_includes_files() -> Result<()> {
let (orchestrator, _temp_dir) = setup_test_environment_with_files("evidence-files");
let config = OrchestratorConfig {
dry_run: true,
..Default::default()
};
let result = orchestrator.execute_requirements_phase(&config).await?;
assert!(result.success);
let receipt_manager =
xchecker::receipt::ReceiptManager::new(orchestrator.artifact_manager().base_path());
let receipt = receipt_manager.read_latest_receipt(PhaseId::Requirements)?;
assert!(receipt.is_some(), "Receipt should exist");
let receipt = receipt.unwrap();
assert!(
!receipt.packet.files.is_empty(),
"Packet evidence should include files"
);
for file_evidence in &receipt.packet.files {
assert!(
!file_evidence.path.is_empty(),
"File path should not be empty"
);
assert!(
!file_evidence.blake3_pre_redaction.is_empty(),
"BLAKE3 hash should not be empty"
);
assert!(
matches!(
file_evidence.priority,
xchecker::types::Priority::Upstream
| xchecker::types::Priority::High
| xchecker::types::Priority::Medium
| xchecker::types::Priority::Low
),
"Priority should be valid"
);
}
Ok(())
}
#[tokio::test]
async fn test_secret_scanning_integration() -> Result<()> {
let (orchestrator, _temp_dir) = setup_test_environment_with_files("secret-scan");
let spec_dir = orchestrator.artifact_manager().base_path();
let token = test_support::github_pat();
fs::write(spec_dir.join("secrets.txt"), format!("API Key: {}", token))?;
let config = OrchestratorConfig {
dry_run: true, ..Default::default()
};
let result = orchestrator.execute_requirements_phase(&config).await;
assert!(
result.is_err(),
"Phase should fail when secrets are detected"
);
let err = result.unwrap_err();
let err_msg = err.to_string();
assert!(
err_msg.contains("secret") || err_msg.contains("Secret"),
"Error should mention secret detection: {}",
err_msg
);
Ok(())
}
#[tokio::test]
async fn test_packet_overflow_detection_requires_future_api() -> Result<()> {
let (orchestrator, _temp_dir) = setup_test_environment_with_files("overflow");
let spec_dir = orchestrator.artifact_manager().base_path();
let large_content = "x".repeat(600); fs::write(spec_dir.join("config.core.yaml"), &large_content)?;
fs::write(spec_dir.join("extra.core.yaml"), &large_content)?;
let mut config = OrchestratorConfig {
dry_run: true,
..Default::default()
};
config
.config
.insert("packet_max_bytes".to_string(), "1000".to_string());
config
.config
.insert("packet_max_lines".to_string(), "10".to_string());
let result = orchestrator.execute_requirements_phase(&config).await;
assert!(
result.is_err(),
"Phase should fail when packet exceeds limits"
);
let err = result.unwrap_err();
let err_msg = err.to_string();
assert!(
err_msg.contains("overflow")
|| err_msg.contains("Overflow")
|| err_msg.contains("exceeded")
|| err_msg.contains("limit"),
"Error should mention packet overflow: {}",
err_msg
);
Ok(())
}
#[tokio::test]
async fn test_end_to_end_phase_progression() -> Result<()> {
let (orchestrator, _temp_dir) = setup_test_environment_with_files("e2e-progression");
let config = OrchestratorConfig {
dry_run: true,
..Default::default()
};
let result1 = orchestrator.execute_requirements_phase(&config).await?;
assert!(result1.success, "Requirements phase should succeed");
assert!(
!result1.artifact_paths.is_empty(),
"Should create artifacts"
);
assert!(
result1
.artifact_paths
.iter()
.any(|p| p.to_string_lossy().contains("requirements")),
"Should create requirements artifacts"
);
let result2 = orchestrator.execute_design_phase(&config).await?;
assert!(result2.success, "Design phase should succeed");
assert!(
!result2.artifact_paths.is_empty(),
"Should create artifacts"
);
assert!(
result2
.artifact_paths
.iter()
.any(|p| p.to_string_lossy().contains("design")),
"Should create design artifacts"
);
let result3 = orchestrator.execute_tasks_phase(&config).await?;
assert!(result3.success, "Tasks phase should succeed");
assert!(
!result3.artifact_paths.is_empty(),
"Should create artifacts"
);
assert!(
result3
.artifact_paths
.iter()
.any(|p| p.to_string_lossy().contains("tasks")),
"Should create tasks artifacts"
);
Ok(())
}
#[tokio::test]
async fn test_phase_dependency_enforcement() -> Result<()> {
let (orchestrator, _temp_dir) = setup_test_environment_with_files("dependency");
let config = OrchestratorConfig {
dry_run: true,
..Default::default()
};
let result = orchestrator.execute_design_phase(&config).await;
assert!(result.is_err(), "Design should fail without Requirements");
orchestrator.execute_requirements_phase(&config).await?;
let result = orchestrator.execute_design_phase(&config).await;
assert!(result.is_ok(), "Design should succeed after Requirements");
let (orchestrator2, _temp_dir2) = setup_test_environment_with_files("dependency2");
orchestrator2.execute_requirements_phase(&config).await?;
let result = orchestrator2.execute_tasks_phase(&config).await;
assert!(result.is_err(), "Tasks should fail without Design");
Ok(())
}
#[tokio::test]
async fn test_packet_preview_written() -> Result<()> {
let (orchestrator, _temp_dir) = setup_test_environment_with_files("preview");
let config = OrchestratorConfig {
dry_run: true,
..Default::default()
};
orchestrator.execute_requirements_phase(&config).await?;
let context_dir = orchestrator.artifact_manager().context_path();
let preview_path = context_dir.join("requirements-packet.txt");
assert!(
preview_path.exists(),
"Packet preview should be written to context directory"
);
let preview_content = fs::read_to_string(&preview_path)?;
assert!(
preview_content.contains("===") || !preview_content.is_empty(),
"Packet preview should contain content"
);
Ok(())
}
#[tokio::test]
async fn test_artifacts_generated() -> Result<()> {
let (orchestrator, _temp_dir) = setup_test_environment_with_files("artifacts");
let config = OrchestratorConfig {
dry_run: true,
..Default::default()
};
let result = orchestrator.execute_requirements_phase(&config).await?;
let has_markdown = result
.artifact_paths
.iter()
.any(|p| p.to_string_lossy().ends_with(".md"));
let has_yaml = result
.artifact_paths
.iter()
.any(|p| p.to_string_lossy().ends_with(".yaml"));
assert!(has_markdown, "Should create markdown artifact");
assert!(has_yaml, "Should create YAML artifact");
for artifact_path in &result.artifact_paths {
assert!(
artifact_path.exists(),
"Artifact should exist: {:?}",
artifact_path
);
}
Ok(())
}
#[tokio::test]
async fn test_receipt_packet_evidence_accuracy() -> Result<()> {
let (orchestrator, _temp_dir) = setup_test_environment_with_files("evidence-accuracy");
let config = OrchestratorConfig {
dry_run: true,
..Default::default()
};
orchestrator.execute_requirements_phase(&config).await?;
let receipt_manager =
xchecker::receipt::ReceiptManager::new(orchestrator.artifact_manager().base_path());
let receipt = receipt_manager
.read_latest_receipt(PhaseId::Requirements)?
.expect("Receipt should exist");
assert!(receipt.packet.max_bytes > 0, "Max bytes should be set");
assert!(receipt.packet.max_lines > 0, "Max lines should be set");
let expected_files = vec!["README.md", "SPEC-001.md", "config.core.yaml"];
for expected_file in expected_files {
let found = receipt
.packet
.files
.iter()
.any(|f| f.path.contains(expected_file));
assert!(
found,
"Packet evidence should include file: {}",
expected_file
);
}
Ok(())
}