Skip to main content

probador/handlers/
init.rs

1//! Init command handler
2
3use crate::config::CliConfig;
4use crate::InitArgs;
5use std::path::Path;
6
7/// Execute the init command
8pub fn execute_init(_config: &CliConfig, args: &InitArgs) {
9    println!("Initializing Probar project in: {}", args.path.display());
10
11    if args.force {
12        println!("Force mode enabled - overwriting existing files");
13    }
14
15    // Create project directory if it doesn't exist
16    if let Err(e) = std::fs::create_dir_all(&args.path) {
17        eprintln!("Failed to create directory: {e}");
18        return;
19    }
20
21    // Create basic test file
22    let test_file = args.path.join("tests").join("basic_test.rs");
23    if let Some(parent) = test_file.parent() {
24        let _ = std::fs::create_dir_all(parent);
25    }
26
27    let test_content = generate_probar_config();
28    if !test_file.exists() || args.force {
29        let _ = std::fs::write(&test_file, test_content);
30        println!("Created: {}", test_file.display());
31    }
32
33    println!("Probar project initialized successfully!");
34}
35
36/// Generate the default Probar test configuration file content
37#[must_use]
38pub const fn generate_probar_config() -> &'static str {
39    r#"//! Basic Probar test
40use jugar_probar::prelude::*;
41
42#[test]
43fn test_example() {
44    let result = TestResult::pass("example_test");
45    assert!(result.passed);
46}
47"#
48}
49
50/// Check if a path is a valid init target
51#[must_use]
52pub fn is_valid_init_path(path: &Path) -> bool {
53    // Path should either not exist or be an empty directory
54    if !path.exists() {
55        return true;
56    }
57
58    if !path.is_dir() {
59        return false;
60    }
61
62    // Check if directory is empty (except for hidden files)
63    match std::fs::read_dir(path) {
64        Ok(entries) => {
65            for entry in entries.flatten() {
66                let name = entry.file_name();
67                let name_str = name.to_string_lossy();
68                if !name_str.starts_with('.') {
69                    return false; // Non-hidden file found
70                }
71            }
72            true
73        }
74        Err(_) => false,
75    }
76}
77
78#[cfg(test)]
79#[allow(clippy::unwrap_used)]
80mod tests {
81    use super::*;
82    use tempfile::TempDir;
83
84    #[test]
85    fn test_generate_probar_config() {
86        let config = generate_probar_config();
87        assert!(config.contains("use jugar_probar::prelude::*"));
88        assert!(config.contains("fn test_example()"));
89        assert!(config.contains("TestResult::pass"));
90    }
91
92    #[test]
93    fn test_is_valid_init_path_nonexistent() {
94        let temp = TempDir::new().unwrap();
95        let nonexistent = temp.path().join("new_project");
96        assert!(is_valid_init_path(&nonexistent));
97    }
98
99    #[test]
100    fn test_is_valid_init_path_empty_dir() {
101        let temp = TempDir::new().unwrap();
102        assert!(is_valid_init_path(temp.path()));
103    }
104
105    #[test]
106    fn test_is_valid_init_path_nonempty_dir() {
107        let temp = TempDir::new().unwrap();
108        std::fs::write(temp.path().join("existing.rs"), "// content").unwrap();
109        assert!(!is_valid_init_path(temp.path()));
110    }
111
112    #[test]
113    fn test_is_valid_init_path_dir_with_hidden_files() {
114        let temp = TempDir::new().unwrap();
115        std::fs::write(temp.path().join(".gitignore"), "target/").unwrap();
116        assert!(is_valid_init_path(temp.path()));
117    }
118
119    #[test]
120    fn test_is_valid_init_path_file() {
121        let temp = TempDir::new().unwrap();
122        let file_path = temp.path().join("file.txt");
123        std::fs::write(&file_path, "content").unwrap();
124        assert!(!is_valid_init_path(&file_path));
125    }
126
127    #[test]
128    fn test_execute_init_creates_directory() {
129        let temp = TempDir::new().unwrap();
130        let project_path = temp.path().join("new_project");
131
132        let config = CliConfig::default();
133        let args = InitArgs {
134            path: project_path.clone(),
135            force: false,
136        };
137
138        execute_init(&config, &args);
139
140        assert!(project_path.exists());
141        assert!(project_path.join("tests").exists());
142        assert!(project_path.join("tests").join("basic_test.rs").exists());
143    }
144
145    #[test]
146    fn test_execute_init_force_overwrites() {
147        let temp = TempDir::new().unwrap();
148        let project_path = temp.path().join("existing_project");
149        std::fs::create_dir_all(project_path.join("tests")).unwrap();
150
151        let old_content = "// old content";
152        std::fs::write(
153            project_path.join("tests").join("basic_test.rs"),
154            old_content,
155        )
156        .unwrap();
157
158        let config = CliConfig::default();
159        let args = InitArgs {
160            path: project_path.clone(),
161            force: true,
162        };
163
164        execute_init(&config, &args);
165
166        let content =
167            std::fs::read_to_string(project_path.join("tests").join("basic_test.rs")).unwrap();
168        assert!(content.contains("jugar_probar"));
169        assert!(!content.contains("old content"));
170    }
171
172    #[test]
173    fn test_execute_init_does_not_overwrite_without_force() {
174        let temp = TempDir::new().unwrap();
175        let project_path = temp.path().join("existing_project");
176        std::fs::create_dir_all(project_path.join("tests")).unwrap();
177
178        let old_content = "// old content that should remain";
179        std::fs::write(
180            project_path.join("tests").join("basic_test.rs"),
181            old_content,
182        )
183        .unwrap();
184
185        let config = CliConfig::default();
186        let args = InitArgs {
187            path: project_path.clone(),
188            force: false,
189        };
190
191        execute_init(&config, &args);
192
193        let content =
194            std::fs::read_to_string(project_path.join("tests").join("basic_test.rs")).unwrap();
195        assert!(content.contains("old content that should remain"));
196    }
197}