/ ferris-proof-cli / tests / cli_commands_unit_test.rs
cli_commands_unit_test.rs
  1  use ferris_proof_cli::commands::{config, explain, init};
  2  use ferris_proof_core::VerificationLevel;
  3  use std::fs;
  4  use tempfile::TempDir;
  5  
  6  #[cfg(test)]
  7  mod init_command_tests {
  8      use super::*;
  9  
 10      #[tokio::test]
 11      async fn test_init_command_creates_config_file() {
 12          let temp_dir = TempDir::new().unwrap();
 13          let temp_path = temp_dir.path().to_path_buf();
 14          let original_dir = std::env::current_dir().unwrap();
 15  
 16          std::env::set_current_dir(&temp_path).unwrap();
 17  
 18          let result = init::run(VerificationLevel::Standard, false, None).await;
 19  
 20          // Restore directory before checking result to avoid issues with temp dir cleanup
 21          std::env::set_current_dir(original_dir).unwrap();
 22  
 23          // Check result
 24          assert!(result.is_ok());
 25          assert_eq!(result.unwrap(), 0);
 26  
 27          // Check that config file was created
 28          let config_path = temp_path.join("ferrisproof.toml");
 29          assert!(config_path.exists());
 30  
 31          let config_content = fs::read_to_string(&config_path).unwrap();
 32          assert!(config_content.contains("level = \"standard\""));
 33      }
 34  
 35      #[tokio::test]
 36      async fn test_init_command_creates_directories_for_minimal_level() {
 37          let temp_dir = TempDir::new().unwrap();
 38          let temp_path = temp_dir.path().to_path_buf();
 39          let original_dir = std::env::current_dir().unwrap();
 40  
 41          std::env::set_current_dir(&temp_path).unwrap();
 42  
 43          let result = init::run(VerificationLevel::Minimal, false, None).await;
 44  
 45          // Check basic directories exist
 46          assert!(temp_path.join("specs").exists());
 47          assert!(temp_path.join("tests").exists());
 48  
 49          // Minimal level should not create additional directories
 50          assert!(!temp_path.join("tests/property").exists());
 51  
 52          // Restore directory before checking result to avoid issues with temp dir cleanup
 53          std::env::set_current_dir(original_dir).unwrap();
 54  
 55          // Check result
 56          assert!(result.is_ok());
 57          assert_eq!(result.unwrap(), 0);
 58      }
 59  
 60      #[tokio::test]
 61      async fn test_init_command_creates_directories_for_standard_level() {
 62          let temp_dir = TempDir::new().unwrap();
 63          let temp_path = temp_dir.path().to_path_buf();
 64          let original_dir = std::env::current_dir().unwrap();
 65  
 66          std::env::set_current_dir(&temp_path).unwrap();
 67  
 68          let result = init::run(VerificationLevel::Standard, false, None).await;
 69  
 70          // Check basic directories exist
 71          assert!(temp_path.join("specs").exists());
 72          assert!(temp_path.join("tests").exists());
 73  
 74          // Standard level should create property test directory
 75          assert!(temp_path.join("tests/property").exists());
 76  
 77          // Restore directory before checking result to avoid issues with temp dir cleanup
 78          std::env::set_current_dir(original_dir).unwrap();
 79  
 80          // Check result
 81          assert!(result.is_ok());
 82          assert_eq!(result.unwrap(), 0);
 83      }
 84  
 85      #[tokio::test]
 86      async fn test_init_command_creates_directories_for_formal_level() {
 87          let temp_dir = TempDir::new().unwrap();
 88          let temp_path = temp_dir.path().to_path_buf();
 89          let original_dir = std::env::current_dir().unwrap();
 90  
 91          std::env::set_current_dir(&temp_path).unwrap();
 92  
 93          let result = init::run(VerificationLevel::Formal, false, None).await;
 94  
 95          // Check all directories for formal level exist
 96          assert!(temp_path.join("specs").exists());
 97          assert!(temp_path.join("tests").exists());
 98          assert!(temp_path.join("tests/property").exists());
 99          assert!(temp_path.join("specs/session-types").exists());
100          assert!(temp_path.join("specs/refinement-types").exists());
101          assert!(temp_path.join("specs/formal").exists());
102          assert!(temp_path.join("specs/formal/tla").exists());
103          assert!(temp_path.join("specs/formal/alloy").exists());
104  
105          // Restore directory before checking result to avoid issues with temp dir cleanup
106          std::env::set_current_dir(original_dir).unwrap();
107  
108          // Check result
109          assert!(result.is_ok());
110          assert_eq!(result.unwrap(), 0);
111      }
112  
113      #[tokio::test]
114      async fn test_init_command_with_template() {
115          let temp_dir = TempDir::new().unwrap();
116          let temp_path = temp_dir.path().to_path_buf();
117          let original_dir = std::env::current_dir().unwrap();
118  
119          std::env::set_current_dir(&temp_path).unwrap();
120  
121          let result = init::run(
122              VerificationLevel::Standard,
123              false,
124              Some("standard".to_string()),
125          )
126          .await;
127  
128          // Check that config file was created while still in temp directory
129          let config_path = temp_path.join("ferrisproof.toml");
130          assert!(config_path.exists());
131  
132          // Check that template files were created while still in temp directory
133          let readme_path = temp_path.join("README.md");
134          assert!(readme_path.exists());
135  
136          let property_test_path = temp_path.join("tests/property/example_properties.rs");
137          assert!(property_test_path.exists());
138  
139          // Restore directory before checking result to avoid issues with temp dir cleanup
140          std::env::set_current_dir(original_dir).unwrap();
141  
142          // Check result
143          assert!(result.is_ok());
144          assert_eq!(result.unwrap(), 0);
145      }
146  }
147  
148  #[cfg(test)]
149  mod config_command_tests {
150      use super::*;
151  
152      #[tokio::test]
153      async fn test_config_command_without_ferrisproof_toml() {
154          let temp_dir = TempDir::new().unwrap();
155          let original_dir = std::env::current_dir().unwrap();
156  
157          std::env::set_current_dir(temp_dir.path()).unwrap();
158  
159          let result = config::run(None, false).await;
160  
161          // Restore directory before checking result to avoid issues with temp dir cleanup
162          std::env::set_current_dir(original_dir).unwrap();
163  
164          assert!(result.is_ok());
165          assert_eq!(result.unwrap(), 1); // Should return 1 when no config found
166      }
167  
168      #[tokio::test]
169      async fn test_config_command_with_valid_config() {
170          let temp_dir = TempDir::new().unwrap();
171          let temp_path = temp_dir.path().to_path_buf();
172          let original_dir = std::env::current_dir().unwrap();
173  
174          std::env::set_current_dir(&temp_path).unwrap();
175  
176          // First create a config file
177          let init_result = init::run(VerificationLevel::Standard, false, None).await;
178          assert!(init_result.is_ok());
179          assert_eq!(init_result.unwrap(), 0);
180  
181          // Then test the config command
182          let result = config::run(None, false).await;
183  
184          // Restore directory before checking result to avoid issues with temp dir cleanup
185          std::env::set_current_dir(original_dir).unwrap();
186  
187          // Check result
188          assert!(result.is_ok());
189          assert_eq!(result.unwrap(), 0);
190      }
191  
192      #[tokio::test]
193      async fn test_config_validation_with_valid_config() {
194          let temp_dir = TempDir::new().unwrap();
195          let temp_path = temp_dir.path().to_path_buf();
196          let original_dir = std::env::current_dir().unwrap();
197  
198          std::env::set_current_dir(&temp_path).unwrap();
199  
200          // First create a config file
201          let init_result = init::run(VerificationLevel::Standard, false, None).await;
202          assert!(init_result.is_ok());
203          assert_eq!(init_result.unwrap(), 0);
204  
205          // Then test validation
206          let result = config::run(None, true).await;
207  
208          // Restore directory before checking result to avoid issues with temp dir cleanup
209          std::env::set_current_dir(original_dir).unwrap();
210  
211          // Check result
212          assert!(result.is_ok());
213          assert_eq!(result.unwrap(), 0); // Should pass validation
214      }
215  
216      #[tokio::test]
217      async fn test_config_validation_without_config() {
218          let temp_dir = TempDir::new().unwrap();
219          let temp_path = temp_dir.path().to_path_buf();
220          let original_dir = std::env::current_dir().unwrap();
221  
222          std::env::set_current_dir(&temp_path).unwrap();
223  
224          let result = config::run(None, true).await;
225  
226          // Restore directory before checking result to avoid issues with temp dir cleanup
227          std::env::set_current_dir(original_dir).unwrap();
228  
229          // Check result
230          assert!(result.is_ok());
231          // Validation without config should still work (uses default config)
232          assert_eq!(result.unwrap(), 0);
233      }
234  
235      #[tokio::test]
236      async fn test_config_command_for_specific_file() {
237          let temp_dir = TempDir::new().unwrap();
238          let temp_path = temp_dir.path().to_path_buf();
239          let original_dir = std::env::current_dir().unwrap();
240  
241          std::env::set_current_dir(&temp_path).unwrap();
242  
243          // Create a config file
244          let init_result = init::run(VerificationLevel::Standard, false, None).await;
245  
246          // Create a test file
247          let test_file = temp_path.join("src").join("main.rs");
248          fs::create_dir_all(test_file.parent().unwrap()).unwrap();
249          fs::write(&test_file, "fn main() {}").unwrap();
250  
251          // Test config for specific file
252          let result = config::run(Some(test_file), false).await;
253  
254          // Restore directory before checking result to avoid issues with temp dir cleanup
255          std::env::set_current_dir(original_dir).unwrap();
256  
257          // Check result
258          assert!(init_result.is_ok());
259          assert_eq!(init_result.unwrap(), 0);
260          assert!(result.is_ok());
261          assert_eq!(result.unwrap(), 0);
262      }
263  }
264  
265  #[cfg(test)]
266  mod explain_command_tests {
267      use super::*;
268  
269      #[tokio::test]
270      async fn test_explain_command_with_known_error_code() {
271          let result = explain::run("FP-CF-001".to_string()).await;
272  
273          assert!(result.is_ok());
274          assert_eq!(result.unwrap(), 0);
275      }
276  
277      #[tokio::test]
278      async fn test_explain_command_with_unknown_error_code() {
279          let result = explain::run("FP-XX-999".to_string()).await;
280  
281          assert!(result.is_ok());
282          assert_eq!(result.unwrap(), 1); // Should return 1 for unknown codes
283      }
284  
285      #[tokio::test]
286      async fn test_explain_command_with_configuration_error() {
287          let result = explain::run("FP-CF-002".to_string()).await;
288  
289          assert!(result.is_ok());
290          assert_eq!(result.unwrap(), 0);
291      }
292  
293      #[tokio::test]
294      async fn test_explain_command_with_verification_error() {
295          let result = explain::run("FP-VR-001".to_string()).await;
296  
297          assert!(result.is_ok());
298          assert_eq!(result.unwrap(), 0);
299      }
300  
301      #[tokio::test]
302      async fn test_explain_command_with_tool_error() {
303          let result = explain::run("FP-TL-001".to_string()).await;
304  
305          assert!(result.is_ok());
306          assert_eq!(result.unwrap(), 0);
307      }
308  
309      #[tokio::test]
310      async fn test_explain_command_with_io_error() {
311          let result = explain::run("FP-IO-001".to_string()).await;
312  
313          assert!(result.is_ok());
314          assert_eq!(result.unwrap(), 0);
315      }
316  
317      #[tokio::test]
318      async fn test_explain_command_with_empty_code() {
319          let result = explain::run("".to_string()).await;
320  
321          assert!(result.is_ok());
322          assert_eq!(result.unwrap(), 1); // Should return 1 for empty/invalid codes
323      }
324  
325      #[tokio::test]
326      async fn test_explain_command_with_partial_code() {
327          let result = explain::run("FP-CF".to_string()).await;
328  
329          assert!(result.is_ok());
330          assert_eq!(result.unwrap(), 1); // Should return 1 for partial codes
331      }
332  }
333  
334  #[cfg(test)]
335  mod integration_tests {
336      use super::*;
337  
338      #[tokio::test]
339      async fn test_init_then_config_workflow() {
340          let temp_dir = TempDir::new().unwrap();
341          let temp_path = temp_dir.path().to_path_buf();
342          let original_dir = std::env::current_dir().unwrap();
343  
344          std::env::set_current_dir(&temp_path).unwrap();
345  
346          // Initialize project
347          let init_result = init::run(VerificationLevel::Strict, false, None).await;
348          assert!(init_result.is_ok());
349          assert_eq!(init_result.unwrap(), 0);
350  
351          // Check configuration
352          let config_result = config::run(None, false).await;
353  
354          // Validate configuration
355          let validate_result = config::run(None, true).await;
356  
357          // Restore directory before checking results to avoid issues with temp dir cleanup
358          std::env::set_current_dir(original_dir).unwrap();
359  
360          assert!(config_result.is_ok());
361          assert_eq!(config_result.unwrap(), 0);
362          assert!(validate_result.is_ok());
363          assert_eq!(validate_result.unwrap(), 0);
364      }
365  
366      #[tokio::test]
367      async fn test_different_verification_levels_create_correct_configs() {
368          let levels = vec![
369              VerificationLevel::Minimal,
370              VerificationLevel::Standard,
371              VerificationLevel::Strict,
372              VerificationLevel::Formal,
373          ];
374  
375          for level in levels {
376              let temp_dir = TempDir::new().unwrap();
377              let temp_path = temp_dir.path().to_path_buf();
378              let original_dir = std::env::current_dir().unwrap();
379  
380              std::env::set_current_dir(&temp_path).unwrap();
381  
382              let result = init::run(level, false, None).await;
383  
384              // Verify config contains correct level while still in temp directory
385              let config_path = temp_path.join("ferrisproof.toml");
386              assert!(config_path.exists());
387  
388              let config_content = fs::read_to_string(&config_path).unwrap();
389              let level_str = format!("{:?}", level).to_lowercase();
390              assert!(config_content.contains(&format!("level = \"{}\"", level_str)));
391  
392              // Restore directory before checking result to avoid issues with temp dir cleanup
393              std::env::set_current_dir(original_dir).unwrap();
394  
395              // Check result
396              assert!(result.is_ok());
397              assert_eq!(result.unwrap(), 0);
398          }
399      }
400  }