/// Mock implementations for testing (pluggable traits) use std::sync::Arc; use std::sync::Mutex; use super::builders::{AccessPolicy, OidcClaims}; use anyhow::Result; /// Mock PolicyProvider for testing (no Vault dependency) pub struct MockPolicyProvider { policies: Arc>>, } impl MockPolicyProvider { pub fn new() -> Self { Self { policies: Arc::new(Mutex::new(std::collections::HashMap::new())), } } pub fn with_policy( mut self, resource_type: &str, resource_name: &str, policy: AccessPolicy, ) -> Self { let key = format!("{}:{}", resource_type, resource_name); self.policies.lock().unwrap().insert(key, policy); self } pub async fn get_policy( &self, resource_type: &str, resource_name: &str, ) -> Result { let key = format!("{}:{}", resource_type, resource_name); self.policies .lock() .unwrap() .get(&key) .cloned() .ok_or_else(|| anyhow::anyhow!("Policy not found: {}", key)) } } /// Mock AuditLogger for testing (records decisions, no I/O) #[derive(Debug, Clone)] pub struct AccessDecision { pub user_id: String, pub resource_type: String, pub resource_name: String, pub decision: String, pub reason: String, } pub struct MockAuditLogger { decisions: Arc>>, } impl MockAuditLogger { pub fn new() -> Self { Self { decisions: Arc::new(Mutex::new(Vec::new())), } } pub async fn log_decision(&self, decision: AccessDecision) -> Result<()> { self.decisions.lock().unwrap().push(decision); Ok(()) } pub fn decisions(&self) -> Vec { self.decisions.lock().unwrap().clone() } pub fn last_decision(&self) -> Option { self.decisions.lock().unwrap().last().cloned() } pub fn clear(&self) { self.decisions.lock().unwrap().clear(); } } /// Mock DocumentScorer for testing (returns constant score) pub struct ConstantScorer { score: f32, } impl ConstantScorer { pub fn new(score: f32) -> Self { Self { score } } pub async fn score(&self, _query: &str, _doc_id: &str) -> Result { Ok(self.score) } pub fn name(&self) -> &str { "constant-mock" } } #[cfg(test)] mod tests { use super::*; #[tokio::test] async fn test_mock_policy_provider() { let provider = MockPolicyProvider::new(); let policy = AccessPolicy { access_level: "public".to_string(), owner_group: "".to_string(), allowed_groups: vec![], required_role: None, required_permission: None, }; let provider = provider.with_policy("project", "poimen", policy.clone()); let retrieved = provider.get_policy("project", "poimen").await.unwrap(); assert_eq!(retrieved, policy); } #[tokio::test] async fn test_mock_audit_logger() { let logger = MockAuditLogger::new(); logger .log_decision(AccessDecision { user_id: "charlie".to_string(), resource_type: "project".to_string(), resource_name: "poimen".to_string(), decision: "allow".to_string(), reason: "in_allowed_group".to_string(), }) .await .unwrap(); let decisions = logger.decisions(); assert_eq!(decisions.len(), 1); assert_eq!(decisions[0].user_id, "charlie"); } #[test] fn test_constant_scorer() { let scorer = ConstantScorer::new(0.75); assert_eq!(scorer.name(), "constant-mock"); } }