"""Configuration de l'assistant IA : parametres non sensibles persistes via app_settings (redirige vers un fichier temporaire pour ne jamais toucher les vrais reglages), cle API exclusivement via CredentialStore, et construction du fournisseur a partir des deux.""" import pathlib import sys sys.path.insert(0, str(pathlib.Path(__file__).resolve().parent.parent)) import tempfile from pathlib import Path from unittest.mock import patch import app.app_settings as app_settings from app.ai.provider import OpenAICompatibleProvider from app.ai.settings import ( AI_API_KEY_CREDENTIAL, DEFAULT_BASE_URL, AISettings, build_provider_from_settings, load_ai_settings, load_api_key, save_ai_settings, save_api_key, ) from app.credentials import CredentialStore class FakeCredentialStore(CredentialStore): def __init__(self): self._data = {} def is_available(self): return True def set_secret(self, key, secret): self._data[key] = secret return True def get_secret(self, key): return self._data.get(key) def delete_secret(self, key): self._data.pop(key, None) tmp = Path(tempfile.mkdtemp()) settings_redirect = patch.object(app_settings, "SETTINGS_FILE", tmp / "settings.json") settings_redirect.start() print("=== 1. Valeurs par defaut : desactive, pas de secret expose ===") default = load_ai_settings() print(" ", default) assert default.enabled is False assert default.base_url == DEFAULT_BASE_URL print(" OK") print() print("=== 2. Sauvegarde/rechargement des parametres non sensibles ===") settings = AISettings(enabled=True, provider_name="OpenRouter", base_url="https://openrouter.ai/api/v1", model="anthropic/claude", timeout=45.0, temperature=0.7) save_ai_settings(settings) reloaded = load_ai_settings() print(" ", reloaded) assert reloaded == settings print(" OK") print() print("=== 3. La cle API ne transite jamais par app_settings (verification du fichier disque) ===") raw = (tmp / "settings.json").read_text(encoding="utf-8") assert "sk-" not in raw assert "api_key" not in raw.lower() or "ai_api" not in raw.lower() # aucune cle presente, seul le nom du champ pourrait print(" contenu persiste:", raw) print(" OK") print() print("=== 4. Cle API : lecture/ecriture via CredentialStore uniquement ===") store = FakeCredentialStore() assert load_api_key(store) == "" save_api_key(store, "sk-test-secret") assert load_api_key(store) == "sk-test-secret" assert store.get_secret(AI_API_KEY_CREDENTIAL) == "sk-test-secret" save_api_key(store, "") # vider la cle -> supprime l'entree assert load_api_key(store) == "" print(" OK") print() print("=== 5. build_provider_from_settings() : None si desactive ===") save_ai_settings(AISettings(enabled=False)) assert build_provider_from_settings(store) is None print(" OK") print() print("=== 6. build_provider_from_settings() : fournisseur pret si active ===") save_ai_settings(AISettings(enabled=True, base_url="https://api.example.com/v1", model="gpt-x")) save_api_key(store, "sk-live") provider = build_provider_from_settings(store) assert isinstance(provider, OpenAICompatibleProvider) assert provider.base_url == "https://api.example.com/v1" assert provider.model == "gpt-x" assert provider.api_key == "sk-live" print(" OK") settings_redirect.stop() print() print("AI SETTINGS : TOUS LES TESTS OK")