use api_keys_simplified::{
ApiKeyManagerV0, Environment, HashConfig, KeyConfig, KeyStatus, SecureString,
};
use api_keys_simplified::{ExposeSecret, SecureStringExt};
use std::sync::{Arc, Barrier};
use std::thread;
#[test]
#[cfg_attr(not(feature = "expensive_tests"), ignore)]
fn test_concurrent_generation_and_uniqueness() {
let generator = Arc::new(ApiKeyManagerV0::init_default_config("sk").unwrap());
let barrier = Arc::new(Barrier::new(10));
let mut handles = vec![];
for _ in 0..10 {
let gen = Arc::clone(&generator);
let barrier = Arc::clone(&barrier);
let handle = thread::spawn(move || {
barrier.wait(); let mut keys = Vec::new();
for _ in 0..100 {
let key = gen.generate(Environment::production()).unwrap();
keys.push(key.key().expose_secret().to_string());
}
keys
});
handles.push(handle);
}
let mut all_keys = Vec::new();
for handle in handles {
all_keys.extend(handle.join().unwrap());
}
let unique_count = all_keys
.iter()
.collect::<std::collections::HashSet<_>>()
.len();
assert_eq!(unique_count, all_keys.len(), "Found duplicate keys");
for i in 0..10.min(all_keys.len()) {
for j in i + 1..10.min(all_keys.len()) {
let parts1: Vec<&str> = all_keys[i].splitn(3, '-').collect();
let parts2: Vec<&str> = all_keys[j].splitn(3, '-').collect();
assert_ne!(parts1[2], parts2[2], "Random data must differ");
}
}
}
#[test]
#[cfg_attr(not(feature = "expensive_tests"), ignore)]
fn test_concurrent_verification_and_checksum() {
let config = KeyConfig::default();
let generator = Arc::new(
ApiKeyManagerV0::init(
"pk",
config,
HashConfig::default(),
std::time::Duration::ZERO,
)
.unwrap(),
);
let mut keys_and_hashes = Vec::new();
for _ in 0..30 {
let key = generator.generate(Environment::test()).unwrap();
keys_and_hashes.push((
key.key().expose_secret().to_string(),
key.expose_hash().hash().to_string(),
));
}
let keys_and_hashes = Arc::new(keys_and_hashes);
let mut handles = vec![];
for _ in 0..5 {
let gen = Arc::clone(&generator);
let data = Arc::clone(&keys_and_hashes);
let handle = thread::spawn(move || {
let mut hash_ok = 0;
let mut checksum_ok = 0;
for (key_str, hash_str) in data.iter() {
let key = SecureString::from(key_str.clone());
if gen.verify(&key, hash_str).unwrap() == KeyStatus::Valid {
hash_ok += 1;
}
if gen.verify_checksum(&key).unwrap() {
checksum_ok += 1;
}
}
(hash_ok, checksum_ok)
});
handles.push(handle);
}
for handle in handles {
let (hash_ok, checksum_ok) = handle.join().unwrap();
assert_eq!(hash_ok, 30, "All hash verifications should succeed");
assert_eq!(checksum_ok, 30, "All checksum verifications should succeed");
}
}
#[test]
fn test_clone_safety_and_config_isolation() {
let gen1 = ApiKeyManagerV0::init(
"g1",
KeyConfig::balanced(),
HashConfig::balanced(),
std::time::Duration::ZERO,
)
.unwrap();
let gen2_cloned = gen1.clone();
let gen3 = ApiKeyManagerV0::init(
"g3",
KeyConfig::high_security(),
HashConfig::high_security(),
std::time::Duration::ZERO,
)
.unwrap();
let gen1 = Arc::new(gen1);
let gen2 = Arc::new(gen2_cloned);
let gen3 = Arc::new(gen3);
let mut handles = vec![];
handles.push({
let gen = Arc::clone(&gen1);
thread::spawn(move || gen.generate(Environment::production()).unwrap())
});
handles.push({
let gen = Arc::clone(&gen2);
thread::spawn(move || gen.generate(Environment::production()).unwrap())
});
handles.push({
let gen = Arc::clone(&gen3);
thread::spawn(move || gen.generate(Environment::production()).unwrap())
});
let key1 = handles.remove(0).join().unwrap();
let key2 = handles.remove(0).join().unwrap();
let key3 = handles.remove(0).join().unwrap();
assert_ne!(key1.key().expose_secret(), key2.key().expose_secret());
assert_ne!(key1.key().expose_secret(), key3.key().expose_secret());
assert_eq!(
gen1.verify(key1.key(), key1.expose_hash().hash()).unwrap(),
KeyStatus::Valid
);
assert_eq!(
gen2.verify(key2.key(), key2.expose_hash().hash()).unwrap(),
KeyStatus::Valid
);
assert_eq!(
gen3.verify(key3.key(), key3.expose_hash().hash()).unwrap(),
KeyStatus::Valid
);
assert_eq!(
gen1.verify(key2.key(), key2.expose_hash().hash()).unwrap(),
KeyStatus::Valid
);
assert_eq!(
gen2.verify(key1.key(), key1.expose_hash().hash()).unwrap(),
KeyStatus::Valid
);
assert!(key1.key().expose_secret().starts_with("g1-"));
assert!(key2.key().expose_secret().starts_with("g1-"));
assert!(key3.key().expose_secret().starts_with("g3-"));
assert!(key3.key().len() > key1.key().len());
}
#[test]
#[cfg_attr(not(feature = "expensive_tests"), ignore)]
fn test_high_contention_mixed_operations() {
let generator = Arc::new(ApiKeyManagerV0::init_high_security_config("stress").unwrap());
let mut handles = vec![];
for thread_id in 0..20 {
let gen = Arc::clone(&generator);
handles.push(thread::spawn(move || {
for op_id in 0..10 {
if (thread_id + op_id) % 2 == 0 {
let key = gen.generate(Environment::production()).unwrap();
assert!(key.key().len() > 0);
} else {
let key = gen.generate(Environment::test()).unwrap();
assert_eq!(
gen.verify(key.key(), key.expose_hash().hash()).unwrap(),
KeyStatus::Valid
);
}
}
}));
}
for handle in handles {
handle.join().unwrap();
}
}