def test_handle_post(self): # Create an initialized plugin so its listeners are registered driver = Driver() plugin = ExamplePlugin().initialize(driver) # Construct a handler with it handler = EventHandler(driver, Settings(), plugins=[plugin]) # Mock the call_function of the plugin so we can make some asserts async def mock_call_function(function, message, groups): # This is the regexp that we're trying to trigger assert function.matcher.pattern == "sleep ([0-9]+)" assert message.text == "sleep 5" # username should be stripped off assert groups == ["5"] # arguments should be matched and passed explicitly with mock.patch.object( plugin, "call_function", wraps=mock_call_function ) as mocked: # Transform the default message into a raw post event so we can pass it new_body = create_message(text="@my_username sleep 5").body.copy() new_body["data"]["post"] = json.dumps(new_body["data"]["post"]) new_body["data"]["mentions"] = json.dumps(new_body["data"]["mentions"]) asyncio.run(handler._handle_post(new_body)) # Assert the function was called, so we know the asserts succeeded. mocked.assert_called_once()
def test_needs_mention(self): # noqa wrapped = mock.create_autospec(example_listener) wrapped.__qualname__ = "wrapped" f = listen_to("", needs_mention=True)(wrapped) f.plugin = ExamplePlugin().initialize(Driver()) # The default message mentions the specified user ID, so should be called f(create_message(mentions=["qmw86q7qsjriura9jos75i4why"])) wrapped.assert_called_once() wrapped.reset_mock() # No mention, so the function should still only have been called once in total f(create_message(mentions=[])) wrapped.assert_not_called() # But if this is a direct message, we do want to trigger f(create_message(mentions=[], channel_type="D")) wrapped.assert_called_once()
def start_bot(request): lock = FileLock("./bot.lock") try: # We want to run the tests in multiple parallel processes, but launch at most # a single bot. lock.acquire(timeout=0.01) bot = Bot( settings=Settings( MATTERMOST_URL="http://127.0.0.1", BOT_TOKEN="e691u15hajdebcnqpfdceqihcc", MATTERMOST_PORT=8065, SSL_VERIFY=False, WEBHOOK_HOST_ENABLED=True, WEBHOOK_HOST_URL="http://127.0.0.1", WEBHOOK_HOST_PORT=8579, ), plugins=[TestPlugin(), ExamplePlugin(), WebHookExample()], ) def run_bot(): bot.run() # Start the bot now bot_process = Process(target=run_bot) bot_process.start() def stop_bot(): time.sleep(5) bot_process.terminate() lock.release() # Once all tests are finished, stop the bot request.addfinalizer(stop_bot) except TimeoutError: # If the lock times out, it means a bot is already running and we don't need # to do anything here. pass finally: time.sleep(5) # Give the bot some time to start up
def test_init(self, login): # Create some plugins and mock their initialize method so we can check calls plugins = [ExamplePlugin(), TestPlugin()] for plugin in plugins: plugin.initialize = mock.MagicMock() # Create a bot and verify that it gets initialized correctly bot = Bot( settings=Settings(MATTERMOST_URL="test_url.org", BOT_TOKEN="random_token"), plugins=plugins, ) assert bot.driver.options["url"] == "test_url.org" assert bot.driver.options["token"] == "random_token" assert bot.plugins == plugins login.assert_called_once() # Verify that all of the passed plugins were initialized for plugin in plugins: assert plugin.initialize.called_once_with(bot.driver)
def test_allowed_users(self): wrapped = mock.create_autospec(example_listener) wrapped.__qualname__ = "wrapped" # Create a driver with a mocked reply function driver = Driver() def fake_reply(message, text): assert "you do not have permission" in text.lower() driver.reply_to = mock.Mock(wraps=fake_reply) f = listen_to("", allowed_users=["Betty"])(wrapped) f.plugin = ExamplePlugin().initialize(driver) # This is fine, the names are not caps sensitive f(create_message(sender_name="betty")) wrapped.assert_called_once() wrapped.reset_mock() # This is not fine, and we expect the fake reply to be called. f(create_message(sender_name="not_betty")) wrapped.assert_not_called() driver.reply_to.assert_called_once()
def test_click_function(self): @click.command() @click.option("--arg1", type=str, default="nothing") @click.option("--arg2", type=str, default="nothing either") @click.option("-f", "--flag", is_flag=True) def wrapped(self, message, arg1, arg2, flag): return arg1, arg2, flag f = MessageFunction(wrapped, matcher=re.compile("")) # Verify that the arguments are passed and returned correctly assert f(create_message(), "--arg1=yes --arg2=no") == ("yes", "no", False) assert f(create_message(), "-f --arg2=no") == ("nothing", "no", True) # If an incorrect argument is passed, the error and help string should be returned. def mocked_reply(message, response): assert "no such option: --nonexistent-arg" in response assert f.docstring in response f.plugin = ExamplePlugin().initialize(Driver(), Settings()) with mock.patch.object(f.plugin.driver, "reply_to", wraps=mocked_reply) as mock_function: f(create_message(), "-f --arg2=no --nonexistent-arg") mock_function.assert_called_once()
def test_init(self): handler = EventHandler( Driver(), Settings(), plugins=[ExamplePlugin(), WebHookExample()] ) # Test the name matcher regexp assert handler._name_matcher.match("@my_username are you there?") assert not handler._name_matcher.match("@other_username are you there?") # Test that all listeners from the individual plugins are now registered on # the handler for plugin in handler.plugins: for pattern, listener in plugin.message_listeners.items(): assert listener in handler.message_listeners[pattern] for pattern, listener in plugin.webhook_listeners.items(): assert listener in handler.webhook_listeners[pattern] # And vice versa, check that any listeners on the handler come from the # registered plugins for pattern, listeners in handler.message_listeners.items(): for listener in listeners: assert any( [ pattern in plugin.message_listeners and listener in plugin.message_listeners[pattern] for plugin in handler.plugins ] ) for pattern, listeners in handler.webhook_listeners.items(): for listener in listeners: assert any( [ pattern in plugin.webhook_listeners and listener in plugin.webhook_listeners[pattern] for plugin in handler.plugins ] )
def test_ensure_response(self): p = ExamplePlugin().initialize(Driver()) def mock_respond(event, response): event.responded = True # SCENARIO 1: We wrap a function that does not send a web response with mock.patch.object(p.driver, "respond_to_web", wraps=mock_respond) as mocked: f = WebHookFunction(example_webhook_listener, matcher=re.compile("")) f.plugin = p event = create_webhook_event() f(event) # We expect the WebHookFunction to automatically respond NoResponse mocked.assert_called_once_with(event, NoResponse) assert event.responded # SCECNARIO 2: Same scenario as above, but with asyncio async def webhook_function(self, event): pass with mock.patch.object(p.driver, "respond_to_web", wraps=mock_respond) as mocked: f = WebHookFunction(webhook_function, matcher=re.compile("")) f.plugin = p event = create_webhook_event() # Asyncio helper to emulate running from async function async def run(): await f(event) asyncio.run(run()) # We expect the WebHookFunction to automatically respond NoResponse mocked.assert_called_once_with(event, NoResponse) assert event.responded # SCENARIO 3: We wrap a function that does send a web response def webhook_function(self, event): self.driver.respond_to_web(event, "Hello!") with mock.patch.object(p.driver, "respond_to_web", wraps=mock_respond) as mocked: f = WebHookFunction(webhook_function, matcher=re.compile("")) f.plugin = p event = create_webhook_event() f(event) # We expect the WebHookFunction to not respond anything, since the function # itself already responded 'Hello!'. mocked.assert_called_once_with(event, "Hello!") assert event.responded # SCNEARIO 4: Same scenario as above, but with asyncio async def webhook_function(self, event): self.driver.respond_to_web(event, "Hello!") with mock.patch.object(p.driver, "respond_to_web", wraps=mock_respond) as mocked: f = WebHookFunction(webhook_function, matcher=re.compile("")) f.plugin = p event = create_webhook_event() # Asyncio helper to emulate running from async function async def run(): await f(event) asyncio.run(run()) # We expect the WebHookFunction to not respond anything, since the function # itself already responded 'Hello!'. mocked.assert_called_once_with(event, "Hello!") assert event.responded
from mmpy_bot import Bot, ExamplePlugin, Settings, WebHookExample bot = Bot( # Either specify your settings here or use environment variables to override them. # See docker-compose.yml for an example you can use for local development. settings=Settings(), plugins=[ExamplePlugin(), WebHookExample()], # Add your own plugins here. ) bot.run()
def bot(): bot = Bot(plugins=[ExamplePlugin()], settings=Settings(DEBUG=True)) yield bot bot.stop() # if the bot was started, stop it