Files
vibe-bot/vibe_bot/tests/test_logging.py
T
2026-08-19 13:16:43 -04:00

187 lines
5.2 KiB
Python

"""No-content logging tests.
A recognizable secret string is seeded into message content, image prompts,
and bot personalities; the logs captured at DEBUG must never contain it,
while metadata (bot name, user id, message id, counts) must.
"""
from __future__ import annotations
import asyncio
import base64
import logging
from typing import Any
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from vibe_bot.services.chat_service import ChatService
from vibe_bot.services.image_service import ImageService
from vibe_bot.tests._helpers import invoke
SECRET = "SECRET-DO-NOT-LOG-12345"
def _registry() -> MagicMock:
"""A mock ToolRegistry."""
reg = MagicMock()
reg.to_openai_tools.return_value = []
reg.execute.return_value = "tool result"
return reg
def _assert_no_secret(
caplog: pytest.LogCaptureFixture,
*metadata: str,
) -> None:
"""The secret must be absent from every record; the metadata must be present."""
for record in caplog.records:
assert (
SECRET not in record.getMessage()
), f"Secret leaked in log record: {record.getMessage()!r}"
assert SECRET not in caplog.text
for token in metadata:
assert token in caplog.text, f"Expected metadata {token!r} missing from logs"
def test_chat_service_logs_metadata_not_content(
caplog: pytest.LogCaptureFixture,
chat_db: Any,
mock_ctx: MagicMock,
) -> None:
"""A full chat turn (RAG + persist + reply) never logs the message content."""
caplog.set_level(logging.DEBUG)
svc = ChatService(chat_db, _registry())
with patch(
"vibe_bot.llm_client.chat_completion_with_tools",
new=AsyncMock(return_value="A perfectly fine response."),
):
asyncio.run(
svc.handle(
mock_ctx,
bot_name="alfred",
message=f"please remember {SECRET} forever",
system_prompt="you are a butler",
response_prefix="alfred response",
)
)
_assert_no_secret(caplog, "alfred", "12345")
def test_add_message_logs_metadata_not_content(
caplog: pytest.LogCaptureFixture,
chat_db: Any,
) -> None:
"""add_message (with embedding) never logs the stored content."""
caplog.set_level(logging.DEBUG)
assert chat_db.add_message(
message_id="msg-1",
user_id="12345",
username="testuser",
content=f"User: tell me about {SECRET}",
bot_name="alfred",
channel_id="channel-1",
guild_id="guild-1",
)
assert chat_db.add_message(
message_id="msg-1_response",
user_id="bot-123",
username="test-bot",
content=f"the bot knew {SECRET}",
bot_name="alfred",
channel_id="channel-1",
guild_id="guild-1",
role="assistant",
embed=False,
)
_assert_no_secret(caplog, "msg-1", "12345")
def test_history_lookups_log_metadata_not_content(
caplog: pytest.LogCaptureFixture,
chat_db: Any,
) -> None:
"""get_user_history / get_bot_history never log message or response content."""
caplog.set_level(logging.DEBUG)
chat_db.add_message(
message_id="msg-2",
user_id="12345",
username="testuser",
content=f"User: what is {SECRET}",
bot_name="alfred",
channel_id="channel-1",
guild_id="guild-1",
)
chat_db.add_message(
message_id="msg-2_response",
user_id="bot-123",
username="test-bot",
content=f"it is {SECRET}, clearly",
bot_name="alfred",
channel_id="channel-1",
guild_id="guild-1",
role="assistant",
embed=False,
)
user_history = chat_db.get_user_history("12345", limit=5)
bot_history = chat_db.get_bot_history("alfred", limit=5)
assert len(user_history) == 1
assert len(bot_history) == 1
_assert_no_secret(caplog, "msg-2", "alfred")
def test_doodlebob_logs_metadata_not_content(
caplog: pytest.LogCaptureFixture,
mock_ctx: MagicMock,
) -> None:
"""A doodlebob generation never logs the prompt or the derived image prompt."""
caplog.set_level(logging.DEBUG)
db = MagicMock()
db.get_image_generation_time_estimate.return_value = 12.0
svc = ImageService(db, MagicMock())
with (
patch(
"vibe_bot.llm_client.chat_completion_instruct",
new=AsyncMock(
side_effect=[
"portrait",
f"a very detailed painting of {SECRET}",
"pass",
]
),
),
patch(
"vibe_bot.llm_client.image_generation",
new=AsyncMock(return_value=base64.b64encode(b"img").decode()),
),
):
asyncio.run(svc.generate(mock_ctx, message=f"draw {SECRET} on the moon"))
_assert_no_secret(caplog, "Doodlebob", "12345")
def test_custom_bot_creation_logs_metadata_not_personality(
caplog: pytest.LogCaptureFixture,
bot: Any,
mock_ctx: MagicMock,
) -> None:
"""!custom-bot never logs the personality text."""
caplog.set_level(logging.DEBUG)
invoke(
bot,
"custom-bot",
mock_ctx,
"secretbot",
personality=f"a butler who knows {SECRET}",
)
_assert_no_secret(caplog, "secretbot", "12345")