metis_docs_cli/commands/
validate.rs

1use anyhow::Result;
2use clap::Args;
3use metis_core::application::services::document::DocumentValidationService;
4use std::path::PathBuf;
5
6#[derive(Args)]
7pub struct ValidateCommand {
8    /// Path to the document file to validate
9    pub file_path: PathBuf,
10}
11
12impl ValidateCommand {
13    pub async fn execute(&self) -> Result<()> {
14        // Use the validation service
15        let service = DocumentValidationService::new();
16        let result = service.validate_document(&self.file_path).await;
17
18        match result {
19            Ok(validation_result) => {
20                if validation_result.is_valid {
21                    println!(
22                        "✓ Valid {} document: {}",
23                        validation_result.document_type,
24                        self.file_path.display()
25                    );
26                    Ok(())
27                } else {
28                    println!("✗ Invalid document: {}", self.file_path.display());
29                    for error in &validation_result.errors {
30                        println!("  - {}", error);
31                    }
32                    anyhow::bail!("Document validation failed")
33                }
34            }
35            Err(e) => {
36                println!("✗ Error validating document: {}", self.file_path.display());
37                println!("  Error: {}", e);
38                Err(e.into())
39            }
40        }
41    }
42}
43
44#[cfg(test)]
45mod tests {
46    use super::*;
47    use crate::commands::InitCommand;
48    use std::fs;
49    use tempfile::tempdir;
50
51    #[tokio::test]
52    async fn test_validate_command_missing_file() {
53        let temp_dir = tempdir().unwrap();
54        let original_dir = std::env::current_dir().ok();
55
56        // Change to temp directory
57        std::env::set_current_dir(temp_dir.path()).unwrap();
58
59        let cmd = ValidateCommand {
60            file_path: PathBuf::from("nonexistent.md"),
61        };
62
63        let result = cmd.execute().await;
64
65        // Always restore original directory first
66        if let Some(original) = original_dir {
67            let _ = std::env::set_current_dir(&original);
68        }
69
70        assert!(result.is_err());
71        assert!(result
72            .unwrap_err()
73            .to_string()
74            .contains("File does not exist"));
75    }
76
77    #[tokio::test]
78    async fn test_validate_command_valid_vision() {
79        let temp_dir = tempdir().unwrap();
80        let original_dir = std::env::current_dir().ok();
81
82        // Change to temp directory
83        std::env::set_current_dir(temp_dir.path()).unwrap();
84
85        // Create workspace with vision document
86        let init_cmd = InitCommand {
87            name: Some("Test Project".to_string()),
88            preset: None,
89            strategies: None,
90            initiatives: None,
91            prefix: None,
92        };
93        init_cmd.execute().await.unwrap();
94
95        let vision_path = temp_dir.path().join(".metis/vision.md");
96
97        let cmd = ValidateCommand {
98            file_path: vision_path,
99        };
100
101        let result = cmd.execute().await;
102
103        // Always restore original directory first
104        if let Some(original) = original_dir {
105            let _ = std::env::set_current_dir(&original);
106        }
107
108        assert!(result.is_ok());
109    }
110
111    #[tokio::test]
112    async fn test_validate_command_invalid_document() {
113        let temp_dir = tempdir().unwrap();
114        let original_dir = std::env::current_dir().ok();
115
116        // Change to temp directory
117        std::env::set_current_dir(temp_dir.path()).unwrap();
118
119        // Create an invalid markdown file
120        let invalid_path = temp_dir.path().join("invalid.md");
121        fs::write(
122            &invalid_path,
123            "# Invalid Document\n\nThis has no frontmatter.",
124        )
125        .unwrap();
126
127        let cmd = ValidateCommand {
128            file_path: invalid_path,
129        };
130
131        let result = cmd.execute().await;
132
133        // Always restore original directory first
134        if let Some(original) = original_dir {
135            let _ = std::env::set_current_dir(&original);
136        }
137
138        assert!(result.is_err());
139    }
140}