use assert_cmd::assert::OutputAssertExt; use assert_cmd::cargo::cargo_bin; use predicates::prelude::*; use std::fs; use std::process::Command; use tempfile::TempDir; #[test] fn test_help_flag() { let mut cmd = Command::new(cargo_bin!("ntu")); cmd.arg("--help"); cmd.assert() .success() .stdout(predicate::str::contains("Usage: ntu")); } #[test] fn test_version_flag() { let mut cmd = Command::new(cargo_bin!("ntu")); cmd.arg("--version"); cmd.assert() .success() .stdout(predicate::str::contains("NameToUnix")); } #[test] fn test_dry_run_no_changes() { let temp_dir = TempDir::new().unwrap(); let file_path = temp_dir.path().join("test file.txt"); fs::write(&file_path, "test content").unwrap(); let mut cmd = Command::new(cargo_bin!("ntu")); cmd.arg("--dry-run").arg("-r").arg(temp_dir.path()); cmd.assert().success(); // File should still have spaces (not renamed) assert!(file_path.exists()); assert!(!temp_dir.path().join("test_file.txt").exists()); } #[test] fn test_actual_rename() { let temp_dir = TempDir::new().unwrap(); let file_path = temp_dir.path().join("test file.txt"); fs::write(&file_path, "test content").unwrap(); let mut cmd = Command::new(cargo_bin!("ntu")); cmd.arg("-r").arg(temp_dir.path()); cmd.assert().success(); // File should be renamed assert!(!file_path.exists()); assert!(temp_dir.path().join("test_file.txt").exists()); } #[test] fn test_hidden_files_preserved() { let temp_dir = TempDir::new().unwrap(); let file_path = temp_dir.path().join(".gitignore"); fs::write(&file_path, "*.tmp").unwrap(); let mut cmd = Command::new(cargo_bin!("ntu")); cmd.arg("-r").arg(temp_dir.path()); cmd.assert().success(); // Hidden file should not be renamed assert!(file_path.exists()); } #[test] fn test_hidden_file_with_spaces() { let temp_dir = TempDir::new().unwrap(); let file_path = temp_dir.path().join(".my config"); fs::write(&file_path, "config content").unwrap(); let mut cmd = Command::new(cargo_bin!("ntu")); cmd.arg("-r").arg(temp_dir.path()); cmd.assert().success(); // Hidden file should be renamed but keep leading dot assert!(!file_path.exists()); assert!(temp_dir.path().join(".my_config").exists()); } #[test] fn test_umlaut_conversion() { let temp_dir = TempDir::new().unwrap(); let file_path = temp_dir.path().join("Müller.txt"); fs::write(&file_path, "test").unwrap(); let mut cmd = Command::new(cargo_bin!("ntu")); cmd.arg("-r").arg(temp_dir.path()); cmd.assert().success(); // Umlaut should be converted assert!(!file_path.exists()); assert!(temp_dir.path().join("Mueller.txt").exists()); } #[test] fn test_double_extension() { let temp_dir = TempDir::new().unwrap(); let file_path = temp_dir.path().join("my archive.tar.gz"); fs::write(&file_path, "archive").unwrap(); let mut cmd = Command::new(cargo_bin!("ntu")); cmd.arg("-r").arg(temp_dir.path()); cmd.assert().success(); // Should keep .tar.gz intact assert!(!file_path.exists()); assert!(temp_dir.path().join("my_archive.tar.gz").exists()); } #[test] fn test_exclude_pattern() { let temp_dir = TempDir::new().unwrap(); let file1 = temp_dir.path().join("test file.txt"); let file2 = temp_dir.path().join("test file.tmp"); fs::write(&file1, "test1").unwrap(); fs::write(&file2, "test2").unwrap(); let mut cmd = Command::new(cargo_bin!("ntu")); cmd.arg("--exclude") .arg("*.tmp") .arg("-r") .arg(temp_dir.path()); cmd.assert().success(); // .txt should be renamed, .tmp should not assert!(!file1.exists()); assert!(temp_dir.path().join("test_file.txt").exists()); assert!(file2.exists()); // Excluded } #[test] fn test_quiet_mode() { let temp_dir = TempDir::new().unwrap(); let file_path = temp_dir.path().join("test file.txt"); fs::write(&file_path, "test").unwrap(); let mut cmd = Command::new(cargo_bin!("ntu")); cmd.arg("--quiet").arg("-r").arg(temp_dir.path()); cmd.assert() .success() .stdout(predicate::str::is_empty()); } #[test] fn test_multiple_paths() { let temp_dir1 = TempDir::new().unwrap(); let temp_dir2 = TempDir::new().unwrap(); let file1 = temp_dir1.path().join("file 1.txt"); let file2 = temp_dir2.path().join("file 2.txt"); fs::write(&file1, "test1").unwrap(); fs::write(&file2, "test2").unwrap(); let mut cmd = Command::new(cargo_bin!("ntu")); cmd.arg("-r").arg(temp_dir1.path()).arg(temp_dir2.path()); cmd.assert().success(); // Both files should be renamed assert!(temp_dir1.path().join("file_1.txt").exists()); assert!(temp_dir2.path().join("file_2.txt").exists()); } #[test] fn test_parentheses_removed() { let temp_dir = TempDir::new().unwrap(); let file_path = temp_dir.path().join("Document (1).txt"); fs::write(&file_path, "test").unwrap(); let mut cmd = Command::new(cargo_bin!("ntu")); cmd.arg("-r").arg(temp_dir.path()); cmd.assert().success(); // Parentheses should be replaced with underscores assert!(!file_path.exists()); assert!(temp_dir.path().join("Document_1.txt").exists()); } #[test] fn test_special_identifiers_preserved() { let temp_dir = TempDir::new().unwrap(); let file_path = temp_dir.path().join("C++ Guide.pdf"); fs::write(&file_path, "test").unwrap(); let mut cmd = Command::new(cargo_bin!("ntu")); cmd.arg("-r").arg(temp_dir.path()); cmd.assert().success(); // C++ should be preserved assert!(!file_path.exists()); assert!(temp_dir.path().join("C++_Guide.pdf").exists()); } #[test] fn test_non_recursive_default() { let temp_dir = TempDir::new().unwrap(); fs::create_dir(temp_dir.path().join("subdir")).unwrap(); let file1 = temp_dir.path().join("top file.txt"); let file2 = temp_dir.path().join("subdir").join("nested file.txt"); fs::write(&file1, "test1").unwrap(); fs::write(&file2, "test2").unwrap(); let mut cmd = Command::new(cargo_bin!("ntu")); cmd.arg(temp_dir.path()); cmd.assert().success(); // Top-Level umbenannt assert!(temp_dir.path().join("top_file.txt").exists()); // Nested NICHT umbenannt (non-recursive) assert!(file2.exists()); } #[test] fn test_recursive_flag() { let temp_dir = TempDir::new().unwrap(); fs::create_dir(temp_dir.path().join("subdir")).unwrap(); let file1 = temp_dir.path().join("top file.txt"); let file2 = temp_dir.path().join("subdir").join("nested file.txt"); fs::write(&file1, "test1").unwrap(); fs::write(&file2, "test2").unwrap(); let mut cmd = Command::new(cargo_bin!("ntu")); cmd.arg("-r").arg(temp_dir.path()); cmd.assert().success(); // Beide umbenannt assert!(temp_dir.path().join("top_file.txt").exists()); assert!(temp_dir.path().join("subdir").join("nested_file.txt").exists()); } #[test] fn test_conf_option_valid_file() { let temp_dir = TempDir::new().unwrap(); let config_file = temp_dir.path().join("custom.toml"); fs::write(&config_file, "[replacements]\n\"test\" = \"xyz\"").unwrap(); let file_path = temp_dir.path().join("test_file.txt"); fs::write(&file_path, "content").unwrap(); let mut cmd = Command::new(cargo_bin!("ntu")); cmd.arg("--conf").arg(&config_file).arg(temp_dir.path()); cmd.assert().success(); assert!(temp_dir.path().join("xyz_file.txt").exists()); } #[test] fn test_conf_option_missing_file() { let temp_dir = TempDir::new().unwrap(); let nonexistent = temp_dir.path().join("nonexistent.toml"); let mut cmd = Command::new(cargo_bin!("ntu")); cmd.arg("--conf").arg(&nonexistent).arg(temp_dir.path()); cmd.assert() .failure() .stderr(predicate::str::contains("nicht gefunden")); } #[test] fn test_sequence_lower() { let temp_dir = TempDir::new().unwrap(); let config_file = temp_dir.path().join("empty.toml"); fs::write(&config_file, "[replacements]\n").unwrap(); let file_path = temp_dir.path().join("Test File.txt"); fs::write(&file_path, "content").unwrap(); let mut cmd = Command::new(cargo_bin!("ntu")); cmd.arg("--conf") .arg(&config_file) .arg("-s") .arg("lower") .arg(temp_dir.path()); cmd.assert().success(); assert!(temp_dir.path().join("test_file.txt").exists()); } #[test] fn test_sequence_upper() { let temp_dir = TempDir::new().unwrap(); let config_file = temp_dir.path().join("empty.toml"); fs::write(&config_file, "[replacements]\n").unwrap(); let file_path = temp_dir.path().join("test file.txt"); fs::write(&file_path, "content").unwrap(); let mut cmd = Command::new(cargo_bin!("ntu")); cmd.arg("--conf") .arg(&config_file) .arg("-s") .arg("upper") .arg(temp_dir.path()); cmd.assert().success(); assert!(temp_dir.path().join("TEST_FILE.TXT").exists()); } #[test] fn test_sequence_minimal() { let temp_dir = TempDir::new().unwrap(); let config_file = temp_dir.path().join("empty.toml"); fs::write(&config_file, "[replacements]\n").unwrap(); let file_path = temp_dir.path().join("Müller Datei.txt"); fs::write(&file_path, "content").unwrap(); let mut cmd = Command::new(cargo_bin!("ntu")); cmd.arg("--conf") .arg(&config_file) .arg("-s") .arg("minimal") .arg(temp_dir.path()); cmd.assert().success(); // Umlaute bleiben erhalten, nur Leerzeichen ersetzt assert!(temp_dir.path().join("Müller_Datei.txt").exists()); } #[test] fn test_sequence_utf8() { let temp_dir = TempDir::new().unwrap(); let config_file = temp_dir.path().join("empty.toml"); fs::write(&config_file, "[replacements]\n").unwrap(); let file_path = temp_dir.path().join("schön (1).txt"); fs::write(&file_path, "content").unwrap(); let mut cmd = Command::new(cargo_bin!("ntu")); cmd.arg("--conf") .arg(&config_file) .arg("-s") .arg("utf-8") .arg(temp_dir.path()); cmd.assert().success(); // Umlaut bleibt, Klammern entfernt assert!(temp_dir.path().join("schön_1.txt").exists()); } #[test] fn test_list_sequences() { let mut cmd = Command::new(cargo_bin!("ntu")); cmd.arg("-L"); cmd.assert() .success() .stdout(predicate::str::contains("default")) .stdout(predicate::str::contains("lower")) .stdout(predicate::str::contains("upper")) .stdout(predicate::str::contains("minimal")) .stdout(predicate::str::contains("utf-8")); } #[test] fn test_list_sequences_verbose() { let mut cmd = Command::new(cargo_bin!("ntu")); cmd.arg("-L").arg("-v"); cmd.assert() .success() .stdout(predicate::str::contains("Umlauts → ASCII")) .stdout(predicate::str::contains("Case transform")); } #[test] fn test_invalid_sequence() { let temp_dir = TempDir::new().unwrap(); let mut cmd = Command::new(cargo_bin!("ntu")); cmd.arg("-s").arg("invalid_seq").arg(temp_dir.path()); cmd.assert() .failure() .stderr(predicate::str::contains("Unbekannte Sequence")); } #[test] fn test_sequence_default_explicit() { let temp_dir = TempDir::new().unwrap(); let config_file = temp_dir.path().join("empty.toml"); fs::write(&config_file, "[replacements]\n").unwrap(); let file_path = temp_dir.path().join("Müller File.txt"); fs::write(&file_path, "content").unwrap(); let mut cmd = Command::new(cargo_bin!("ntu")); cmd.arg("--conf") .arg(&config_file) .arg("-s") .arg("default") .arg(temp_dir.path()); cmd.assert().success(); // Default: Umlaut → ASCII assert!(temp_dir.path().join("Mueller_File.txt").exists()); } #[test] fn test_max_depth_option() { let temp_dir = TempDir::new().unwrap(); // Erstelle Verzeichnisstruktur mit 4 Ebenen // depth 0: temp_dir (root) // depth 1: level 1 // depth 2: level 2 // depth 3: level 3 let level1 = temp_dir.path().join("level 1"); let level2 = level1.join("level 2"); let level3 = level2.join("level 3"); fs::create_dir_all(&level3).unwrap(); fs::write(level1.join("file 1.txt"), "content1").unwrap(); fs::write(level2.join("file 2.txt"), "content2").unwrap(); fs::write(level3.join("file 3.txt"), "content3").unwrap(); let mut cmd = Command::new(cargo_bin!("ntu")); cmd.arg("-r") .arg("--max-depth") .arg("3") // Gehe bis depth 3 .arg(temp_dir.path()); cmd.assert().success(); // Level 1, 2 und deren Dateien sollten umbenannt sein assert!(temp_dir.path().join("level_1").exists()); assert!(temp_dir.path().join("level_1/file_1.txt").exists()); assert!(temp_dir.path().join("level_1/level_2").exists()); assert!(temp_dir.path().join("level_1/level_2/file_2.txt").exists()); assert!(temp_dir.path().join("level_1/level_2/level_3").exists()); // Level 3/file sollte NICHT umbenannt sein (depth 4 > max 3) assert!(temp_dir.path().join("level_1/level_2/level_3/file 3.txt").exists()); } #[test] fn test_max_depth_requires_recursive() { let temp_dir = TempDir::new().unwrap(); // --max-depth ohne -r sollte fehlschlagen let mut cmd = Command::new(cargo_bin!("ntu")); cmd.arg("--max-depth") .arg("2") .arg(temp_dir.path()); let assert = cmd.assert().failure(); // Prüfe dass die Fehlermeldung "required" oder "recursive" enthält assert.stderr( predicate::str::contains("required") .or(predicate::str::contains("recursive")) ); } #[test] #[cfg(unix)] fn test_symlinks_default_behavior() { use std::os::unix::fs::symlink; let temp_dir = TempDir::new().unwrap(); let file_path = temp_dir.path().join("real file.txt"); let link_path = temp_dir.path().join("link to file"); fs::write(&file_path, "content").unwrap(); symlink(&file_path, &link_path).unwrap(); let mut cmd = Command::new(cargo_bin!("ntu")); cmd.arg(temp_dir.path()); cmd.assert().success(); // Echte Datei sollte umbenannt sein assert!(temp_dir.path().join("real_file.txt").exists()); // Symlink sollte NICHT umbenannt sein (default behavior) // Verwende symlink_metadata() um zu prüfen ob der Link selbst existiert let link_unchanged = temp_dir.path().join("link to file"); assert!(link_unchanged.symlink_metadata().is_ok()); assert!(!temp_dir.path().join("link_to_file").symlink_metadata().is_ok()); } #[test] #[cfg(unix)] fn test_symlinks_with_special_flag() { use std::os::unix::fs::symlink; let temp_dir = TempDir::new().unwrap(); let file_path = temp_dir.path().join("real_file.txt"); let link_path = temp_dir.path().join("link to file"); fs::write(&file_path, "content").unwrap(); symlink(&file_path, &link_path).unwrap(); let mut cmd = Command::new(cargo_bin!("ntu")); cmd.arg("--special").arg(temp_dir.path()); cmd.assert().success(); // Symlink sollte MIT --special umbenannt sein assert!(temp_dir.path().join("link_to_file").exists()); } #[test] #[cfg(unix)] fn test_symlinks_not_followed() { use std::os::unix::fs::symlink; let temp_dir = TempDir::new().unwrap(); let target_dir = TempDir::new().unwrap(); let target_file = target_dir.path().join("target file.txt"); fs::write(&target_file, "content").unwrap(); let link_path = temp_dir.path().join("link_to_dir"); symlink(target_dir.path(), &link_path).unwrap(); let mut cmd = Command::new(cargo_bin!("ntu")); cmd.arg("-r").arg("--special").arg(temp_dir.path()); cmd.assert().success(); // Symlink-Name sollte bereinigt sein assert!(temp_dir.path().join("link_to_dir").exists()); // Aber das Ziel sollte NICHT verändert sein (Link nicht gefolgt) assert!(target_dir.path().join("target file.txt").exists()); assert!(!target_dir.path().join("target_file.txt").exists()); }