test: cleanup

This commit is contained in:
zhom
2025-08-10 04:14:43 +04:00
parent fbcec2cbc1
commit a9720676ae
13 changed files with 1526 additions and 178 deletions
+9 -4
View File
@@ -1717,16 +1717,21 @@ mod tests {
let unsupported_file = temp_dir.join("test.rar");
// Create a mock runtime to test the logic
let rt = tokio::runtime::Runtime::new().unwrap();
let rt = tokio::runtime::Runtime::new().expect("Failed to create tokio runtime");
// This would fail because .rar is not supported, which proves
// our method is using the Extractor logic
let result = rt.block_on(async { updater.extract_update(&unsupported_file, &temp_dir).await });
// Should fail with unsupported format error
assert!(result.is_err());
let error_msg = result.unwrap_err().to_string();
assert!(error_msg.contains("Unsupported archive format: rar"));
assert!(result.is_err(), "Unsupported format should return error");
let error_msg = result.expect_err("Should have error").to_string();
assert!(
error_msg.contains("Unsupported archive format: rar")
|| error_msg.contains("unknown")
|| error_msg.contains("unsupported"),
"Error should mention unsupported format, got: {error_msg}"
);
}
#[test]
+54 -28
View File
@@ -779,13 +779,15 @@ mod tests {
let state_file = test_settings_manager
.get_settings_dir()
.join("auto_update_state.json");
std::fs::create_dir_all(test_settings_manager.get_settings_dir()).unwrap();
let json = serde_json::to_string_pretty(&state).unwrap();
std::fs::write(&state_file, json).unwrap();
std::fs::create_dir_all(test_settings_manager.get_settings_dir())
.expect("Failed to create settings directory");
let json = serde_json::to_string_pretty(&state).expect("Failed to serialize state");
std::fs::write(&state_file, json).expect("Failed to write state file");
// Load state
let content = std::fs::read_to_string(&state_file).unwrap();
let loaded_state: AutoUpdateState = serde_json::from_str(&content).unwrap();
let content = std::fs::read_to_string(&state_file).expect("Failed to read state file");
let loaded_state: AutoUpdateState =
serde_json::from_str(&content).expect("Failed to deserialize state");
assert_eq!(loaded_state.disabled_browsers.len(), 1);
assert!(loaded_state.disabled_browsers.contains("firefox"));
@@ -823,11 +825,15 @@ mod tests {
let state_file = test_settings_manager
.get_settings_dir()
.join("auto_update_state.json");
std::fs::create_dir_all(test_settings_manager.get_settings_dir()).unwrap();
std::fs::create_dir_all(test_settings_manager.get_settings_dir())
.expect("Failed to create settings directory");
// Initially not disabled (empty state file means default state)
let state = AutoUpdateState::default();
assert!(!state.disabled_browsers.contains("firefox"));
assert!(
!state.disabled_browsers.contains("firefox"),
"Firefox should not be disabled initially"
);
// Start update (should disable)
let mut state = AutoUpdateState::default();
@@ -835,27 +841,41 @@ mod tests {
state
.auto_update_downloads
.insert("firefox-1.1.0".to_string());
let json = serde_json::to_string_pretty(&state).unwrap();
std::fs::write(&state_file, json).unwrap();
let json = serde_json::to_string_pretty(&state).expect("Failed to serialize state");
std::fs::write(&state_file, json).expect("Failed to write state file");
// Check that it's disabled
let content = std::fs::read_to_string(&state_file).unwrap();
let loaded_state: AutoUpdateState = serde_json::from_str(&content).unwrap();
assert!(loaded_state.disabled_browsers.contains("firefox"));
assert!(loaded_state.auto_update_downloads.contains("firefox-1.1.0"));
let content = std::fs::read_to_string(&state_file).expect("Failed to read state file");
let loaded_state: AutoUpdateState =
serde_json::from_str(&content).expect("Failed to deserialize state");
assert!(
loaded_state.disabled_browsers.contains("firefox"),
"Firefox should be disabled"
);
assert!(
loaded_state.auto_update_downloads.contains("firefox-1.1.0"),
"Firefox download should be tracked"
);
// Complete update (should enable)
let mut state = loaded_state;
state.disabled_browsers.remove("firefox");
state.auto_update_downloads.remove("firefox-1.1.0");
let json = serde_json::to_string_pretty(&state).unwrap();
std::fs::write(&state_file, json).unwrap();
let json = serde_json::to_string_pretty(&state).expect("Failed to serialize final state");
std::fs::write(&state_file, json).expect("Failed to write final state file");
// Check that it's enabled again
let content = std::fs::read_to_string(&state_file).unwrap();
let final_state: AutoUpdateState = serde_json::from_str(&content).unwrap();
assert!(!final_state.disabled_browsers.contains("firefox"));
assert!(!final_state.auto_update_downloads.contains("firefox-1.1.0"));
let content = std::fs::read_to_string(&state_file).expect("Failed to read final state file");
let final_state: AutoUpdateState =
serde_json::from_str(&content).expect("Failed to deserialize final state");
assert!(
!final_state.disabled_browsers.contains("firefox"),
"Firefox should be enabled again"
);
assert!(
!final_state.auto_update_downloads.contains("firefox-1.1.0"),
"Firefox download should not be tracked anymore"
);
}
#[test]
@@ -863,7 +883,7 @@ mod tests {
use tempfile::TempDir;
// Create a temporary directory for testing
let temp_dir = TempDir::new().unwrap();
let temp_dir = TempDir::new().expect("Failed to create temp directory");
// Create a mock settings manager that uses the temp directory
struct TestSettingsManager {
@@ -897,21 +917,27 @@ mod tests {
let state_file = test_settings_manager
.get_settings_dir()
.join("auto_update_state.json");
std::fs::create_dir_all(test_settings_manager.get_settings_dir()).unwrap();
let json = serde_json::to_string_pretty(&state).unwrap();
std::fs::write(&state_file, json).unwrap();
std::fs::create_dir_all(test_settings_manager.get_settings_dir())
.expect("Failed to create settings directory");
let json = serde_json::to_string_pretty(&state).expect("Failed to serialize initial state");
std::fs::write(&state_file, json).expect("Failed to write initial state file");
// Dismiss notification (remove from pending updates)
state
.pending_updates
.retain(|n| n.id != "test_notification");
let json = serde_json::to_string_pretty(&state).unwrap();
std::fs::write(&state_file, json).unwrap();
let json = serde_json::to_string_pretty(&state).expect("Failed to serialize updated state");
std::fs::write(&state_file, json).expect("Failed to write updated state file");
// Check that it's removed
let content = std::fs::read_to_string(&state_file).unwrap();
let loaded_state: AutoUpdateState = serde_json::from_str(&content).unwrap();
assert_eq!(loaded_state.pending_updates.len(), 0);
let content = std::fs::read_to_string(&state_file).expect("Failed to read updated state file");
let loaded_state: AutoUpdateState =
serde_json::from_str(&content).expect("Failed to deserialize updated state");
assert_eq!(
loaded_state.pending_updates.len(),
0,
"Pending updates should be empty after dismissal"
);
}
}
+122 -57
View File
@@ -889,38 +889,55 @@ mod tests {
assert_eq!(BrowserType::TorBrowser.as_str(), "tor-browser");
assert_eq!(BrowserType::Camoufox.as_str(), "camoufox");
// Test from_str
// Test from_str - use expect with descriptive messages instead of unwrap
assert_eq!(
BrowserType::from_str("mullvad-browser").unwrap(),
BrowserType::from_str("mullvad-browser").expect("mullvad-browser should be valid"),
BrowserType::MullvadBrowser
);
assert_eq!(
BrowserType::from_str("firefox").unwrap(),
BrowserType::from_str("firefox").expect("firefox should be valid"),
BrowserType::Firefox
);
assert_eq!(
BrowserType::from_str("firefox-developer").unwrap(),
BrowserType::from_str("firefox-developer").expect("firefox-developer should be valid"),
BrowserType::FirefoxDeveloper
);
assert_eq!(
BrowserType::from_str("chromium").unwrap(),
BrowserType::from_str("chromium").expect("chromium should be valid"),
BrowserType::Chromium
);
assert_eq!(BrowserType::from_str("brave").unwrap(), BrowserType::Brave);
assert_eq!(BrowserType::from_str("zen").unwrap(), BrowserType::Zen);
assert_eq!(
BrowserType::from_str("tor-browser").unwrap(),
BrowserType::from_str("brave").expect("brave should be valid"),
BrowserType::Brave
);
assert_eq!(
BrowserType::from_str("zen").expect("zen should be valid"),
BrowserType::Zen
);
assert_eq!(
BrowserType::from_str("tor-browser").expect("tor-browser should be valid"),
BrowserType::TorBrowser
);
assert_eq!(
BrowserType::from_str("camoufox").unwrap(),
BrowserType::from_str("camoufox").expect("camoufox should be valid"),
BrowserType::Camoufox
);
// Test invalid browser type
assert!(BrowserType::from_str("invalid").is_err());
assert!(BrowserType::from_str("").is_err());
assert!(BrowserType::from_str("Firefox").is_err()); // Case sensitive
// Test invalid browser type - these should properly fail
let invalid_result = BrowserType::from_str("invalid");
assert!(
invalid_result.is_err(),
"Invalid browser type should return error"
);
let empty_result = BrowserType::from_str("");
assert!(empty_result.is_err(), "Empty string should return error");
let case_sensitive_result = BrowserType::from_str("Firefox");
assert!(
case_sensitive_result.is_err(),
"Case sensitive check should fail"
);
}
#[test]
@@ -929,9 +946,12 @@ mod tests {
let browser = FirefoxBrowser::new(BrowserType::Firefox);
let args = browser
.create_launch_args("/path/to/profile", None, None)
.unwrap();
.expect("Failed to create launch args for Firefox");
assert_eq!(args, vec!["-profile", "/path/to/profile"]);
assert!(!args.contains(&"-no-remote".to_string()));
assert!(
!args.contains(&"-no-remote".to_string()),
"Firefox should not use -no-remote"
);
let args = browser
.create_launch_args(
@@ -939,7 +959,7 @@ mod tests {
None,
Some("https://example.com".to_string()),
)
.unwrap();
.expect("Failed to create launch args for Firefox with URL");
assert_eq!(
args,
vec!["-profile", "/path/to/profile", "https://example.com"]
@@ -949,23 +969,26 @@ mod tests {
let browser = FirefoxBrowser::new(BrowserType::MullvadBrowser);
let args = browser
.create_launch_args("/path/to/profile", None, None)
.unwrap();
.expect("Failed to create launch args for Mullvad Browser");
assert_eq!(args, vec!["-profile", "/path/to/profile", "-no-remote"]);
// Test Tor Browser (should use -no-remote)
let browser = FirefoxBrowser::new(BrowserType::TorBrowser);
let args = browser
.create_launch_args("/path/to/profile", None, None)
.unwrap();
.expect("Failed to create launch args for Tor Browser");
assert_eq!(args, vec!["-profile", "/path/to/profile", "-no-remote"]);
// Test Zen Browser (should not use -no-remote)
let browser = FirefoxBrowser::new(BrowserType::Zen);
let args = browser
.create_launch_args("/path/to/profile", None, None)
.unwrap();
.expect("Failed to create launch args for Zen Browser");
assert_eq!(args, vec!["-profile", "/path/to/profile"]);
assert!(!args.contains(&"-no-remote".to_string()));
assert!(
!args.contains(&"-no-remote".to_string()),
"Zen Browser should not use -no-remote"
);
}
#[test]
@@ -973,15 +996,27 @@ mod tests {
let browser = ChromiumBrowser::new(BrowserType::Chromium);
let args = browser
.create_launch_args("/path/to/profile", None, None)
.unwrap();
.expect("Failed to create launch args for Chromium");
// Test that basic required arguments are present
assert!(args.contains(&"--user-data-dir=/path/to/profile".to_string()));
assert!(args.contains(&"--no-default-browser-check".to_string()));
assert!(
args.contains(&"--user-data-dir=/path/to/profile".to_string()),
"Chromium args should contain user-data-dir"
);
assert!(
args.contains(&"--no-default-browser-check".to_string()),
"Chromium args should contain no-default-browser-check"
);
// Test that automatic update disabling arguments are present
assert!(args.contains(&"--disable-background-mode".to_string()));
assert!(args.contains(&"--disable-component-update".to_string()));
assert!(
args.contains(&"--disable-background-mode".to_string()),
"Chromium args should contain disable-background-mode"
);
assert!(
args.contains(&"--disable-component-update".to_string()),
"Chromium args should contain disable-component-update"
);
let args_with_url = browser
.create_launch_args(
@@ -989,11 +1024,17 @@ mod tests {
None,
Some("https://example.com".to_string()),
)
.unwrap();
assert!(args_with_url.contains(&"https://example.com".to_string()));
.expect("Failed to create launch args for Chromium with URL");
assert!(
args_with_url.contains(&"https://example.com".to_string()),
"Chromium args should contain the URL"
);
// Verify URL is at the end
assert_eq!(args_with_url.last().unwrap(), "https://example.com");
assert_eq!(
args_with_url.last().expect("Args should not be empty"),
"https://example.com"
);
}
#[test]
@@ -1026,40 +1067,44 @@ mod tests {
#[test]
fn test_version_downloaded_check() {
let temp_dir = TempDir::new().unwrap();
let temp_dir = TempDir::new().expect("Failed to create temp directory");
let binaries_dir = temp_dir.path();
// Create a mock Firefox browser installation with new path structure: binaries/<browser>/<version>/
let browser_dir = binaries_dir.join("firefox").join("139.0");
fs::create_dir_all(&browser_dir).unwrap();
fs::create_dir_all(&browser_dir).expect("Failed to create browser directory");
#[cfg(target_os = "macos")]
{
// Create a mock .app directory for macOS
let app_dir = browser_dir.join("Firefox.app");
fs::create_dir_all(&app_dir).unwrap();
fs::create_dir_all(&app_dir).expect("Failed to create Firefox.app directory");
}
#[cfg(target_os = "linux")]
{
// Create a mock firefox subdirectory and executable for Linux
let firefox_subdir = browser_dir.join("firefox");
fs::create_dir_all(&firefox_subdir).unwrap();
fs::create_dir_all(&firefox_subdir).expect("Failed to create firefox subdirectory");
let executable_path = firefox_subdir.join("firefox");
fs::write(&executable_path, "mock executable").unwrap();
fs::write(&executable_path, "mock executable").expect("Failed to write mock executable");
// Set executable permissions on Linux
use std::os::unix::fs::PermissionsExt;
let mut permissions = executable_path.metadata().unwrap().permissions();
let mut permissions = executable_path
.metadata()
.expect("Failed to get file metadata")
.permissions();
permissions.set_mode(0o755);
fs::set_permissions(&executable_path, permissions).unwrap();
fs::set_permissions(&executable_path, permissions)
.expect("Failed to set executable permissions");
}
#[cfg(target_os = "windows")]
{
// Create a mock firefox.exe for Windows
let executable_path = browser_dir.join("firefox.exe");
fs::write(&executable_path, "mock executable").unwrap();
fs::write(&executable_path, "mock executable").expect("Failed to write mock executable");
}
let browser = FirefoxBrowser::new(BrowserType::Firefox);
@@ -1068,60 +1113,76 @@ mod tests {
// Test with Chromium browser with new path structure
let chromium_dir = binaries_dir.join("chromium").join("1465660");
fs::create_dir_all(&chromium_dir).unwrap();
fs::create_dir_all(&chromium_dir).expect("Failed to create chromium directory");
#[cfg(target_os = "macos")]
{
let chromium_app_dir = chromium_dir.join("Chromium.app");
fs::create_dir_all(chromium_app_dir.join("Contents").join("MacOS")).unwrap();
fs::create_dir_all(chromium_app_dir.join("Contents").join("MacOS"))
.expect("Failed to create Chromium.app structure");
// Create a mock executable
let executable_path = chromium_app_dir
.join("Contents")
.join("MacOS")
.join("Chromium");
fs::write(&executable_path, "mock executable").unwrap();
fs::write(&executable_path, "mock executable")
.expect("Failed to write mock Chromium executable");
}
#[cfg(target_os = "linux")]
{
// Create a mock chromium executable for Linux
let executable_path = chromium_dir.join("chromium");
fs::write(&executable_path, "mock executable").unwrap();
fs::write(&executable_path, "mock executable")
.expect("Failed to write mock chromium executable");
// Set executable permissions on Linux
use std::os::unix::fs::PermissionsExt;
let mut permissions = executable_path.metadata().unwrap().permissions();
let mut permissions = executable_path
.metadata()
.expect("Failed to get chromium metadata")
.permissions();
permissions.set_mode(0o755);
fs::set_permissions(&executable_path, permissions).unwrap();
fs::set_permissions(&executable_path, permissions)
.expect("Failed to set chromium permissions");
}
#[cfg(target_os = "windows")]
{
// Create a mock chromium.exe for Windows
let executable_path = chromium_dir.join("chromium.exe");
fs::write(&executable_path, "mock executable").unwrap();
fs::write(&executable_path, "mock executable").expect("Failed to write mock chromium.exe");
}
let chromium_browser = ChromiumBrowser::new(BrowserType::Chromium);
assert!(chromium_browser.is_version_downloaded("1465660", binaries_dir));
assert!(!chromium_browser.is_version_downloaded("1465661", binaries_dir));
assert!(
chromium_browser.is_version_downloaded("1465660", binaries_dir),
"Chromium version should be detected as downloaded"
);
assert!(
!chromium_browser.is_version_downloaded("1465661", binaries_dir),
"Non-existent Chromium version should not be detected as downloaded"
);
}
#[test]
fn test_version_downloaded_no_app_directory() {
let temp_dir = TempDir::new().unwrap();
let temp_dir = TempDir::new().expect("Failed to create temp directory");
let binaries_dir = temp_dir.path();
// Create browser directory but no proper executable structure
let browser_dir = binaries_dir.join("firefox").join("139.0");
fs::create_dir_all(&browser_dir).unwrap();
fs::create_dir_all(&browser_dir).expect("Failed to create browser directory");
// Create some other files but no proper executable structure
fs::write(browser_dir.join("readme.txt"), "Some content").unwrap();
fs::write(browser_dir.join("readme.txt"), "Some content").expect("Failed to write readme file");
let browser = FirefoxBrowser::new(BrowserType::Firefox);
assert!(!browser.is_version_downloaded("139.0", binaries_dir));
assert!(
!browser.is_version_downloaded("139.0", binaries_dir),
"Firefox version should not be detected without proper executable structure"
);
}
#[test]
@@ -1146,16 +1207,20 @@ mod tests {
};
// Test that it can be serialized (implements Serialize)
let json = serde_json::to_string(&proxy).unwrap();
assert!(json.contains("127.0.0.1"));
assert!(json.contains("8080"));
assert!(json.contains("http"));
let json = serde_json::to_string(&proxy).expect("Failed to serialize proxy settings");
assert!(json.contains("127.0.0.1"), "JSON should contain host IP");
assert!(json.contains("8080"), "JSON should contain port number");
assert!(json.contains("http"), "JSON should contain proxy type");
// Test that it can be deserialized (implements Deserialize)
let deserialized: ProxySettings = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized.proxy_type, proxy.proxy_type);
assert_eq!(deserialized.host, proxy.host);
assert_eq!(deserialized.port, proxy.port);
let deserialized: ProxySettings =
serde_json::from_str(&json).expect("Failed to deserialize proxy settings");
assert_eq!(
deserialized.proxy_type, proxy.proxy_type,
"Proxy type should match"
);
assert_eq!(deserialized.host, proxy.host, "Host should match");
assert_eq!(deserialized.port, proxy.port, "Port should match");
}
}
+25 -7
View File
@@ -496,15 +496,21 @@ mod tests {
registry.mark_download_started("firefox", "139.0", PathBuf::from("/test/path"));
// Should be considered downloaded immediately
assert!(registry.is_browser_downloaded("firefox", "139.0"));
assert!(
registry.is_browser_downloaded("firefox", "139.0"),
"Browser should be considered downloaded after marking as started"
);
// Mark as completed
registry
.mark_download_completed("firefox", "139.0")
.unwrap();
.expect("Failed to mark download as completed");
// Should still be considered downloaded
assert!(registry.is_browser_downloaded("firefox", "139.0"));
assert!(
registry.is_browser_downloaded("firefox", "139.0"),
"Browser should still be considered downloaded after completion"
);
}
#[test]
@@ -517,11 +523,20 @@ mod tests {
};
registry.add_browser(info);
assert!(registry.is_browser_downloaded("firefox", "139.0"));
assert!(
registry.is_browser_downloaded("firefox", "139.0"),
"Browser should be downloaded after adding"
);
let removed = registry.remove_browser("firefox", "139.0");
assert!(removed.is_some());
assert!(!registry.is_browser_downloaded("firefox", "139.0"));
assert!(
removed.is_some(),
"Remove operation should return the removed browser info"
);
assert!(
!registry.is_browser_downloaded("firefox", "139.0"),
"Browser should not be downloaded after removal"
);
}
#[test]
@@ -532,6 +547,9 @@ mod tests {
registry.mark_download_started("zen", "twilight", PathBuf::from("/test/zen-twilight"));
// Check that it's registered
assert!(registry.is_browser_downloaded("zen", "twilight"));
assert!(
registry.is_browser_downloaded("zen", "twilight"),
"Zen twilight version should be registered as downloaded"
);
}
}
+213 -53
View File
@@ -1444,93 +1444,211 @@ mod tests {
#[tokio::test]
async fn test_extract_zip_with_test_archive() {
let extractor = Extractor::instance();
let temp_dir = TempDir::new().unwrap();
let temp_dir = TempDir::new().expect("Failed to create temp directory");
let dest_dir = temp_dir.path().join("extracted");
// Use the test ZIP archive
let zip_path = std::path::Path::new("test-assets/test.zip");
if !zip_path.exists() {
// Skip test if test archive doesn't exist
return;
// Create a test ZIP archive in memory
let zip_path = temp_dir.path().join("test.zip");
{
let file = std::fs::File::create(&zip_path).expect("Failed to create test zip file");
let mut zip = zip::ZipWriter::new(file);
let options =
zip::write::FileOptions::<()>::default().compression_method(zip::CompressionMethod::Stored);
zip
.start_file("test.txt", options)
.expect("Failed to start zip file");
zip
.write_all(b"Hello, World!")
.expect("Failed to write to zip");
zip.finish().expect("Failed to finish zip");
}
let _result = extractor.extract_zip(zip_path, &dest_dir).await;
let result = extractor.extract_zip(&zip_path, &dest_dir).await;
// The result might fail because we're looking for executables, but the extraction should work
// Let's just check if the file was extracted
// Let's check if the file was extracted regardless of the result
let extracted_file = dest_dir.join("test.txt");
if extracted_file.exists() {
let content = std::fs::read_to_string(&extracted_file).unwrap();
assert_eq!(content.trim(), "Hello, World!");
assert!(extracted_file.exists(), "Extracted file should exist");
let content = std::fs::read_to_string(&extracted_file).expect("Failed to read extracted file");
assert_eq!(
content.trim(),
"Hello, World!",
"Extracted content should match"
);
// If the result is an error, it should be because no executable was found, not extraction failure
if let Err(e) = result {
let error_msg = e.to_string();
assert!(
error_msg.contains("No executable found") || error_msg.contains("executable"),
"Error should be about missing executable, not extraction failure: {error_msg}"
);
}
}
#[tokio::test]
async fn test_extract_tar_gz_with_test_archive() {
let extractor = Extractor::instance();
let temp_dir = TempDir::new().unwrap();
let temp_dir = TempDir::new().expect("Failed to create temp directory");
let dest_dir = temp_dir.path().join("extracted");
// Use the test tar.gz archive
let tar_gz_path = std::path::Path::new("test-assets/test.tar.gz");
if !tar_gz_path.exists() {
// Skip test if test archive doesn't exist
return;
// Create a test tar.gz archive in memory
let tar_gz_path = temp_dir.path().join("test.tar.gz");
{
let tar_gz_file =
std::fs::File::create(&tar_gz_path).expect("Failed to create test tar.gz file");
let enc = flate2::write::GzEncoder::new(tar_gz_file, flate2::Compression::default());
let mut tar = tar::Builder::new(enc);
let mut header = tar::Header::new_gnu();
header.set_path("test.txt").expect("Failed to set tar path");
header.set_size(13); // "Hello, World!" length
header.set_cksum();
tar
.append(&header, "Hello, World!".as_bytes())
.expect("Failed to append to tar");
tar.finish().expect("Failed to finish tar");
}
let _result = extractor.extract_tar_gz(tar_gz_path, &dest_dir).await;
let result = extractor.extract_tar_gz(&tar_gz_path, &dest_dir).await;
// Check if the file was extracted
let extracted_file = dest_dir.join("test.txt");
if extracted_file.exists() {
let content = std::fs::read_to_string(&extracted_file).unwrap();
assert_eq!(content.trim(), "Hello, World!");
assert!(extracted_file.exists(), "Extracted file should exist");
let content = std::fs::read_to_string(&extracted_file).expect("Failed to read extracted file");
assert_eq!(
content.trim(),
"Hello, World!",
"Extracted content should match"
);
// If the result is an error, it should be because no executable was found, not extraction failure
if let Err(e) = result {
let error_msg = e.to_string();
assert!(
error_msg.contains("No executable found")
|| error_msg.contains("executable")
|| error_msg.contains("No .app found")
|| error_msg.contains("app not found"),
"Error should be about missing executable/app, not extraction failure: {error_msg}"
);
}
}
#[tokio::test]
async fn test_extract_tar_bz2_with_test_archive() {
let extractor = Extractor::instance();
let temp_dir = TempDir::new().unwrap();
let temp_dir = TempDir::new().expect("Failed to create temp directory");
let dest_dir = temp_dir.path().join("extracted");
// Use the test tar.bz2 archive
let tar_bz2_path = std::path::Path::new("test-assets/test.tar.bz2");
if !tar_bz2_path.exists() {
// Skip test if test archive doesn't exist
return;
// Create a test tar.bz2 archive in memory
let tar_bz2_path = temp_dir.path().join("test.tar.bz2");
{
let tar_bz2_file =
std::fs::File::create(&tar_bz2_path).expect("Failed to create test tar.bz2 file");
let enc = bzip2::write::BzEncoder::new(tar_bz2_file, bzip2::Compression::default());
let mut tar = tar::Builder::new(enc);
let mut header = tar::Header::new_gnu();
header.set_path("test.txt").expect("Failed to set tar path");
header.set_size(13); // "Hello, World!" length
header.set_cksum();
tar
.append(&header, "Hello, World!".as_bytes())
.expect("Failed to append to tar");
tar.finish().expect("Failed to finish tar");
}
let _result = extractor.extract_tar_bz2(tar_bz2_path, &dest_dir).await;
let result = extractor.extract_tar_bz2(&tar_bz2_path, &dest_dir).await;
// Check if the file was extracted
let extracted_file = dest_dir.join("test.txt");
if extracted_file.exists() {
let content = std::fs::read_to_string(&extracted_file).unwrap();
assert_eq!(content.trim(), "Hello, World!");
assert!(extracted_file.exists(), "Extracted file should exist");
let content = std::fs::read_to_string(&extracted_file).expect("Failed to read extracted file");
assert_eq!(
content.trim(),
"Hello, World!",
"Extracted content should match"
);
// If the result is an error, it should be because no executable was found, not extraction failure
if let Err(e) = result {
let error_msg = e.to_string();
assert!(
error_msg.contains("No executable found")
|| error_msg.contains("executable")
|| error_msg.contains("No .app found")
|| error_msg.contains("app not found"),
"Error should be about missing executable/app, not extraction failure: {error_msg}"
);
}
}
#[tokio::test]
async fn test_extract_tar_xz_with_test_archive() {
let extractor = Extractor::instance();
let temp_dir = TempDir::new().unwrap();
let temp_dir = TempDir::new().expect("Failed to create temp directory");
let dest_dir = temp_dir.path().join("extracted");
// Use the test tar.xz archive
let tar_xz_path = std::path::Path::new("test-assets/test.tar.xz");
if !tar_xz_path.exists() {
// Skip test if test archive doesn't exist
return;
// Create a test tar.xz archive in memory
let tar_xz_path = temp_dir.path().join("test.tar.xz");
{
// First create a tar archive in memory
let mut tar_data = Vec::new();
{
let mut tar = tar::Builder::new(&mut tar_data);
let mut header = tar::Header::new_gnu();
header.set_path("test.txt").expect("Failed to set tar path");
header.set_size(13); // "Hello, World!" length
header.set_cksum();
tar
.append(&header, "Hello, World!".as_bytes())
.expect("Failed to append to tar");
tar.finish().expect("Failed to finish tar");
}
// Then compress with xz
let tar_xz_file =
std::fs::File::create(&tar_xz_path).expect("Failed to create test tar.xz file");
let mut compressed_data = Vec::new();
lzma_rs::xz_compress(&mut std::io::Cursor::new(tar_data), &mut compressed_data)
.expect("Failed to compress with xz");
std::io::Write::write_all(&mut std::io::BufWriter::new(tar_xz_file), &compressed_data)
.expect("Failed to write compressed data");
}
let _result = extractor.extract_tar_xz(tar_xz_path, &dest_dir).await;
let result = extractor.extract_tar_xz(&tar_xz_path, &dest_dir).await;
// Check if the file was extracted
let extracted_file = dest_dir.join("test.txt");
if extracted_file.exists() {
let content = std::fs::read_to_string(&extracted_file).unwrap();
assert_eq!(content.trim(), "Hello, World!");
assert!(extracted_file.exists(), "Extracted file should exist");
let content = std::fs::read_to_string(&extracted_file).expect("Failed to read extracted file");
assert_eq!(
content.trim(),
"Hello, World!",
"Extracted content should match"
);
// If the result is an error, it should be because no executable was found, not extraction failure
if let Err(e) = result {
let error_msg = e.to_string();
assert!(
error_msg.contains("No executable found")
|| error_msg.contains("executable")
|| error_msg.contains("No .app found")
|| error_msg.contains("app not found"),
"Error should be about missing executable/app, not extraction failure: {error_msg}"
);
}
}
@@ -1578,27 +1696,69 @@ mod tests {
assert!(found_app.exists());
}
#[cfg(target_os = "linux")]
#[test]
fn test_is_executable() {
#[allow(unused_variables)]
let extractor = Extractor::instance();
let temp_dir = TempDir::new().unwrap();
let temp_dir = TempDir::new().expect("Failed to create temp directory");
// Create a regular file
let regular_file = temp_dir.path().join("regular.txt");
File::create(&regular_file).unwrap();
File::create(&regular_file).expect("Failed to create test file");
// Should not be executable initially
assert!(!extractor.is_executable(&regular_file));
#[cfg(target_os = "linux")]
{
// Should not be executable initially
assert!(
!extractor.is_executable(&regular_file),
"File should not be executable initially"
);
// Make it executable
use std::os::unix::fs::PermissionsExt;
let mut permissions = regular_file.metadata().unwrap().permissions();
permissions.set_mode(0o755);
std::fs::set_permissions(&regular_file, permissions).unwrap();
// Make it executable
use std::os::unix::fs::PermissionsExt;
let mut permissions = regular_file
.metadata()
.expect("Failed to get file metadata")
.permissions();
permissions.set_mode(0o755);
std::fs::set_permissions(&regular_file, permissions).expect("Failed to set permissions");
// Should now be executable
assert!(extractor.is_executable(&regular_file));
// Should now be executable
assert!(
extractor.is_executable(&regular_file),
"File should be executable after setting permissions"
);
}
#[cfg(not(target_os = "linux"))]
{
// On non-Linux systems, the is_executable method is not available
// We'll just verify the file exists since executable permissions work differently on Windows/macOS
assert!(regular_file.exists(), "Test file should exist");
// On Unix systems (but not Linux), we can still test basic permission setting
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let mut permissions = regular_file
.metadata()
.expect("Failed to get file metadata")
.permissions();
permissions.set_mode(0o755);
std::fs::set_permissions(&regular_file, permissions).expect("Failed to set permissions");
// Verify the permissions were set
let new_permissions = regular_file
.metadata()
.expect("Failed to get updated metadata")
.permissions();
assert_eq!(
new_permissions.mode() & 0o777,
0o755,
"Permissions should be set to 755"
);
}
}
}
}
+19 -5
View File
@@ -293,12 +293,26 @@ mod tests {
#[test]
fn test_is_geoip_database_available() {
// This test will return false unless the database actually exists
// In a real environment, this would check the actual file system
// Test that the function works correctly regardless of file system state
let is_available = GeoIPDownloader::is_geoip_database_available();
// We can't assert a specific value since it depends on the system state
// But we can verify the function doesn't panic
println!("GeoIP database available: {is_available}");
// The function should return a boolean value (either true or false)
// The function should return a boolean value - we just verify it doesn't panic
// and returns the expected result based on file existence
// Verify the function logic by checking if the path resolution works
let mmdb_path_result = GeoIPDownloader::get_mmdb_file_path();
assert!(
mmdb_path_result.is_ok(),
"Should be able to get MMDB file path"
);
let mmdb_path = mmdb_path_result.unwrap();
let expected_available = mmdb_path.exists();
assert_eq!(
is_available, expected_available,
"Function result should match actual file existence"
);
}
}
+252
View File
@@ -185,6 +185,258 @@ lazy_static::lazy_static! {
pub static ref GROUP_MANAGER: Mutex<GroupManager> = Mutex::new(GroupManager::new());
}
#[cfg(test)]
mod tests {
use super::*;
use std::env;
use tempfile::TempDir;
fn create_test_group_manager() -> (GroupManager, TempDir) {
let temp_dir = TempDir::new().expect("Failed to create temp directory");
// Set up a temporary home directory for testing
env::set_var("HOME", temp_dir.path());
let manager = GroupManager::new();
(manager, temp_dir)
}
#[test]
fn test_group_manager_creation() {
let (_manager, _temp_dir) = create_test_group_manager();
// Test passes if no panic occurs
}
#[test]
fn test_create_and_get_groups() {
let (manager, _temp_dir) = create_test_group_manager();
// Initially should have no groups
let groups = manager
.get_all_groups()
.expect("Should be able to get groups");
assert!(groups.is_empty(), "Should start with no groups");
// Create a group
let group_name = "Test Group".to_string();
let created_group = manager
.create_group(group_name.clone())
.expect("Should create group successfully");
assert_eq!(
created_group.name, group_name,
"Created group should have correct name"
);
assert!(
!created_group.id.is_empty(),
"Created group should have an ID"
);
// Verify group was saved
let groups = manager
.get_all_groups()
.expect("Should be able to get groups");
assert_eq!(groups.len(), 1, "Should have one group");
assert_eq!(
groups[0].name, group_name,
"Retrieved group should have correct name"
);
assert_eq!(
groups[0].id, created_group.id,
"Retrieved group should have correct ID"
);
}
#[test]
fn test_create_duplicate_group_fails() {
let (manager, _temp_dir) = create_test_group_manager();
let group_name = "Duplicate Group".to_string();
// Create first group
let _first_group = manager
.create_group(group_name.clone())
.expect("Should create first group");
// Try to create duplicate group
let result = manager.create_group(group_name.clone());
assert!(result.is_err(), "Should fail to create duplicate group");
let error_msg = result.unwrap_err().to_string();
assert!(
error_msg.contains("already exists"),
"Error should mention group already exists"
);
}
#[test]
fn test_update_group() {
let (manager, _temp_dir) = create_test_group_manager();
// Create a group
let original_name = "Original Name".to_string();
let created_group = manager
.create_group(original_name)
.expect("Should create group");
// Update the group
let new_name = "Updated Name".to_string();
let updated_group = manager
.update_group(created_group.id.clone(), new_name.clone())
.expect("Should update group successfully");
assert_eq!(
updated_group.name, new_name,
"Updated group should have new name"
);
assert_eq!(
updated_group.id, created_group.id,
"Updated group should keep same ID"
);
// Verify update was persisted
let groups = manager.get_all_groups().expect("Should get groups");
assert_eq!(groups.len(), 1, "Should still have one group");
assert_eq!(
groups[0].name, new_name,
"Persisted group should have updated name"
);
}
#[test]
fn test_update_nonexistent_group_fails() {
let (manager, _temp_dir) = create_test_group_manager();
let result = manager.update_group("nonexistent-id".to_string(), "New Name".to_string());
assert!(result.is_err(), "Should fail to update nonexistent group");
let error_msg = result.unwrap_err().to_string();
assert!(
error_msg.contains("not found"),
"Error should mention group not found"
);
}
#[test]
fn test_delete_group() {
let (manager, _temp_dir) = create_test_group_manager();
// Create a group
let group_name = "To Delete".to_string();
let created_group = manager
.create_group(group_name)
.expect("Should create group");
// Verify group exists
let groups = manager.get_all_groups().expect("Should get groups");
assert_eq!(groups.len(), 1, "Should have one group");
// Delete the group
manager
.delete_group(created_group.id)
.expect("Should delete group successfully");
// Verify group was deleted
let groups = manager.get_all_groups().expect("Should get groups");
assert!(groups.is_empty(), "Should have no groups after deletion");
}
#[test]
fn test_delete_nonexistent_group_fails() {
let (manager, _temp_dir) = create_test_group_manager();
let result = manager.delete_group("nonexistent-id".to_string());
assert!(result.is_err(), "Should fail to delete nonexistent group");
let error_msg = result.unwrap_err().to_string();
assert!(
error_msg.contains("not found"),
"Error should mention group not found"
);
}
#[test]
fn test_get_groups_with_profile_counts() {
let (manager, _temp_dir) = create_test_group_manager();
// Create test groups
let group1 = manager
.create_group("Group 1".to_string())
.expect("Should create group 1");
let _group2 = manager
.create_group("Group 2".to_string())
.expect("Should create group 2");
// Create mock profiles
let profiles = vec![
crate::profile::BrowserProfile {
id: uuid::Uuid::new_v4(),
name: "Profile 1".to_string(),
browser: "firefox".to_string(),
version: "1.0".to_string(),
proxy_id: None,
process_id: None,
last_launch: None,
release_type: "stable".to_string(),
camoufox_config: None,
group_id: Some(group1.id.clone()),
},
crate::profile::BrowserProfile {
id: uuid::Uuid::new_v4(),
name: "Profile 2".to_string(),
browser: "firefox".to_string(),
version: "1.0".to_string(),
proxy_id: None,
process_id: None,
last_launch: None,
release_type: "stable".to_string(),
camoufox_config: None,
group_id: Some(group1.id.clone()),
},
crate::profile::BrowserProfile {
id: uuid::Uuid::new_v4(),
name: "Profile 3".to_string(),
browser: "firefox".to_string(),
version: "1.0".to_string(),
proxy_id: None,
process_id: None,
last_launch: None,
release_type: "stable".to_string(),
camoufox_config: None,
group_id: None, // Default group
},
];
let groups_with_counts = manager
.get_groups_with_profile_counts(&profiles)
.expect("Should get groups with counts");
// Should have default group + group1 (_group2 has no profiles so shouldn't appear)
assert_eq!(
groups_with_counts.len(),
2,
"Should have 2 groups with profiles"
);
// Check default group
let default_group = groups_with_counts
.iter()
.find(|g| g.id == "default")
.expect("Should have default group");
assert_eq!(
default_group.count, 1,
"Default group should have 1 profile"
);
// Check group1
let group1_with_count = groups_with_counts
.iter()
.find(|g| g.id == group1.id)
.expect("Should have group1");
assert_eq!(group1_with_count.count, 2, "Group1 should have 2 profiles");
}
}
// Helper function to get groups with counts
pub fn get_groups_with_counts(profiles: &[crate::profile::BrowserProfile]) -> Vec<GroupWithCount> {
let group_manager = GROUP_MANAGER.lock().unwrap();
+129 -2
View File
@@ -1030,8 +1030,135 @@ mod tests {
let (manager, _temp_dir) = create_test_profile_manager();
let profiles_dir = manager.get_profiles_dir();
assert!(profiles_dir.to_string_lossy().contains("DonutBrowser"));
assert!(profiles_dir.to_string_lossy().contains("profiles"));
assert!(
profiles_dir.to_string_lossy().contains("DonutBrowser"),
"Profiles dir should contain DonutBrowser"
);
assert!(
profiles_dir.to_string_lossy().contains("profiles"),
"Profiles dir should contain profiles"
);
}
#[test]
fn test_list_profiles_empty() {
let (manager, _temp_dir) = create_test_profile_manager();
let result = manager.list_profiles();
assert!(
result.is_ok(),
"Should successfully list profiles even when empty"
);
let profiles = result.unwrap();
assert!(
profiles.is_empty(),
"Should return empty vector when no profiles exist"
);
}
#[test]
fn test_get_common_firefox_preferences() {
let (manager, _temp_dir) = create_test_profile_manager();
let prefs = manager.get_common_firefox_preferences();
assert!(!prefs.is_empty(), "Should return non-empty preferences");
// Check for some expected preferences
let prefs_string = prefs.join("\n");
assert!(
prefs_string.contains("browser.shell.checkDefaultBrowser"),
"Should contain default browser check preference"
);
assert!(
prefs_string.contains("app.update.enabled"),
"Should contain update preference"
);
}
#[test]
fn test_get_binaries_dir() {
let (manager, _temp_dir) = create_test_profile_manager();
let binaries_dir = manager.get_binaries_dir();
let path_str = binaries_dir.to_string_lossy();
assert!(
path_str.contains("DonutBrowser"),
"Binaries dir should contain DonutBrowser"
);
assert!(
path_str.contains("binaries"),
"Binaries dir should contain binaries"
);
}
#[test]
fn test_disable_proxy_settings_in_profile() {
let (manager, temp_dir) = create_test_profile_manager();
// Create a test profile directory
let profile_dir = temp_dir.path().join("test_profile");
fs::create_dir_all(&profile_dir).expect("Should create profile directory");
let result = manager.disable_proxy_settings_in_profile(&profile_dir);
assert!(result.is_ok(), "Should successfully disable proxy settings");
// Check that user.js was created
let user_js_path = profile_dir.join("user.js");
assert!(user_js_path.exists(), "user.js should be created");
let content = fs::read_to_string(&user_js_path).expect("Should read user.js");
assert!(
content.contains("network.proxy.type"),
"Should contain proxy type setting"
);
assert!(
content.contains("0"),
"Should set proxy type to 0 (no proxy)"
);
}
#[test]
fn test_apply_proxy_settings_to_profile() {
let (manager, temp_dir) = create_test_profile_manager();
// Create a test profile directory structure
let uuid_dir = temp_dir.path().join("test_uuid");
let profile_dir = uuid_dir.join("profile");
fs::create_dir_all(&profile_dir).expect("Should create profile directory");
let proxy_settings = ProxySettings {
proxy_type: "http".to_string(),
host: "proxy.example.com".to_string(),
port: 8080,
username: Some("user".to_string()),
password: Some("pass".to_string()),
};
let result = manager.apply_proxy_settings_to_profile(&profile_dir, &proxy_settings, None);
assert!(result.is_ok(), "Should successfully apply proxy settings");
// Check that user.js was created
let user_js_path = profile_dir.join("user.js");
assert!(user_js_path.exists(), "user.js should be created");
let content = fs::read_to_string(&user_js_path).expect("Should read user.js");
assert!(
content.contains("network.proxy.type"),
"Should contain proxy type setting"
);
assert!(content.contains("2"), "Should set proxy type to 2 (PAC)");
// Check that PAC file was created
let pac_path = uuid_dir.join("proxy.pac");
assert!(pac_path.exists(), "proxy.pac should be created");
let pac_content = fs::read_to_string(&pac_path).expect("Should read proxy.pac");
assert!(
pac_content.contains("FindProxyForURL"),
"PAC file should contain FindProxyForURL function"
);
}
}
+203
View File
@@ -778,3 +778,206 @@ pub async fn import_browser_profile(
lazy_static::lazy_static! {
static ref PROFILE_IMPORTER: ProfileImporter = ProfileImporter::new();
}
#[cfg(test)]
mod tests {
use super::*;
use std::env;
use tempfile::TempDir;
fn create_test_profile_importer() -> (ProfileImporter, TempDir) {
let temp_dir = TempDir::new().expect("Failed to create temp directory");
// Set up a temporary home directory for testing
env::set_var("HOME", temp_dir.path());
let importer = ProfileImporter::new();
(importer, temp_dir)
}
#[test]
fn test_profile_importer_creation() {
let (_importer, _temp_dir) = create_test_profile_importer();
// Test passes if no panic occurs
}
#[test]
fn test_get_browser_display_name() {
let (importer, _temp_dir) = create_test_profile_importer();
assert_eq!(importer.get_browser_display_name("firefox"), "Firefox");
assert_eq!(
importer.get_browser_display_name("firefox-developer"),
"Firefox Developer"
);
assert_eq!(
importer.get_browser_display_name("chromium"),
"Chrome/Chromium"
);
assert_eq!(importer.get_browser_display_name("brave"), "Brave");
assert_eq!(
importer.get_browser_display_name("mullvad-browser"),
"Mullvad Browser"
);
assert_eq!(importer.get_browser_display_name("zen"), "Zen Browser");
assert_eq!(
importer.get_browser_display_name("tor-browser"),
"Tor Browser"
);
assert_eq!(
importer.get_browser_display_name("unknown"),
"Unknown Browser"
);
}
#[test]
fn test_detect_existing_profiles_no_panic() {
let (importer, _temp_dir) = create_test_profile_importer();
// This should not panic even if no browser profiles exist
let result = importer.detect_existing_profiles();
assert!(result.is_ok(), "detect_existing_profiles should not fail");
let _profiles = result.unwrap();
// We can't assert specific profiles since they depend on the system
// but we can verify the result is a valid Vec
// We can't assert specific profiles since they depend on the system
// but we can verify the result is a valid Vec (length check is always true for Vec, but shows intent)
}
#[test]
fn test_scan_firefox_profiles_dir_nonexistent() {
let (importer, temp_dir) = create_test_profile_importer();
let nonexistent_dir = temp_dir.path().join("nonexistent");
let result = importer.scan_firefox_profiles_dir(&nonexistent_dir, "firefox");
assert!(
result.is_ok(),
"Should handle nonexistent directory gracefully"
);
let profiles = result.unwrap();
assert!(
profiles.is_empty(),
"Should return empty vector for nonexistent directory"
);
}
#[test]
fn test_scan_chrome_profiles_dir_nonexistent() {
let (importer, temp_dir) = create_test_profile_importer();
let nonexistent_dir = temp_dir.path().join("nonexistent");
let result = importer.scan_chrome_profiles_dir(&nonexistent_dir, "chromium");
assert!(
result.is_ok(),
"Should handle nonexistent directory gracefully"
);
let profiles = result.unwrap();
assert!(
profiles.is_empty(),
"Should return empty vector for nonexistent directory"
);
}
#[test]
fn test_parse_firefox_profiles_ini_empty() {
let (importer, _temp_dir) = create_test_profile_importer();
let empty_content = "";
let profiles_dir = Path::new("/tmp");
let result = importer.parse_firefox_profiles_ini(empty_content, profiles_dir, "firefox");
assert!(result.is_ok(), "Should handle empty profiles.ini");
let profiles = result.unwrap();
assert!(
profiles.is_empty(),
"Should return empty vector for empty content"
);
}
#[test]
fn test_parse_firefox_profiles_ini_valid() {
let (importer, temp_dir) = create_test_profile_importer();
// Create a mock profile directory
let profiles_dir = temp_dir.path().join("profiles");
let profile_dir = profiles_dir.join("test.profile");
fs::create_dir_all(&profile_dir).expect("Should create profile directory");
// Create a prefs.js file to make it look like a valid profile
let prefs_file = profile_dir.join("prefs.js");
fs::write(&prefs_file, "// Firefox preferences").expect("Should create prefs.js");
let profiles_ini_content = r#"
[Profile0]
Name=Test Profile
IsRelative=1
Path=test.profile
"#;
let result =
importer.parse_firefox_profiles_ini(profiles_ini_content, &profiles_dir, "firefox");
assert!(result.is_ok(), "Should parse valid profiles.ini");
let profiles = result.unwrap();
assert_eq!(profiles.len(), 1, "Should find one profile");
assert_eq!(profiles[0].name, "Firefox - Test Profile");
assert_eq!(profiles[0].browser, "firefox");
}
#[test]
fn test_copy_directory_recursive() {
let temp_dir = TempDir::new().expect("Failed to create temp directory");
// Create source directory structure
let source_dir = temp_dir.path().join("source");
let source_subdir = source_dir.join("subdir");
fs::create_dir_all(&source_subdir).expect("Should create source directories");
// Create some test files
let source_file1 = source_dir.join("file1.txt");
let source_file2 = source_subdir.join("file2.txt");
fs::write(&source_file1, "content1").expect("Should create file1");
fs::write(&source_file2, "content2").expect("Should create file2");
// Create destination directory
let dest_dir = temp_dir.path().join("dest");
// Copy recursively
let result = ProfileImporter::copy_directory_recursive(&source_dir, &dest_dir);
assert!(result.is_ok(), "Should copy directory successfully");
// Verify files were copied
let dest_file1 = dest_dir.join("file1.txt");
let dest_file2 = dest_dir.join("subdir").join("file2.txt");
assert!(dest_file1.exists(), "file1.txt should be copied");
assert!(dest_file2.exists(), "file2.txt should be copied");
let content1 = fs::read_to_string(&dest_file1).expect("Should read file1");
let content2 = fs::read_to_string(&dest_file2).expect("Should read file2");
assert_eq!(content1, "content1", "file1 content should match");
assert_eq!(content2, "content2", "file2 content should match");
}
#[test]
fn test_get_default_version_for_browser_no_versions() {
let (importer, _temp_dir) = create_test_profile_importer();
// This should fail since no versions are downloaded in test environment
let result = importer.get_default_version_for_browser("firefox");
assert!(
result.is_err(),
"Should fail when no versions are available"
);
let error_msg = result.unwrap_err().to_string();
assert!(
error_msg.contains("No downloaded versions found"),
"Error should mention no versions found"
);
}
}
+59 -9
View File
@@ -573,8 +573,23 @@ mod tests {
password: Some("pass".to_string()),
};
assert!(!valid_settings.host.is_empty());
assert!(valid_settings.port > 0);
assert!(
!valid_settings.host.is_empty(),
"Valid settings should have non-empty host"
);
assert!(
valid_settings.port > 0,
"Valid settings should have positive port"
);
assert_eq!(valid_settings.proxy_type, "http", "Proxy type should match");
assert!(
valid_settings.username.is_some(),
"Username should be present"
);
assert!(
valid_settings.password.is_some(),
"Password should be present"
);
// Test proxy settings with empty values
let empty_settings = ProxySettings {
@@ -585,7 +600,16 @@ mod tests {
password: None,
};
assert!(empty_settings.host.is_empty());
assert!(
empty_settings.host.is_empty(),
"Empty settings should have empty host"
);
assert_eq!(
empty_settings.port, 0,
"Empty settings should have zero port"
);
assert!(empty_settings.username.is_none(), "Username should be None");
assert!(empty_settings.password.is_none(), "Password should be None");
}
#[tokio::test]
@@ -743,7 +767,7 @@ mod tests {
};
// Test command arguments match expected format
let _expected_args = [
let expected_args = [
"proxy",
"start",
"--host",
@@ -759,11 +783,37 @@ mod tests {
];
// This test verifies the argument structure without actually running the command
assert_eq!(proxy_settings.host, "proxy.example.com");
assert_eq!(proxy_settings.port, 8080);
assert_eq!(proxy_settings.proxy_type, "http");
assert_eq!(proxy_settings.username.as_ref().unwrap(), "user");
assert_eq!(proxy_settings.password.as_ref().unwrap(), "pass");
assert_eq!(
proxy_settings.host, "proxy.example.com",
"Host should match expected value"
);
assert_eq!(
proxy_settings.port, 8080,
"Port should match expected value"
);
assert_eq!(
proxy_settings.proxy_type, "http",
"Proxy type should match expected value"
);
assert_eq!(
proxy_settings.username.as_ref().unwrap(),
"user",
"Username should match expected value"
);
assert_eq!(
proxy_settings.password.as_ref().unwrap(),
"pass",
"Password should match expected value"
);
// Verify expected args structure
assert_eq!(expected_args[0], "proxy", "First arg should be 'proxy'");
assert_eq!(expected_args[1], "start", "Second arg should be 'start'");
assert_eq!(expected_args[2], "--host", "Third arg should be '--host'");
assert_eq!(
expected_args[3], "proxy.example.com",
"Fourth arg should be host value"
);
}
// Test the CLI detachment specifically - ensure the CLI exits properly
+210
View File
@@ -245,3 +245,213 @@ pub async fn clear_all_version_cache_and_refetch(
lazy_static::lazy_static! {
static ref SETTINGS_MANAGER: SettingsManager = SettingsManager::new();
}
#[cfg(test)]
mod tests {
use super::*;
use std::env;
use tempfile::TempDir;
fn create_test_settings_manager() -> (SettingsManager, TempDir) {
let temp_dir = TempDir::new().expect("Failed to create temp directory");
// Set up a temporary home directory for testing
env::set_var("HOME", temp_dir.path());
let manager = SettingsManager::new();
(manager, temp_dir)
}
#[test]
fn test_settings_manager_creation() {
let (_manager, _temp_dir) = create_test_settings_manager();
// Test passes if no panic occurs
}
#[test]
fn test_default_app_settings() {
let default_settings = AppSettings::default();
assert!(
!default_settings.set_as_default_browser,
"Default should not set as default browser"
);
assert_eq!(
default_settings.theme, "system",
"Default theme should be system"
);
}
#[test]
fn test_default_table_sorting_settings() {
let default_sorting = TableSortingSettings::default();
assert_eq!(
default_sorting.column, "name",
"Default sort column should be name"
);
assert_eq!(
default_sorting.direction, "asc",
"Default sort direction should be asc"
);
}
#[test]
fn test_load_settings_nonexistent_file() {
let (manager, _temp_dir) = create_test_settings_manager();
let result = manager.load_settings();
assert!(
result.is_ok(),
"Should handle nonexistent settings file gracefully"
);
let settings = result.unwrap();
assert!(
!settings.set_as_default_browser,
"Should return default settings"
);
assert_eq!(settings.theme, "system", "Should return default theme");
}
#[test]
fn test_save_and_load_settings() {
let (manager, _temp_dir) = create_test_settings_manager();
let test_settings = AppSettings {
set_as_default_browser: true,
theme: "dark".to_string(),
};
// Save settings
let save_result = manager.save_settings(&test_settings);
assert!(save_result.is_ok(), "Should save settings successfully");
// Load settings back
let load_result = manager.load_settings();
assert!(load_result.is_ok(), "Should load settings successfully");
let loaded_settings = load_result.unwrap();
assert!(
loaded_settings.set_as_default_browser,
"Loaded settings should match saved"
);
assert_eq!(
loaded_settings.theme, "dark",
"Loaded theme should match saved"
);
}
#[test]
fn test_load_table_sorting_nonexistent_file() {
let (manager, _temp_dir) = create_test_settings_manager();
let result = manager.load_table_sorting();
assert!(
result.is_ok(),
"Should handle nonexistent sorting file gracefully"
);
let sorting = result.unwrap();
assert_eq!(sorting.column, "name", "Should return default sorting");
assert_eq!(sorting.direction, "asc", "Should return default direction");
}
#[test]
fn test_save_and_load_table_sorting() {
let (manager, _temp_dir) = create_test_settings_manager();
let test_sorting = TableSortingSettings {
column: "browser".to_string(),
direction: "desc".to_string(),
};
// Save sorting
let save_result = manager.save_table_sorting(&test_sorting);
assert!(save_result.is_ok(), "Should save sorting successfully");
// Load sorting back
let load_result = manager.load_table_sorting();
assert!(load_result.is_ok(), "Should load sorting successfully");
let loaded_sorting = load_result.unwrap();
assert_eq!(
loaded_sorting.column, "browser",
"Loaded column should match saved"
);
assert_eq!(
loaded_sorting.direction, "desc",
"Loaded direction should match saved"
);
}
#[test]
fn test_should_show_settings_on_startup() {
let (manager, _temp_dir) = create_test_settings_manager();
let result = manager.should_show_settings_on_startup();
assert!(result.is_ok(), "Should not fail");
let should_show = result.unwrap();
assert!(
!should_show,
"Should always return false as per implementation"
);
}
#[test]
fn test_load_corrupted_settings_file() {
let (manager, _temp_dir) = create_test_settings_manager();
// Create settings directory
let settings_dir = manager.get_settings_dir();
fs::create_dir_all(&settings_dir).expect("Should create settings directory");
// Write corrupted JSON
let settings_file = manager.get_settings_file();
fs::write(&settings_file, "{ invalid json }").expect("Should write corrupted file");
// Should handle corrupted file gracefully
let result = manager.load_settings();
assert!(
result.is_ok(),
"Should handle corrupted settings file gracefully"
);
let settings = result.unwrap();
assert!(
!settings.set_as_default_browser,
"Should return default settings for corrupted file"
);
assert_eq!(
settings.theme, "system",
"Should return default theme for corrupted file"
);
}
#[test]
fn test_settings_file_paths() {
let (manager, _temp_dir) = create_test_settings_manager();
let settings_dir = manager.get_settings_dir();
let settings_file = manager.get_settings_file();
let sorting_file = manager.get_table_sorting_file();
assert!(
settings_dir.to_string_lossy().contains("settings"),
"Settings dir should contain 'settings'"
);
assert!(
settings_file
.to_string_lossy()
.ends_with("app_settings.json"),
"Settings file should end with app_settings.json"
);
assert!(
sorting_file
.to_string_lossy()
.ends_with("table_sorting.json"),
"Sorting file should end with table_sorting.json"
);
}
}
+99 -2
View File
@@ -529,16 +529,113 @@ mod tests {
#[test]
fn test_theme_detector_creation() {
let detector = ThemeDetector::instance();
// Should not panic when creating detector
assert!(
std::ptr::eq(detector, ThemeDetector::instance()),
"Should return same instance (singleton)"
);
}
#[test]
fn test_detect_system_theme_returns_valid_value() {
let detector = ThemeDetector::instance();
let theme = detector.detect_system_theme();
// Should return a valid theme string
assert!(matches!(theme.theme.as_str(), "light" | "dark" | "unknown"));
assert!(
matches!(theme.theme.as_str(), "light" | "dark" | "unknown"),
"Theme should be one of: light, dark, unknown. Got: {}",
theme.theme
);
// Theme string should not be empty
assert!(!theme.theme.is_empty(), "Theme string should not be empty");
}
#[test]
fn test_get_system_theme_command() {
let theme = get_system_theme();
assert!(matches!(theme.theme.as_str(), "light" | "dark" | "unknown"));
assert!(
matches!(theme.theme.as_str(), "light" | "dark" | "unknown"),
"Command should return valid theme. Got: {}",
theme.theme
);
// Should be consistent with direct detector call
let detector = ThemeDetector::instance();
let direct_theme = detector.detect_system_theme();
assert_eq!(
theme.theme, direct_theme.theme,
"Command and direct call should return same theme"
);
}
#[test]
fn test_system_theme_serialization() {
let theme = SystemTheme {
theme: "dark".to_string(),
};
// Test serialization
let serialized = serde_json::to_string(&theme);
assert!(
serialized.is_ok(),
"Should serialize SystemTheme successfully"
);
let json_str = serialized.unwrap();
assert!(
json_str.contains("dark"),
"Serialized JSON should contain theme value"
);
// Test deserialization
let deserialized: Result<SystemTheme, _> = serde_json::from_str(&json_str);
assert!(
deserialized.is_ok(),
"Should deserialize SystemTheme successfully"
);
let theme_back = deserialized.unwrap();
assert_eq!(
theme_back.theme, "dark",
"Deserialized theme should match original"
);
}
#[cfg(target_os = "linux")]
#[test]
fn test_linux_command_availability_check() {
use super::linux::is_command_available;
// Test with a command that should exist on most systems
let ls_available = is_command_available("ls");
assert!(ls_available, "ls command should be available on Linux");
// Test with a command that definitely doesn't exist
let fake_available = is_command_available("definitely_nonexistent_command_12345");
assert!(!fake_available, "Fake command should not be available");
}
#[test]
fn test_theme_detector_consistency() {
let detector = ThemeDetector::instance();
// Call detect_system_theme multiple times - should be consistent
let theme1 = detector.detect_system_theme();
let theme2 = detector.detect_system_theme();
let theme3 = detector.detect_system_theme();
assert_eq!(
theme1.theme, theme2.theme,
"Multiple calls should return consistent results"
);
assert_eq!(
theme2.theme, theme3.theme,
"Multiple calls should return consistent results"
);
}
}
+132 -11
View File
@@ -519,31 +519,152 @@ mod tests {
#[test]
fn test_should_run_background_update_logic() {
// Note: This test uses the shared state file, so results may vary
// depending on previous test runs. This is expected behavior.
// Create isolated test states to avoid interference
let current_time = VersionUpdater::get_current_timestamp();
// Test with recent update (should not update)
let recent_state = BackgroundUpdateState {
last_update_time: VersionUpdater::get_current_timestamp() - 60, // 1 minute ago
last_update_time: current_time - 60, // 1 minute ago
update_interval_hours: 3,
};
VersionUpdater::save_background_update_state(&recent_state).unwrap();
assert!(!VersionUpdater::should_run_background_update());
// Save and test recent state
let save_result = VersionUpdater::save_background_update_state(&recent_state);
assert!(save_result.is_ok(), "Should save recent state successfully");
let should_update_recent = VersionUpdater::should_run_background_update();
assert!(
!should_update_recent,
"Should not update when last update was recent"
);
// Test with old update (should update)
let old_state = BackgroundUpdateState {
last_update_time: VersionUpdater::get_current_timestamp() - (4 * 60 * 60), // 4 hours ago
last_update_time: current_time - (4 * 60 * 60), // 4 hours ago
update_interval_hours: 3,
};
VersionUpdater::save_background_update_state(&old_state).unwrap();
assert!(VersionUpdater::should_run_background_update());
// Save and test old state
let save_result = VersionUpdater::save_background_update_state(&old_state);
assert!(save_result.is_ok(), "Should save old state successfully");
let should_update_old = VersionUpdater::should_run_background_update();
assert!(should_update_old, "Should update when last update was old");
// Test with never updated (should update)
let never_updated_state = BackgroundUpdateState {
last_update_time: 0,
update_interval_hours: 3,
};
let save_result = VersionUpdater::save_background_update_state(&never_updated_state);
assert!(
save_result.is_ok(),
"Should save never updated state successfully"
);
let should_update_never = VersionUpdater::should_run_background_update();
assert!(
should_update_never,
"Should update when never updated before"
);
}
#[test]
fn test_cache_dir_creation() {
// This should not panic and should create the directory if it doesn't exist
let cache_dir = VersionUpdater::get_cache_dir().unwrap();
assert!(cache_dir.exists());
assert!(cache_dir.is_dir());
let cache_dir_result = VersionUpdater::get_cache_dir();
assert!(
cache_dir_result.is_ok(),
"Should successfully get cache directory"
);
let cache_dir = cache_dir_result.unwrap();
assert!(
cache_dir.exists(),
"Cache directory should exist after creation"
);
assert!(cache_dir.is_dir(), "Cache directory should be a directory");
// Verify the path contains expected components
let path_str = cache_dir.to_string_lossy();
assert!(
path_str.contains("version_cache"),
"Path should contain version_cache"
);
// Test that calling it again returns the same directory
let cache_dir2 = VersionUpdater::get_cache_dir().unwrap();
assert_eq!(
cache_dir, cache_dir2,
"Multiple calls should return same directory"
);
}
#[test]
fn test_version_updater_creation() {
let updater = VersionUpdater::new();
// Should have valid references to services
assert!(
!std::ptr::eq(updater.version_service as *const _, std::ptr::null()),
"Version service should not be null"
);
assert!(
!std::ptr::eq(updater.auto_updater as *const _, std::ptr::null()),
"Auto updater should not be null"
);
assert!(
updater.app_handle.is_none(),
"App handle should initially be None"
);
}
#[test]
fn test_get_current_timestamp() {
let timestamp1 = VersionUpdater::get_current_timestamp();
// Should be a reasonable timestamp (after year 2020)
assert!(
timestamp1 > 1577836800,
"Timestamp should be after 2020-01-01"
); // 2020-01-01 00:00:00 UTC
// Should be before year 2100
assert!(
timestamp1 < 4102444800,
"Timestamp should be before 2100-01-01"
); // 2100-01-01 00:00:00 UTC
// Wait a tiny bit and check it increases
std::thread::sleep(std::time::Duration::from_millis(1));
let timestamp2 = VersionUpdater::get_current_timestamp();
assert!(timestamp2 >= timestamp1, "Timestamp should not decrease");
}
#[test]
fn test_background_update_state_default() {
let state = BackgroundUpdateState::default();
assert_eq!(
state.last_update_time, 0,
"Default last update time should be 0"
);
assert_eq!(
state.update_interval_hours, 3,
"Default update interval should be 3 hours"
);
}
#[test]
fn test_get_version_updater_singleton() {
let updater1 = get_version_updater();
let updater2 = get_version_updater();
// Should return the same Arc instance
assert!(
Arc::ptr_eq(&updater1, &updater2),
"Should return same singleton instance"
);
}
}