From 7df074fba96eb0a99c2ebbf3d11c1d992e7e042a Mon Sep 17 00:00:00 2001 From: Thorsten Date: Fri, 19 Apr 2024 17:44:02 +0200 Subject: [PATCH] implement local message broker for testing with some crude hack to replicate amqp matching --- tests/test_integration/bot_test_utils.py | 37 ++++++++++++++++++++++-- tests/test_integration/conftest.py | 13 ++++++++- tests/test_integration/test_plugins.py | 12 ++++---- 3 files changed, 53 insertions(+), 9 deletions(-) diff --git a/tests/test_integration/bot_test_utils.py b/tests/test_integration/bot_test_utils.py index e102f6e..64fca83 100644 --- a/tests/test_integration/bot_test_utils.py +++ b/tests/test_integration/bot_test_utils.py @@ -1,12 +1,15 @@ # -*- coding: utf-8 -*- +import re from dataclasses import dataclass from unittest import mock import pika.channel import pika.spec +import pytest from slixmpp import Message, JID from distbot.bot.bot import Bot +from distbot.bot.worker import Worker @dataclass @@ -26,7 +29,7 @@ class AMQPCallback: return AMQPCallback(ch=channel, method=method, properties=props, body=body) -def inject_callback(msg: str, sender: str, bot_nick: str): +def get_amqp_callback_params(msg: str, sender: str, bot_nick: str) -> AMQPCallback: # Message interface is used like a dict # msg_mock = mock.MagicMock(spec=Message) msg_mock = { @@ -35,4 +38,34 @@ def inject_callback(msg: str, sender: str, bot_nick: str): "type": "groupchat", "body": msg, } - return AMQPCallback.from_message(msg=msg_mock, bot_nick=bot_nick).__dict__ + return AMQPCallback.from_message(msg=msg_mock, bot_nick=bot_nick) + +class LocalMessageBroker: + def __init__(self): + self.recipients = [] + + def add_recipient(self, recipient: Worker): + self.recipients.append(recipient) + + def route_message(self, msg: str, sender: str, bot_nick: str): + params = get_amqp_callback_params(msg, sender, bot_nick) + for recipient in self.recipients: + for binding_key in recipient.binding_keys: + if self.matches(binding_key, params.method.routing_key): + recipient.callback(**params.__dict__) + break + + @staticmethod + def matches(binding_key, message_routing_key): + regex = binding_key.replace('.', '\\.').replace('*', '[^.]+').replace('#', '.*') + # fix .# leading to \..* + regex = re.sub(r"\\.\.\*$", ".*", regex) + return re.fullmatch(regex, message_routing_key) is not None + + +@pytest.mark.parametrize("bind,msg_routing_key", [ + ("nick.dice.#", "nick.dice"), + ("nick.dice.#", "nick.dice.2"), +]) +def test_local_message_broker(bind, msg_routing_key): + assert LocalMessageBroker.matches(bind, msg_routing_key) \ No newline at end of file diff --git a/tests/test_integration/conftest.py b/tests/test_integration/conftest.py index 015e2ce..f762689 100644 --- a/tests/test_integration/conftest.py +++ b/tests/test_integration/conftest.py @@ -6,6 +6,7 @@ import pytest from distbot.bot import worker from distbot.plugins import openai +from bot_test_utils import LocalMessageBroker @pytest.fixture(name="send_action") @@ -37,4 +38,14 @@ def mock_openai(monkeypatch): @pytest.fixture def bot_nick(): - return "doofibot" \ No newline at end of file + return "doofibot" + +@pytest.fixture(scope="function") +def message_broker(): + lmb = LocalMessageBroker() + # TODO another fixture maybe + # from distbot.minijobber.run import PLUGIN_MODULES + # for plugin_bunch in PLUGIN_MODULES.values(): + # for plugin in plugin_bunch: + # lmb.add_recipient(plugin(None)) + return lmb diff --git a/tests/test_integration/test_plugins.py b/tests/test_integration/test_plugins.py index 1d1955d..a83f8c3 100644 --- a/tests/test_integration/test_plugins.py +++ b/tests/test_integration/test_plugins.py @@ -5,7 +5,6 @@ import pytest from distbot.common.action import Action from distbot.plugins.basic import Dice, Choose -from tests.test_integration.bot_test_utils import inject_callback """ Goal: have a parameterized pipeline of @@ -17,15 +16,15 @@ It should 2. [somehow] replicate matching of rabbitmq topic 3. invoke Worker-interface with the raw body? using Worker.callback(..) -TODO: plugins.resolve(message).callback() for (2) """ @pytest.mark.parametrize(["dice_count"], argvalues=( [None], [1], [3], [100] )) -def test_dice(send_action, randomint, bot_nick, dice_count): +def test_dice(send_action, randomint, bot_nick, dice_count, message_broker): args = f" {dice_count}" if dice_count else "" - Dice(None).callback(**inject_callback(msg=f"{bot_nick}: dice" + args, sender="you", bot_nick=bot_nick)) + message_broker.add_recipient(Dice(None)) + message_broker.route_message(msg=f"{bot_nick}: dice" + args, sender="you", bot_nick=bot_nick) result = { "None": "rolling a dice for you: ⚂ (​3​)", @@ -46,10 +45,11 @@ def test_dice(send_action, randomint, bot_nick, dice_count): [2, "do_something do_nothing"], [3, """ "to do something" "to do nothing" """], )) -def test_quoted_choose(send_action, mock_openai, randomchoice, bot_nick, i, choices, monkeypatch): +def test_quoted_choose(send_action, mock_openai, randomchoice, bot_nick, i, choices, monkeypatch, message_broker): with mock.patch.object(Choose, "weighted_choice") as mock_weighted_choice: mock_weighted_choice.side_effect = lambda l: l[0][0] # first option wins - Choose(None).callback(**inject_callback(msg=f"{bot_nick}: choose " + choices, sender="you", bot_nick=bot_nick)) + message_broker.add_recipient(Choose(None)) + message_broker.route_message(msg=f"{bot_nick}: choose " + choices, sender="you", bot_nick=bot_nick) result = { 0: "you: Yes.", -- 2.39.2