generated from nhcarrigan/template
feat: add tests and assert coverage (#71)
### Explanation _No response_ ### Issue _No response_ ### Attestations - [ ] I have read and agree to the [Code of Conduct](https://docs.nhcarrigan.com/community/coc/) - [ ] I have read and agree to the [Community Guidelines](https://docs.nhcarrigan.com/community/guide/). - [ ] My contribution complies with the [Contributor Covenant](https://docs.nhcarrigan.com/dev/covenant/). ### Dependencies - [ ] I have pinned the dependencies to a specific patch version. ### Style - [ ] I have run the linter and resolved any errors. - [ ] My pull request uses an appropriate title, matching the conventional commit standards. - [ ] My scope of feat/fix/chore/etc. correctly matches the nature of changes in my pull request. ### Tests - [ ] My contribution adds new code, and I have added tests to cover it. - [ ] My contribution modifies existing code, and I have updated the tests to reflect these changes. - [ ] All new and existing tests pass locally with my changes. - [ ] Code coverage remains at or above the configured threshold. ### Documentation _No response_ ### Versioning _No response_ Co-authored-by: Hikari <hikari@nhcarrigan.com> Reviewed-on: #71 Co-authored-by: Naomi Carrigan <commits@nhcarrigan.com> Co-committed-by: Naomi Carrigan <commits@nhcarrigan.com>
This commit was merged in pull request #71.
This commit is contained in:
@@ -393,3 +393,263 @@ pub async fn get_file_size(file_path: String) -> Result<u64, String> {
|
||||
.map_err(|e| format!("Failed to get file metadata: {}", e))?;
|
||||
Ok(metadata.len())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::fs::{self, File};
|
||||
use std::io::Write;
|
||||
use tempfile::TempDir;
|
||||
|
||||
// Helper to run async tests
|
||||
fn run_async<F: std::future::Future>(f: F) -> F::Output {
|
||||
tokio::runtime::Runtime::new().unwrap().block_on(f)
|
||||
}
|
||||
|
||||
// ==================== validate_directory tests ====================
|
||||
|
||||
#[test]
|
||||
fn test_validate_directory_absolute_path_exists() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let path = temp_dir.path().to_string_lossy().to_string();
|
||||
|
||||
let result = run_async(validate_directory(path.clone(), None));
|
||||
assert!(result.is_ok());
|
||||
// Canonicalized path should be returned
|
||||
assert!(result.unwrap().contains(&temp_dir.path().file_name().unwrap().to_string_lossy().to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_validate_directory_path_not_exists() {
|
||||
let result = run_async(validate_directory(
|
||||
"/nonexistent/path/that/does/not/exist".to_string(),
|
||||
None,
|
||||
));
|
||||
assert!(result.is_err());
|
||||
assert!(result.unwrap_err().contains("does not exist"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_validate_directory_path_is_file() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let file_path = temp_dir.path().join("test_file.txt");
|
||||
File::create(&file_path).unwrap();
|
||||
|
||||
let result = run_async(validate_directory(
|
||||
file_path.to_string_lossy().to_string(),
|
||||
None,
|
||||
));
|
||||
assert!(result.is_err());
|
||||
assert!(result.unwrap_err().contains("not a directory"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_validate_directory_home_expansion() {
|
||||
// This test assumes HOME is set (which it should be on most systems)
|
||||
if std::env::var_os("HOME").is_some() {
|
||||
let result = run_async(validate_directory("~".to_string(), None));
|
||||
assert!(result.is_ok());
|
||||
// Should not contain ~ after expansion
|
||||
assert!(!result.unwrap().contains("~"));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_validate_directory_home_subpath_expansion() {
|
||||
// This test assumes HOME is set and has some subdirectory
|
||||
if let Some(home) = std::env::var_os("HOME") {
|
||||
let home_path = std::path::Path::new(&home);
|
||||
// Find any subdirectory in home
|
||||
if let Ok(entries) = fs::read_dir(home_path) {
|
||||
for entry in entries.flatten() {
|
||||
if entry.path().is_dir() {
|
||||
let subdir_name = entry.file_name().to_string_lossy().to_string();
|
||||
let tilde_path = format!("~/{}", subdir_name);
|
||||
let result = run_async(validate_directory(tilde_path, None));
|
||||
assert!(result.is_ok());
|
||||
assert!(!result.unwrap().contains("~"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_validate_directory_relative_path_with_current_dir() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let subdir = temp_dir.path().join("subdir");
|
||||
fs::create_dir(&subdir).unwrap();
|
||||
|
||||
let result = run_async(validate_directory(
|
||||
"subdir".to_string(),
|
||||
Some(temp_dir.path().to_string_lossy().to_string()),
|
||||
));
|
||||
assert!(result.is_ok());
|
||||
assert!(result.unwrap().contains("subdir"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_validate_directory_dot_path() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
|
||||
let result = run_async(validate_directory(
|
||||
".".to_string(),
|
||||
Some(temp_dir.path().to_string_lossy().to_string()),
|
||||
));
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_validate_directory_dotdot_path() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let subdir = temp_dir.path().join("subdir");
|
||||
fs::create_dir(&subdir).unwrap();
|
||||
|
||||
let result = run_async(validate_directory(
|
||||
"..".to_string(),
|
||||
Some(subdir.to_string_lossy().to_string()),
|
||||
));
|
||||
assert!(result.is_ok());
|
||||
// Should resolve to parent
|
||||
let resolved = result.unwrap();
|
||||
assert!(resolved.contains(&temp_dir.path().file_name().unwrap().to_string_lossy().to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_validate_directory_relative_without_current_dir() {
|
||||
// Relative path without current_dir - should fail since relative path likely won't exist
|
||||
let result = run_async(validate_directory(
|
||||
"some_random_nonexistent_relative_path".to_string(),
|
||||
None,
|
||||
));
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
// ==================== get_file_size tests ====================
|
||||
|
||||
#[test]
|
||||
fn test_get_file_size_empty_file() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let file_path = temp_dir.path().join("empty.txt");
|
||||
File::create(&file_path).unwrap();
|
||||
|
||||
let result = run_async(get_file_size(file_path.to_string_lossy().to_string()));
|
||||
assert!(result.is_ok());
|
||||
assert_eq!(result.unwrap(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_file_size_with_content() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let file_path = temp_dir.path().join("content.txt");
|
||||
let mut file = File::create(&file_path).unwrap();
|
||||
file.write_all(b"Hello, Hikari!").unwrap();
|
||||
|
||||
let result = run_async(get_file_size(file_path.to_string_lossy().to_string()));
|
||||
assert!(result.is_ok());
|
||||
assert_eq!(result.unwrap(), 14); // "Hello, Hikari!" is 14 bytes
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_file_size_larger_file() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let file_path = temp_dir.path().join("large.txt");
|
||||
let mut file = File::create(&file_path).unwrap();
|
||||
// Write 1000 bytes
|
||||
let data = vec![b'x'; 1000];
|
||||
file.write_all(&data).unwrap();
|
||||
|
||||
let result = run_async(get_file_size(file_path.to_string_lossy().to_string()));
|
||||
assert!(result.is_ok());
|
||||
assert_eq!(result.unwrap(), 1000);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_file_size_nonexistent_file() {
|
||||
let result = run_async(get_file_size(
|
||||
"/nonexistent/path/file.txt".to_string(),
|
||||
));
|
||||
assert!(result.is_err());
|
||||
assert!(result.unwrap_err().contains("Failed to get file metadata"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_file_size_directory() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
|
||||
// Getting "size" of a directory should work but return directory metadata
|
||||
// This is actually valid - directories have metadata too
|
||||
let result = run_async(get_file_size(temp_dir.path().to_string_lossy().to_string()));
|
||||
assert!(result.is_ok());
|
||||
// Directory size is platform-dependent, just check it returns something
|
||||
}
|
||||
|
||||
// ==================== list_skills tests ====================
|
||||
|
||||
#[test]
|
||||
fn test_list_skills_no_skills_dir() {
|
||||
// This test is tricky because it depends on HOME being set
|
||||
// and potentially affecting real user data, so we'll just
|
||||
// verify the function doesn't panic
|
||||
let result = run_async(list_skills());
|
||||
// Should either return Ok with a list or Ok with empty vec
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
// ==================== select_wsl_directory tests ====================
|
||||
|
||||
#[test]
|
||||
fn test_select_wsl_directory_returns_home() {
|
||||
let result = run_async(select_wsl_directory());
|
||||
assert!(result.is_ok());
|
||||
assert_eq!(result.unwrap(), "/home");
|
||||
}
|
||||
|
||||
// ==================== UpdateInfo struct tests ====================
|
||||
|
||||
#[test]
|
||||
fn test_update_info_serialization() {
|
||||
let info = UpdateInfo {
|
||||
current_version: "0.3.0".to_string(),
|
||||
latest_version: "0.4.0".to_string(),
|
||||
has_update: true,
|
||||
release_url: "https://example.com/release".to_string(),
|
||||
release_notes: Some("New features!".to_string()),
|
||||
};
|
||||
|
||||
let json = serde_json::to_string(&info).unwrap();
|
||||
assert!(json.contains("0.3.0"));
|
||||
assert!(json.contains("0.4.0"));
|
||||
assert!(json.contains("true"));
|
||||
assert!(json.contains("New features!"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_update_info_without_notes() {
|
||||
let info = UpdateInfo {
|
||||
current_version: "0.3.0".to_string(),
|
||||
latest_version: "0.3.0".to_string(),
|
||||
has_update: false,
|
||||
release_url: "https://example.com/release".to_string(),
|
||||
release_notes: None,
|
||||
};
|
||||
|
||||
let json = serde_json::to_string(&info).unwrap();
|
||||
assert!(json.contains("null") || json.contains("release_notes"));
|
||||
}
|
||||
|
||||
// ==================== SavedFileInfo struct tests ====================
|
||||
|
||||
#[test]
|
||||
fn test_saved_file_info_serialization() {
|
||||
let info = SavedFileInfo {
|
||||
path: "/tmp/test.txt".to_string(),
|
||||
filename: "test.txt".to_string(),
|
||||
};
|
||||
|
||||
let json = serde_json::to_string(&info).unwrap();
|
||||
assert!(json.contains("/tmp/test.txt"));
|
||||
assert!(json.contains("test.txt"));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user