use std::process::Command;
fn get_exe_path(path: &str) -> String {
std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.join(path)
.to_string_lossy()
.to_string()
}
const STRONG_PASSWORD: &str = "correct horse battery staple";
const WEAK_PASSWORD: &str = "password";
#[test]
fn test_hash_command_with_explicit_username() {
let output = Command::new(get_exe_path("./target/debug/torc-htpasswd"))
.arg("hash")
.arg("--password")
.arg(STRONG_PASSWORD)
.arg("testuser")
.output()
.expect("Failed to run torc-htpasswd hash");
assert!(output.status.success(), "hash command should succeed");
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
stdout.starts_with("testuser:$2b$12$"),
"stdout should contain htpasswd line starting with 'testuser:$2b$12$', got: {}",
stdout
);
assert!(
stderr.contains("Hashing password"),
"stderr should contain progress message, got: {}",
stderr
);
assert!(
stderr.contains("Send the line above"),
"stderr should contain instruction message, got: {}",
stderr
);
}
#[test]
fn test_hash_command_defaults_to_env_user() {
let output = Command::new(get_exe_path("./target/debug/torc-htpasswd"))
.arg("hash")
.arg("--password")
.arg(STRONG_PASSWORD)
.env("USER", "envuser")
.output()
.expect("Failed to run torc-htpasswd hash");
assert!(output.status.success(), "hash command should succeed");
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(
stdout.starts_with("envuser:$2b$12$"),
"stdout should contain htpasswd line starting with 'envuser:$2b$12$', got: {}",
stdout
);
}
#[test]
fn test_hash_command_falls_back_to_username_env() {
let output = Command::new(get_exe_path("./target/debug/torc-htpasswd"))
.arg("hash")
.arg("--password")
.arg(STRONG_PASSWORD)
.env_remove("USER")
.env("USERNAME", "winuser")
.output()
.expect("Failed to run torc-htpasswd hash");
assert!(output.status.success(), "hash command should succeed");
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(
stdout.starts_with("winuser:$2b$12$"),
"stdout should contain htpasswd line starting with 'winuser:$2b$12$', got: {}",
stdout
);
}
#[test]
fn test_hash_command_fails_without_username_or_env() {
let output = Command::new(get_exe_path("./target/debug/torc-htpasswd"))
.arg("hash")
.arg("--password")
.arg(STRONG_PASSWORD)
.env_remove("USER")
.env_remove("USERNAME")
.output()
.expect("Failed to run torc-htpasswd hash");
assert!(
!output.status.success(),
"hash command should fail when no username available"
);
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
stderr.contains("could not read from $USER or $USERNAME"),
"stderr should contain error about missing username, got: {}",
stderr
);
}
#[test]
fn test_hash_command_explicit_username_overrides_env() {
let output = Command::new(get_exe_path("./target/debug/torc-htpasswd"))
.arg("hash")
.arg("--password")
.arg(STRONG_PASSWORD)
.arg("explicituser")
.env("USER", "envuser")
.output()
.expect("Failed to run torc-htpasswd hash");
assert!(output.status.success(), "hash command should succeed");
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(
stdout.starts_with("explicituser:$2b$12$"),
"stdout should use explicit username 'explicituser', not env var, got: {}",
stdout
);
}
#[test]
fn test_hash_command_custom_cost() {
let output = Command::new(get_exe_path("./target/debug/torc-htpasswd"))
.arg("hash")
.arg("--password")
.arg(STRONG_PASSWORD)
.arg("--cost")
.arg("4")
.arg("testuser")
.output()
.expect("Failed to run torc-htpasswd hash");
assert!(output.status.success(), "hash command should succeed");
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(
stdout.starts_with("testuser:$2b$04$"),
"stdout should contain hash with cost 4 ($2b$04$), got: {}",
stdout
);
}
#[test]
fn test_hash_command_rejects_weak_password() {
let output = Command::new(get_exe_path("./target/debug/torc-htpasswd"))
.arg("hash")
.arg("--password")
.arg(WEAK_PASSWORD)
.arg("testuser")
.output()
.expect("Failed to run torc-htpasswd hash");
assert!(
!output.status.success(),
"hash command should fail with weak password"
);
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
stderr.contains("too weak"),
"stderr should contain 'too weak' message, got: {}",
stderr
);
assert!(
stderr.contains("score"),
"stderr should contain score info, got: {}",
stderr
);
}
#[test]
fn test_hash_command_rejects_short_password() {
let output = Command::new(get_exe_path("./target/debug/torc-htpasswd"))
.arg("hash")
.arg("--password")
.arg("Ab3$xyz")
.arg("testuser")
.output()
.expect("Failed to run torc-htpasswd hash");
assert!(
!output.status.success(),
"hash command should fail with short password"
);
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
stderr.contains("too short"),
"stderr should contain 'too short' message, got: {}",
stderr
);
}
#[test]
fn test_hash_command_rejects_password_matching_username() {
let output = Command::new(get_exe_path("./target/debug/torc-htpasswd"))
.arg("hash")
.arg("--password")
.arg("testuser")
.arg("testuser")
.output()
.expect("Failed to run torc-htpasswd hash");
assert!(
!output.status.success(),
"hash command should fail when password matches username"
);
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
stderr.contains("too weak"),
"stderr should indicate the password is too weak, got: {}",
stderr
);
}