use super::key::ApiKey;
use std::collections::HashMap;
use std::sync::RwLock;
pub trait ApiKeyRepository: Send + Sync {
fn find_by_key(&self, key: &str) -> Option<ApiKey>;
}
#[derive(Debug)]
pub struct InMemoryApiKeyRepository {
keys: RwLock<HashMap<String, ApiKey>>,
}
impl Default for InMemoryApiKeyRepository {
fn default() -> Self {
Self::new()
}
}
impl InMemoryApiKeyRepository {
pub fn new() -> Self {
Self {
keys: RwLock::new(HashMap::new()),
}
}
pub fn with_key(self, key: ApiKey) -> Self {
self.add_key(key);
self
}
pub fn add_key(&self, key: ApiKey) {
let mut keys = self.keys.write().unwrap();
keys.insert(key.get_key().to_string(), key);
}
pub fn remove_key(&self, key: &str) -> Option<ApiKey> {
let mut keys = self.keys.write().unwrap();
keys.remove(key)
}
pub fn len(&self) -> usize {
let keys = self.keys.read().unwrap();
keys.len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn clear(&self) {
let mut keys = self.keys.write().unwrap();
keys.clear();
}
pub fn get_all_keys(&self) -> Vec<ApiKey> {
let keys = self.keys.read().unwrap();
keys.values().cloned().collect()
}
}
impl ApiKeyRepository for InMemoryApiKeyRepository {
fn find_by_key(&self, key: &str) -> Option<ApiKey> {
let keys = self.keys.read().unwrap();
keys.get(key).cloned()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_empty_repository() {
let repo = InMemoryApiKeyRepository::new();
assert!(repo.is_empty());
assert_eq!(repo.len(), 0);
assert!(repo.find_by_key("nonexistent").is_none());
}
#[test]
fn test_add_and_find_key() {
let repo =
InMemoryApiKeyRepository::new().with_key(ApiKey::new("sk_test_123").name("Test Key"));
assert_eq!(repo.len(), 1);
let found = repo.find_by_key("sk_test_123");
assert!(found.is_some());
let key = found.unwrap();
assert_eq!(key.get_key(), "sk_test_123");
assert_eq!(key.get_name(), Some("Test Key"));
}
#[test]
fn test_add_key_method() {
let repo = InMemoryApiKeyRepository::new();
repo.add_key(ApiKey::new("sk_test_123"));
assert_eq!(repo.len(), 1);
}
#[test]
fn test_remove_key() {
let repo = InMemoryApiKeyRepository::new().with_key(ApiKey::new("sk_test_123"));
let removed = repo.remove_key("sk_test_123");
assert!(removed.is_some());
assert!(repo.is_empty());
}
#[test]
fn test_clear() {
let repo = InMemoryApiKeyRepository::new()
.with_key(ApiKey::new("key1"))
.with_key(ApiKey::new("key2"))
.with_key(ApiKey::new("key3"));
assert_eq!(repo.len(), 3);
repo.clear();
assert!(repo.is_empty());
}
#[test]
fn test_get_all_keys() {
let repo = InMemoryApiKeyRepository::new()
.with_key(ApiKey::new("key1"))
.with_key(ApiKey::new("key2"));
let all_keys = repo.get_all_keys();
assert_eq!(all_keys.len(), 2);
}
#[test]
fn test_key_with_full_metadata() {
let repo = InMemoryApiKeyRepository::new().with_key(
ApiKey::new("sk_live_abc123")
.name("Production API Key")
.owner("admin@example.com")
.roles(vec!["API_USER".into(), "API_ADMIN".into()])
.authorities(vec!["api:read".into(), "api:write".into()])
.with_metadata("environment", "production"),
);
let found = repo.find_by_key("sk_live_abc123").unwrap();
assert_eq!(found.get_name(), Some("Production API Key"));
assert_eq!(found.get_owner(), Some("admin@example.com"));
assert!(found.has_role("API_USER"));
assert!(found.has_role("API_ADMIN"));
assert!(found.has_authority("api:read"));
assert!(found.has_authority("api:write"));
assert_eq!(
found.get_metadata().get("environment"),
Some(&"production".to_string())
);
}
}