aether-llm 0.1.9

Multi-provider LLM abstraction layer for the Aether AI agent framework
Documentation
use std::collections::HashMap;
use std::sync::Mutex;

use crate::oauth::OAuthError;
use crate::oauth::credential_store::{OAuthCredential, OAuthCredentialStorage};

#[derive(Default)]
pub struct FakeOAuthCredentialStore {
    credentials: Mutex<HashMap<String, OAuthCredential>>,
}

impl FakeOAuthCredentialStore {
    pub fn new() -> Self {
        Self { credentials: Mutex::new(HashMap::new()) }
    }

    pub fn with_credential(self, server_id: &str, credential: OAuthCredential) -> Self {
        self.credentials.lock().unwrap().insert(server_id.to_string(), credential);
        self
    }
}

impl OAuthCredentialStorage for FakeOAuthCredentialStore {
    async fn load_credential(&self, server_id: &str) -> Result<Option<OAuthCredential>, OAuthError> {
        Ok(self.credentials.lock().unwrap().get(server_id).cloned())
    }

    async fn save_credential(&self, server_id: &str, credential: OAuthCredential) -> Result<(), OAuthError> {
        self.credentials.lock().unwrap().insert(server_id.to_string(), credential);
        Ok(())
    }

    fn has_credential(&self, server_id: &str) -> bool {
        self.credentials.lock().unwrap().contains_key(server_id)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn load_returns_none_when_empty() {
        let store = FakeOAuthCredentialStore::new();
        let result = tokio::runtime::Runtime::new().unwrap().block_on(store.load_credential("unknown"));
        assert!(result.unwrap().is_none());
    }

    #[tokio::test]
    async fn save_then_load_round_trips() {
        let store = FakeOAuthCredentialStore::new();
        let cred = OAuthCredential {
            client_id: "client_1".to_string(),
            access_token: "tok_abc".to_string(),
            refresh_token: Some("ref_xyz".to_string()),
            expires_at: Some(9_999_999_999_999),
        };

        store.save_credential("my-server", cred.clone()).await.unwrap();

        let loaded = store.load_credential("my-server").await.unwrap().expect("should find saved credential");
        assert_eq!(loaded.client_id, "client_1");
        assert_eq!(loaded.access_token, "tok_abc");
        assert_eq!(loaded.refresh_token.as_deref(), Some("ref_xyz"));
    }

    #[test]
    fn has_credential_reflects_state() {
        let store = FakeOAuthCredentialStore::new().with_credential(
            "present",
            OAuthCredential {
                client_id: "c".to_string(),
                access_token: "t".to_string(),
                refresh_token: None,
                expires_at: None,
            },
        );

        assert!(store.has_credential("present"));
        assert!(!store.has_credential("absent"));
    }
}