sal/src/process/tests.rs
2025-04-04 15:05:48 +02:00

169 lines
6.1 KiB
Rust

#[cfg(test)]
mod tests {
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
use crate::process::run::{run, RunError};
use crate::text::dedent;
#[test]
fn test_run_command() {
// Test running a simple echo command using the builder pattern
let result = run("echo hello").execute().unwrap();
assert!(result.success);
assert_eq!(result.code, 0);
assert!(result.stdout.trim().contains("hello"));
assert_eq!(result.stderr, "");
}
#[test]
fn test_run_silent_command() {
// Test running a command silently using the builder pattern
let result = run("echo silent test").silent(true).execute().unwrap();
assert!(result.success);
assert_eq!(result.code, 0);
assert!(result.stdout.trim().contains("silent test"));
assert_eq!(result.stderr, "");
}
#[test]
fn test_run_script() {
// Test running a multi-line script using the builder pattern
let script = r#"
echo "line 1"
echo "line 2"
"#;
let result = run(script).execute().unwrap();
assert!(result.success);
assert_eq!(result.code, 0);
assert!(result.stdout.contains("line 1"));
assert!(result.stdout.contains("line 2"));
assert_eq!(result.stderr, "");
}
#[test]
fn test_run_with_dedent() {
// Test that run properly dedents scripts
let script = r#"
echo "This has 12 spaces of indentation"
echo "This has 16 spaces (4 more than the common indentation)"
"#;
// The dedent function should remove the common 12 spaces
let dedented = dedent(script);
assert!(dedented.contains("echo \"This has 12 spaces of indentation\""));
assert!(dedented.contains(" echo \"This has 16 spaces (4 more than the common indentation)\""));
// Running the script should work with the dedented content
let result = run(script).execute().unwrap();
assert!(result.success);
assert_eq!(result.code, 0);
assert!(result.stdout.contains("This has 12 spaces of indentation"));
assert!(result.stdout.contains("This has 16 spaces (4 more than the common indentation)"));
}
#[test]
fn test_run_detects_script_vs_command() {
// Test that run correctly identifies scripts vs commands
// One-liner should be treated as a command
let one_liner = "echo one-liner test";
let result = run(one_liner).execute().unwrap();
assert!(result.success);
assert!(result.stdout.contains("one-liner test"));
// Multi-line input should be treated as a script
let multi_line = "echo first line\necho second line";
let result = run(multi_line).execute().unwrap();
assert!(result.success);
assert!(result.stdout.contains("first line"));
assert!(result.stdout.contains("second line"));
}
#[test]
fn test_run_empty_command() {
// Test handling of empty commands
let result = run("").execute();
assert!(result.is_err());
// The specific error should be EmptyCommand
match result {
Err(RunError::EmptyCommand) => (),
_ => panic!("Expected EmptyCommand error"),
}
}
#[test]
fn test_run_die_option() {
// Test the die option - when false, it should return a CommandResult with success=false
// instead of an Err when the command fails
// With die=true (default), a non-existent command should return an error
let result = run("non_existent_command").execute();
assert!(result.is_err());
// With die=false, it should return a CommandResult with success=false
let result = run("non_existent_command").die(false).execute().unwrap();
assert!(!result.success);
assert_ne!(result.code, 0);
assert!(result.stderr.contains("Error:"));
}
#[test]
fn test_run_async_option() {
// Test the async option - when true, it should spawn the process and return immediately
// Create a shared variable to track if the command has completed
let completed = Arc::new(Mutex::new(false));
let completed_clone = completed.clone();
// Run a command that sleeps for 2 seconds, with async=true
let start = std::time::Instant::now();
let result = run("sleep 2").async_exec(true).execute().unwrap();
let elapsed = start.elapsed();
// The command should return immediately (much less than 2 seconds)
assert!(elapsed < Duration::from_secs(1));
// The result should have empty stdout/stderr and success=true
assert!(result.success);
assert_eq!(result.code, 0);
assert_eq!(result.stdout, "");
assert_eq!(result.stderr, "");
// Wait a bit to ensure the command has time to complete
thread::sleep(Duration::from_secs(3));
// Verify the command completed (this is just a placeholder since we can't easily
// check if the async command completed in this test framework)
*completed_clone.lock().unwrap() = true;
assert!(*completed.lock().unwrap());
}
#[test]
fn test_run_log_option() {
// Test the log option - when true, it should log command execution
// Note: We can't easily capture stdout in tests, so this is more of a smoke test
// Run a command with log=true
let result = run("echo log test").log(true).execute().unwrap();
assert!(result.success);
assert_eq!(result.code, 0);
assert!(result.stdout.trim().contains("log test"));
}
#[test]
fn test_builder_method_chaining() {
// Test that all builder methods can be chained together
let result = run("echo chaining test")
.silent(true)
.die(true)
.log(true)
.execute()
.unwrap();
assert!(result.success);
assert_eq!(result.code, 0);
assert!(result.stdout.trim().contains("chaining test"));
}
}