use std::{
env, fs,
process::Command,
time::{SystemTime, UNIX_EPOCH},
};
use pxh::test_utils::pxh_path;
use tempfile::TempDir;
type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
fn pxh_command() -> Command {
let mut cmd = Command::new(pxh_path());
if let Ok(profile_file) = env::var("LLVM_PROFILE_FILE") {
cmd.env("LLVM_PROFILE_FILE", profile_file);
}
if let Ok(llvm_cov) = env::var("CARGO_LLVM_COV") {
cmd.env("CARGO_LLVM_COV", llvm_cov);
}
cmd
}
#[test]
fn test_bash_shell_config_simulation() -> Result<()> {
let temp_dir = TempDir::new()?;
let db_path = temp_dir.path().join("pxh.db");
let session_id = "12345";
let hostname = "testhost";
let username = "testuser";
fs::create_dir_all(db_path.parent().unwrap())?;
let start_time = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs();
let insert_output = pxh_command()
.args([
"--db",
db_path.to_str().unwrap(),
"insert",
"--working-directory",
"/tmp",
"--hostname",
hostname,
"--shellname",
"bash",
"--username",
username,
"--session-id",
session_id,
"--start-unix-timestamp",
&start_time.to_string(),
"echo 'Hello from bash'",
])
.output()?;
assert!(
insert_output.status.success(),
"Insert failed: {}",
String::from_utf8_lossy(&insert_output.stderr)
);
let end_time = start_time + 1;
let exit_status = 0;
let seal_output = pxh_command()
.args([
"--db",
db_path.to_str().unwrap(),
"seal",
"--session-id",
session_id,
"--end-unix-timestamp",
&end_time.to_string(),
"--exit-status",
&exit_status.to_string(),
])
.output()?;
assert!(
seal_output.status.success(),
"Seal failed: {}",
String::from_utf8_lossy(&seal_output.stderr)
);
let start_time2 = end_time + 1;
let insert_output2 = pxh_command()
.args([
"--db",
db_path.to_str().unwrap(),
"insert",
"--working-directory",
"/home/user",
"--hostname",
hostname,
"--shellname",
"bash",
"--username",
username,
"--session-id",
session_id,
"--start-unix-timestamp",
&start_time2.to_string(),
"ls -la",
])
.output()?;
assert!(insert_output2.status.success());
let seal_output2 = pxh_command()
.args([
"--db",
db_path.to_str().unwrap(),
"seal",
"--session-id",
session_id,
"--end-unix-timestamp",
&(start_time2 + 2).to_string(),
"--exit-status",
"0",
])
.output()?;
assert!(seal_output2.status.success());
let show_output = pxh_command()
.args(["--db", db_path.to_str().unwrap(), "show", "--limit", "10"])
.output()?;
assert!(show_output.status.success());
let history = String::from_utf8_lossy(&show_output.stdout);
assert!(history.contains("echo 'Hello from bash'"), "First command should be in history");
assert!(history.contains("ls -la"), "Second command should be in history");
Ok(())
}
#[test]
fn test_zsh_shell_config_simulation() -> Result<()> {
let temp_dir = TempDir::new()?;
let db_path = temp_dir.path().join("pxh.db");
let session_id = "67890";
let hostname = "zshhost";
let username = "zshuser";
fs::create_dir_all(db_path.parent().unwrap())?;
let start_time = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs();
let insert_output = pxh_command()
.args([
"--db",
db_path.to_str().unwrap(),
"insert",
"--working-directory",
"/Users/test",
"--hostname",
hostname,
"--shellname",
"zsh",
"--username",
username,
"--session-id",
session_id,
"--start-unix-timestamp",
&start_time.to_string(),
"git status",
])
.output()?;
assert!(
insert_output.status.success(),
"Insert failed: {}",
String::from_utf8_lossy(&insert_output.stderr)
);
let end_time = start_time + 3;
let exit_status = 0;
let seal_output = pxh_command()
.args([
"--db",
db_path.to_str().unwrap(),
"seal",
"--session-id",
session_id,
"--end-unix-timestamp",
&end_time.to_string(),
"--exit-status",
&exit_status.to_string(),
])
.output()?;
assert!(
seal_output.status.success(),
"Seal failed: {}",
String::from_utf8_lossy(&seal_output.stderr)
);
let show_output =
pxh_command().args(["--db", db_path.to_str().unwrap(), "show", "--limit", "5"]).output()?;
assert!(show_output.status.success());
let history = String::from_utf8_lossy(&show_output.stdout);
assert!(history.contains("git status"), "Command should be in history");
Ok(())
}
#[test]
fn test_shell_config_environment_variables() -> Result<()> {
let temp_dir = TempDir::new()?;
let custom_db_path = temp_dir.path().join("custom/location/pxh.db");
fs::create_dir_all(custom_db_path.parent().unwrap())?;
let session_id = "99999";
let start_time = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs();
let insert_output = pxh_command()
.env("PXH_DB_PATH", &custom_db_path)
.args([
"--db",
custom_db_path.to_str().unwrap(),
"insert",
"--working-directory",
"/custom/path",
"--hostname",
"customhost",
"--shellname",
"bash",
"--username",
"customuser",
"--session-id",
session_id,
"--start-unix-timestamp",
&start_time.to_string(),
"test custom db path",
])
.output()?;
assert!(insert_output.status.success());
assert!(custom_db_path.exists(), "Database should exist at custom path");
let seal_output = pxh_command()
.args([
"--db",
custom_db_path.to_str().unwrap(),
"seal",
"--session-id",
session_id,
"--end-unix-timestamp",
&(start_time + 1).to_string(),
"--exit-status",
"0",
])
.output()?;
assert!(seal_output.status.success());
let show_output =
pxh_command().args(["--db", custom_db_path.to_str().unwrap(), "show"]).output()?;
assert!(show_output.status.success());
let history = String::from_utf8_lossy(&show_output.stdout);
assert!(history.contains("test custom db path"));
Ok(())
}
#[test]
fn test_leading_space_commands_not_logged() -> Result<()> {
let temp_dir = TempDir::new()?;
let db_path = temp_dir.path().join("pxh.db");
let session_id = "55555";
let hostname = "testhost";
let username = "testuser";
fs::create_dir_all(db_path.parent().unwrap())?;
let start_time = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs();
let insert_output = pxh_command()
.args([
"--db",
db_path.to_str().unwrap(),
"insert",
"--working-directory",
"/tmp",
"--hostname",
hostname,
"--shellname",
"bash",
"--username",
username,
"--session-id",
session_id,
"--start-unix-timestamp",
&start_time.to_string(),
"echo 'this should be logged'",
])
.output()?;
assert!(insert_output.status.success());
let seal_output = pxh_command()
.args([
"--db",
db_path.to_str().unwrap(),
"seal",
"--session-id",
session_id,
"--end-unix-timestamp",
&(start_time + 1).to_string(),
"--exit-status",
"0",
])
.output()?;
assert!(seal_output.status.success());
let start_time2 = start_time + 2;
let insert_output2 = pxh_command()
.args([
"--db",
db_path.to_str().unwrap(),
"insert",
"--working-directory",
"/tmp",
"--hostname",
hostname,
"--shellname",
"bash",
"--username",
username,
"--session-id",
session_id,
"--start-unix-timestamp",
&start_time2.to_string(),
"echo 'another logged command'",
])
.output()?;
assert!(insert_output2.status.success());
let seal_output2 = pxh_command()
.args([
"--db",
db_path.to_str().unwrap(),
"seal",
"--session-id",
session_id,
"--end-unix-timestamp",
&(start_time2 + 1).to_string(),
"--exit-status",
"0",
])
.output()?;
assert!(seal_output2.status.success());
let show_output = pxh_command()
.args(["--db", db_path.to_str().unwrap(), "show", "--limit", "10"])
.output()?;
assert!(show_output.status.success());
let history = String::from_utf8_lossy(&show_output.stdout);
assert!(
history.contains("echo 'this should be logged'"),
"Normal command should be in history"
);
assert!(
history.contains("echo 'another logged command'"),
"Second normal command should be in history"
);
let command_count = history.lines().filter(|l| l.contains("echo")).count();
assert_eq!(command_count, 2, "Should have exactly 2 commands in history");
Ok(())
}
#[test]
fn test_concurrent_sessions() -> Result<()> {
let temp_dir = TempDir::new()?;
let db_path = temp_dir.path().join("pxh.db");
let session1_id = "11111";
let start_time1 = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs();
let insert1 = pxh_command()
.args([
"--db",
db_path.to_str().unwrap(),
"insert",
"--hostname",
"host1",
"--shellname",
"bash",
"--username",
"user1",
"--session-id",
session1_id,
"--start-unix-timestamp",
&start_time1.to_string(),
"session 1 command",
])
.output()?;
assert!(insert1.status.success());
let session2_id = "22222";
let start_time2 = start_time1 + 1;
let insert2 = pxh_command()
.args([
"--db",
db_path.to_str().unwrap(),
"insert",
"--hostname",
"host2",
"--shellname",
"zsh",
"--username",
"user2",
"--session-id",
session2_id,
"--start-unix-timestamp",
&start_time2.to_string(),
"session 2 command",
])
.output()?;
assert!(insert2.status.success());
let seal1 = pxh_command()
.args([
"--db",
db_path.to_str().unwrap(),
"seal",
"--session-id",
session1_id,
"--end-unix-timestamp",
&(start_time1 + 2).to_string(),
"--exit-status",
"0",
])
.output()?;
assert!(seal1.status.success());
let seal2 = pxh_command()
.args([
"--db",
db_path.to_str().unwrap(),
"seal",
"--session-id",
session2_id,
"--end-unix-timestamp",
&(start_time2 + 2).to_string(),
"--exit-status",
"1",
])
.output()?;
assert!(seal2.status.success());
let show_all = pxh_command()
.args(["--db", db_path.to_str().unwrap(), "show", "--limit", "10"])
.output()?;
assert!(show_all.status.success());
let all_history = String::from_utf8_lossy(&show_all.stdout);
eprintln!("All history output:\n{}", all_history);
assert!(all_history.contains("session 1 command"), "Should contain session 1 command");
assert!(all_history.contains("session 2 command"), "Should contain session 2 command");
Ok(())
}