admin_bot: add emergency pause, manual fallback configuration, and admin management UI
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
"""Errors can be grouped, marked fixed, and reported as metrics."""
|
||||
import asyncio
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, "/app")
|
||||
|
||||
from db.database import init_db, close_db_pool
|
||||
from db.repository import Repository
|
||||
from services.admin_bot import AdminBotService, clamp_for_telegram, MAX_MEDIA_CAPTION, MAX_TEXT_MESSAGE
|
||||
|
||||
SVC = "test.tracking_service"
|
||||
|
||||
|
||||
async def _cleanup(repo):
|
||||
pool = await repo._get_pool()
|
||||
async with pool.acquire() as conn:
|
||||
await conn.execute("DELETE FROM error_logs WHERE service_name LIKE 'test.tracking%';")
|
||||
|
||||
|
||||
async def _log(repo, service, etype, msg):
|
||||
pool = await repo._get_pool()
|
||||
async with pool.acquire() as conn:
|
||||
await conn.execute(
|
||||
"INSERT INTO error_logs (service_name, error_type, error_message) VALUES ($1,$2,$3);",
|
||||
service, etype, msg)
|
||||
|
||||
|
||||
def test_clamping():
|
||||
# Short text is untouched.
|
||||
assert clamp_for_telegram("hello", True) == "hello"
|
||||
|
||||
long_text = "x" * 5000
|
||||
media = clamp_for_telegram(long_text, has_media=True)
|
||||
assert len(media) <= MAX_MEDIA_CAPTION, f"media caption still {len(media)} chars"
|
||||
assert media.endswith("</i>"), "truncation marker missing"
|
||||
|
||||
plain = clamp_for_telegram(long_text, has_media=False)
|
||||
assert len(plain) <= MAX_TEXT_MESSAGE, f"text message still {len(plain)} chars"
|
||||
|
||||
# A message that exactly fits must not be altered.
|
||||
exact = "y" * MAX_MEDIA_CAPTION
|
||||
assert clamp_for_telegram(exact, True) == exact
|
||||
|
||||
|
||||
async def run_tests():
|
||||
test_clamping()
|
||||
|
||||
await init_db()
|
||||
repo = Repository()
|
||||
await _cleanup(repo)
|
||||
|
||||
await _log(repo, SVC, "ValueError", "bad value one")
|
||||
await _log(repo, SVC, "ValueError", "bad value two")
|
||||
await _log(repo, SVC, "KeyError", "missing key")
|
||||
await _log(repo, SVC + "_b", "ValueError", "other service")
|
||||
|
||||
groups = await repo.get_open_error_summary(limit=50)
|
||||
mine = {(g["service_name"], g["error_type"]): g for g in groups if g["service_name"].startswith("test.tracking")}
|
||||
assert len(mine) == 3, f"expected 3 groups, got {list(mine)}"
|
||||
assert mine[(SVC, "ValueError")]["occurrences"] == 2
|
||||
assert mine[(SVC, "ValueError")]["last_message"] == "bad value two", "last_message should be the newest"
|
||||
|
||||
# Resolving one group leaves the others untouched.
|
||||
fixed = await repo.resolve_errors(service_name=SVC, error_type="ValueError", note="fixed in tests")
|
||||
assert fixed == 2, f"expected 2 rows resolved, got {fixed}"
|
||||
groups = await repo.get_open_error_summary(limit=50)
|
||||
mine = {(g["service_name"], g["error_type"]) for g in groups if g["service_name"].startswith("test.tracking")}
|
||||
assert (SVC, "ValueError") not in mine
|
||||
assert (SVC, "KeyError") in mine and (SVC + "_b", "ValueError") in mine
|
||||
|
||||
# Resolving again is a no-op, not a double count.
|
||||
assert await repo.resolve_errors(service_name=SVC, error_type="ValueError") == 0
|
||||
|
||||
# The gauge reflects what is still open.
|
||||
bot = AdminBotService(repo=repo, review_channel_id=0, admin_user_ids=[1])
|
||||
total = await bot.refresh_error_metrics()
|
||||
assert total == await repo.count_open_errors(), "gauge total must match the open count"
|
||||
|
||||
# The report renders and caches groups for the callback buttons.
|
||||
text, buttons = await bot._render_error_report()
|
||||
assert "KeyError" in text
|
||||
assert bot.error_group_cache, "groups must be cached for errfix callbacks"
|
||||
assert any(b.data.decode().startswith("errfix:") for row in buttons for b in row)
|
||||
|
||||
await _cleanup(repo)
|
||||
await close_db_pool()
|
||||
print("All error-tracking tests passed successfully!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(run_tests())
|
||||
Reference in New Issue
Block a user