1use secrecy::SecretString;
11
12pub trait TokenProvider: Send + Sync {
21 fn github_token(&self) -> Option<SecretString>;
25
26 fn ai_api_key(&self, provider: &str) -> Option<SecretString>;
33}
34
35#[cfg(test)]
36mod tests {
37 use super::*;
38 use crate::ai::registry::all_providers;
39 use std::collections::HashMap;
40
41 struct MockTokenProvider {
43 github_token: Option<SecretString>,
44 ai_keys: HashMap<String, SecretString>,
45 }
46
47 impl TokenProvider for MockTokenProvider {
48 fn github_token(&self) -> Option<SecretString> {
49 self.github_token.clone()
50 }
51
52 fn ai_api_key(&self, provider: &str) -> Option<SecretString> {
53 self.ai_keys.get(provider).cloned()
54 }
55 }
56
57 #[test]
58 fn test_mock_provider_with_tokens() {
59 let mut ai_keys = HashMap::new();
60 for provider_config in all_providers() {
61 ai_keys.insert(
62 provider_config.name.to_string(),
63 SecretString::from(format!("{}_key", provider_config.name)),
64 );
65 }
66
67 let provider = MockTokenProvider {
68 github_token: Some(SecretString::from("gh_token")),
69 ai_keys,
70 };
71
72 assert!(provider.github_token().is_some());
73 for provider_config in all_providers() {
74 assert!(
75 provider.ai_api_key(provider_config.name).is_some(),
76 "Expected key for provider: {}",
77 provider_config.name
78 );
79 }
80 }
81
82 #[test]
83 fn test_mock_provider_without_tokens() {
84 let provider = MockTokenProvider {
85 github_token: None,
86 ai_keys: HashMap::new(),
87 };
88
89 assert!(provider.github_token().is_none());
90 for provider_config in all_providers() {
91 assert!(
92 provider.ai_api_key(provider_config.name).is_none(),
93 "Expected no key for provider: {}",
94 provider_config.name
95 );
96 }
97 }
98}