#![allow(missing_docs)]
#![allow(clippy::unwrap_used, clippy::expect_used)]
#![allow(deprecated)]
use hsh::models::hash::{Hash, HashBuilder};
use hsh::models::hash_algorithm::HashAlgorithm;
use hsh::Error;
const SALT_16: &[u8; 16] = b"abcdefghijklmnop";
#[cfg(feature = "compat-v0_0_x")]
#[test]
fn new_argon2i_constructor_round_trip() {
let h = Hash::new_argon2i(
"correct horse battery staple",
SALT_16.to_vec(),
)
.unwrap();
assert!(matches!(h.algorithm(), HashAlgorithm::Argon2i));
assert!(!h.hash().is_empty());
}
#[test]
fn new_argon2id_constructor_yields_argon2id_algorithm() {
let h = Hash::new_argon2id(
"correct horse battery staple",
SALT_16.to_vec(),
)
.unwrap();
assert!(matches!(h.algorithm(), HashAlgorithm::Argon2id));
assert_eq!(h.hash_length(), 32);
}
#[test]
fn new_scrypt_constructor_yields_scrypt_algorithm() {
let salt: Vec<u8> = b"abcdefghijklmnopabcdefghijklmnop".to_vec();
let h = Hash::new_scrypt("hunter2!", salt).unwrap();
assert!(matches!(h.algorithm(), HashAlgorithm::Scrypt));
assert!(!h.hash().is_empty());
}
#[test]
fn new_bcrypt_constructor_yields_bcrypt_algorithm() {
let h = Hash::new_bcrypt("hunter2!", 4).unwrap();
assert!(matches!(h.algorithm(), HashAlgorithm::Bcrypt));
assert!(!h.hash().is_empty());
}
#[test]
fn hash_length_matches_hash_bytes() {
let h = Hash::new_argon2id("hunter2!", SALT_16.to_vec()).unwrap();
assert_eq!(h.hash_length(), h.hash().len());
assert_eq!(h.hash_length(), 32);
}
#[test]
fn generate_hash_supports_pbkdf2_alias() {
let bytes =
Hash::generate_hash("pw1234", "abcdefghijklmnop", "pbkdf2")
.unwrap();
assert!(!bytes.is_empty());
}
#[test]
fn generate_hash_supports_pbkdf2_sha256_alias() {
let bytes = Hash::generate_hash(
"pw1234",
"abcdefghijklmnop",
"pbkdf2-sha256",
)
.unwrap();
assert!(!bytes.is_empty());
}
#[test]
fn generate_salt_argon2id_returns_16_chars() {
let salt = Hash::generate_salt("argon2id").unwrap();
assert_eq!(salt.len(), 16);
}
#[test]
fn generate_salt_bcrypt_returns_base64() {
let salt = Hash::generate_salt("bcrypt").unwrap();
assert!(!salt.is_empty());
}
#[test]
fn generate_salt_scrypt_returns_base64() {
let salt = Hash::generate_salt("scrypt").unwrap();
assert!(!salt.is_empty());
}
#[test]
fn generate_salt_rejects_unknown_algorithm() {
let err = Hash::generate_salt("not-an-algo").unwrap_err();
assert!(matches!(err, Error::UnsupportedAlgorithm(_)));
}
#[test]
fn generate_random_string_produces_alphanumeric_chars() {
let s = Hash::generate_random_string(32).unwrap();
assert_eq!(s.len(), 32);
assert!(s.chars().all(|c| c.is_ascii_alphanumeric()));
}
#[test]
fn set_hash_replaces_bytes() {
let mut h = Hash::new_argon2id("pw1234", SALT_16.to_vec()).unwrap();
let original = h.hash().to_vec();
h.set_hash(&[0xAA; 32]);
assert_eq!(h.hash(), &[0xAA; 32]);
assert_ne!(h.hash(), original.as_slice());
}
#[test]
fn set_salt_replaces_bytes() {
let mut h = Hash::new_argon2id("pw1234", SALT_16.to_vec()).unwrap();
h.set_salt(b"zzzzzzzzzzzzzzzz");
assert_eq!(h.salt(), b"zzzzzzzzzzzzzzzz");
}
#[test]
fn set_password_rehashes_in_place() {
let mut h = Hash::new_argon2id("pw1234", SALT_16.to_vec()).unwrap();
let original = h.hash().to_vec();
h.set_password("different", "abcdefghijklmnop", "argon2id")
.unwrap();
assert_ne!(h.hash(), original.as_slice());
}
#[test]
fn from_string_rejects_wrong_field_count() {
let err =
Hash::from_string("$argon2id$not-enough-fields").unwrap_err();
assert!(matches!(err, Error::InvalidHashString(_)));
}
#[test]
fn parse_algorithm_rejects_missing_marker() {
let err = Hash::parse_algorithm("no-marker").unwrap_err();
assert!(matches!(err, Error::InvalidHashString(_)));
}
#[test]
fn parse_algorithm_rejects_unknown_marker() {
let err = Hash::parse_algorithm("$nopealgo$x$y$z$w$v").unwrap_err();
assert!(matches!(err, Error::UnsupportedAlgorithm(_)));
}
#[test]
fn to_string_representation_carries_hash_and_salt() {
let h = Hash::new_argon2id("pw1234", SALT_16.to_vec()).unwrap();
let s = h.to_string_representation();
assert!(s.contains(':'));
assert!(s.contains("abcdefghijklmnop"));
}
#[test]
fn from_hash_constructs_with_known_tag() {
let h = Hash::from_hash(&[0xCC; 32], "argon2id").unwrap();
assert_eq!(h.hash(), &[0xCC; 32]);
assert!(matches!(h.algorithm(), HashAlgorithm::Argon2id));
}
#[test]
fn from_hash_rejects_unknown_tag() {
let err =
Hash::from_hash(&[0xCC; 32], "not-a-real-algo").unwrap_err();
assert!(matches!(err, Error::UnsupportedAlgorithm(_)));
}
#[test]
fn parse_round_trips_serialised_hash() {
let original =
Hash::new_argon2id("pw1234", SALT_16.to_vec()).unwrap();
let json = serde_json::to_string(&original).unwrap();
let back = Hash::parse(&json).unwrap();
assert_eq!(original.hash(), back.hash());
assert_eq!(original.salt(), back.salt());
}
#[test]
fn hash_builder_requires_hash_salt_and_algorithm() {
let err = HashBuilder::new().build().unwrap_err();
assert!(matches!(err, Error::InvalidHashString(_)));
let err = HashBuilder::new()
.hash(vec![0; 32])
.salt(SALT_16.to_vec())
.build()
.unwrap_err();
assert!(matches!(err, Error::InvalidHashString(_)));
}
#[test]
fn hash_builder_yields_hash_when_complete() {
let h = HashBuilder::new()
.hash(vec![0xAB; 32])
.salt(SALT_16.to_vec())
.algorithm(HashAlgorithm::Argon2id)
.build()
.unwrap();
assert_eq!(h.hash(), &[0xAB; 32]);
assert!(matches!(h.algorithm(), HashAlgorithm::Argon2id));
}
#[test]
fn verify_round_trips_argon2id() {
let h = Hash::new_argon2id("pw1234", SALT_16.to_vec()).unwrap();
assert!(h.verify("pw1234").unwrap());
assert!(!h.verify("wrong").unwrap());
}
#[test]
fn verify_round_trips_scrypt() {
let salt: Vec<u8> = b"abcdefghijklmnopabcdefghijklmnop".to_vec();
let h = Hash::new_scrypt("pw1234", salt).unwrap();
assert!(h.verify("pw1234").unwrap());
assert!(!h.verify("wrong").unwrap());
}
#[test]
fn verify_round_trips_bcrypt() {
let h = Hash::new_bcrypt("pw1234", 4).unwrap();
assert!(h.verify("pw1234").unwrap());
assert!(!h.verify("wrong").unwrap());
}
#[test]
fn verify_argon2d_via_set_password() {
let mut h = Hash::new_argon2id("pw1234", SALT_16.to_vec()).unwrap();
h.set_password("pw1234", "abcdefghijklmnop", "argon2d")
.unwrap();
assert!(!h.hash().is_empty());
}
#[test]
fn verify_pbkdf2_via_from_hash() {
let pw = "pw1234";
let salt = "abcdefghijklmnop";
let bytes = Hash::generate_hash(pw, salt, "pbkdf2-sha256").unwrap();
let mut h = Hash::from_hash(&[], "pbkdf2").unwrap();
h.set_hash(&bytes);
h.set_salt(salt.as_bytes());
assert!(h.verify(pw).unwrap());
assert!(!h.verify("wrong").unwrap());
}
#[test]
fn verify_argon2d_via_from_hash() {
let pw = "pw1234";
let salt = "abcdefghijklmnop";
let bytes = Hash::generate_hash(pw, salt, "argon2d").unwrap();
let mut h = Hash::from_hash(&[], "argon2d").unwrap();
h.set_hash(&bytes);
h.set_salt(salt.as_bytes());
assert!(h.verify(pw).unwrap());
assert!(!h.verify("wrong").unwrap());
}
#[test]
fn verify_argon2i_via_from_hash() {
let pw = "pw1234";
let salt = "abcdefghijklmnop";
let bytes = Hash::generate_hash(pw, salt, "argon2i").unwrap();
let mut h = Hash::from_hash(&[], "argon2i").unwrap();
h.set_hash(&bytes);
h.set_salt(salt.as_bytes());
assert!(h.verify(pw).unwrap());
assert!(!h.verify("wrong").unwrap());
}