class TestBot(object): def setup(self): with open('config.yaml.example', 'r') as f: self.object = Bot(config=yaml.load(f), test_mode=True) def teardown(self): self.object = None def test_init(self): assert self.object.version == version assert self.object.commit == 'HEAD' def test_start(self): self.object.start() assert self.object.is_setup is True def test_stop(self): self.object.stop() def test_run_without_start(self): with pytest.raises(NotSetupError) as e: self.object.run() assert 'Bot not setup' in str(e) def test_handler_slack_reconnect_event(self): self.object.reconnect_needed = False self.object._event_team_migration_started(None) assert self.object.reconnect_needed is True
def main(): def sigterm_handler(signum, frame): bot.runnable = False p = argparse.ArgumentParser() p.add_argument('--config', action='store', default='config.yaml', help='Specify a config file (default: config.yaml)') p.add_argument('--test', action='store_true', help='Load plugins and exit') args = p.parse_args() with open(args.config, 'rb') as f: config = yaml.load(f) level = logging.DEBUG if config['debug'] else logging.INFO logging.basicConfig( level=level, format='%(asctime)s %(name)s %(levelname)s: %(message)s') bot = Bot(config, args.test) bot.start() if not args.test: signal.signal(signal.SIGTERM, sigterm_handler) bot.run() bot.stop() if args.test: test_passed = True output = ["Bot Test Results"] metrics = bot.plugin_manager.metrics output.append("Plugins Loaded") for p in bot.plugin_manager.plugins: context = { 'name': type(p).__name__, 'version': '-'.join([p._version, p._commit]), 'load_time': metrics['load_times'][type(p).__name__] } output.append( "{name:<30} {version:<20} {load_time:>7.03f} ms".format( **context)) if len(metrics['plugins_failed']) > 0: output.append("") output.append("Plugins failed to load") for p in metrics['plugins_failed']: output.append(p) output.append("") output.append("Bot startup time: %.03f ms" % bot.metrics['startup_time']) output.append("Plugins: %d total, %d loaded, %d failed" % (metrics['plugins_total'], metrics['plugins_loaded'], len(metrics['plugins_failed']))) if metrics['plugins_total'] != metrics['plugins_loaded']: output.append("") output.append("=== Bot Failed Startup Tests ===") test_passed = False logging.getLogger().info('\n'.join(output)) if not test_passed: sys.exit(1)
def main(): def sigterm_handler(signum, frame): bot.runnable = False p = argparse.ArgumentParser() p.add_argument('--config', action='store', default='config.yaml', help='Specify a config file (default: config.yaml)') p.add_argument('--test', action='store_true', help='Load plugins and exit') args = p.parse_args() with open(args.config, 'rb') as f: config = yaml.load(f) level = logging.DEBUG if config['debug'] else logging.INFO logging.basicConfig(level=level, format='%(asctime)s %(name)s %(levelname)s: %(message)s') bot = Bot(config, args.test) bot.start() if not args.test: signal.signal(signal.SIGTERM, sigterm_handler) bot.run() bot.stop() if args.test: test_passed = True output = ["Bot Test Results"] metrics = bot.plugins.metrics output.append("Plugins Loaded") for p in bot.plugins.plugins: context = { 'name': type(p).__name__, 'version': '-'.join([p._version, p._commit]), 'load_time': metrics['load_times'][type(p).__name__] } output.append("{name:<30} {version:<20} {load_time:>7.03f} ms".format(**context)) if len(metrics['plugins_failed']) > 0: output.append("") output.append("Plugins failed to load") for p in metrics['plugins_failed']: output.append(p) output.append("") output.append("Bot startup time: %.03f ms" % bot.metrics['startup_time']) output.append("Plugins: %d total, %d loaded, %d failed" % ( metrics['plugins_total'], metrics['plugins_loaded'], len(metrics['plugins_failed']))) if metrics['plugins_total'] != metrics['plugins_loaded']: output.append("") output.append("=== Bot Failed Startup Tests ===") test_passed = False logging.getLogger().info('\n'.join(output)) if not test_passed: sys.exit(1)
class TestBot(unittest.TestCase): @mock.patch('slackminion.slack.SlackUser') def setUp(self, mock_user): with open('config.yaml.example', 'r') as f: self.object = Bot(config=yaml.safe_load(f), test_mode=True) self.test_event = SlackEvent(event_type='tests', **test_payload) self.object.rtm_client = AsyncMock() self.object.api_client = AsyncMock() self.object.log = mock.Mock() self.test_payload = deepcopy(test_payload) def tearDown(self): self.object = None def test_init(self): assert self.object.version == version assert self.object.commit == 'HEAD' @mock.patch('slackminion.bot.AsyncTaskManager') @mock.patch('slackminion.bot.slack') def test_start(self, mock_slack, mock_async): self.object.start() assert self.object.is_setup is True @async_test async def test_stop(self): self.object.task_manager = mock.Mock() self.object.task_manager.shutdown = AsyncMock() self.object.task_manager.shutdown.coro.return_value = None await self.object.stop() self.object.task_manager.shutdown.assert_called() @async_test async def test_run_without_start(self): with self.assertRaises(NotSetupError) as e: await self.object.run() assert 'Bot not setup' in str(e) @mock.patch('slackminion.bot.MyRTMClient') def test_add_callbacks(self, mock_rtm): self.object._add_event_handlers() self.assertEqual(mock_rtm.on.call_count, 3) @async_test async def test_event_message_no_user_manager(self): # mock out the methods we don't want to actually call self.object._parse_event = AsyncMock() self.object._parse_event.coro.return_value = self.test_event self.object.dispatcher = mock.Mock() self.object.dispatcher.push = AsyncMock() self.object.dispatcher.push.coro.return_value = (test_command, test_output, None) self.object.dispatcher.push.coro.return_value = (test_command, test_output, None) self.object._prepare_and_send_output = AsyncMock() self.object._load_user_rights = mock.Mock() # for this test, bot has no user manager self.object.user_manager = None await self.object._event_message(**test_payload) self.object._parse_event.assert_called_with(test_payload) self.object._load_user_rights.assert_not_called() self.object.dispatcher.push.assert_called_with(self.test_event, False) self.object.log.debug.assert_called_with( f'Output from dispatcher: {test_output}') self.object._prepare_and_send_output.assert_called_with( test_command, self.test_event, None, test_output) @async_test async def test_event_message_with_user_manager(self): # mock out the methods we don't want to actually call self.object._parse_event = AsyncMock() self.object._parse_event.coro.return_value = self.test_event self.object.dispatcher = mock.Mock() self.object.dispatcher.push = AsyncMock() self.object.dispatcher.push.coro.return_value = (test_command, test_output, None) self.object._prepare_and_send_output = AsyncMock() self.object._load_user_rights = mock.Mock() self.object.log = mock.Mock() # for this test, bot has a user manager self.object.user_manager = mock.Mock() await self.object._event_message(**test_payload) self.object._parse_event.assert_called_with(test_payload) self.object._load_user_rights.assert_not_called() self.object.dispatcher.push.assert_called_with(self.test_event, False) self.object.log.debug.assert_called_with( f'Output from dispatcher: {test_output}') self.object._prepare_and_send_output.assert_called_with( test_command, self.test_event, None, test_output) # test reloading user if user is None @async_test async def test_event_message_with_manager_reload(self): self.test_payload['data'].update() # mock out the methods we don't want to actually call self.object._parse_event = AsyncMock() self.test_event.user = mock.Mock() self.object._parse_event.coro.return_value = self.test_event self.object.dispatcher = mock.Mock() self.object.dispatcher.push = AsyncMock() self.object.log = mock.Mock() self.object.dispatcher.push.coro.return_value = (test_command, test_output, None) self.object._prepare_and_send_output = mock.Mock() self.object._load_user_rights = mock.Mock() self.object.user_manager = None delattr(self.object, 'user_manager') await self.object._event_message(**test_payload) self.object._parse_event.assert_called_with(test_payload) self.object.dispatcher.push.assert_called_with(self.test_event, False) self.object.log.debug.assert_called_with( f'Output from dispatcher: {test_output}') self.object._prepare_and_send_output.assert_called_with( test_command, self.test_event, None, test_output) @async_test async def test_event_message_with_user_manager_but_no_user(self): # mock out the methods we don't want to actually call self.object._parse_event = AsyncMock() self.object._parse_event.coro.return_value = self.test_event self.object.dispatcher = mock.Mock() self.object.dispatcher.push = AsyncMock() self.object.log = mock.Mock() self.object.dispatcher.push.coro.return_value = (test_command, test_output, None) self.object._prepare_and_send_output = AsyncMock() self.object._load_user_rights = mock.Mock() self.object.user_manager = mock.Mock() await self.object._event_message(**test_payload) self.object._parse_event.assert_called_with(test_payload) self.object._load_user_rights.assert_not_called() self.object.dispatcher.push.assert_called_with(self.test_event, False) self.object.log.debug.assert_called_with( f'Output from dispatcher: {test_output}') self.object._prepare_and_send_output.assert_called_with( test_command, self.test_event, None, test_output) @async_test async def test_parse_event_uncached_user(self): self.object.log = mock.Mock() # for this test, bot has a user manager but .get() returns None # so the bot has to to look up user again using .set() self.object.user_manager = mock.Mock() self.object.user_manager.get.return_value = None self.object.user_manager.set.return_value = test_user self.object.get_channel = AsyncMock() self.object.api_client.users_info = AsyncMock() self.object.api_client.users_info.coro.return_value = test_user_response await self.object._parse_event(test_payload) self.object.user_manager.get.assert_called_with(test_user_id) self.object.user_manager.set.assert_called() # test _prepare_and_send_output without any command options set (reply in thread, etc.) def test_prepare_and_send_output_no_cmd_options(self): self.object.send_message = AsyncMock() self.object.send_im = mock.Mock() self.object.web_client = AsyncMock() # async def _prepare_and_send_output(self, cmd, msg, cmd_options, output): self.object._prepare_and_send_output(test_command, self.test_event, {}, test_output) self.object.send_message.assert_called_with(self.test_event.channel, test_output, thread=test_thread_ts, reply_broadcast=None, parse=None) # test _prepare_and_send_output with various options def test_prepare_and_send_output_with_cmd_options(self): self.object.send_message = mock.Mock() self.object.send_im = mock.Mock() self.object.web_client = AsyncMock() # async def _prepare_and_send_output(self, cmd, msg, cmd_options, output): cmd_options = {'reply_in_thread': True} self.assertEqual(self.test_event.thread_ts, test_thread_ts) self.object._prepare_and_send_output(test_command, self.test_event, cmd_options, test_output) self.object.send_message.assert_called_with(self.test_event.channel, test_output, thread=test_thread_ts, reply_broadcast=None, parse=None) cmd_options = { 'reply_in_thread': True, 'reply_broadcast': True, } self.object._prepare_and_send_output(test_command, self.test_event, cmd_options, test_output) self.object.send_message.assert_called_with(self.test_event.channel, test_output, thread=test_thread_ts, reply_broadcast=True, parse=None) cmd_options = {'parse': "full"} self.object._prepare_and_send_output(test_command, self.test_event, cmd_options, test_output) self.object.send_message.assert_called_with(self.test_event.channel, test_output, thread=test_thread_ts, reply_broadcast=None, parse="full") cmd_options = {} self.object._prepare_and_send_output(test_command, self.test_event, cmd_options, test_output) self.object.send_message.assert_called_with(self.test_event.channel, test_output, thread=test_thread_ts, reply_broadcast=None, parse=None) @async_test async def test_event_error(self): await self.object._event_error(**test_payload) self.object.log.error.assert_called_with( f"Received an error response from Slack: {test_payload}") def test_get_channel_by_name(self): self.object.is_setup = True self.object._channels = {test_channel_name: test_conversation} self.assertEqual(self.object.get_channel_by_name(test_channel_name), test_conversation) def test_get_channel_by_name_bot_not_setup(self): self.object.is_setup = False self.object._channels = {test_channel_name: TestChannel} with self.assertRaises(RuntimeError): self.object.get_channel_by_name(test_channel_name) self.object.log.warning.assert_called_with( 'Bot.channels was called before bot was setup.') def test_get_channel_by_name_bot_no_channels(self): self.object.is_setup = True self.object._channels = {} with self.assertRaises(RuntimeError): self.object.get_channel_by_name(test_channel_name) self.object.log.warning.assert_called_with( 'Bot.channels was called but self._bot_channels was empty!') def test_at_user(self): self.object.send_message = mock.Mock() test_message = "hi" expected_message = f'{test_user.at_user}: {test_message}' self.object.at_user(test_user, test_channel_id, test_message) self.object.send_message.assert_called_with(test_channel_id, expected_message) def test_unpack_payload(self): event_type, data = self.object._unpack_payload(**test_payload) self.assertEqual(event_type, test_payload['data']['type']) self.assertEqual(data, test_payload['data']) @mock.patch('slackminion.bot.MyRTMClient') @async_test async def test_handle_plugin_event(self, mock_rtm): self.object.plugin_manager = mock.Mock() plugin = PluginWithEvents(self.object) plugin.handle_event = mock.Mock() self.object.plugin_manager.broadcast_event = AsyncMock() self.object.plugin_manager.plugins = [plugin] self.object._add_event_handlers() self.assertEqual(mock_rtm.on.call_count, 4) mock_rtm.on.assert_called_with(event=test_event_type, callback=self.object._event_plugin) await self.object._event_plugin(**test_payload) self.object.plugin_manager.broadcast_event.assert_called_with( test_event_type, test_payload['data'])
def main(): def sigterm_handler(signum, frame): bot.runnable = False p = argparse.ArgumentParser() p.add_argument( "--config", action="store", default="config.yaml", help="Specify a config file (default: config.yaml)", ) p.add_argument("--test", action="store_true", help="Load plugins and exit") args = p.parse_args() with open(args.config, "rb") as f: config = yaml.load(f, Loader=yaml.SafeLoader) level = logging.DEBUG if config["debug"] else logging.INFO logging.basicConfig( level=level, format="%(asctime)s %(name)s %(levelname)s: %(message)s") bot = Bot(config, args.test) bot.start() if not args.test: signal.signal(signal.SIGTERM, sigterm_handler) asyncio.run(bot.run()) bot.stop() if args.test: test_passed = True output = ["Bot Test Results"] metrics = bot.plugin_manager.metrics output.append("Plugins Loaded") for p in bot.plugin_manager.plugins: context = { "name": type(p).__name__, "version": "-".join([p._version, p._commit]), "load_time": metrics["load_times"][type(p).__name__], } output.append( "{name:<30} {version:<20} {load_time:>7.03f} ms".format( **context)) if len(metrics["plugins_failed"]) > 0: output.append("") output.append("Plugins failed to load") for p in metrics["plugins_failed"]: output.append(p) output.append("") output.append("Bot startup time: %.03f ms" % bot.metrics["startup_time"]) output.append("Plugins: %d total, %d loaded, %d failed" % ( metrics["plugins_total"], metrics["plugins_loaded"], len(metrics["plugins_failed"]), )) if metrics["plugins_total"] != metrics["plugins_loaded"]: output.append("") output.append("=== Bot Failed Startup Tests ===") test_passed = False logging.getLogger().info("\n".join(output)) if not test_passed: sys.exit(1)