import os import unittest from unittest.mock import AsyncMock, MagicMock, Mock, PropertyMock, mock_open, patch import toml from discord import Message, TextChannel, User from fjerkroa_bot import FjerkroaBot from fjerkroa_bot.ai_responder import AIMessage, AIResponse class TestBotBase(unittest.IsolatedAsyncioTestCase): async def asyncSetUp(self): self.mock_response = Mock() self.mock_response.choices = [Mock(text="Nice day today!")] self.config_data = { "openai-token": os.environ.get("OPENAI_TOKEN", "test"), "model": "gpt-3.5-turbo", "max-tokens": 1024, "temperature": 0.9, "top-p": 1.0, "presence-penalty": 1.0, "frequency-penalty": 1.0, "history-limit": 10, "system": "You are an smart AI", "additional-responders": [], } self.history_data = [] with ( patch.object(FjerkroaBot, "load_config", new=lambda s, c: self.config_data), patch.object(FjerkroaBot, "user", new_callable=PropertyMock) as mock_user, ): mock_user.return_value = MagicMock(spec=User) mock_user.return_value.id = 12 self.bot = FjerkroaBot("config.toml") self.bot.staff_channel = AsyncMock(spec=TextChannel) self.bot.staff_channel.send = AsyncMock() self.bot.welcome_channel = AsyncMock(spec=TextChannel) self.bot.welcome_channel.send = AsyncMock() self.bot.airesponder.config = self.config_data def create_message(self, message: str) -> Message: message = MagicMock(spec=Message) message.content = "Hello, how are you?" message.author = AsyncMock(spec=User) message.author.name = "Lala" message.author.id = 123 message.author.bot = False message.channel = AsyncMock(spec=TextChannel) message.channel.send = AsyncMock() return message class TestFunctionality(TestBotBase): def test_load_config(self) -> None: with patch("builtins.open", mock_open(read_data=toml.dumps(self.config_data))): result = FjerkroaBot.load_config("config.toml") self.assertEqual(result, self.config_data) async def test_message_lings(self) -> None: request = AIMessage( "Lala", "Hello there!", "chat", False, ) message = { "answer": "Test [Link](https://www.example.com/test)", "answer_needed": True, "channel": "chat", "staff": None, "picture": None, "hack": False, } expected = AIResponse("Test https://www.example.com/test", True, "chat", None, None, False, False) self.assertEqual(str(await self.bot.airesponder.post_process(request, message)), str(expected)) message = { "answer": "Test @[Link](https://www.example.com/test)", "answer_needed": True, "channel": "chat", "staff": None, "picture": None, "hack": False, } expected = AIResponse("Test Link", True, "chat", None, None, False, False) self.assertEqual(str(await self.bot.airesponder.post_process(request, message)), str(expected)) message = { "answer": "Test [Link](https://www.example.com/test) and [Link2](https://xxx) lala", "answer_needed": True, "channel": "chat", "staff": None, "picture": None, "hack": False, } expected = AIResponse("Test https://www.example.com/test and https://xxx lala", True, "chat", None, None, False, False) self.assertEqual(str(await self.bot.airesponder.post_process(request, message)), str(expected)) async def test_on_message_stort_path(self) -> None: message = self.create_message("Hello there! How are you?") message.author.name = "madeup_name" message.channel.name = "some_channel" # type: ignore self.bot.config["short-path"] = [[r"some.*", r"madeup.*"]] await self.bot.on_message(message) self.assertEqual( self.bot.airesponder.history[-1]["content"], '{"user": "madeup_name", "message": "Hello, how are you?",' ' "channel": "some_channel", "direct": false, "historise_question": true}', ) def test_update_history_trims_to_limit(self): self.bot.airesponder.update_history({"content": '{"q": "What\'s your name?"}'}, {"content": '{"a": "AI"}'}, 10) self.assertEqual(len(self.bot.airesponder.history), 2) self.bot.airesponder.update_history({"content": '{"q1": "Q1"}'}, {"content": '{"a1": "A1"}'}, 2) self.bot.airesponder.update_history({"content": '{"q2": "Q2"}'}, {"content": '{"a2": "A2"}'}, 2) self.assertEqual(len(self.bot.airesponder.history), 2) # File persistence moved to the SQLite store — covered by PER-01 (SPEC-009) if __name__ == "__mait__": unittest.main()