RA.Aid/tests/ra_aid/test_env.py

159 lines
6.1 KiB
Python

import os
import pytest
from dataclasses import dataclass
from typing import Optional
from ra_aid.env import validate_environment
@dataclass
class MockArgs:
provider: str
expert_provider: str
model: Optional[str] = None
expert_model: Optional[str] = None
@pytest.fixture
def clean_env(monkeypatch):
"""Remove relevant environment variables before each test"""
env_vars = [
'ANTHROPIC_API_KEY', 'OPENAI_API_KEY', 'OPENROUTER_API_KEY',
'OPENAI_API_BASE', 'EXPERT_ANTHROPIC_API_KEY', 'EXPERT_OPENAI_API_KEY',
'EXPERT_OPENROUTER_API_KEY', 'EXPERT_OPENAI_API_BASE'
]
for var in env_vars:
monkeypatch.delenv(var, raising=False)
def test_anthropic_validation(clean_env, monkeypatch):
args = MockArgs(provider="anthropic", expert_provider="openai")
# Should fail without API key
with pytest.raises(SystemExit):
validate_environment(args)
# Should pass with API key
monkeypatch.setenv('ANTHROPIC_API_KEY', 'test-key')
expert_enabled, missing = validate_environment(args)
assert not expert_enabled
assert 'EXPERT_OPENAI_API_KEY environment variable is not set' in missing
def test_openai_validation(clean_env, monkeypatch):
args = MockArgs(provider="openai", expert_provider="openai")
# Should fail without API key
with pytest.raises(SystemExit):
validate_environment(args)
# Should pass with API key and enable expert mode with fallback
monkeypatch.setenv('OPENAI_API_KEY', 'test-key')
expert_enabled, missing = validate_environment(args)
assert expert_enabled
assert not missing
assert os.environ.get('EXPERT_OPENAI_API_KEY') == 'test-key'
def test_openai_compatible_validation(clean_env, monkeypatch):
args = MockArgs(provider="openai-compatible", expert_provider="openai-compatible")
# Should fail without API key and base URL
with pytest.raises(SystemExit):
validate_environment(args)
# Should fail with only API key
monkeypatch.setenv('OPENAI_API_KEY', 'test-key')
with pytest.raises(SystemExit):
validate_environment(args)
# Should pass with both API key and base URL
monkeypatch.setenv('OPENAI_API_BASE', 'http://test')
expert_enabled, missing = validate_environment(args)
assert expert_enabled
assert not missing
assert os.environ.get('EXPERT_OPENAI_API_KEY') == 'test-key'
assert os.environ.get('EXPERT_OPENAI_API_BASE') == 'http://test'
def test_expert_fallback(clean_env, monkeypatch):
args = MockArgs(provider="openai", expert_provider="openai")
# Set only base API key
monkeypatch.setenv('OPENAI_API_KEY', 'test-key')
# Should enable expert mode with fallback
expert_enabled, missing = validate_environment(args)
assert expert_enabled
assert not missing
assert os.environ.get('EXPERT_OPENAI_API_KEY') == 'test-key'
# Should use explicit expert key if available
monkeypatch.setenv('EXPERT_OPENAI_API_KEY', 'expert-key')
expert_enabled, missing = validate_environment(args)
assert expert_enabled
assert not missing
assert os.environ.get('EXPERT_OPENAI_API_KEY') == 'expert-key'
def test_cross_provider_fallback(clean_env, monkeypatch):
"""Test that fallback works even when providers differ"""
args = MockArgs(provider="openai", expert_provider="anthropic")
# Set base API key for main provider and expert provider
monkeypatch.setenv('OPENAI_API_KEY', 'openai-key')
monkeypatch.setenv('ANTHROPIC_API_KEY', 'anthropic-key')
# Should enable expert mode with fallback to ANTHROPIC base key
expert_enabled, missing = validate_environment(args)
assert expert_enabled
assert not missing
assert os.environ.get('EXPERT_ANTHROPIC_API_KEY') == 'anthropic-key'
# Try with openai-compatible expert provider
args = MockArgs(provider="anthropic", expert_provider="openai-compatible")
monkeypatch.setenv('OPENAI_API_KEY', 'openai-key')
monkeypatch.setenv('OPENAI_API_BASE', 'http://test')
expert_enabled, missing = validate_environment(args)
assert expert_enabled
assert not missing
assert os.environ.get('EXPERT_OPENAI_API_KEY') == 'openai-key'
assert os.environ.get('EXPERT_OPENAI_API_BASE') == 'http://test'
def test_no_warning_on_fallback(clean_env, monkeypatch):
"""Test that no warning is issued when fallback succeeds"""
args = MockArgs(provider="openai", expert_provider="openai")
# Set only base API key
monkeypatch.setenv('OPENAI_API_KEY', 'test-key')
# Should enable expert mode with fallback and no warnings
expert_enabled, expert_missing = validate_environment(args)
assert expert_enabled
assert not expert_missing # List should be empty
assert os.environ.get('EXPERT_OPENAI_API_KEY') == 'test-key'
def test_different_providers_no_expert_key(clean_env, monkeypatch):
"""Test behavior when providers differ and only base keys are available"""
args = MockArgs(provider="anthropic", expert_provider="openai")
# Set only base keys
monkeypatch.setenv('ANTHROPIC_API_KEY', 'anthropic-key')
monkeypatch.setenv('OPENAI_API_KEY', 'openai-key')
# Should enable expert mode and use base OPENAI key
expert_enabled, missing = validate_environment(args)
assert expert_enabled
assert not missing
assert os.environ.get('EXPERT_OPENAI_API_KEY') == 'openai-key'
def test_mixed_provider_openai_compatible(clean_env, monkeypatch):
"""Test behavior with openai-compatible expert and different main provider"""
args = MockArgs(provider="anthropic", expert_provider="openai-compatible")
# Set all required keys and URLs
monkeypatch.setenv('ANTHROPIC_API_KEY', 'anthropic-key')
monkeypatch.setenv('OPENAI_API_KEY', 'openai-key')
monkeypatch.setenv('OPENAI_API_BASE', 'http://test')
# Should enable expert mode and use base openai key and URL
expert_enabled, missing = validate_environment(args)
assert expert_enabled
assert not missing
assert os.environ.get('EXPERT_OPENAI_API_KEY') == 'openai-key'
assert os.environ.get('EXPERT_OPENAI_API_BASE') == 'http://test'