ppt-web / tests /test_deep_research_service.py
26fwyzpz6f-max
Clean deploy without binary files
6aecb2e
Raw
History Blame Contribute Delete
5.46 kB
import os
import sys
import types
import pytest
os.environ["DEBUG"] = "false"
if "tavily" not in sys.modules:
fake_tavily = types.ModuleType("tavily")
class _BootstrapTavilyClient:
def __init__(self, api_key, api_base_url=None):
self.api_key = api_key
self.api_base_url = api_base_url
def search(self, **kwargs):
return {"results": []}
fake_tavily.TavilyClient = _BootstrapTavilyClient
sys.modules["tavily"] = fake_tavily
from landppt.services import deep_research_service as drs
from landppt.services.deep_research_service import (
DEEPResearchService,
_is_tavily_auth_error,
_normalize_secret_value,
)
def test_normalize_secret_value_filters_empty_and_masked_values():
assert _normalize_secret_value(None) is None
assert _normalize_secret_value("") is None
assert _normalize_secret_value(" ") is None
assert _normalize_secret_value("********") is None
assert _normalize_secret_value(" tvly-good ") == "tvly-good"
def test_is_tavily_auth_error_matches_common_messages():
assert _is_tavily_auth_error(Exception("Unauthorized: missing or invalid API key.")) is True
assert _is_tavily_auth_error(Exception("invalid_api_key")) is True
assert _is_tavily_auth_error(Exception("timeout")) is False
@pytest.mark.asyncio
async def test_tavily_search_retries_next_key_on_auth_error(monkeypatch):
service = DEEPResearchService()
async def fake_candidates():
return [
("user database override", "bad-key"),
("system database default", "good-key"),
]
class FakeTavilyClient:
def __init__(self, api_key, api_base_url=None):
self.api_key = api_key
self.api_base_url = api_base_url
def search(self, **kwargs):
if self.api_key == "bad-key":
raise Exception("Unauthorized: missing or invalid API key.")
return {
"results": [
{
"title": "SkillNet",
"url": "https://example.com",
"content": "ok",
"score": 0.9,
"published_date": "2026-03-08",
}
]
}
monkeypatch.setattr(service, "_get_tavily_api_key_candidates_async", fake_candidates)
monkeypatch.setattr(drs, "TavilyClient", FakeTavilyClient)
monkeypatch.setattr(drs.ai_config, "tavily_include_domains", None, raising=False)
monkeypatch.setattr(drs.ai_config, "tavily_exclude_domains", None, raising=False)
results = await service._tavily_search("SkillNet", "zh")
assert results == [
{
"title": "SkillNet",
"url": "https://example.com",
"content": "ok",
"score": 0.9,
"published_date": "2026-03-08",
}
]
assert service._active_tavily_key_source == "system database default"
@pytest.mark.asyncio
async def test_tavily_search_uses_runtime_base_url(monkeypatch):
service = DEEPResearchService()
created_clients = []
async def fake_candidates():
return [("process environment", "good-key")]
async def fake_runtime_config():
return {
"base_url": "https://gateway.example.com/tavily",
"max_results": 7,
"search_depth": "advanced",
"include_domains": ["example.com"],
"exclude_domains": None,
}
class FakeTavilyClient:
def __init__(self, api_key, api_base_url=None):
self.api_key = api_key
self.api_base_url = api_base_url
created_clients.append(self)
def search(self, **kwargs):
assert kwargs["max_results"] == 7
assert kwargs["search_depth"] == "advanced"
assert kwargs["include_domains"] == ["example.com"]
return {"results": []}
monkeypatch.setattr(service, "_get_tavily_api_key_candidates_async", fake_candidates)
monkeypatch.setattr(service, "_get_tavily_runtime_config_async", fake_runtime_config)
monkeypatch.setattr(drs, "TavilyClient", FakeTavilyClient)
results = await service._tavily_search("SkillNet", "zh")
assert results == []
assert created_clients[0].api_base_url == "https://gateway.example.com/tavily"
@pytest.mark.asyncio
async def test_tavily_runtime_config_ignores_blank_db_base_url(monkeypatch):
import landppt.database.database as database_mod
import landppt.database.repositories as repo_mod
service = DEEPResearchService(user_id=123)
class FakeSession:
async def __aenter__(self):
return self
async def __aexit__(self, exc_type, exc, tb):
return False
class FakeRepo:
def __init__(self, session):
self.session = session
async def get_all_configs(self, user_id=None):
return {
"tavily_base_url": {
"value": "",
"type": "url",
"category": "generation_params",
"is_user_override": True,
}
}
monkeypatch.setattr(database_mod, "AsyncSessionLocal", lambda: FakeSession())
monkeypatch.setattr(repo_mod, "UserConfigRepository", FakeRepo)
config = await service._get_tavily_runtime_config_async()
assert config["base_url"] == "https://api.tavily.com"