2018-01-05 16:22:38 -05:00
|
|
|
from unittest.mock import patch
|
2017-05-24 23:12:57 -04:00
|
|
|
from unittest import TestCase
|
|
|
|
|
2017-12-11 09:12:43 -05:00
|
|
|
from typing import List, Dict, Any, Tuple
|
2017-05-26 00:58:19 -04:00
|
|
|
|
2017-12-11 10:05:06 -05:00
|
|
|
from zulip_bots.request_test_lib import (
|
|
|
|
mock_http_conversation,
|
|
|
|
)
|
|
|
|
|
2017-11-30 17:09:28 -05:00
|
|
|
from zulip_bots.simple_lib import (
|
|
|
|
SimpleStorage,
|
|
|
|
SimpleMessageServer,
|
|
|
|
)
|
|
|
|
|
2017-12-11 10:23:24 -05:00
|
|
|
from zulip_bots.test_file_utils import (
|
|
|
|
get_bot_message_handler,
|
|
|
|
read_bot_fixture_data,
|
|
|
|
)
|
|
|
|
|
2017-11-30 17:09:28 -05:00
|
|
|
class StubBotHandler:
|
|
|
|
def __init__(self):
|
|
|
|
# type: () -> None
|
|
|
|
self.storage = SimpleStorage()
|
|
|
|
self.message_server = SimpleMessageServer()
|
2017-12-01 14:49:47 -05:00
|
|
|
self.reset_transcript()
|
|
|
|
|
|
|
|
def reset_transcript(self):
|
|
|
|
# type: () -> None
|
|
|
|
self.transcript = [] # type: List[Tuple[str, Dict[str, Any]]]
|
2017-11-30 17:09:28 -05:00
|
|
|
|
|
|
|
def send_message(self, message):
|
|
|
|
# type: (Dict[str, Any]) -> Dict[str, Any]
|
2017-12-01 14:49:47 -05:00
|
|
|
self.transcript.append(('send_message', message))
|
2017-11-30 17:09:28 -05:00
|
|
|
return self.message_server.send(message)
|
|
|
|
|
|
|
|
def send_reply(self, message, response):
|
|
|
|
# type: (Dict[str, Any], str) -> Dict[str, Any]
|
|
|
|
response_message = dict(
|
|
|
|
content=response
|
|
|
|
)
|
2017-12-01 14:49:47 -05:00
|
|
|
self.transcript.append(('send_reply', response_message))
|
2017-11-30 17:09:28 -05:00
|
|
|
return self.message_server.send(response_message)
|
|
|
|
|
|
|
|
def update_message(self, message):
|
|
|
|
# type: (Dict[str, Any]) -> None
|
|
|
|
self.message_server.update(message)
|
|
|
|
|
2017-12-30 15:40:24 -05:00
|
|
|
class BotQuitException(Exception):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def quit(self, message = ""):
|
|
|
|
# type: (str) -> None
|
|
|
|
raise self.BotQuitException()
|
|
|
|
|
2017-12-06 22:41:19 -05:00
|
|
|
def get_config_info(self, bot_name, optional=False):
|
|
|
|
# type: (str, bool) -> Dict[str, Any]
|
2017-12-23 00:41:17 -05:00
|
|
|
return {}
|
2017-12-06 22:41:19 -05:00
|
|
|
|
2017-12-01 15:53:03 -05:00
|
|
|
def unique_reply(self):
|
|
|
|
# type: () -> Dict[str, Any]
|
|
|
|
responses = [
|
|
|
|
message
|
|
|
|
for (method, message)
|
|
|
|
in self.transcript
|
|
|
|
if method == 'send_reply'
|
|
|
|
]
|
|
|
|
self.ensure_unique_response(responses)
|
|
|
|
return responses[0]
|
|
|
|
|
2017-12-01 14:49:47 -05:00
|
|
|
def unique_response(self):
|
2017-11-30 17:09:28 -05:00
|
|
|
# type: () -> Dict[str, Any]
|
2017-12-01 14:49:47 -05:00
|
|
|
responses = [
|
|
|
|
message
|
|
|
|
for (method, message)
|
|
|
|
in self.transcript
|
|
|
|
]
|
|
|
|
self.ensure_unique_response(responses)
|
|
|
|
return responses[0]
|
|
|
|
|
|
|
|
def ensure_unique_response(self, responses):
|
|
|
|
# type: (List[Dict[str, Any]]) -> None
|
|
|
|
if not responses:
|
|
|
|
raise Exception('The bot is not responding for some reason.')
|
|
|
|
if len(responses) > 1:
|
|
|
|
raise Exception('The bot is giving too many responses for some reason.')
|
2017-11-30 17:09:28 -05:00
|
|
|
|
2017-12-14 05:24:11 -05:00
|
|
|
class BotTestCase(TestCase):
|
2017-11-30 17:09:28 -05:00
|
|
|
bot_name = ''
|
|
|
|
|
2017-12-10 09:35:51 -05:00
|
|
|
def _get_handlers(self):
|
|
|
|
# type: () -> Tuple[Any, StubBotHandler]
|
2017-12-08 17:34:21 -05:00
|
|
|
bot = get_bot_message_handler(self.bot_name)
|
|
|
|
bot_handler = StubBotHandler()
|
|
|
|
|
|
|
|
if hasattr(bot, 'initialize'):
|
|
|
|
bot.initialize(bot_handler)
|
|
|
|
|
2017-12-10 09:35:51 -05:00
|
|
|
return (bot, bot_handler)
|
|
|
|
|
|
|
|
def get_response(self, message):
|
|
|
|
# type: (Dict[str, Any]) -> Dict[str, Any]
|
|
|
|
bot, bot_handler = self._get_handlers()
|
2017-12-08 17:34:21 -05:00
|
|
|
bot_handler.reset_transcript()
|
|
|
|
bot.handle_message(message, bot_handler)
|
|
|
|
return bot_handler.unique_response()
|
|
|
|
|
2017-12-11 10:39:48 -05:00
|
|
|
def make_request_message(self, content):
|
|
|
|
# type: (str) -> Dict[str, Any]
|
|
|
|
'''
|
|
|
|
This is mostly used internally but
|
|
|
|
tests can override this behavior by
|
|
|
|
mocking/subclassing.
|
|
|
|
'''
|
2017-12-01 15:53:03 -05:00
|
|
|
message = dict(
|
2017-12-11 10:39:48 -05:00
|
|
|
display_recipient='foo_stream',
|
2017-12-01 15:53:03 -05:00
|
|
|
sender_email='foo@example.com',
|
2017-12-10 18:25:57 -05:00
|
|
|
sender_full_name='Foo Test User',
|
2017-12-09 15:11:18 -05:00
|
|
|
sender_id='123',
|
2017-12-11 10:39:48 -05:00
|
|
|
content=content,
|
2017-12-01 15:53:03 -05:00
|
|
|
)
|
2017-12-11 10:39:48 -05:00
|
|
|
return message
|
|
|
|
|
2018-01-10 11:36:05 -05:00
|
|
|
def get_reply_dict(self, request):
|
|
|
|
# type: (str) -> Dict[str, Any]
|
2017-12-11 10:39:48 -05:00
|
|
|
bot, bot_handler = self._get_handlers()
|
|
|
|
message = self.make_request_message(request)
|
2017-12-01 15:53:03 -05:00
|
|
|
bot_handler.reset_transcript()
|
|
|
|
bot.handle_message(message, bot_handler)
|
|
|
|
reply = bot_handler.unique_reply()
|
2018-01-10 11:36:05 -05:00
|
|
|
return reply
|
|
|
|
|
|
|
|
def verify_reply(self, request, response):
|
|
|
|
# type: (str, str) -> None
|
|
|
|
reply = self.get_reply_dict(request)
|
2017-12-01 15:53:03 -05:00
|
|
|
self.assertEqual(response, reply['content'])
|
|
|
|
|
2017-11-30 17:09:28 -05:00
|
|
|
def verify_dialog(self, conversation):
|
|
|
|
# type: (List[Tuple[str, str]]) -> None
|
|
|
|
|
|
|
|
# Start a new message handler for the full conversation.
|
2017-12-10 09:35:51 -05:00
|
|
|
bot, bot_handler = self._get_handlers()
|
2017-11-30 17:09:28 -05:00
|
|
|
|
|
|
|
for (request, expected_response) in conversation:
|
2017-12-11 10:39:48 -05:00
|
|
|
message = self.make_request_message(request)
|
2017-12-01 14:49:47 -05:00
|
|
|
bot_handler.reset_transcript()
|
2017-11-30 17:09:28 -05:00
|
|
|
bot.handle_message(message, bot_handler)
|
2017-12-01 14:49:47 -05:00
|
|
|
response = bot_handler.unique_response()
|
2017-11-30 17:09:28 -05:00
|
|
|
self.assertEqual(expected_response, response['content'])
|
|
|
|
|
2017-11-30 19:24:20 -05:00
|
|
|
def test_bot_usage(self):
|
|
|
|
# type: () -> None
|
|
|
|
bot = get_bot_message_handler(self.bot_name)
|
|
|
|
self.assertNotEqual(bot.usage(), '')
|
|
|
|
|
2017-12-09 14:30:39 -05:00
|
|
|
def test_bot_responds_to_empty_message(self) -> None:
|
2017-12-11 10:39:48 -05:00
|
|
|
message = self.make_request_message('')
|
2017-12-10 09:12:06 -05:00
|
|
|
|
|
|
|
# get_response will fail if we don't respond at all
|
|
|
|
response = self.get_response(message)
|
|
|
|
|
|
|
|
# we also want a non-blank response
|
|
|
|
self.assertTrue(len(response['content']) >= 1)
|
2017-12-09 14:30:39 -05:00
|
|
|
|
2017-12-01 15:49:40 -05:00
|
|
|
def mock_http_conversation(self, test_name):
|
|
|
|
# type: (str) -> Any
|
|
|
|
assert test_name is not None
|
|
|
|
http_data = read_bot_fixture_data(self.bot_name, test_name)
|
|
|
|
return mock_http_conversation(http_data)
|
|
|
|
|
2017-12-06 22:41:19 -05:00
|
|
|
def mock_config_info(self, config_info):
|
|
|
|
# type: (Dict[str, str]) -> Any
|
|
|
|
return patch('zulip_bots.test_lib.StubBotHandler.get_config_info', return_value=config_info)
|