903 lines
31 KiB
Rust
903 lines
31 KiB
Rust
use std::process::Command;
|
|
use crate::process::CommandResult;
|
|
|
|
/// Error type for package management operations
|
|
#[derive(Debug)]
|
|
pub enum PackageError {
|
|
/// Command failed with error message
|
|
CommandFailed(String),
|
|
/// Command execution failed with IO error
|
|
CommandExecutionFailed(std::io::Error),
|
|
/// Unsupported platform
|
|
UnsupportedPlatform(String),
|
|
/// Other error
|
|
Other(String),
|
|
}
|
|
|
|
impl std::fmt::Display for PackageError {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
match self {
|
|
PackageError::CommandFailed(msg) => write!(f, "Command failed: {}", msg),
|
|
PackageError::CommandExecutionFailed(e) => write!(f, "Command execution failed: {}", e),
|
|
PackageError::UnsupportedPlatform(msg) => write!(f, "Unsupported platform: {}", msg),
|
|
PackageError::Other(msg) => write!(f, "Error: {}", msg),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl std::error::Error for PackageError {}
|
|
|
|
/// Platform enum for detecting the current operating system
|
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
pub enum Platform {
|
|
/// Ubuntu Linux
|
|
Ubuntu,
|
|
/// macOS
|
|
MacOS,
|
|
/// Unknown platform
|
|
Unknown,
|
|
}
|
|
|
|
impl Platform {
|
|
/// Detect the current platform
|
|
pub fn detect() -> Self {
|
|
// Check for macOS
|
|
if std::path::Path::new("/usr/bin/sw_vers").exists() {
|
|
return Platform::MacOS;
|
|
}
|
|
|
|
// Check for Ubuntu
|
|
if std::path::Path::new("/etc/lsb-release").exists() {
|
|
// Read the file to confirm it's Ubuntu
|
|
if let Ok(content) = std::fs::read_to_string("/etc/lsb-release") {
|
|
if content.contains("Ubuntu") {
|
|
return Platform::Ubuntu;
|
|
}
|
|
}
|
|
}
|
|
|
|
Platform::Unknown
|
|
}
|
|
}
|
|
|
|
/// Thread-local storage for debug flag
|
|
thread_local! {
|
|
static DEBUG: std::cell::RefCell<bool> = std::cell::RefCell::new(false);
|
|
}
|
|
|
|
/// Set the debug flag for the current thread
|
|
pub fn set_thread_local_debug(debug: bool) {
|
|
DEBUG.with(|cell| {
|
|
*cell.borrow_mut() = debug;
|
|
});
|
|
}
|
|
|
|
/// Get the debug flag for the current thread
|
|
pub fn thread_local_debug() -> bool {
|
|
DEBUG.with(|cell| {
|
|
*cell.borrow()
|
|
})
|
|
}
|
|
|
|
/// Execute a package management command and return the result
|
|
pub fn execute_package_command(args: &[&str], debug: bool) -> Result<CommandResult, PackageError> {
|
|
// Save the current debug flag
|
|
let previous_debug = thread_local_debug();
|
|
|
|
// Set the thread-local debug flag
|
|
set_thread_local_debug(debug);
|
|
|
|
if debug {
|
|
println!("Executing command: {}", args.join(" "));
|
|
}
|
|
|
|
let output = Command::new(args[0])
|
|
.args(&args[1..])
|
|
.output();
|
|
|
|
// Restore the previous debug flag
|
|
set_thread_local_debug(previous_debug);
|
|
|
|
match output {
|
|
Ok(output) => {
|
|
let stdout = String::from_utf8_lossy(&output.stdout).to_string();
|
|
let stderr = String::from_utf8_lossy(&output.stderr).to_string();
|
|
|
|
let result = CommandResult {
|
|
stdout,
|
|
stderr,
|
|
success: output.status.success(),
|
|
code: output.status.code().unwrap_or(-1),
|
|
};
|
|
|
|
// Always output stdout/stderr when debug is true
|
|
if debug {
|
|
if !result.stdout.is_empty() {
|
|
println!("Command stdout: {}", result.stdout);
|
|
}
|
|
|
|
if !result.stderr.is_empty() {
|
|
println!("Command stderr: {}", result.stderr);
|
|
}
|
|
|
|
if result.success {
|
|
println!("Command succeeded with code {}", result.code);
|
|
} else {
|
|
println!("Command failed with code {}", result.code);
|
|
}
|
|
}
|
|
|
|
if result.success {
|
|
Ok(result)
|
|
} else {
|
|
// If command failed and debug is false, output stderr
|
|
if !debug {
|
|
println!("Command failed with code {}: {}", result.code, result.stderr.trim());
|
|
}
|
|
Err(PackageError::CommandFailed(format!("Command failed with code {}: {}",
|
|
result.code, result.stderr.trim())))
|
|
}
|
|
},
|
|
Err(e) => {
|
|
// Always output error information
|
|
println!("Command execution failed: {}", e);
|
|
Err(PackageError::CommandExecutionFailed(e))
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Trait for package managers
|
|
pub trait PackageManager {
|
|
/// Install a package
|
|
fn install(&self, package: &str) -> Result<CommandResult, PackageError>;
|
|
|
|
/// Remove a package
|
|
fn remove(&self, package: &str) -> Result<CommandResult, PackageError>;
|
|
|
|
/// Update package lists
|
|
fn update(&self) -> Result<CommandResult, PackageError>;
|
|
|
|
/// Upgrade installed packages
|
|
fn upgrade(&self) -> Result<CommandResult, PackageError>;
|
|
|
|
/// List installed packages
|
|
fn list_installed(&self) -> Result<Vec<String>, PackageError>;
|
|
|
|
/// Search for packages
|
|
fn search(&self, query: &str) -> Result<Vec<String>, PackageError>;
|
|
|
|
/// Check if a package is installed
|
|
fn is_installed(&self, package: &str) -> Result<bool, PackageError>;
|
|
}
|
|
|
|
/// APT package manager for Ubuntu
|
|
pub struct AptPackageManager {
|
|
debug: bool,
|
|
}
|
|
|
|
impl AptPackageManager {
|
|
/// Create a new APT package manager
|
|
pub fn new(debug: bool) -> Self {
|
|
Self { debug }
|
|
}
|
|
}
|
|
|
|
impl PackageManager for AptPackageManager {
|
|
fn install(&self, package: &str) -> Result<CommandResult, PackageError> {
|
|
// Use -y to make it non-interactive and --quiet to reduce output
|
|
execute_package_command(&["apt-get", "install", "-y", "--quiet", package], self.debug)
|
|
}
|
|
|
|
fn remove(&self, package: &str) -> Result<CommandResult, PackageError> {
|
|
// Use -y to make it non-interactive and --quiet to reduce output
|
|
execute_package_command(&["apt-get", "remove", "-y", "--quiet", package], self.debug)
|
|
}
|
|
|
|
fn update(&self) -> Result<CommandResult, PackageError> {
|
|
// Use -y to make it non-interactive and --quiet to reduce output
|
|
execute_package_command(&["apt-get", "update", "-y", "--quiet"], self.debug)
|
|
}
|
|
|
|
fn upgrade(&self) -> Result<CommandResult, PackageError> {
|
|
// Use -y to make it non-interactive and --quiet to reduce output
|
|
execute_package_command(&["apt-get", "upgrade", "-y", "--quiet"], self.debug)
|
|
}
|
|
|
|
fn list_installed(&self) -> Result<Vec<String>, PackageError> {
|
|
let result = execute_package_command(&["dpkg", "--get-selections"], self.debug)?;
|
|
let packages = result.stdout
|
|
.lines()
|
|
.filter_map(|line| {
|
|
let parts: Vec<&str> = line.split_whitespace().collect();
|
|
if parts.len() >= 2 && parts[1] == "install" {
|
|
Some(parts[0].to_string())
|
|
} else {
|
|
None
|
|
}
|
|
})
|
|
.collect();
|
|
Ok(packages)
|
|
}
|
|
|
|
fn search(&self, query: &str) -> Result<Vec<String>, PackageError> {
|
|
let result = execute_package_command(&["apt-cache", "search", query], self.debug)?;
|
|
let packages = result.stdout
|
|
.lines()
|
|
.map(|line| {
|
|
let parts: Vec<&str> = line.split_whitespace().collect();
|
|
if !parts.is_empty() {
|
|
parts[0].to_string()
|
|
} else {
|
|
String::new()
|
|
}
|
|
})
|
|
.filter(|s| !s.is_empty())
|
|
.collect();
|
|
Ok(packages)
|
|
}
|
|
|
|
fn is_installed(&self, package: &str) -> Result<bool, PackageError> {
|
|
let result = execute_package_command(&["dpkg", "-s", package], self.debug);
|
|
match result {
|
|
Ok(cmd_result) => Ok(cmd_result.success),
|
|
Err(_) => Ok(false),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Homebrew package manager for macOS
|
|
pub struct BrewPackageManager {
|
|
debug: bool,
|
|
}
|
|
|
|
impl BrewPackageManager {
|
|
/// Create a new Homebrew package manager
|
|
pub fn new(debug: bool) -> Self {
|
|
Self { debug }
|
|
}
|
|
}
|
|
|
|
impl PackageManager for BrewPackageManager {
|
|
fn install(&self, package: &str) -> Result<CommandResult, PackageError> {
|
|
// Use --quiet to reduce output
|
|
execute_package_command(&["brew", "install", "--quiet", package], self.debug)
|
|
}
|
|
|
|
fn remove(&self, package: &str) -> Result<CommandResult, PackageError> {
|
|
// Use --quiet to reduce output
|
|
execute_package_command(&["brew", "uninstall", "--quiet", package], self.debug)
|
|
}
|
|
|
|
fn update(&self) -> Result<CommandResult, PackageError> {
|
|
// Use --quiet to reduce output
|
|
execute_package_command(&["brew", "update", "--quiet"], self.debug)
|
|
}
|
|
|
|
fn upgrade(&self) -> Result<CommandResult, PackageError> {
|
|
// Use --quiet to reduce output
|
|
execute_package_command(&["brew", "upgrade", "--quiet"], self.debug)
|
|
}
|
|
|
|
fn list_installed(&self) -> Result<Vec<String>, PackageError> {
|
|
let result = execute_package_command(&["brew", "list", "--formula"], self.debug)?;
|
|
let packages = result.stdout
|
|
.lines()
|
|
.map(|line| line.trim().to_string())
|
|
.filter(|s| !s.is_empty())
|
|
.collect();
|
|
Ok(packages)
|
|
}
|
|
|
|
fn search(&self, query: &str) -> Result<Vec<String>, PackageError> {
|
|
let result = execute_package_command(&["brew", "search", query], self.debug)?;
|
|
let packages = result.stdout
|
|
.lines()
|
|
.map(|line| line.trim().to_string())
|
|
.filter(|s| !s.is_empty())
|
|
.collect();
|
|
Ok(packages)
|
|
}
|
|
|
|
fn is_installed(&self, package: &str) -> Result<bool, PackageError> {
|
|
let result = execute_package_command(&["brew", "list", package], self.debug);
|
|
match result {
|
|
Ok(cmd_result) => Ok(cmd_result.success),
|
|
Err(_) => Ok(false),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// PackHero factory for package management
|
|
pub struct PackHero {
|
|
platform: Platform,
|
|
debug: bool,
|
|
}
|
|
|
|
impl PackHero {
|
|
/// Create a new PackHero instance
|
|
pub fn new() -> Self {
|
|
let platform = Platform::detect();
|
|
Self {
|
|
platform,
|
|
debug: false,
|
|
}
|
|
}
|
|
|
|
/// Set the debug mode
|
|
pub fn set_debug(&mut self, debug: bool) -> &mut Self {
|
|
self.debug = debug;
|
|
self
|
|
}
|
|
|
|
/// Get the debug mode
|
|
pub fn debug(&self) -> bool {
|
|
self.debug
|
|
}
|
|
|
|
/// Get the detected platform
|
|
pub fn platform(&self) -> Platform {
|
|
self.platform
|
|
}
|
|
|
|
/// Get a package manager for the current platform
|
|
fn get_package_manager(&self) -> Result<Box<dyn PackageManager>, PackageError> {
|
|
match self.platform {
|
|
Platform::Ubuntu => Ok(Box::new(AptPackageManager::new(self.debug))),
|
|
Platform::MacOS => Ok(Box::new(BrewPackageManager::new(self.debug))),
|
|
Platform::Unknown => Err(PackageError::UnsupportedPlatform("Unsupported platform".to_string())),
|
|
}
|
|
}
|
|
|
|
/// Install a package
|
|
pub fn install(&self, package: &str) -> Result<CommandResult, PackageError> {
|
|
let pm = self.get_package_manager()?;
|
|
pm.install(package)
|
|
}
|
|
|
|
/// Remove a package
|
|
pub fn remove(&self, package: &str) -> Result<CommandResult, PackageError> {
|
|
let pm = self.get_package_manager()?;
|
|
pm.remove(package)
|
|
}
|
|
|
|
/// Update package lists
|
|
pub fn update(&self) -> Result<CommandResult, PackageError> {
|
|
let pm = self.get_package_manager()?;
|
|
pm.update()
|
|
}
|
|
|
|
/// Upgrade installed packages
|
|
pub fn upgrade(&self) -> Result<CommandResult, PackageError> {
|
|
let pm = self.get_package_manager()?;
|
|
pm.upgrade()
|
|
}
|
|
|
|
/// List installed packages
|
|
pub fn list_installed(&self) -> Result<Vec<String>, PackageError> {
|
|
let pm = self.get_package_manager()?;
|
|
pm.list_installed()
|
|
}
|
|
|
|
/// Search for packages
|
|
pub fn search(&self, query: &str) -> Result<Vec<String>, PackageError> {
|
|
let pm = self.get_package_manager()?;
|
|
pm.search(query)
|
|
}
|
|
|
|
/// Check if a package is installed
|
|
pub fn is_installed(&self, package: &str) -> Result<bool, PackageError> {
|
|
let pm = self.get_package_manager()?;
|
|
pm.is_installed(package)
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
// Import the std::process::Command directly for some test-specific commands
|
|
use std::process::Command as StdCommand;
|
|
use super::*;
|
|
use std::sync::{Arc, Mutex};
|
|
|
|
#[test]
|
|
fn test_platform_detection() {
|
|
// This test will return different results depending on the platform it's run on
|
|
let platform = Platform::detect();
|
|
println!("Detected platform: {:?}", platform);
|
|
|
|
// Just ensure it doesn't panic
|
|
assert!(true);
|
|
}
|
|
|
|
#[test]
|
|
fn test_debug_flag() {
|
|
// Test setting and getting the debug flag
|
|
set_thread_local_debug(true);
|
|
assert_eq!(thread_local_debug(), true);
|
|
|
|
set_thread_local_debug(false);
|
|
assert_eq!(thread_local_debug(), false);
|
|
}
|
|
|
|
#[test]
|
|
fn test_package_error_display() {
|
|
// Test the Display implementation for PackageError
|
|
let err1 = PackageError::CommandFailed("command failed".to_string());
|
|
assert_eq!(err1.to_string(), "Command failed: command failed");
|
|
|
|
let err2 = PackageError::UnsupportedPlatform("test platform".to_string());
|
|
assert_eq!(err2.to_string(), "Unsupported platform: test platform");
|
|
|
|
let err3 = PackageError::Other("other error".to_string());
|
|
assert_eq!(err3.to_string(), "Error: other error");
|
|
|
|
// We can't easily test CommandExecutionFailed because std::io::Error doesn't implement PartialEq
|
|
}
|
|
|
|
// Mock package manager for testing
|
|
struct MockPackageManager {
|
|
debug: bool,
|
|
install_called: Arc<Mutex<bool>>,
|
|
remove_called: Arc<Mutex<bool>>,
|
|
update_called: Arc<Mutex<bool>>,
|
|
upgrade_called: Arc<Mutex<bool>>,
|
|
list_installed_called: Arc<Mutex<bool>>,
|
|
search_called: Arc<Mutex<bool>>,
|
|
is_installed_called: Arc<Mutex<bool>>,
|
|
// Control what the mock returns
|
|
should_succeed: bool,
|
|
}
|
|
|
|
impl MockPackageManager {
|
|
fn new(debug: bool, should_succeed: bool) -> Self {
|
|
Self {
|
|
debug,
|
|
install_called: Arc::new(Mutex::new(false)),
|
|
remove_called: Arc::new(Mutex::new(false)),
|
|
update_called: Arc::new(Mutex::new(false)),
|
|
upgrade_called: Arc::new(Mutex::new(false)),
|
|
list_installed_called: Arc::new(Mutex::new(false)),
|
|
search_called: Arc::new(Mutex::new(false)),
|
|
is_installed_called: Arc::new(Mutex::new(false)),
|
|
should_succeed,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl PackageManager for MockPackageManager {
|
|
fn install(&self, package: &str) -> Result<CommandResult, PackageError> {
|
|
*self.install_called.lock().unwrap() = true;
|
|
if self.should_succeed {
|
|
Ok(CommandResult {
|
|
stdout: format!("Installed package {}", package),
|
|
stderr: String::new(),
|
|
success: true,
|
|
code: 0,
|
|
})
|
|
} else {
|
|
Err(PackageError::CommandFailed("Mock install failed".to_string()))
|
|
}
|
|
}
|
|
|
|
fn remove(&self, package: &str) -> Result<CommandResult, PackageError> {
|
|
*self.remove_called.lock().unwrap() = true;
|
|
if self.should_succeed {
|
|
Ok(CommandResult {
|
|
stdout: format!("Removed package {}", package),
|
|
stderr: String::new(),
|
|
success: true,
|
|
code: 0,
|
|
})
|
|
} else {
|
|
Err(PackageError::CommandFailed("Mock remove failed".to_string()))
|
|
}
|
|
}
|
|
|
|
fn update(&self) -> Result<CommandResult, PackageError> {
|
|
*self.update_called.lock().unwrap() = true;
|
|
if self.should_succeed {
|
|
Ok(CommandResult {
|
|
stdout: "Updated package lists".to_string(),
|
|
stderr: String::new(),
|
|
success: true,
|
|
code: 0,
|
|
})
|
|
} else {
|
|
Err(PackageError::CommandFailed("Mock update failed".to_string()))
|
|
}
|
|
}
|
|
|
|
fn upgrade(&self) -> Result<CommandResult, PackageError> {
|
|
*self.upgrade_called.lock().unwrap() = true;
|
|
if self.should_succeed {
|
|
Ok(CommandResult {
|
|
stdout: "Upgraded packages".to_string(),
|
|
stderr: String::new(),
|
|
success: true,
|
|
code: 0,
|
|
})
|
|
} else {
|
|
Err(PackageError::CommandFailed("Mock upgrade failed".to_string()))
|
|
}
|
|
}
|
|
|
|
fn list_installed(&self) -> Result<Vec<String>, PackageError> {
|
|
*self.list_installed_called.lock().unwrap() = true;
|
|
if self.should_succeed {
|
|
Ok(vec!["package1".to_string(), "package2".to_string()])
|
|
} else {
|
|
Err(PackageError::CommandFailed("Mock list_installed failed".to_string()))
|
|
}
|
|
}
|
|
|
|
fn search(&self, query: &str) -> Result<Vec<String>, PackageError> {
|
|
*self.search_called.lock().unwrap() = true;
|
|
if self.should_succeed {
|
|
Ok(vec![format!("result1-{}", query), format!("result2-{}", query)])
|
|
} else {
|
|
Err(PackageError::CommandFailed("Mock search failed".to_string()))
|
|
}
|
|
}
|
|
|
|
fn is_installed(&self, package: &str) -> Result<bool, PackageError> {
|
|
*self.is_installed_called.lock().unwrap() = true;
|
|
if self.should_succeed {
|
|
Ok(package == "installed-package")
|
|
} else {
|
|
Err(PackageError::CommandFailed("Mock is_installed failed".to_string()))
|
|
}
|
|
}
|
|
}
|
|
|
|
// Custom PackHero for testing with a mock package manager
|
|
struct TestPackHero {
|
|
platform: Platform,
|
|
debug: bool,
|
|
mock_manager: MockPackageManager,
|
|
}
|
|
|
|
impl TestPackHero {
|
|
fn new(platform: Platform, debug: bool, should_succeed: bool) -> Self {
|
|
Self {
|
|
platform,
|
|
debug,
|
|
mock_manager: MockPackageManager::new(debug, should_succeed),
|
|
}
|
|
}
|
|
|
|
fn get_package_manager(&self) -> Result<&dyn PackageManager, PackageError> {
|
|
match self.platform {
|
|
Platform::Ubuntu | Platform::MacOS => Ok(&self.mock_manager),
|
|
Platform::Unknown => Err(PackageError::UnsupportedPlatform("Unsupported platform".to_string())),
|
|
}
|
|
}
|
|
|
|
fn install(&self, package: &str) -> Result<CommandResult, PackageError> {
|
|
let pm = self.get_package_manager()?;
|
|
pm.install(package)
|
|
}
|
|
|
|
fn remove(&self, package: &str) -> Result<CommandResult, PackageError> {
|
|
let pm = self.get_package_manager()?;
|
|
pm.remove(package)
|
|
}
|
|
|
|
fn update(&self) -> Result<CommandResult, PackageError> {
|
|
let pm = self.get_package_manager()?;
|
|
pm.update()
|
|
}
|
|
|
|
fn upgrade(&self) -> Result<CommandResult, PackageError> {
|
|
let pm = self.get_package_manager()?;
|
|
pm.upgrade()
|
|
}
|
|
|
|
fn list_installed(&self) -> Result<Vec<String>, PackageError> {
|
|
let pm = self.get_package_manager()?;
|
|
pm.list_installed()
|
|
}
|
|
|
|
fn search(&self, query: &str) -> Result<Vec<String>, PackageError> {
|
|
let pm = self.get_package_manager()?;
|
|
pm.search(query)
|
|
}
|
|
|
|
fn is_installed(&self, package: &str) -> Result<bool, PackageError> {
|
|
let pm = self.get_package_manager()?;
|
|
pm.is_installed(package)
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_packhero_with_mock_success() {
|
|
// Test PackHero with a mock package manager that succeeds
|
|
let hero = TestPackHero::new(Platform::Ubuntu, false, true);
|
|
|
|
// Test install
|
|
let result = hero.install("test-package");
|
|
assert!(result.is_ok());
|
|
assert!(*hero.mock_manager.install_called.lock().unwrap());
|
|
|
|
// Test remove
|
|
let result = hero.remove("test-package");
|
|
assert!(result.is_ok());
|
|
assert!(*hero.mock_manager.remove_called.lock().unwrap());
|
|
|
|
// Test update
|
|
let result = hero.update();
|
|
assert!(result.is_ok());
|
|
assert!(*hero.mock_manager.update_called.lock().unwrap());
|
|
|
|
// Test upgrade
|
|
let result = hero.upgrade();
|
|
assert!(result.is_ok());
|
|
assert!(*hero.mock_manager.upgrade_called.lock().unwrap());
|
|
|
|
// Test list_installed
|
|
let result = hero.list_installed();
|
|
assert!(result.is_ok());
|
|
assert_eq!(result.unwrap(), vec!["package1".to_string(), "package2".to_string()]);
|
|
assert!(*hero.mock_manager.list_installed_called.lock().unwrap());
|
|
|
|
// Test search
|
|
let result = hero.search("query");
|
|
assert!(result.is_ok());
|
|
assert_eq!(result.unwrap(), vec!["result1-query".to_string(), "result2-query".to_string()]);
|
|
assert!(*hero.mock_manager.search_called.lock().unwrap());
|
|
|
|
// Test is_installed
|
|
let result = hero.is_installed("installed-package");
|
|
assert!(result.is_ok());
|
|
assert!(result.unwrap());
|
|
assert!(*hero.mock_manager.is_installed_called.lock().unwrap());
|
|
|
|
let result = hero.is_installed("not-installed-package");
|
|
assert!(result.is_ok());
|
|
assert!(!result.unwrap());
|
|
}
|
|
|
|
#[test]
|
|
fn test_packhero_with_mock_failure() {
|
|
// Test PackHero with a mock package manager that fails
|
|
let hero = TestPackHero::new(Platform::Ubuntu, false, false);
|
|
|
|
// Test install
|
|
let result = hero.install("test-package");
|
|
assert!(result.is_err());
|
|
assert!(*hero.mock_manager.install_called.lock().unwrap());
|
|
|
|
// Test remove
|
|
let result = hero.remove("test-package");
|
|
assert!(result.is_err());
|
|
assert!(*hero.mock_manager.remove_called.lock().unwrap());
|
|
|
|
// Test update
|
|
let result = hero.update();
|
|
assert!(result.is_err());
|
|
assert!(*hero.mock_manager.update_called.lock().unwrap());
|
|
|
|
// Test upgrade
|
|
let result = hero.upgrade();
|
|
assert!(result.is_err());
|
|
assert!(*hero.mock_manager.upgrade_called.lock().unwrap());
|
|
|
|
// Test list_installed
|
|
let result = hero.list_installed();
|
|
assert!(result.is_err());
|
|
assert!(*hero.mock_manager.list_installed_called.lock().unwrap());
|
|
|
|
// Test search
|
|
let result = hero.search("query");
|
|
assert!(result.is_err());
|
|
assert!(*hero.mock_manager.search_called.lock().unwrap());
|
|
|
|
// Test is_installed
|
|
let result = hero.is_installed("installed-package");
|
|
assert!(result.is_err());
|
|
assert!(*hero.mock_manager.is_installed_called.lock().unwrap());
|
|
}
|
|
|
|
#[test]
|
|
fn test_packhero_unsupported_platform() {
|
|
// Test PackHero with an unsupported platform
|
|
let hero = TestPackHero::new(Platform::Unknown, false, true);
|
|
|
|
// All operations should fail with UnsupportedPlatform error
|
|
let result = hero.install("test-package");
|
|
assert!(result.is_err());
|
|
match result {
|
|
Err(PackageError::UnsupportedPlatform(_)) => (),
|
|
_ => panic!("Expected UnsupportedPlatform error"),
|
|
}
|
|
|
|
let result = hero.remove("test-package");
|
|
assert!(result.is_err());
|
|
match result {
|
|
Err(PackageError::UnsupportedPlatform(_)) => (),
|
|
_ => panic!("Expected UnsupportedPlatform error"),
|
|
}
|
|
|
|
let result = hero.update();
|
|
assert!(result.is_err());
|
|
match result {
|
|
Err(PackageError::UnsupportedPlatform(_)) => (),
|
|
_ => panic!("Expected UnsupportedPlatform error"),
|
|
}
|
|
}
|
|
|
|
// Real-world tests that actually install and remove packages on Ubuntu
|
|
// These tests will only run on Ubuntu and will be skipped on other platforms
|
|
#[test]
|
|
fn test_real_package_operations_on_ubuntu() {
|
|
// Check if we're on Ubuntu
|
|
let platform = Platform::detect();
|
|
if platform != Platform::Ubuntu {
|
|
println!("Skipping real package operations test on non-Ubuntu platform: {:?}", platform);
|
|
return;
|
|
}
|
|
|
|
println!("Running real package operations test on Ubuntu");
|
|
|
|
// Create a PackHero instance with debug enabled
|
|
let mut hero = PackHero::new();
|
|
hero.set_debug(true);
|
|
|
|
// Test package to install/remove
|
|
let test_package = "wget";
|
|
|
|
// First, check if the package is already installed
|
|
let is_installed_before = match hero.is_installed(test_package) {
|
|
Ok(result) => result,
|
|
Err(e) => {
|
|
println!("Error checking if package is installed: {}", e);
|
|
return;
|
|
}
|
|
};
|
|
|
|
println!("Package {} is installed before test: {}", test_package, is_installed_before);
|
|
|
|
// If the package is already installed, we'll remove it first
|
|
if is_installed_before {
|
|
println!("Removing existing package {} before test", test_package);
|
|
match hero.remove(test_package) {
|
|
Ok(_) => println!("Successfully removed package {}", test_package),
|
|
Err(e) => {
|
|
println!("Error removing package {}: {}", test_package, e);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Verify it was removed
|
|
match hero.is_installed(test_package) {
|
|
Ok(is_installed) => {
|
|
if is_installed {
|
|
println!("Failed to remove package {}", test_package);
|
|
return;
|
|
} else {
|
|
println!("Verified package {} was removed", test_package);
|
|
}
|
|
},
|
|
Err(e) => {
|
|
println!("Error checking if package is installed after removal: {}", e);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Now install the package
|
|
println!("Installing package {}", test_package);
|
|
match hero.install(test_package) {
|
|
Ok(_) => println!("Successfully installed package {}", test_package),
|
|
Err(e) => {
|
|
println!("Error installing package {}: {}", test_package, e);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Verify it was installed
|
|
match hero.is_installed(test_package) {
|
|
Ok(is_installed) => {
|
|
if !is_installed {
|
|
println!("Failed to install package {}", test_package);
|
|
return;
|
|
} else {
|
|
println!("Verified package {} was installed", test_package);
|
|
}
|
|
},
|
|
Err(e) => {
|
|
println!("Error checking if package is installed after installation: {}", e);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Test the search functionality
|
|
println!("Searching for packages with 'wget'");
|
|
match hero.search("wget") {
|
|
Ok(results) => {
|
|
println!("Search results: {:?}", results);
|
|
assert!(results.iter().any(|r| r.contains("wget")), "Search results should contain wget");
|
|
},
|
|
Err(e) => {
|
|
println!("Error searching for packages: {}", e);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Test listing installed packages
|
|
println!("Listing installed packages");
|
|
match hero.list_installed() {
|
|
Ok(packages) => {
|
|
println!("Found {} installed packages", packages.len());
|
|
// Check if our test package is in the list
|
|
assert!(packages.iter().any(|p| p == test_package),
|
|
"Installed packages list should contain {}", test_package);
|
|
},
|
|
Err(e) => {
|
|
println!("Error listing installed packages: {}", e);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Now remove the package if it wasn't installed before
|
|
if !is_installed_before {
|
|
println!("Removing package {} after test", test_package);
|
|
match hero.remove(test_package) {
|
|
Ok(_) => println!("Successfully removed package {}", test_package),
|
|
Err(e) => {
|
|
println!("Error removing package {}: {}", test_package, e);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Verify it was removed
|
|
match hero.is_installed(test_package) {
|
|
Ok(is_installed) => {
|
|
if is_installed {
|
|
println!("Failed to remove package {}", test_package);
|
|
return;
|
|
} else {
|
|
println!("Verified package {} was removed", test_package);
|
|
}
|
|
},
|
|
Err(e) => {
|
|
println!("Error checking if package is installed after removal: {}", e);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Test update functionality
|
|
println!("Testing package list update");
|
|
match hero.update() {
|
|
Ok(_) => println!("Successfully updated package lists"),
|
|
Err(e) => {
|
|
println!("Error updating package lists: {}", e);
|
|
return;
|
|
}
|
|
}
|
|
|
|
println!("All real package operations tests passed on Ubuntu");
|
|
}
|
|
|
|
// Test to check if apt-get is available on the system
|
|
#[test]
|
|
fn test_apt_get_availability() {
|
|
// This test checks if apt-get is available on the system
|
|
let output = StdCommand::new("which")
|
|
.arg("apt-get")
|
|
.output()
|
|
.expect("Failed to execute which apt-get");
|
|
|
|
let success = output.status.success();
|
|
let stdout = String::from_utf8_lossy(&output.stdout).to_string();
|
|
|
|
println!("apt-get available: {}", success);
|
|
if success {
|
|
println!("apt-get path: {}", stdout.trim());
|
|
}
|
|
|
|
// On Ubuntu, this should pass
|
|
if Platform::detect() == Platform::Ubuntu {
|
|
assert!(success, "apt-get should be available on Ubuntu");
|
|
}
|
|
}
|
|
} |