#![allow(missing_docs)]
use entropy_auth::{PasswordConfig, PasswordHash};
fn fast_config() -> PasswordConfig {
PasswordConfig::new()
.with_memory_kib(entropy_auth::local::password::MIN_MEMORY_KIB)
.with_iterations(1)
.with_parallelism(1)
}
#[test]
fn generate_hash_verify_correct_password_returns_true() {
let config = fast_config();
let hash = PasswordHash::generate(b"correct-horse-battery-staple", &config).unwrap();
assert!(hash.verify(b"correct-horse-battery-staple"));
}
#[test]
fn verify_wrong_password_returns_false() {
let config = fast_config();
let hash = PasswordHash::generate(b"correct-password", &config).unwrap();
assert!(!hash.verify(b"wrong-password"));
}
#[test]
fn phc_string_round_trip_verify_still_works() {
let config = fast_config();
let hash = PasswordHash::generate(b"round-trip-test", &config).unwrap();
let phc = hash.to_phc_string();
let parsed = PasswordHash::parse(phc).unwrap();
assert!(parsed.verify(b"round-trip-test"));
assert!(!parsed.verify(b"not-round-trip-test"));
}
#[test]
fn phc_string_format_is_valid() {
let config = fast_config();
let hash = PasswordHash::generate(b"test", &config).unwrap();
let phc = hash.to_phc_string();
assert!(phc.starts_with("$argon2id$"));
}
#[test]
fn needs_rehash_false_when_config_matches() {
let config = fast_config();
let hash = PasswordHash::generate(b"pw", &config).unwrap();
assert!(!hash.needs_rehash(&config));
}
#[test]
fn needs_rehash_true_when_memory_insufficient() {
let weak_config = fast_config();
let hash = PasswordHash::generate(b"pw", &weak_config).unwrap();
let strong_config = PasswordConfig::new()
.with_memory_kib(65_536)
.with_iterations(1)
.with_parallelism(1);
assert!(hash.needs_rehash(&strong_config));
}
#[test]
fn needs_rehash_true_when_iterations_insufficient() {
let config = fast_config();
let hash = PasswordHash::generate(b"pw", &config).unwrap();
let stronger = PasswordConfig::new()
.with_memory_kib(entropy_auth::local::password::MIN_MEMORY_KIB)
.with_iterations(3)
.with_parallelism(1);
assert!(hash.needs_rehash(&stronger));
}
#[test]
fn needs_rehash_true_when_parallelism_insufficient() {
let config = fast_config();
let hash = PasswordHash::generate(b"pw", &config).unwrap();
let stronger = PasswordConfig::new()
.with_memory_kib(entropy_auth::local::password::MIN_MEMORY_KIB)
.with_iterations(1)
.with_parallelism(4);
assert!(hash.needs_rehash(&stronger));
}
#[test]
fn generate_rejects_low_memory() {
let config = PasswordConfig::new()
.with_memory_kib(1024)
.with_iterations(1)
.with_parallelism(1);
assert!(PasswordHash::generate(b"test", &config).is_err());
}
#[test]
fn generate_rejects_zero_iterations() {
let config = PasswordConfig::new()
.with_memory_kib(entropy_auth::local::password::MIN_MEMORY_KIB)
.with_iterations(0)
.with_parallelism(1);
assert!(PasswordHash::generate(b"test", &config).is_err());
}
#[test]
fn generate_rejects_zero_parallelism() {
let config = PasswordConfig::new()
.with_memory_kib(entropy_auth::local::password::MIN_MEMORY_KIB)
.with_iterations(1)
.with_parallelism(0);
assert!(PasswordHash::generate(b"test", &config).is_err());
}
#[test]
fn password_error_display_is_descriptive() {
let err = PasswordHash::parse("garbage").unwrap_err();
let msg = err.to_string();
assert!(
msg.starts_with("password:"),
"error should be prefixed: {msg}"
);
}