/// 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) pub struct MockAuditLogger { decisions: Arc>>, // Store serialized decisions } impl MockAuditLogger { pub fn new() -> Self { Self { decisions: Arc::new(Mutex::new(Vec::new())), } } pub fn log_decision_sync(&self, decision_str: String) { self.decisions.lock().unwrap().push(decision_str); } pub fn decisions(&self) -> Vec { self.decisions.lock().unwrap().clone() } 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); } #[test] fn test_mock_audit_logger() { let logger = MockAuditLogger::new(); logger.log_decision_sync("charlie:allow".to_string()); let decisions = logger.decisions(); assert_eq!(decisions.len(), 1); assert!(decisions[0].contains("charlie")); } #[test] fn test_constant_scorer() { let scorer = ConstantScorer::new(0.75); assert_eq!(scorer.name(), "constant-mock"); } }