Spaces:
Running
Running
File size: 4,901 Bytes
659da01 db6342b | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | """
test_bot_search.py — сквозные тесты внешнего поиска в _handle_message_core.
Что защищают: инцидент 11.10.2026 — явная просьба "погугли" при мёртвой квоте
2.5 шла на модели без поиска и отвечала по памяти. Первый тест ловит подклейку
сниппетов в промпт, второй — честную пометку, когда и внешний поиск пуст.
Квота гасится точечно (только поисковые Gemini), fetch мокается на границе I/O.
Запуск:
pytest tests/test_bot_search.py -v
"""
import asyncio
import bot
from tests.bot_test_helpers import _run_core_capturing_prompt
_DEAD_SEARCH_QUOTA = {("gemini", "gemini-2.5-flash"), ("gemini", "gemini-2.5-flash-lite")}
def _kill_search_quota(monkeypatch):
import lumen_router_config
monkeypatch.setattr(
lumen_router_config,
"_is_quota_exhausted",
lambda provider, model_id: (provider, model_id) in _DEAD_SEARCH_QUOTA,
)
def test_explicit_search_request_gets_snippets_without_live_search(rate_guard_setup, monkeypatch):
message = rate_guard_setup()
message.text = "погугли что происходит с ДЦ Яндекса"
_kill_search_quota(monkeypatch)
async def fake_fetch(query, *, timeout_sec=None, max_results=None):
assert "ДЦ" in query
return [{"title": "Новость", "url": "https://example.com/n", "snippet": "Свежие данные"}]
monkeypatch.setattr("lumen_message_core.fetch_web_snippets", fake_fetch)
prompt = _run_core_capturing_prompt(message, monkeypatch)
assert "Web search snippets" in prompt
assert "https://example.com/n" in prompt
bot.chat_state.pop(123, None)
def test_empty_fallback_search_marks_reply_without_live_search(rate_guard_setup, monkeypatch):
message = rate_guard_setup()
message.text = "погугли что происходит с ДЦ Яндекса"
_kill_search_quota(monkeypatch)
async def fake_fetch(query, *, timeout_sec=None, max_results=None):
return []
async def fake_run_route(chat_id, ai_prompt, route, message, **kwargs):
return "ok", False
monkeypatch.setattr("lumen_message_core.fetch_web_snippets", fake_fetch)
monkeypatch.setattr(bot, "_run_route", fake_run_route)
asyncio.run(bot._handle_message_core(message))
sent_text = bot._safe_reply.await_args[0][1]
assert "without live web search" in sent_text
bot.chat_state.pop(123, None)
def test_disabled_search_marks_reply_without_live_search(rate_guard_setup, monkeypatch):
# Ревью: выключенный свитч при мёртвой квоте молча отвечал по памяти — пометка обязана быть.
message = rate_guard_setup()
message.text = "погугли что происходит с ДЦ Яндекса"
_kill_search_quota(monkeypatch)
monkeypatch.setenv("WEB_SEARCH_ENABLED", "0")
async def fake_fetch(query, *, timeout_sec=None, max_results=None):
raise AssertionError("fetch must not run while disabled")
async def fake_run_route(chat_id, ai_prompt, route, message, **kwargs):
return "ok", False
monkeypatch.setattr("lumen_message_core.fetch_web_snippets", fake_fetch)
monkeypatch.setattr(bot, "_run_route", fake_run_route)
asyncio.run(bot._handle_message_core(message))
sent_text = bot._safe_reply.await_args[0][1]
assert "without live web search" in sent_text
bot.chat_state.pop(123, None)
def test_daily_limit_keeps_note_and_skips_external_search(rate_guard_setup, monkeypatch):
# Граница: дневной лимит пользователя — честная пометка без чужого поиска
# (лимит именно его, обходить поиском нельзя) и без единого сетевого вызова в тестах.
message = rate_guard_setup()
message.text = "погугли что происходит с ДЦ Яндекса"
monkeypatch.setattr(bot, "_user_daily_gemini_exhausted", lambda uid, entry: True)
async def fake_fetch(query, *, timeout_sec=None, max_results=None):
raise AssertionError("fetch must not run on daily user limit")
async def fake_run_route(chat_id, ai_prompt, route, message, **kwargs):
assert all(p != "gemini" for p, _ in route)
return "ok", False
monkeypatch.setattr("lumen_message_core.fetch_web_snippets", fake_fetch)
monkeypatch.setattr(bot, "_run_route", fake_run_route)
asyncio.run(bot._handle_message_core(message))
sent_text = bot._safe_reply.await_args[0][1]
assert sent_text.startswith("Answering without web search")
bot.chat_state.pop(123, None)
|