From bbf88fc4fc94d84ae2f2bcac39ac6b32f4eb1760 Mon Sep 17 00:00:00 2001 From: glitchySid Date: Mon, 29 Dec 2025 00:05:08 +0530 Subject: [PATCH] refactor: Separate tests into dedicated files to reduce source file sizes - Extract all tests from cache.rs, files.rs, config.rs to separate test files - Create cache_tests.rs, files_tests.rs, config_tests.rs - Reduce cache.rs: 421 -> 220 lines (-191 lines) - Reduce files.rs: 390 -> 264 lines (-126 lines) - Reduce config.rs: 354 -> 275 lines (-79 lines) - Total reduction: 396 lines from main source files - All 31 tests still passing --- .env.example | 2 - src/cache.rs | 205 +------------------------------------------- src/cache_tests.rs | 201 +++++++++++++++++++++++++++++++++++++++++++ src/config.rs | 83 +----------------- src/config_tests.rs | 79 +++++++++++++++++ src/files.rs | 130 +--------------------------- src/files_tests.rs | 126 +++++++++++++++++++++++++++ 7 files changed, 412 insertions(+), 414 deletions(-) delete mode 100644 .env.example create mode 100644 src/cache_tests.rs create mode 100644 src/config_tests.rs create mode 100644 src/files_tests.rs diff --git a/.env.example b/.env.example deleted file mode 100644 index 61f32f1..0000000 --- a/.env.example +++ /dev/null @@ -1,2 +0,0 @@ -GEMINI_API_KEY= -DOWNLOAD_FOLDER= diff --git a/src/cache.rs b/src/cache.rs index e8b8507..79a8416 100644 --- a/src/cache.rs +++ b/src/cache.rs @@ -216,206 +216,5 @@ impl Cache { } #[cfg(test)] -mod tests { - use super::*; - use crate::files::FileCategory; - use std::fs::File; - use std::io::Write; - - #[test] - fn test_cache_new() { - let cache = Cache::new(); - assert_eq!(cache.max_entries, 1000); - assert_eq!(cache.entries.len(), 0); - } - - #[test] - fn test_cache_with_max_entries() { - let cache = Cache::with_max_entries(5); - assert_eq!(cache.max_entries, 5); - } - - #[test] - fn test_cache_default() { - let cache = Cache::default(); - assert_eq!(cache.max_entries, 1000); - } - - #[test] - fn test_cache_response_and_retrieve() { - let temp_dir = tempfile::tempdir().unwrap(); - let base_path = temp_dir.path(); - - let mut cache = Cache::new(); - let filenames = vec!["file1.txt".to_string(), "file2.txt".to_string()]; - - for filename in &filenames { - let file_path = base_path.join(filename); - let mut file = File::create(&file_path).unwrap(); - file.write_all(b"test content").unwrap(); - } - - let response = OrganizationPlan { - files: vec![FileCategory { - filename: "file1.txt".to_string(), - category: "Documents".to_string(), - sub_category: "Text".to_string(), - }], - }; - - cache.cache_response(&filenames, response.clone(), base_path); - - let cached = cache.get_cached_response(&filenames, base_path); - assert!(cached.is_some()); - assert_eq!(cached.unwrap().files[0].category, "Documents"); - } - - #[test] - fn test_cache_response_file_changed() { - let temp_dir = tempfile::tempdir().unwrap(); - let base_path = temp_dir.path(); - - let mut cache = Cache::new(); - let filenames = vec!["file1.txt".to_string()]; - - let file_path = base_path.join("file1.txt"); - let mut file = File::create(&file_path).unwrap(); - file.write_all(b"original content").unwrap(); - - let response = OrganizationPlan { - files: vec![FileCategory { - filename: "file1.txt".to_string(), - category: "Documents".to_string(), - sub_category: "Text".to_string(), - }], - }; - - cache.cache_response(&filenames, response.clone(), base_path); - - std::thread::sleep(std::time::Duration::from_millis(100)); - - let mut file = File::create(&file_path).unwrap(); - file.write_all(b"modified content longer than original").unwrap(); - - let cached = cache.get_cached_response(&filenames, base_path); - assert!(cached.is_none()); - } - - #[test] - fn test_cache_save_and_load() { - let temp_dir = tempfile::tempdir().unwrap(); - let cache_path = temp_dir.path().join("cache.json"); - let base_path = temp_dir.path(); - - let mut cache = Cache::new(); - let filenames = vec!["file1.txt".to_string()]; - - let file_path = base_path.join("file1.txt"); - let mut file = File::create(&file_path).unwrap(); - file.write_all(b"test").unwrap(); - - let response = OrganizationPlan { - files: vec![FileCategory { - filename: "file1.txt".to_string(), - category: "Documents".to_string(), - sub_category: "Text".to_string(), - }], - }; - - cache.cache_response(&filenames, response, base_path); - cache.save(&cache_path).unwrap(); - - let loaded_cache = Cache::load_or_create(&cache_path); - assert_eq!(loaded_cache.entries.len(), 1); - } - - #[test] - fn test_cache_cleanup_old_entries() { - let temp_dir = tempfile::tempdir().unwrap(); - let base_path = temp_dir.path(); - - let mut cache = Cache::new(); - let filenames = vec!["file1.txt".to_string()]; - - let file_path = base_path.join("file1.txt"); - let mut file = File::create(&file_path).unwrap(); - file.write_all(b"test").unwrap(); - - let response = OrganizationPlan { - files: vec![FileCategory { - filename: "file1.txt".to_string(), - category: "Documents".to_string(), - sub_category: "Text".to_string(), - }], - }; - - cache.cache_response(&filenames, response, base_path); - - cache.cleanup_old_entries(0); - assert_eq!(cache.entries.len(), 0); - } - - #[test] - fn test_cache_max_entries_eviction() { - let temp_dir = tempfile::tempdir().unwrap(); - let base_path = temp_dir.path(); - - let mut cache = Cache::with_max_entries(2); - - for i in 1..=3 { - let filename = format!("file{}.txt", i); - let file_path = base_path.join(&filename); - let mut file = File::create(&file_path).unwrap(); - file.write_all(b"test").unwrap(); - - let response = OrganizationPlan { - files: vec![FileCategory { - filename: filename.clone(), - category: "Documents".to_string(), - sub_category: "Text".to_string(), - }], - }; - - cache.cache_response(&vec![filename], response, base_path); - } - - assert_eq!(cache.entries.len(), 2); - } - - #[test] - fn test_cache_serialization() { - let cache = Cache::new(); - let json = serde_json::to_string(&cache).unwrap(); - let deserialized: Cache = serde_json::from_str(&json).unwrap(); - assert_eq!(cache.max_entries, deserialized.max_entries); - } - - #[test] - fn test_file_metadata_equality() { - let temp_dir = tempfile::tempdir().unwrap(); - let file_path = temp_dir.path().join("test.txt"); - - let mut file = File::create(&file_path).unwrap(); - file.write_all(b"test content").unwrap(); - - let metadata1 = Cache::get_file_metadata(&file_path).unwrap(); - let metadata2 = Cache::get_file_metadata(&file_path).unwrap(); - - assert_eq!(metadata1, metadata2); - } - - #[test] - fn test_cache_key_generation() { - let cache = Cache::new(); - let filenames1 = vec!["a.txt".to_string(), "b.txt".to_string()]; - let filenames2 = vec!["b.txt".to_string(), "a.txt".to_string()]; - let filenames3 = vec!["c.txt".to_string()]; - - let key1 = cache.generate_cache_key(&filenames1); - let key2 = cache.generate_cache_key(&filenames2); - let key3 = cache.generate_cache_key(&filenames3); - - assert_eq!(key1, key2); - assert_ne!(key1, key3); - } -} +#[path = "cache_tests.rs"] +mod tests; diff --git a/src/cache_tests.rs b/src/cache_tests.rs new file mode 100644 index 0000000..28769fd --- /dev/null +++ b/src/cache_tests.rs @@ -0,0 +1,201 @@ +use crate::cache::*; +use crate::files::FileCategory; +use std::fs::File; +use std::io::Write; + +#[test] +fn test_cache_new() { + let cache = Cache::new(); + assert_eq!(cache.max_entries, 1000); + assert_eq!(cache.entries.len(), 0); +} + +#[test] +fn test_cache_with_max_entries() { + let cache = Cache::with_max_entries(5); + assert_eq!(cache.max_entries, 5); +} + +#[test] +fn test_cache_default() { + let cache = Cache::default(); + assert_eq!(cache.max_entries, 1000); +} + +#[test] +fn test_cache_response_and_retrieve() { + let temp_dir = tempfile::tempdir().unwrap(); + let base_path = temp_dir.path(); + + let mut cache = Cache::new(); + let filenames = vec!["file1.txt".to_string(), "file2.txt".to_string()]; + + for filename in &filenames { + let file_path = base_path.join(filename); + let mut file = File::create(&file_path).unwrap(); + file.write_all(b"test content").unwrap(); + } + + let response = OrganizationPlan { + files: vec![FileCategory { + filename: "file1.txt".to_string(), + category: "Documents".to_string(), + sub_category: "Text".to_string(), + }], + }; + + cache.cache_response(&filenames, response.clone(), base_path); + + let cached = cache.get_cached_response(&filenames, base_path); + assert!(cached.is_some()); + assert_eq!(cached.unwrap().files[0].category, "Documents"); +} + +#[test] +fn test_cache_response_file_changed() { + let temp_dir = tempfile::tempdir().unwrap(); + let base_path = temp_dir.path(); + + let mut cache = Cache::new(); + let filenames = vec!["file1.txt".to_string()]; + + let file_path = base_path.join("file1.txt"); + let mut file = File::create(&file_path).unwrap(); + file.write_all(b"original content").unwrap(); + + let response = OrganizationPlan { + files: vec![FileCategory { + filename: "file1.txt".to_string(), + category: "Documents".to_string(), + sub_category: "Text".to_string(), + }], + }; + + cache.cache_response(&filenames, response.clone(), base_path); + + std::thread::sleep(std::time::Duration::from_millis(100)); + + let mut file = File::create(&file_path).unwrap(); + file.write_all(b"modified content longer than original").unwrap(); + + let cached = cache.get_cached_response(&filenames, base_path); + assert!(cached.is_none()); +} + +#[test] +fn test_cache_save_and_load() { + let temp_dir = tempfile::tempdir().unwrap(); + let cache_path = temp_dir.path().join("cache.json"); + let base_path = temp_dir.path(); + + let mut cache = Cache::new(); + let filenames = vec!["file1.txt".to_string()]; + + let file_path = base_path.join("file1.txt"); + let mut file = File::create(&file_path).unwrap(); + file.write_all(b"test").unwrap(); + + let response = OrganizationPlan { + files: vec![FileCategory { + filename: "file1.txt".to_string(), + category: "Documents".to_string(), + sub_category: "Text".to_string(), + }], + }; + + cache.cache_response(&filenames, response, base_path); + cache.save(&cache_path).unwrap(); + + let loaded_cache = Cache::load_or_create(&cache_path); + assert_eq!(loaded_cache.entries.len(), 1); +} + +#[test] +fn test_cache_cleanup_old_entries() { + let temp_dir = tempfile::tempdir().unwrap(); + let base_path = temp_dir.path(); + + let mut cache = Cache::new(); + let filenames = vec!["file1.txt".to_string()]; + + let file_path = base_path.join("file1.txt"); + let mut file = File::create(&file_path).unwrap(); + file.write_all(b"test").unwrap(); + + let response = OrganizationPlan { + files: vec![FileCategory { + filename: "file1.txt".to_string(), + category: "Documents".to_string(), + sub_category: "Text".to_string(), + }], + }; + + cache.cache_response(&filenames, response, base_path); + + cache.cleanup_old_entries(0); + assert_eq!(cache.entries.len(), 0); +} + +#[test] +fn test_cache_max_entries_eviction() { + let temp_dir = tempfile::tempdir().unwrap(); + let base_path = temp_dir.path(); + + let mut cache = Cache::with_max_entries(2); + + for i in 1..=3 { + let filename = format!("file{}.txt", i); + let file_path = base_path.join(&filename); + let mut file = File::create(&file_path).unwrap(); + file.write_all(b"test").unwrap(); + + let response = OrganizationPlan { + files: vec![FileCategory { + filename: filename.clone(), + category: "Documents".to_string(), + sub_category: "Text".to_string(), + }], + }; + + cache.cache_response(&vec![filename], response, base_path); + } + + assert_eq!(cache.entries.len(), 2); +} + +#[test] +fn test_cache_serialization() { + let cache = Cache::new(); + let json = serde_json::to_string(&cache).unwrap(); + let deserialized: Cache = serde_json::from_str(&json).unwrap(); + assert_eq!(cache.max_entries, deserialized.max_entries); +} + +#[test] +fn test_file_metadata_equality() { + let temp_dir = tempfile::tempdir().unwrap(); + let file_path = temp_dir.path().join("test.txt"); + + let mut file = File::create(&file_path).unwrap(); + file.write_all(b"test content").unwrap(); + + let metadata1 = Cache::get_file_metadata(&file_path).unwrap(); + let metadata2 = Cache::get_file_metadata(&file_path).unwrap(); + + assert_eq!(metadata1, metadata2); +} + +#[test] +fn test_cache_key_generation() { + let cache = Cache::new(); + let filenames1 = vec!["a.txt".to_string(), "b.txt".to_string()]; + let filenames2 = vec!["b.txt".to_string(), "a.txt".to_string()]; + let filenames3 = vec!["c.txt".to_string()]; + + let key1 = cache.generate_cache_key(&filenames1); + let key2 = cache.generate_cache_key(&filenames2); + let key3 = cache.generate_cache_key(&filenames3); + + assert_eq!(key1, key2); + assert_ne!(key1, key3); +} diff --git a/src/config.rs b/src/config.rs index e93dd31..22d5596 100644 --- a/src/config.rs +++ b/src/config.rs @@ -271,84 +271,5 @@ fn expand_home(path: &str) -> String { } #[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_config_serialization() { - let config = Config { - api_key: "test_key_12345".to_string(), - download_folder: PathBuf::from("/test/path"), - }; - - let toml_str = toml::to_string_pretty(&config).unwrap(); - assert!(toml_str.contains("test_key_12345")); - - let deserialized: Config = toml::from_str(&toml_str).unwrap(); - assert_eq!(config.api_key, deserialized.api_key); - assert_eq!(config.download_folder, deserialized.download_folder); - } - - #[test] - fn test_validate_api_key_valid() { - assert!(validate_api_key("AIzaSyB1234567890123456789012345678")); - assert!(validate_api_key("AIzaSyB123456789012345678901234567890")); - } - - #[test] - fn test_validate_api_key_invalid() { - assert!(!validate_api_key("")); - assert!(!validate_api_key("invalid_key")); - assert!(!validate_api_key("BizaSyB1234567890123456789012345678")); - assert!(!validate_api_key("short")); - } - - #[test] - fn test_validate_folder_path_valid() { - let temp_dir = tempfile::tempdir().unwrap(); - assert!(validate_folder_path(temp_dir.path())); - } - - #[test] - fn test_validate_folder_path_invalid() { - assert!(!validate_folder_path(Path::new("/nonexistent/path/that/does/not/exist"))); - - let temp_file = tempfile::NamedTempFile::new().unwrap(); - assert!(!validate_folder_path(temp_file.path())); - } - - #[test] - fn test_expand_home_with_tilde() { - if let Some(base_dirs) = BaseDirs::new() { - let home = base_dirs.home_dir(); - let expanded = expand_home("~/test/path"); - assert!(expanded.starts_with(home.to_string_lossy().as_ref())); - assert!(expanded.contains("test/path")); - } - } - - #[test] - fn test_expand_home_without_tilde() { - let expanded = expand_home("/absolute/path"); - assert_eq!(expanded, "/absolute/path"); - - let expanded = expand_home("relative/path"); - assert_eq!(expanded, "relative/path"); - } - - #[test] - fn test_get_default_downloads_folder() { - let path = get_default_downloads_folder(); - assert!(path.ends_with("Downloads")); - } - - #[test] - fn test_config_empty_api_key_error() { - let config = Config { - api_key: String::new(), - download_folder: PathBuf::from("/test/path"), - }; - - assert!(config.api_key.is_empty()); - } -} +#[path = "config_tests.rs"] +mod tests; diff --git a/src/config_tests.rs b/src/config_tests.rs new file mode 100644 index 0000000..57e1c53 --- /dev/null +++ b/src/config_tests.rs @@ -0,0 +1,79 @@ +use crate::config::*; + +#[test] +fn test_config_serialization() { + let config = Config { + api_key: "test_key_12345".to_string(), + download_folder: PathBuf::from("/test/path"), + }; + + let toml_str = toml::to_string_pretty(&config).unwrap(); + assert!(toml_str.contains("test_key_12345")); + + let deserialized: Config = toml::from_str(&toml_str).unwrap(); + assert_eq!(config.api_key, deserialized.api_key); + assert_eq!(config.download_folder, deserialized.download_folder); +} + +#[test] +fn test_validate_api_key_valid() { + assert!(validate_api_key("AIzaSyB1234567890123456789012345678")); + assert!(validate_api_key("AIzaSyB123456789012345678901234567890")); +} + +#[test] +fn test_validate_api_key_invalid() { + assert!(!validate_api_key("")); + assert!(!validate_api_key("invalid_key")); + assert!(!validate_api_key("BizaSyB1234567890123456789012345678")); + assert!(!validate_api_key("short")); +} + +#[test] +fn test_validate_folder_path_valid() { + let temp_dir = tempfile::tempdir().unwrap(); + assert!(validate_folder_path(temp_dir.path())); +} + +#[test] +fn test_validate_folder_path_invalid() { + assert!(!validate_folder_path(Path::new("/nonexistent/path/that/does/not/exist"))); + + let temp_file = tempfile::NamedTempFile::new().unwrap(); + assert!(!validate_folder_path(temp_file.path())); +} + +#[test] +fn test_expand_home_with_tilde() { + if let Some(base_dirs) = BaseDirs::new() { + let home = base_dirs.home_dir(); + let expanded = expand_home("~/test/path"); + assert!(expanded.starts_with(home.to_string_lossy().as_ref())); + assert!(expanded.contains("test/path")); + } +} + +#[test] +fn test_expand_home_without_tilde() { + let expanded = expand_home("/absolute/path"); + assert_eq!(expanded, "/absolute/path"); + + let expanded = expand_home("relative/path"); + assert_eq!(expanded, "relative/path"); +} + +#[test] +fn test_get_default_downloads_folder() { + let path = get_default_downloads_folder(); + assert!(path.ends_with("Downloads")); +} + +#[test] +fn test_config_empty_api_key_error() { + let config = Config { + api_key: String::new(), + download_folder: PathBuf::from("/test/path"), + }; + + assert!(config.api_key.is_empty()); +} diff --git a/src/files.rs b/src/files.rs index 326fd3a..c3a0555 100644 --- a/src/files.rs +++ b/src/files.rs @@ -260,131 +260,5 @@ pub fn read_file_sample(path: &Path, max_chars: usize) -> Option { } #[cfg(test)] -mod tests { - use super::*; - use std::fs::{self, File}; - use std::io::Write; - - #[test] - fn test_is_text_file_with_text_extensions() { - assert!(is_text_file(Path::new("test.txt"))); - assert!(is_text_file(Path::new("test.rs"))); - assert!(is_text_file(Path::new("test.py"))); - assert!(is_text_file(Path::new("test.md"))); - assert!(is_text_file(Path::new("test.json"))); - } - - #[test] - fn test_is_text_file_with_binary_extensions() { - assert!(!is_text_file(Path::new("test.exe"))); - assert!(!is_text_file(Path::new("test.bin"))); - assert!(!is_text_file(Path::new("test.jpg"))); - assert!(!is_text_file(Path::new("test.pdf"))); - } - - #[test] - fn test_is_text_file_case_insensitive() { - assert!(is_text_file(Path::new("test.TXT"))); - assert!(is_text_file(Path::new("test.RS"))); - assert!(is_text_file(Path::new("test.Py"))); - } - - #[test] - fn test_file_batch_from_path() { - let temp_dir = tempfile::tempdir().unwrap(); - let dir_path = temp_dir.path(); - - File::create(dir_path.join("file1.txt")).unwrap(); - File::create(dir_path.join("file2.rs")).unwrap(); - fs::create_dir(dir_path.join("subdir")).unwrap(); - - let batch = FileBatch::from_path(dir_path.to_path_buf()); - assert_eq!(batch.count(), 2); - assert!(batch.filenames.contains(&"file1.txt".to_string())); - assert!(batch.filenames.contains(&"file2.rs".to_string())); - } - - #[test] - fn test_file_batch_from_path_nonexistent() { - let batch = FileBatch::from_path(PathBuf::from("/nonexistent/path")); - assert_eq!(batch.count(), 0); - } - - #[test] - fn test_read_file_sample() { - let temp_dir = tempfile::tempdir().unwrap(); - let file_path = temp_dir.path().join("test.txt"); - - let mut file = File::create(&file_path).unwrap(); - file.write_all(b"Hello, World!").unwrap(); - - let content = read_file_sample(&file_path, 1000); - assert_eq!(content, Some("Hello, World!".to_string())); - } - - #[test] - fn test_read_file_sample_with_limit() { - let temp_dir = tempfile::tempdir().unwrap(); - let file_path = temp_dir.path().join("test.txt"); - - let mut file = File::create(&file_path).unwrap(); - file.write_all(b"Hello, World! This is a long text.").unwrap(); - - let content = read_file_sample(&file_path, 5); - assert_eq!(content, Some("Hello".to_string())); - } - - #[test] - fn test_read_file_sample_binary_file() { - let temp_dir = tempfile::tempdir().unwrap(); - let file_path = temp_dir.path().join("test.bin"); - - let mut file = File::create(&file_path).unwrap(); - file.write_all(&[0x00, 0xFF, 0x80, 0x90]).unwrap(); - - let content = read_file_sample(&file_path, 1000); - assert_eq!(content, None); - } - - #[test] - fn test_read_file_sample_nonexistent() { - let content = read_file_sample(Path::new("/nonexistent/file.txt"), 1000); - assert_eq!(content, None); - } - - #[test] - fn test_organization_plan_serialization() { - let plan = OrganizationPlan { - files: vec![ - FileCategory { - filename: "test.txt".to_string(), - category: "Documents".to_string(), - sub_category: "Text".to_string(), - }, - ], - }; - - let json = serde_json::to_string(&plan).unwrap(); - assert!(json.contains("test.txt")); - assert!(json.contains("Documents")); - - let deserialized: OrganizationPlan = serde_json::from_str(&json).unwrap(); - assert_eq!(deserialized.files[0].filename, "test.txt"); - } - - #[test] - fn test_file_category_serialization() { - let fc = FileCategory { - filename: "file.rs".to_string(), - category: "Code".to_string(), - sub_category: "Rust".to_string(), - }; - - let json = serde_json::to_string(&fc).unwrap(); - let deserialized: FileCategory = serde_json::from_str(&json).unwrap(); - - assert_eq!(fc.filename, deserialized.filename); - assert_eq!(fc.category, deserialized.category); - assert_eq!(fc.sub_category, deserialized.sub_category); - } -} +#[path = "files_tests.rs"] +mod tests; diff --git a/src/files_tests.rs b/src/files_tests.rs new file mode 100644 index 0000000..07ca7ff --- /dev/null +++ b/src/files_tests.rs @@ -0,0 +1,126 @@ +use crate::files::*; +use std::fs::{self, File}; +use std::io::Write; + +#[test] +fn test_is_text_file_with_text_extensions() { + assert!(is_text_file(Path::new("test.txt"))); + assert!(is_text_file(Path::new("test.rs"))); + assert!(is_text_file(Path::new("test.py"))); + assert!(is_text_file(Path::new("test.md"))); + assert!(is_text_file(Path::new("test.json"))); +} + +#[test] +fn test_is_text_file_with_binary_extensions() { + assert!(!is_text_file(Path::new("test.exe"))); + assert!(!is_text_file(Path::new("test.bin"))); + assert!(!is_text_file(Path::new("test.jpg"))); + assert!(!is_text_file(Path::new("test.pdf"))); +} + +#[test] +fn test_is_text_file_case_insensitive() { + assert!(is_text_file(Path::new("test.TXT"))); + assert!(is_text_file(Path::new("test.RS"))); + assert!(is_text_file(Path::new("test.Py"))); +} + +#[test] +fn test_file_batch_from_path() { + let temp_dir = tempfile::tempdir().unwrap(); + let dir_path = temp_dir.path(); + + File::create(dir_path.join("file1.txt")).unwrap(); + File::create(dir_path.join("file2.rs")).unwrap(); + fs::create_dir(dir_path.join("subdir")).unwrap(); + + let batch = FileBatch::from_path(dir_path.to_path_buf()); + assert_eq!(batch.count(), 2); + assert!(batch.filenames.contains(&"file1.txt".to_string())); + assert!(batch.filenames.contains(&"file2.rs".to_string())); +} + +#[test] +fn test_file_batch_from_path_nonexistent() { + let batch = FileBatch::from_path(PathBuf::from("/nonexistent/path")); + assert_eq!(batch.count(), 0); +} + +#[test] +fn test_read_file_sample() { + let temp_dir = tempfile::tempdir().unwrap(); + let file_path = temp_dir.path().join("test.txt"); + + let mut file = File::create(&file_path).unwrap(); + file.write_all(b"Hello, World!").unwrap(); + + let content = read_file_sample(&file_path, 1000); + assert_eq!(content, Some("Hello, World!".to_string())); +} + +#[test] +fn test_read_file_sample_with_limit() { + let temp_dir = tempfile::tempdir().unwrap(); + let file_path = temp_dir.path().join("test.txt"); + + let mut file = File::create(&file_path).unwrap(); + file.write_all(b"Hello, World! This is a long text.").unwrap(); + + let content = read_file_sample(&file_path, 5); + assert_eq!(content, Some("Hello".to_string())); +} + +#[test] +fn test_read_file_sample_binary_file() { + let temp_dir = tempfile::tempdir().unwrap(); + let file_path = temp_dir.path().join("test.bin"); + + let mut file = File::create(&file_path).unwrap(); + file.write_all(&[0x00, 0xFF, 0x80, 0x90]).unwrap(); + + let content = read_file_sample(&file_path, 1000); + assert_eq!(content, None); +} + +#[test] +fn test_read_file_sample_nonexistent() { + let content = read_file_sample(Path::new("/nonexistent/file.txt"), 1000); + assert_eq!(content, None); +} + +#[test] +fn test_organization_plan_serialization() { + let plan = OrganizationPlan { + files: vec![ + FileCategory { + filename: "test.txt".to_string(), + category: "Documents".to_string(), + sub_category: "Text".to_string(), + }, + ], + }; + + let json = serde_json::to_string(&plan).unwrap(); + assert!(json.contains("test.txt")); + assert!(json.contains("Documents")); + + let deserialized: OrganizationPlan = serde_json::from_str(&json).unwrap(); + assert_eq!(deserialized.files[0].filename, "test.txt"); +} + +#[test] +fn test_file_category_serialization() { + let fc = FileCategory { + filename: "file.rs".to_string(), + category: "Code".to_string(), + sub_category: "Rust".to_string(), + }; + + let json = serde_json::to_string(&fc).unwrap(); + let deserialized: FileCategory = serde_json::from_str(&json).unwrap(); + + assert_eq!(fc.filename, deserialized.filename); + assert_eq!(fc.category, deserialized.category); + assert_eq!(fc.sub_category, deserialized.sub_category); +}