use dx_forge::{
Forge, ForgeConfig, DxTool, ExecutionContext, ToolOutput, Orchestrator,
Version, SnapshotManager,
};
use anyhow::Result;
use std::path::PathBuf;
use tempfile::TempDir;
struct TestTool {
name: String,
priority: u32,
should_run: bool,
}
impl TestTool {
fn new(name: impl Into<String>, priority: u32) -> Self {
Self {
name: name.into(),
priority,
should_run: true,
}
}
}
impl DxTool for TestTool {
fn name(&self) -> &str {
&self.name
}
fn version(&self) -> &str {
"1.0.0"
}
fn priority(&self) -> u32 {
self.priority
}
fn execute(&mut self, _ctx: &ExecutionContext) -> Result<ToolOutput> {
Ok(ToolOutput::success())
}
fn should_run(&self, _ctx: &ExecutionContext) -> bool {
self.should_run
}
}
#[test]
fn test_forge_initialization() -> Result<()> {
let temp_dir = TempDir::new()?;
let forge = Forge::new(temp_dir.path())?;
assert_eq!(forge.project_root(), temp_dir.path());
assert!(forge.forge_dir().exists());
Ok(())
}
#[test]
fn test_forge_with_custom_config() -> Result<()> {
let temp_dir = TempDir::new()?;
let custom_forge_dir = temp_dir.path().join("custom_forge");
let config = ForgeConfig::new(temp_dir.path())
.with_forge_dir(&custom_forge_dir)
.without_auto_watch()
.without_lsp();
let forge = Forge::with_config(config)?;
assert_eq!(forge.forge_dir(), &custom_forge_dir);
assert!(custom_forge_dir.exists());
Ok(())
}
#[test]
fn test_tool_lifecycle() -> Result<()> {
let temp_dir = TempDir::new()?;
let forge = Forge::new(temp_dir.path())?;
let mut rx = forge.subscribe_lifecycle_events();
assert!(rx.try_recv().is_err());
Ok(())
}
#[test]
fn test_orchestrator_priority_ordering() -> Result<()> {
let temp_dir = TempDir::new()?;
let mut orch = Orchestrator::new(temp_dir.path())?;
orch.register_tool(Box::new(TestTool::new("tool-c", 30)))?;
orch.register_tool(Box::new(TestTool::new("tool-a", 10)))?;
orch.register_tool(Box::new(TestTool::new("tool-b", 20)))?;
let outputs = orch.execute_all()?;
assert_eq!(outputs.len(), 3);
assert!(outputs.iter().all(|o| o.success));
Ok(())
}
#[test]
fn test_orchestrator_dependencies() -> Result<()> {
let temp_dir = TempDir::new()?;
let mut orch = Orchestrator::new(temp_dir.path())?;
struct ToolWithDeps {
name: String,
deps: Vec<String>,
}
impl DxTool for ToolWithDeps {
fn name(&self) -> &str {
&self.name
}
fn version(&self) -> &str {
"1.0.0"
}
fn priority(&self) -> u32 {
50
}
fn execute(&mut self, _ctx: &ExecutionContext) -> Result<ToolOutput> {
Ok(ToolOutput::success())
}
fn dependencies(&self) -> Vec<String> {
self.deps.clone()
}
}
orch.register_tool(Box::new(ToolWithDeps {
name: "tool-b".to_string(),
deps: vec!["tool-a".to_string()],
}))?;
orch.register_tool(Box::new(ToolWithDeps {
name: "tool-a".to_string(),
deps: vec![],
}))?;
let outputs = orch.execute_all()?;
assert_eq!(outputs.len(), 2);
assert!(outputs.iter().all(|o| o.success));
Ok(())
}
#[test]
fn test_orchestrator_missing_dependency() -> Result<()> {
let temp_dir = TempDir::new()?;
let mut orch = Orchestrator::new(temp_dir.path())?;
struct ToolWithMissingDep;
impl DxTool for ToolWithMissingDep {
fn name(&self) -> &str {
"tool-a"
}
fn version(&self) -> &str {
"1.0.0"
}
fn priority(&self) -> u32 {
50
}
fn execute(&mut self, _ctx: &ExecutionContext) -> Result<ToolOutput> {
Ok(ToolOutput::success())
}
fn dependencies(&self) -> Vec<String> {
vec!["missing-tool".to_string()]
}
}
orch.register_tool(Box::new(ToolWithMissingDep))?;
assert!(orch.execute_all().is_err());
Ok(())
}
#[test]
fn test_version_snapshot_system() -> Result<()> {
let temp_dir = TempDir::new()?;
let mut manager = SnapshotManager::new(temp_dir.path())?;
let mut tool_states = std::collections::HashMap::new();
tool_states.insert(
"test-tool".to_string(),
dx_forge::ToolState {
tool_name: "test-tool".to_string(),
version: Version::new(1, 0, 0),
config: std::collections::HashMap::new(),
output_files: vec![],
},
);
let snapshot1 = manager.create_snapshot("Initial commit", tool_states.clone(), vec![])?;
let loaded = manager.get_snapshot(&snapshot1)?;
assert!(loaded.is_some());
assert_eq!(loaded.unwrap().message, "Initial commit");
let snapshot2 = manager.create_snapshot("Second commit", tool_states, vec![])?;
let history = manager.history(10)?;
assert_eq!(history.len(), 2);
assert_eq!(history[0].id, snapshot2);
assert_eq!(history[1].id, snapshot1);
Ok(())
}
#[test]
fn test_version_branching() -> Result<()> {
let temp_dir = TempDir::new()?;
let mut manager = SnapshotManager::new(temp_dir.path())?;
manager.create_snapshot("Initial", std::collections::HashMap::new(), vec![])?;
manager.create_branch("feature")?;
manager.checkout_branch("feature")?;
assert_eq!(manager.current_branch(), "feature");
manager.create_snapshot("Feature commit", std::collections::HashMap::new(), vec![])?;
manager.checkout_branch("main")?;
let branches = manager.list_branches()?;
assert!(branches.iter().any(|b| b.name == "main"));
assert!(branches.iter().any(|b| b.name == "feature"));
Ok(())
}
#[test]
fn test_generated_code_tracking() -> Result<()> {
let temp_dir = TempDir::new()?;
let mut forge = Forge::new(temp_dir.path())?;
let test_file = temp_dir.path().join("generated.txt");
std::fs::write(&test_file, "generated content")?;
let mut metadata = std::collections::HashMap::new();
metadata.insert("generator".to_string(), "test-tool".to_string());
forge.track_generated_file(test_file.clone(), "test-tool", metadata)?;
let files = forge.get_generated_files("test-tool");
assert_eq!(files.len(), 1);
assert_eq!(files[0], test_file);
Ok(())
}
#[tokio::test]
async fn test_generated_code_cleanup() -> Result<()> {
let temp_dir = TempDir::new()?;
let mut forge = Forge::new(temp_dir.path())?;
let test_file1 = temp_dir.path().join("gen1.txt");
let test_file2 = temp_dir.path().join("gen2.txt");
std::fs::write(&test_file1, "content1")?;
std::fs::write(&test_file2, "content2")?;
forge.track_generated_file(test_file1.clone(), "test-tool", Default::default())?;
forge.track_generated_file(test_file2.clone(), "test-tool", Default::default())?;
let removed = forge.cleanup_generated("test-tool").await?;
assert_eq!(removed.len(), 2);
assert!(!test_file1.exists());
assert!(!test_file2.exists());
Ok(())
}
#[test]
fn test_version_parsing() -> Result<()> {
use std::str::FromStr;
let v1 = Version::from_str("1.2.3")?;
assert_eq!(v1.major, 1);
assert_eq!(v1.minor, 2);
assert_eq!(v1.patch, 3);
let v2 = Version::from_str("v2.0.0-beta.1")?;
assert_eq!(v2.major, 2);
assert!(v2.is_prerelease());
let v3 = Version::from_str("1.0.0+build.123")?;
assert!(v3.is_stable());
Ok(())
}
#[test]
fn test_version_comparison() -> Result<()> {
let v1 = Version::new(1, 2, 3);
let v2 = Version::new(1, 2, 4);
let v3 = Version::new(2, 0, 0);
assert!(v1 < v2);
assert!(v2 < v3);
assert!(v1.is_compatible_with(&v2));
assert!(!v1.is_compatible_with(&v3));
Ok(())
}