bot lib tests: Move tests of bot library to tests/.

This makes it clearer which files are for writing bot tests
vs tests of the infrastructure.
This commit is contained in:
neiljp (Neil Pilgrim) 2018-05-30 14:53:10 -07:00 committed by Tim Abbott
parent 9e6b4edee6
commit 4c8d86c1d9
4 changed files with 4 additions and 4 deletions

View file

@ -0,0 +1,168 @@
from unittest import TestCase
from unittest.mock import MagicMock, patch, ANY, create_autospec
from zulip_bots.lib import (
ExternalBotHandler,
StateHandler,
run_message_handler_for_bot,
)
class FakeClient:
def __init__(self, *args, **kwargs):
self.storage = dict()
def get_profile(self):
return dict(
user_id='alice',
full_name='Alice',
email='alice@example.com',
)
def update_storage(self, payload):
new_data = payload['storage']
self.storage.update(new_data)
return dict(
result='success',
)
def get_storage(self):
return dict(
result='success',
storage=self.storage,
)
def send_message(self, message):
pass
class FakeBotHandler:
def usage(self):
return '''
This is a fake bot handler that is used
to spec BotHandler mocks.
'''
def handle_message(self, message, bot_handler):
pass
class LibTest(TestCase):
def test_basics(self):
client = FakeClient()
handler = ExternalBotHandler(
client=client,
root_dir=None,
bot_details=None,
bot_config_file=None
)
message = None
handler.send_message(message)
def test_state_handler(self):
client = FakeClient()
state_handler = StateHandler(client)
state_handler.put('key', [1, 2, 3])
val = state_handler.get('key')
self.assertEqual(val, [1, 2, 3])
# force us to get non-cached values
state_handler = StateHandler(client)
val = state_handler.get('key')
self.assertEqual(val, [1, 2, 3])
def test_state_handler(self):
client = MagicMock()
state_handler = StateHandler(client)
client.get_storage.assert_not_called()
client.update_storage = MagicMock(return_value=dict(result='success'))
state_handler.put('key', [1, 2, 3])
client.update_storage.assert_called_with(dict(storage=dict(key='[1, 2, 3]')))
val = state_handler.get('key')
client.get_storage.assert_not_called()
self.assertEqual(val, [1, 2, 3])
# force us to get non-cached values
client.get_storage = MagicMock(return_value=dict(
result='success',
storage=dict(non_cached_key='[5]')))
val = state_handler.get('non_cached_key')
client.get_storage.assert_called_with(keys=('non_cached_key',))
self.assertEqual(val, [5])
# value must already be cached
client.get_storage = MagicMock()
val = state_handler.get('non_cached_key')
client.get_storage.assert_not_called()
self.assertEqual(val, [5])
def test_send_reply(self):
client = FakeClient()
profile = client.get_profile()
handler = ExternalBotHandler(
client=client,
root_dir=None,
bot_details=None,
bot_config_file=None
)
to = {'email': 'Some@User'}
expected = [({'type': 'private', 'display_recipient': [to]},
{'type': 'private', 'to': [to['email']]}),
({'type': 'private', 'display_recipient': [to, profile]},
{'type': 'private', 'to': [to['email']]}),
({'type': 'stream', 'display_recipient': 'Stream name', 'subject': 'Topic'},
{'type': 'stream', 'to': 'Stream name', 'subject': 'Topic'})]
response_text = "Response"
for test in expected:
client.send_message = MagicMock()
handler.send_reply(test[0], response_text)
client.send_message.assert_called_once_with(dict(test[1], content=response_text))
def test_content_and_full_content(self):
client = FakeClient()
profile = client.get_profile()
handler = ExternalBotHandler(
client=client,
root_dir=None,
bot_details=None,
bot_config_file=None
)
to = {'email': 'Some@User'}
def test_run_message_handler_for_bot(self):
with patch('zulip_bots.lib.Client', new=FakeClient) as fake_client:
mock_lib_module = MagicMock()
# __file__ is not mocked by MagicMock(), so we assign a mock value manually.
mock_lib_module.__file__ = "foo"
mock_bot_handler = create_autospec(FakeBotHandler)
mock_lib_module.handler_class.return_value = mock_bot_handler
def call_on_each_event_mock(self, callback, event_types=None, narrow=None):
def test_message(message, flags):
event = {'message': message,
'flags': flags,
'type': 'message'}
callback(event)
# In the following test, expected_message is the dict that we expect
# to be passed to the bot's handle_message function.
original_message = {'content': '@**Alice** bar',
'type': 'stream'}
expected_message = {'type': 'stream',
'content': 'bar',
'full_content': '@**Alice** bar'}
test_message(original_message, {'mentioned'})
mock_bot_handler.handle_message.assert_called_with(
message=expected_message,
bot_handler=ANY)
fake_client.call_on_each_event = call_on_each_event_mock.__get__(
fake_client, fake_client.__class__)
run_message_handler_for_bot(lib_module=mock_lib_module,
quiet=True,
config_file=None,
bot_config_file=None,
bot_name='testbot')

View file

@ -0,0 +1,89 @@
#!/usr/bin/env python3
import os
import sys
import zulip_bots.run
from zulip_bots.lib import extract_query_without_mention
import unittest
from typing import Optional
from unittest import TestCase
from unittest import mock
from unittest.mock import patch
class TestDefaultArguments(TestCase):
our_dir = os.path.dirname(__file__)
path_to_bot = os.path.abspath(os.path.join(our_dir, '../bots/giphy/giphy.py'))
@patch('sys.argv', ['zulip-run-bot', 'giphy', '--config-file', '/foo/bar/baz.conf'])
@patch('zulip_bots.run.run_message_handler_for_bot')
def test_argument_parsing_with_bot_name(self, mock_run_message_handler_for_bot: mock.Mock) -> None:
with patch('zulip_bots.run.exit_gracefully_if_zulip_config_file_does_not_exist'):
zulip_bots.run.main()
mock_run_message_handler_for_bot.assert_called_with(bot_name='giphy',
config_file='/foo/bar/baz.conf',
bot_config_file=None,
lib_module=mock.ANY,
quiet=False)
@patch('sys.argv', ['zulip-run-bot', path_to_bot, '--config-file', '/foo/bar/baz.conf'])
@patch('zulip_bots.run.run_message_handler_for_bot')
def test_argument_parsing_with_bot_path(self, mock_run_message_handler_for_bot: mock.Mock) -> None:
with patch('zulip_bots.run.exit_gracefully_if_zulip_config_file_does_not_exist'):
zulip_bots.run.main()
mock_run_message_handler_for_bot.assert_called_with(
bot_name='giphy',
config_file='/foo/bar/baz.conf',
bot_config_file=None,
lib_module=mock.ANY,
quiet=False)
def test_adding_bot_parent_dir_to_sys_path_when_bot_name_specified(self) -> None:
bot_name = 'any_bot_name'
expected_bot_dir_path = os.path.join(
os.path.dirname(zulip_bots.run.__file__),
'bots',
bot_name
)
self._test_adding_bot_parent_dir_to_sys_path(bot_qualifier=bot_name, bot_dir_path=expected_bot_dir_path)
@patch('os.path.isfile', return_value=True)
def test_adding_bot_parent_dir_to_sys_path_when_bot_path_specified(self, mock_os_path_isfile: mock.Mock) -> None:
bot_path = '/path/to/bot'
expected_bot_dir_path = '/path/to'
self._test_adding_bot_parent_dir_to_sys_path(bot_qualifier=bot_path, bot_dir_path=expected_bot_dir_path)
def _test_adding_bot_parent_dir_to_sys_path(self, bot_qualifier, bot_dir_path):
# type: (str, str) -> None
with patch('sys.argv', ['zulip-run-bot', bot_qualifier, '--config-file', '/path/to/config']):
with patch('zulip_bots.run.import_module_from_source', return_value=mock.Mock()):
with patch('zulip_bots.run.run_message_handler_for_bot'):
with patch('zulip_bots.run.exit_gracefully_if_zulip_config_file_does_not_exist'):
zulip_bots.run.main()
self.assertIn(bot_dir_path, sys.path)
class TestBotLib(TestCase):
def test_extract_query_without_mention(self) -> None:
def test_message(name: str, message: str, expected_return: Optional[str]) -> None:
mock_client = mock.MagicMock()
mock_client.full_name = name
mock_message = {'content': message}
self.assertEqual(expected_return, extract_query_without_mention(mock_message, mock_client))
test_message("xkcd", "@**xkcd**foo", "foo")
test_message("xkcd", "@**xkcd** foo", "foo")
test_message("xkcd", "@**xkcd** foo bar baz", "foo bar baz")
test_message("xkcd", "@**xkcd** foo bar baz", "foo bar baz")
test_message("xkcd", "@**xkcd** 123_) (/&%) +}}}l", "123_) (/&%) +}}}l")
test_message("brokenmention", "@**brokenmention* foo", None)
test_message("nomention", "foo", None)
test_message("Max Mustermann", "@**Max Mustermann** foo", "foo")
test_message("Max (Mustermann)#(*$&12]\]", "@**Max (Mustermann)#(*$&12]\]** foo", "foo")
if __name__ == '__main__':
unittest.main()