provider_client_integration.rs 2.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. use std::ffi::OsString;
  2. use std::sync::{Mutex, OnceLock};
  3. use api::{read_xai_base_url, ApiError, AuthSource, ProviderClient, ProviderKind};
  4. #[test]
  5. fn provider_client_routes_grok_aliases_through_xai() {
  6. let _lock = env_lock();
  7. let _xai_api_key = EnvVarGuard::set("XAI_API_KEY", Some("xai-test-key"));
  8. let client = ProviderClient::from_model("grok-mini").expect("grok alias should resolve");
  9. assert_eq!(client.provider_kind(), ProviderKind::Xai);
  10. }
  11. #[test]
  12. fn provider_client_reports_missing_xai_credentials_for_grok_models() {
  13. let _lock = env_lock();
  14. let _xai_api_key = EnvVarGuard::set("XAI_API_KEY", None);
  15. let error = ProviderClient::from_model("grok-3")
  16. .expect_err("grok requests without XAI_API_KEY should fail fast");
  17. match error {
  18. ApiError::MissingCredentials { provider, env_vars } => {
  19. assert_eq!(provider, "xAI");
  20. assert_eq!(env_vars, &["XAI_API_KEY"]);
  21. }
  22. other => panic!("expected missing xAI credentials, got {other:?}"),
  23. }
  24. }
  25. #[test]
  26. fn provider_client_uses_explicit_anthropic_auth_without_env_lookup() {
  27. let _lock = env_lock();
  28. let _anthropic_api_key = EnvVarGuard::set("ANTHROPIC_API_KEY", None);
  29. let _anthropic_auth_token = EnvVarGuard::set("ANTHROPIC_AUTH_TOKEN", None);
  30. let client = ProviderClient::from_model_with_anthropic_auth(
  31. "claude-sonnet-4-6",
  32. Some(AuthSource::ApiKey("anthropic-test-key".to_string())),
  33. )
  34. .expect("explicit anthropic auth should avoid env lookup");
  35. assert_eq!(client.provider_kind(), ProviderKind::Anthropic);
  36. }
  37. #[test]
  38. fn read_xai_base_url_prefers_env_override() {
  39. let _lock = env_lock();
  40. let _xai_base_url = EnvVarGuard::set("XAI_BASE_URL", Some("https://example.xai.test/v1"));
  41. assert_eq!(read_xai_base_url(), "https://example.xai.test/v1");
  42. }
  43. fn env_lock() -> std::sync::MutexGuard<'static, ()> {
  44. static LOCK: OnceLock<Mutex<()>> = OnceLock::new();
  45. LOCK.get_or_init(|| Mutex::new(()))
  46. .lock()
  47. .unwrap_or_else(std::sync::PoisonError::into_inner)
  48. }
  49. struct EnvVarGuard {
  50. key: &'static str,
  51. original: Option<OsString>,
  52. }
  53. impl EnvVarGuard {
  54. fn set(key: &'static str, value: Option<&str>) -> Self {
  55. let original = std::env::var_os(key);
  56. match value {
  57. Some(value) => std::env::set_var(key, value),
  58. None => std::env::remove_var(key),
  59. }
  60. Self { key, original }
  61. }
  62. }
  63. impl Drop for EnvVarGuard {
  64. fn drop(&mut self) {
  65. match &self.original {
  66. Some(value) => std::env::set_var(self.key, value),
  67. None => std::env::remove_var(self.key),
  68. }
  69. }
  70. }