use api_keys_simplified::{
ApiKeyManagerV0, Environment, ExposeSecret, HashConfig, KeyConfig, KeyStatus, SecureString,
};
use chrono::{Duration, Utc};
#[test]
fn test_future_expiry_is_valid() {
let manager = ApiKeyManagerV0::init_default_config("sk").unwrap();
let expiry = Utc::now() + Duration::days(7);
let key = manager
.generate_with_expiry(Environment::production(), expiry)
.unwrap();
assert_eq!(
manager.verify(key.key(), key.expose_hash().hash()).unwrap(),
KeyStatus::Valid
);
}
#[test]
fn test_past_expiry_is_expired() {
let manager = ApiKeyManagerV0::init_default_config("sk").unwrap();
let expiry = Utc::now() - Duration::days(1);
let key = manager
.generate_with_expiry(Environment::production(), expiry)
.unwrap();
assert_eq!(
manager.verify(key.key(), key.expose_hash().hash()).unwrap(),
KeyStatus::Invalid
);
}
#[test]
fn test_expiry_at_current_time() {
let manager = ApiKeyManagerV0::init_default_config("sk").unwrap();
let expiry = Utc::now();
let key = manager
.generate_with_expiry(Environment::production(), expiry)
.unwrap();
let status = manager.verify(key.key(), key.expose_hash().hash()).unwrap();
assert!(
status == KeyStatus::Valid || status == KeyStatus::Invalid,
"Status at boundary should be Valid or Expired depending on timing"
);
}
#[test]
fn test_no_expiry_never_expires() {
let manager = ApiKeyManagerV0::init_default_config("sk").unwrap();
let key = manager.generate(Environment::production()).unwrap();
assert_eq!(
manager.verify(key.key(), key.expose_hash().hash()).unwrap(),
KeyStatus::Valid
);
}
#[test]
fn test_expired_key_wrong_hash() {
let manager = ApiKeyManagerV0::init_default_config("sk").unwrap();
let expiry = Utc::now() - Duration::days(1);
let expired_key = manager
.generate_with_expiry(Environment::production(), expiry)
.unwrap();
let other_key = manager.generate(Environment::production()).unwrap();
assert_eq!(
manager
.verify(expired_key.key(), other_key.expose_hash().hash())
.unwrap(),
KeyStatus::Invalid
);
}
#[test]
fn test_short_expiry() {
const EXPIRY: i64 = 3;
let config = KeyConfig::default();
let h_config = HashConfig::default();
let manager = ApiKeyManagerV0::init("sk", config, h_config, std::time::Duration::ZERO).unwrap();
let expiry = Utc::now() + Duration::seconds(EXPIRY);
let key = manager
.generate_with_expiry(Environment::production(), expiry)
.unwrap();
assert_eq!(
manager.verify(key.key(), key.expose_hash().hash()).unwrap(),
KeyStatus::Valid
);
std::thread::sleep(std::time::Duration::from_secs(EXPIRY as u64 + 1));
assert_eq!(
manager.verify(key.key(), key.expose_hash().hash()).unwrap(),
KeyStatus::Invalid
);
}
#[test]
fn test_long_expiry() {
let manager = ApiKeyManagerV0::init_default_config("sk").unwrap();
let expiry = Utc::now() + Duration::days(365 * 10);
let key = manager
.generate_with_expiry(Environment::production(), expiry)
.unwrap();
assert_eq!(
manager.verify(key.key(), key.expose_hash().hash()).unwrap(),
KeyStatus::Valid
);
}
#[test]
fn test_expiry_across_environments() {
let manager = ApiKeyManagerV0::init_default_config("sk").unwrap();
let future = Utc::now() + Duration::days(1);
let past = Utc::now() - Duration::days(1);
let dev_valid = manager
.generate_with_expiry(Environment::dev(), future)
.unwrap();
let test_expired = manager
.generate_with_expiry(Environment::test(), past)
.unwrap();
let staging_valid = manager
.generate_with_expiry(Environment::staging(), future)
.unwrap();
let live_expired = manager
.generate_with_expiry(Environment::production(), past)
.unwrap();
assert_eq!(
manager
.verify(dev_valid.key(), dev_valid.expose_hash().hash())
.unwrap(),
KeyStatus::Valid
);
assert_eq!(
manager
.verify(test_expired.key(), test_expired.expose_hash().hash())
.unwrap(),
KeyStatus::Invalid
);
assert_eq!(
manager
.verify(staging_valid.key(), staging_valid.expose_hash().hash())
.unwrap(),
KeyStatus::Valid
);
assert_eq!(
manager
.verify(live_expired.key(), live_expired.expose_hash().hash())
.unwrap(),
KeyStatus::Invalid
);
}
#[test]
fn test_expiry_embedded_in_key() {
let manager = ApiKeyManagerV0::init_default_config("sk").unwrap();
let expiry = Utc::now() - Duration::days(1);
let key = manager
.generate_with_expiry(Environment::production(), expiry)
.unwrap();
let hash_str = key.expose_hash().hash().to_string();
assert_eq!(
manager.verify(key.key(), &hash_str).unwrap(),
KeyStatus::Invalid
);
}
#[test]
fn test_expiry_without_checksum() {
use api_keys_simplified::{HashConfig, KeyConfig};
let config = KeyConfig::default().disable_checksum();
let manager = ApiKeyManagerV0::init(
"sk",
config,
HashConfig::default(),
std::time::Duration::ZERO,
)
.unwrap();
let past = Utc::now() - Duration::hours(1);
let future = Utc::now() + Duration::hours(1);
let expired_key = manager
.generate_with_expiry(Environment::production(), past)
.unwrap();
let valid_key = manager
.generate_with_expiry(Environment::production(), future)
.unwrap();
assert_eq!(
manager
.verify(expired_key.key(), expired_key.expose_hash().hash())
.unwrap(),
KeyStatus::Invalid
);
assert_eq!(
manager
.verify(valid_key.key(), valid_key.expose_hash().hash())
.unwrap(),
KeyStatus::Valid
);
}
#[test]
fn test_multiple_keys_same_expiry() {
let manager = ApiKeyManagerV0::init_default_config("sk").unwrap();
let expiry = Utc::now() + Duration::days(30);
let key1 = manager
.generate_with_expiry(Environment::production(), expiry)
.unwrap();
let key2 = manager
.generate_with_expiry(Environment::production(), expiry)
.unwrap();
let key3 = manager
.generate_with_expiry(Environment::production(), expiry)
.unwrap();
assert_ne!(key1.key().expose_secret(), key2.key().expose_secret());
assert_ne!(key2.key().expose_secret(), key3.key().expose_secret());
assert_ne!(
key1.expose_hash().hash().to_string(),
key2.expose_hash().hash().to_string()
);
assert_eq!(
manager
.verify(key1.key(), key1.expose_hash().hash())
.unwrap(),
KeyStatus::Valid
);
assert_eq!(
manager
.verify(key2.key(), key2.expose_hash().hash())
.unwrap(),
KeyStatus::Valid
);
assert_eq!(
manager
.verify(key3.key(), key3.expose_hash().hash())
.unwrap(),
KeyStatus::Valid
);
}
#[test]
fn test_expired_with_valid_checksum() {
let manager = ApiKeyManagerV0::init_default_config("sk").unwrap();
let expiry = Utc::now() - Duration::days(1);
let key = manager
.generate_with_expiry(Environment::production(), expiry)
.unwrap();
assert_eq!(
manager.verify(key.key(), key.expose_hash().hash()).unwrap(),
KeyStatus::Invalid
);
}
#[test]
fn test_expiry_timestamp_round_trip() {
let manager = ApiKeyManagerV0::init_default_config("sk").unwrap();
let timestamps = vec![
Utc::now(),
Utc::now() + Duration::days(1),
Utc::now() + Duration::days(365),
Utc::now() - Duration::days(1),
];
for ts in timestamps {
let key = manager
.generate_with_expiry(Environment::production(), ts)
.unwrap();
let status = manager.verify(key.key(), key.expose_hash().hash()).unwrap();
assert!(
matches!(status, KeyStatus::Valid | KeyStatus::Invalid),
"Expected Valid or Expired, got {:?}",
status
);
}
}
#[test]
fn test_expiry_with_high_security() {
let manager = ApiKeyManagerV0::init_high_security_config("sk").unwrap();
let future = Utc::now() + Duration::days(7);
let past = Utc::now() - Duration::days(1);
let valid_key = manager
.generate_with_expiry(Environment::production(), future)
.unwrap();
let expired_key = manager
.generate_with_expiry(Environment::production(), past)
.unwrap();
assert_eq!(
manager
.verify(valid_key.key(), valid_key.expose_hash().hash())
.unwrap(),
KeyStatus::Valid
);
assert_eq!(
manager
.verify(expired_key.key(), expired_key.expose_hash().hash())
.unwrap(),
KeyStatus::Invalid
);
}
#[test]
fn test_corrupted_expiry_returns_invalid() {
let manager = ApiKeyManagerV0::init_default_config("sk").unwrap();
let expiry = Utc::now() + Duration::days(1);
let key = manager
.generate_with_expiry(Environment::production(), expiry)
.unwrap();
let key_str = key.key().expose_secret();
let parts: Vec<&str> = key_str.split('.').collect();
if parts.len() >= 2 {
let corrupted = format!("{}.corrupted.{}", parts[0], parts.get(2).unwrap_or(&""));
let corrupted_key = SecureString::from(corrupted);
let result = manager.verify(&corrupted_key, key.expose_hash().hash());
assert!(result.is_ok(), "Corrupted expiry should not cause panic");
}
}
#[test]
fn test_trial_key_lifecycle() {
let manager = ApiKeyManagerV0::init_default_config("trial").unwrap();
let trial_expiry = Utc::now() + Duration::days(7);
let trial_key = manager
.generate_with_expiry(Environment::production(), trial_expiry)
.unwrap();
assert_eq!(
manager
.verify(trial_key.key(), trial_key.expose_hash().hash())
.unwrap(),
KeyStatus::Valid
);
let expired_trial = Utc::now() - Duration::days(1);
let expired_key = manager
.generate_with_expiry(Environment::production(), expired_trial)
.unwrap();
assert_eq!(
manager
.verify(expired_key.key(), expired_key.expose_hash().hash())
.unwrap(),
KeyStatus::Invalid
);
}
#[test]
fn test_expiry_with_custom_prefix() {
let manager1 = ApiKeyManagerV0::init_default_config("api").unwrap();
let manager2 = ApiKeyManagerV0::init_default_config("partner").unwrap();
let expiry = Utc::now() - Duration::hours(1);
let key1 = manager1
.generate_with_expiry(Environment::production(), expiry)
.unwrap();
let key2 = manager2
.generate_with_expiry(Environment::production(), expiry)
.unwrap();
assert_eq!(
manager1
.verify(key1.key(), key1.expose_hash().hash())
.unwrap(),
KeyStatus::Invalid
);
assert_eq!(
manager2
.verify(key2.key(), key2.expose_hash().hash())
.unwrap(),
KeyStatus::Invalid
);
}