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 }