Spaces:
Running
Running
SilverElixir commited on
Commit ·
db6342b
1
Parent(s): 73ec2bd
Cover kill-switch note, daily-limit boundary and whole-line cut
Browse files- tests/test_bot_search.py +43 -0
- tests/test_lumen_search.py +12 -0
tests/test_bot_search.py
CHANGED
|
@@ -59,3 +59,46 @@ def test_empty_fallback_search_marks_reply_without_live_search(rate_guard_setup,
|
|
| 59 |
sent_text = bot._safe_reply.await_args[0][1]
|
| 60 |
assert "without live web search" in sent_text
|
| 61 |
bot.chat_state.pop(123, None)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 59 |
sent_text = bot._safe_reply.await_args[0][1]
|
| 60 |
assert "without live web search" in sent_text
|
| 61 |
bot.chat_state.pop(123, None)
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
def test_disabled_search_marks_reply_without_live_search(rate_guard_setup, monkeypatch):
|
| 65 |
+
# Ревью: выключенный свитч при мёртвой квоте молча отвечал по памяти — пометка обязана быть.
|
| 66 |
+
message = rate_guard_setup()
|
| 67 |
+
message.text = "погугли что происходит с ДЦ Яндекса"
|
| 68 |
+
_kill_search_quota(monkeypatch)
|
| 69 |
+
monkeypatch.setenv("WEB_SEARCH_ENABLED", "0")
|
| 70 |
+
|
| 71 |
+
async def fake_fetch(query, *, timeout_sec=None, max_results=None):
|
| 72 |
+
raise AssertionError("fetch must not run while disabled")
|
| 73 |
+
|
| 74 |
+
async def fake_run_route(chat_id, ai_prompt, route, message, **kwargs):
|
| 75 |
+
return "ok", False
|
| 76 |
+
|
| 77 |
+
monkeypatch.setattr("lumen_message_core.fetch_web_snippets", fake_fetch)
|
| 78 |
+
monkeypatch.setattr(bot, "_run_route", fake_run_route)
|
| 79 |
+
asyncio.run(bot._handle_message_core(message))
|
| 80 |
+
sent_text = bot._safe_reply.await_args[0][1]
|
| 81 |
+
assert "without live web search" in sent_text
|
| 82 |
+
bot.chat_state.pop(123, None)
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
def test_daily_limit_keeps_note_and_skips_external_search(rate_guard_setup, monkeypatch):
|
| 86 |
+
# Граница: дневной лимит пользователя — честная пометка без чужого поиска
|
| 87 |
+
# (лимит именно его, обходить поиском нельзя) и без единого сетевого вызова в тестах.
|
| 88 |
+
message = rate_guard_setup()
|
| 89 |
+
message.text = "погугли что происходит с ДЦ Яндекса"
|
| 90 |
+
monkeypatch.setattr(bot, "_user_daily_gemini_exhausted", lambda uid, entry: True)
|
| 91 |
+
|
| 92 |
+
async def fake_fetch(query, *, timeout_sec=None, max_results=None):
|
| 93 |
+
raise AssertionError("fetch must not run on daily user limit")
|
| 94 |
+
|
| 95 |
+
async def fake_run_route(chat_id, ai_prompt, route, message, **kwargs):
|
| 96 |
+
assert all(p != "gemini" for p, _ in route)
|
| 97 |
+
return "ok", False
|
| 98 |
+
|
| 99 |
+
monkeypatch.setattr("lumen_message_core.fetch_web_snippets", fake_fetch)
|
| 100 |
+
monkeypatch.setattr(bot, "_run_route", fake_run_route)
|
| 101 |
+
asyncio.run(bot._handle_message_core(message))
|
| 102 |
+
sent_text = bot._safe_reply.await_args[0][1]
|
| 103 |
+
assert sent_text.startswith("Answering without web search")
|
| 104 |
+
bot.chat_state.pop(123, None)
|
tests/test_lumen_search.py
CHANGED
|
@@ -58,6 +58,18 @@ def test_format_snippets_for_prompt_marks_data_and_skips_empties():
|
|
| 58 |
assert lumen_search.format_snippets_for_prompt([{"title": "", "url": "", "snippet": ""}]) == ""
|
| 59 |
|
| 60 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 61 |
def test_route_has_live_search_follows_grounding_flags():
|
| 62 |
assert lumen_search.route_has_live_search([("groq", "qwen/qwen3.8-27b")]) is False
|
| 63 |
assert lumen_search.route_has_live_search([("gemini", "gemini-3.8-flash")]) is False
|
|
|
|
| 58 |
assert lumen_search.format_snippets_for_prompt([{"title": "", "url": "", "snippet": ""}]) == ""
|
| 59 |
|
| 60 |
|
| 61 |
+
def test_format_snippets_for_prompt_cuts_on_whole_lines():
|
| 62 |
+
# Ревью: срез на 1500 символах рвал URL посередине — режем по целым строкам.
|
| 63 |
+
long_snippets = [
|
| 64 |
+
{"title": f"Title {i} " + "x" * 400, "url": f"https://example.com/{i}", "snippet": "y" * 400}
|
| 65 |
+
for i in range(10)
|
| 66 |
+
]
|
| 67 |
+
block = lumen_search.format_snippets_for_prompt(long_snippets)
|
| 68 |
+
assert len(block) <= lumen_search._BLOCK_MAX_CHARS
|
| 69 |
+
assert block.endswith(")")
|
| 70 |
+
assert "https://example.com/9" not in block
|
| 71 |
+
|
| 72 |
+
|
| 73 |
def test_route_has_live_search_follows_grounding_flags():
|
| 74 |
assert lumen_search.route_has_live_search([("groq", "qwen/qwen3.8-27b")]) is False
|
| 75 |
assert lumen_search.route_has_live_search([("gemini", "gemini-3.8-flash")]) is False
|