diff --git a/ai-hub/integration_tests/test_provider_config.py b/ai-hub/integration_tests/test_provider_config.py new file mode 100644 index 0000000..bc54cff --- /dev/null +++ b/ai-hub/integration_tests/test_provider_config.py @@ -0,0 +1,126 @@ +import os +import httpx +import pytest + +BASE_URL = os.getenv("SYNC_TEST_BASE_URL", "http://127.0.0.1:8002/api/v1") + +def _headers(): + return { + "X-User-ID": os.environ.get("SYNC_TEST_USER_ID", "") + } + +def test_verify_llm_success_gemini(): + """ + Test 1: Check LLM verification with a valid API Key and Model format directly. + """ + payload = { + "provider_name": "gemini", + "api_key": os.getenv("GEMINI_API_KEY", "fallback_key_if_missing"), + "model": "gemini/gemini-3-flash-preview" + } + + with httpx.Client(timeout=15.0) as client: + r = client.post(f"{BASE_URL}/users/me/config/verify_llm", headers=_headers(), json=payload) + + assert r.status_code == 200, f"Expected 200 OK: {r.text}" + data = r.json() + + # Depending on if the key works for litellm.acompletion('Hello') right now + # If it returns success: True or False doesn't matter for the HTTP connection, + # but logically it should be processed without 400s or 500s. + # If the key provided by the user is real, 'success' will be True. + assert "success" in data, "Response missing 'success'" + +def test_verify_llm_failure_invalid_key(): + """ + Test 2: Check LLM verification with an explicitly invalid API Key fails gracefully. + """ + payload = { + "provider_name": "gemini", + "api_key": "junk_invalid_key_123", + "model": "gemini/gemini-3-flash-preview" + } + + with httpx.Client(timeout=15.0) as client: + r = client.post(f"{BASE_URL}/users/me/config/verify_llm", headers=_headers(), json=payload) + + assert r.status_code == 200, f"Expected 200 OK: {r.text}" + data = r.json() + assert data["success"] is False, "Verification should fail for an invalid key." + assert "message" in data and len(data["message"]) > 0 + +def test_update_user_llm_preferences(): + """ + Test 3: Update LLM user preferences fully and ensure they save. + """ + payload = { + "llm": { + "active_provider": "gemini", + "providers": { + "gemini": { + "api_key": os.getenv("GEMINI_API_KEY", "fallback_key_if_missing"), + "model": "gemini/gemini-3-flash-preview" + } + } + }, + "tts": {}, + "stt": {}, + "statuses": {} + } + + with httpx.Client(timeout=10.0) as client: + r = client.put(f"{BASE_URL}/users/me/config", headers=_headers(), json=payload) + assert r.status_code == 200, f"Expected 200 OK: {r.text}" + + # Fetch config to verify + r_get = client.get(f"{BASE_URL}/users/me/config", headers=_headers()) + assert r_get.status_code == 200 + saved_prefs = r_get.json() + + # Verify provider configs + assert "preferences" in saved_prefs + assert "llm" in saved_prefs["preferences"] + assert saved_prefs["preferences"]["llm"]["active_provider"] == "gemini" + + # The API likely masks the key on GET requests inside `merge_user_config` + # We ensure it exists + saved_gemini = saved_prefs["preferences"]["llm"]["providers"].get("gemini", {}) + assert "api_key" in saved_gemini + assert "model" in saved_gemini + assert saved_gemini["model"] == "gemini/gemini-3-flash-preview" + +def test_verify_llm_success_gemini_masked_key_fallback(): + """ + Test 4: Verify that a masked key (***) correctly pulls the true key from + the user's DB configuration automatically. + """ + payload = { + "provider_name": "gemini", + "api_key": "***", + "model": "gemini/gemini-3-flash-preview" + } + + with httpx.Client(timeout=15.0) as client: + r = client.post(f"{BASE_URL}/users/me/config/verify_llm", headers=_headers(), json=payload) + + # If DB lookup succeeds, it executes test using the real key saved in Test 3 + assert r.status_code == 200, f"Expected 200 OK: {r.text}" + data = r.json() + assert "success" in data + +def test_verify_llm_unrecognized_provider(): + """ + Test 5: Check behavior for a completely unrecognized provider name. + """ + payload = { + "provider_name": "non_existent_provider_xyz", + "api_key": "123", + "model": "unknown_model" + } + with httpx.Client(timeout=10.0) as client: + r = client.post(f"{BASE_URL}/users/me/config/verify_llm", headers=_headers(), json=payload) + + assert r.status_code == 200 + data = r.json() + # Should gracefully fail + assert data["success"] is False