async def test_listen_break_loop(self): """Test that listening consumes from the socket.""" connector = ConnectorSlack({"api-token": "abc123"}, opsdroid=OpsDroid()) connector.receive_from_websocket = amock.CoroutineMock() connector.receive_from_websocket.side_effect = AttributeError await connector.listen() self.assertTrue(connector.receive_from_websocket.called)
async def test_process_message(self): """Test processing a slack message.""" connector = ConnectorSlack({"api-token": "abc123"}, opsdroid=OpsDroid()) connector.lookup_username = amock.CoroutineMock() connector.lookup_username.return_value = {"name": "testuser"} connector.opsdroid = amock.CoroutineMock() connector.opsdroid.parse = amock.CoroutineMock() message = { # https://api.slack.com/events/message "type": "message", "channel": "C2147483705", "user": "******", "text": "Hello, world!", "ts": "1355517523.000005", "edited": { "user": "******", "ts": "1355517536.000001" } } await connector.process_message(message) self.assertTrue(connector.opsdroid.parse.called) connector.opsdroid.parse.reset_mock() message["subtype"] = "bot_message" await connector.process_message(message) self.assertFalse(connector.opsdroid.parse.called) del message["subtype"] connector.opsdroid.parse.reset_mock() connector.lookup_username.side_effect = ValueError await connector.process_message(message) self.assertFalse(connector.opsdroid.parse.called)
async def test_reconnect(self): connector = ConnectorSlack({"api-token": "abc123"}, opsdroid=OpsDroid()) connector.connect = amock.CoroutineMock() with amock.patch('asyncio.sleep') as mocked_sleep: await connector.reconnect(10) self.assertTrue(connector.connect.called) self.assertTrue(mocked_sleep.called)
async def test_keepalive_websocket_loop(self): """Test that listening consumes from the socket.""" connector = ConnectorSlack({"api-token": "abc123"}, opsdroid=OpsDroid()) connector.ping_websocket = amock.CoroutineMock() connector.ping_websocket.side_effect = Exception() with self.assertRaises(Exception): await connector.keepalive_websocket() self.assertTrue(connector.ping_websocket.called)
async def test_reconnect_on_error(self): import aiohttp connector = ConnectorSlack({"api-token": "abc123"}, opsdroid=OpsDroid()) connector.slacker.rtm.start = amock.CoroutineMock() connector.slacker.rtm.start.side_effect = aiohttp.ClientOSError() connector.reconnect = amock.CoroutineMock() await connector.connect() self.assertTrue(connector.reconnect.called)
async def test_connect(self): connector = ConnectorSlack({"api-token": "abc123"}, opsdroid=OpsDroid()) opsdroid = amock.CoroutineMock() opsdroid.eventloop = self.loop connector.slacker.rtm.start = amock.CoroutineMock() connector.keepalive_websocket = amock.CoroutineMock() with amock.patch('websockets.connect', new=amock.CoroutineMock()) \ as mocked_websocket_connect: await connector.connect() self.assertTrue(connector.slacker.rtm.start.called) self.assertTrue(mocked_websocket_connect.called) self.assertTrue(connector.keepalive_websocket.called)
async def test_ping_websocket(self): """Test pinging the websocket.""" import websockets connector = ConnectorSlack({"api-token": "abc123"}, opsdroid=OpsDroid()) with amock.patch('asyncio.sleep', new=amock.CoroutineMock()) \ as mocked_sleep: connector.websocket = amock.CoroutineMock() connector.websocket.send = amock.CoroutineMock() await connector.ping_websocket() self.assertTrue(mocked_sleep.called) self.assertTrue(connector.websocket.send.called) connector.reconnect = amock.CoroutineMock() connector.websocket.send.side_effect = \ websockets.exceptions.ConnectionClosed(500, "Mock Error") await connector.ping_websocket() self.assertTrue(connector.reconnect.called)
async def test_receive_from_websocket(self): """Test receive_from_websocket receives and reconnects.""" import websockets connector = ConnectorSlack({"api-token": "abc123"}, opsdroid=OpsDroid()) connector.websocket = amock.CoroutineMock() connector.websocket.recv = amock.CoroutineMock() connector.websocket.recv.return_value = '[]' connector.process_message = amock.CoroutineMock() await connector.receive_from_websocket() self.assertTrue(connector.websocket.recv.called) self.assertTrue(connector.process_message.called) connector.websocket.recv.side_effect = \ websockets.exceptions.ConnectionClosed(500, "Mock Error") connector.reconnect = amock.CoroutineMock() await connector.receive_from_websocket() self.assertTrue(connector.reconnect.called)
async def test_react_invalid_name(self): connector = ConnectorSlack({"token": "abc123"}, opsdroid=self.od) connector.slack.api_call = amock.CoroutineMock( side_effect=slack.errors.SlackApiError("invalid_name", "invalid_name")) prev_message = events.Message( text="test", user="******", target="room", connector=connector, raw_event={"ts": 0}, ) await prev_message.respond(events.Reaction("😀")) self.assertLogs("_LOGGER", "warning")
async def test_react_invalid_name(self): import slack connector = ConnectorSlack({"api-token": "abc123"}) connector.slack.api_call = amock.CoroutineMock( side_effect=slack.errors.SlackApiError("invalid_name", "invalid_name")) prev_message = Message("test", "user", "room", connector, raw_event={"ts": 0}) with OpsDroid(): await prev_message.respond(Reaction("😀")) self.assertLogs("_LOGGER", "warning")
async def test_react(self): connector = ConnectorSlack({"api-token": "abc123"}) connector.slacker.reactions.post = amock.CoroutineMock() prev_message = Message("test", "user", "room", connector, raw_event={"ts": 0}) with OpsDroid() as opsdroid: await prev_message.respond(Reaction("😀")) self.assertTrue(connector.slacker.reactions.post) self.assertEqual( connector.slacker.reactions.post.call_args[1]["data"]["name"], ":grinning_face:", )
async def test_block_actions_interactivity(self): """Test the block_actions interactivity type in Slack interactions handler.""" connector = ConnectorSlack({"token": "abc123"}, opsdroid=OpsDroid()) connector.opsdroid = amock.CoroutineMock() connector.opsdroid.parse = amock.CoroutineMock() req_ob = { "type": "block_actions", "team": {"id": "T9TK3CUKW", "domain": "example"}, "user": { "id": "UA8RXUSPL", "username": "******", "team_id": "T9TK3CUKW", }, "channel": {"id": "CBR2V3XEX", "name": "review-updates"}, "actions": [ { "action_id": "WaXA", "block_id": "=qXel", "text": {"type": "plain_text", "text": "View", "emoji": True}, "value": "click_me_123", "type": "button", "action_ts": "1548426417.840180", } ], } mock_request = amock.CoroutineMock() mock_request.post = amock.CoroutineMock() mock_request.post.return_value = {"payload": json.dumps(req_ob)} response = await connector.slack_interactions_handler(mock_request) self.assertTrue(connector.opsdroid.parse.called) self.assertEqual(type(response), aiohttp.web.Response) self.assertEqual(response.status, 200)
async def test_connect(self): connector = ConnectorSlack({"token": "abc123"}, opsdroid=OpsDroid()) opsdroid = amock.CoroutineMock() opsdroid.eventloop = self.loop connector.slack_rtm._connect_and_read = amock.CoroutineMock() connector.slack.api_call = amock.CoroutineMock() connector.opsdroid.web_server = amock.CoroutineMock() connector.opsdroid.web_server.web_app = amock.CoroutineMock() connector.opsdroid.web_server.web_app.router = amock.CoroutineMock() connector.opsdroid.web_server.web_app.router.add_post = amock.CoroutineMock( ) await connector.connect() self.assertTrue(connector.slack_rtm._connect_and_read.called) self.assertTrue(connector.slack.api_call.called) self.assertTrue( connector.opsdroid.web_server.web_app.router.add_post.called)
async def test_react(self): connector = ConnectorSlack({"token": "abc123"}) connector.slack.api_call = amock.CoroutineMock() prev_message = Message( text="test", user="******", target="room", connector=connector, raw_event={"ts": 0}, ) with OpsDroid(): await prev_message.respond(Reaction("😀")) self.assertTrue(connector.slack.api_call) self.assertEqual( connector.slack.api_call.call_args[1]["data"]["name"], "grinning_face" )
async def test_react_unknown_error(self): import slack connector = ConnectorSlack({"token": "abc123"}) connector.slack.api_call = amock.CoroutineMock( side_effect=slack.errors.SlackApiError("unknown", "unknown") ) with self.assertRaises(slack.errors.SlackApiError), OpsDroid(): prev_message = Message( text="test", user="******", target="room", connector=connector, raw_event={"ts": 0}, ) await prev_message.respond(Reaction("😀"))
async def test_send_blocks(self): connector = ConnectorSlack({"token": "abc123"}, opsdroid=OpsDroid()) connector.slack.api_call = amock.CoroutineMock() await connector.send( Blocks( [{ "type": "section", "text": { "type": "mrkdwn", "text": "*Test*" } }], "user", "room", connector, )) self.assertTrue(connector.slack.api_call.called)
async def test_update_blocks(self): connector = ConnectorSlack({"token": "abc123"}, opsdroid=self.od) connector.slack.api_call = amock.CoroutineMock() await connector.send( slackevents.EditedBlocks( [{ "type": "section", "text": { "type": "mrkdwn", "text": "*Test*" } }], user="******", target="room", connector=connector, linked_event="1358878749.000002", )) self.assertTrue(connector.slack.api_call.called)
async def test_send_pin_removed(self): connector = ConnectorSlack({"token": "abc123"}, opsdroid=self.od) connector.slack.api_call = amock.CoroutineMock() await connector.send( events.UnpinMessage( target="an-existing-room", linked_event=events.Message( "An important message", user="******", user_id="U9S8JGF45", target="an-existing-room", connector=connector, event_id="1582838099.000600", ), )) connector.slack.api_call.assert_called_once_with( "pins.remove", data={ "channel": "an-existing-room", "timestamp": "1582838099.000600" }, )
async def test_lookup_username(self): """Test that looking up a username works and that it caches.""" connector = ConnectorSlack({"token": "abc123"}, opsdroid=OpsDroid()) connector.slack.users_info = amock.CoroutineMock() mock_user = mock.Mock() mock_user.data = {"user": {"name": "testuser"}} connector.slack.users_info.return_value = mock_user self.assertEqual(len(connector.known_users), 0) await connector.lookup_username("testuser") self.assertTrue(len(connector.known_users), 1) self.assertTrue(connector.slack.users_info.called) connector.slack.users_info.reset_mock() await connector.lookup_username("testuser") self.assertEqual(len(connector.known_users), 1) self.assertFalse(connector.slack.users_info.called) with self.assertRaises(ValueError): mock_user.data = {"user": None} connector.slack.users_info.return_value = mock_user await connector.lookup_username("invaliduser")
async def test_edit_message(self): connector = ConnectorSlack({"token": "abc123"}, opsdroid=self.od) connector.slack.api_call = amock.CoroutineMock() linked_event = "1582838099.000600" edited_message = events.EditedMessage( text="edited_message", user="******", target="room", connector=connector, linked_event=linked_event, ) await connector.send(edited_message) connector.slack.api_call.assert_called_once_with( "chat.update", data={ "channel": "room", "ts": "1582838099.000600", "text": "edited_message", "as_user": False, }, )
async def test_process_message(self): """Test processing a slack message.""" connector = ConnectorSlack({"token": "abc123"}, opsdroid=OpsDroid()) connector.lookup_username = amock.CoroutineMock() connector.lookup_username.return_value = {"name": "testuser"} connector.opsdroid = amock.CoroutineMock() connector.opsdroid.parse = amock.CoroutineMock() message = { # https://api.slack.com/events/message "type": "message", "channel": "C2147483705", "user": "******", "text": "Hello, world!", "ts": "1355517523.000005", "edited": {"user": "******", "ts": "1355517536.000001"}, } await connector.process_message(data=message) self.assertTrue(connector.opsdroid.parse.called) connector.opsdroid.parse.reset_mock() message["bot_id"] = "abc" message["subtype"] = "bot_message" connector.bot_id = message["bot_id"] await connector.process_message(data=message) self.assertFalse(connector.opsdroid.parse.called) del message["bot_id"] del message["subtype"] connector.bot_id = None connector.opsdroid.parse.reset_mock() message["subtype"] = "message_changed" await connector.process_message(data=message) self.assertFalse(connector.opsdroid.parse.called) del message["subtype"] connector.opsdroid.parse.reset_mock() connector.lookup_username.side_effect = ValueError await connector.process_message(data=message) self.assertFalse(connector.opsdroid.parse.called) connector.opsdroid.parse.reset_mock() connector.lookup_username.side_effect = KeyError await connector.process_message(data=message) self.assertFalse(connector.opsdroid.parse.called)
async def test_replace_usernames(self): connector = ConnectorSlack({"token": "abc123"}, opsdroid=OpsDroid()) connector.lookup_username = amock.CoroutineMock() connector.lookup_username.return_value = {"name": "user"} result = await connector.replace_usernames("Hello <@U023BECGF>!") self.assertEqual(result, "Hello user!")
def test_init(self): """Test that the connector is initialised properly.""" connector = ConnectorSlack({"token": "abc123"}, opsdroid=OpsDroid()) self.assertEqual("#general", connector.default_target) self.assertEqual("slack", connector.name) self.assertEqual(10, connector.timeout)
async def test_react_unknown_error(self): import slacker connector = ConnectorSlack({"api-token": "abc123"}) connector.slacker.reactions.post = amock.CoroutineMock(side_effect=slacker.Error('unknown')) with self.assertRaises(slacker.Error): await connector.react(Message("test", "user", "room", connector, {'ts': 0}), "😀")
async def test_listen_loop(self): """Test that listening consumes from the socket.""" connector = ConnectorSlack({"token": "abc123"}, opsdroid=OpsDroid()) connector.listening = False await connector.listen()
async def test_react_invalid_name(self): import slacker connector = ConnectorSlack({"api-token": "abc123"}) connector.slacker.reactions.post = amock.CoroutineMock(side_effect=slacker.Error('invalid_name')) await connector.react(Message("test", "user", "room", connector, {'ts': 0}), "😀") self.assertLogs('_LOGGER', 'warning')
async def test_respond_on_interactive_actions(self): """Test the respond method for interactive actions in Slack.""" result = amock.Mock() result.json = amock.CoroutineMock() result.json.return_value = {"success": "payload sent."} payload = { "type": "message_action", "team": { "id": "TXXXXXX", "domain": "coverbands" }, "user": { "id": "UXXXXXX", "name": "dreamweaver" }, "response_url": "https://hooks.slack.com/app-actions/T0MJR11A4/21974584944/yk1S9ndf35Q1flupVG5JbpM6", } interactive_action = InteractiveAction(payload) with amock.patch("aiohttp.ClientSession.post") as patched_request: patched_request.return_value = asyncio.Future() patched_request.return_value.set_result(result) await interactive_action.respond("Respond called with response_url" ) self.assertTrue(patched_request.called) payload = { "type": "view_closed", "team": { "id": "TXXXXXX", "domain": "coverbands" }, "user": { "id": "UXXXXXX", "name": "dreamweaver" }, } interactive_action = InteractiveAction(payload) with amock.patch("aiohttp.ClientSession.post") as patched_request: patched_request.return_value = asyncio.Future() patched_request.return_value.set_result(result) await interactive_action.respond( "Respond called without response_url") self.assertFalse(patched_request.called) with OpsDroid() as opsdroid: connector = ConnectorSlack({"token": "abc123"}, opsdroid=OpsDroid()) raw_message = { "text": "Hello world", "user_id": "user_id", "user": "******", "room": "default", } message = Message( text="Hello world", user_id="user_id", user="******", target="default", connector=connector, raw_event=raw_message, ) opsdroid.send = amock.CoroutineMock() with amock.patch("aiohttp.ClientSession.post") as patched_request: patched_request.return_value = asyncio.Future() patched_request.return_value.set_result(result) await interactive_action.respond(message) self.assertTrue(opsdroid.send.called) self.assertFalse(patched_request.called)
async def test_block_actions_interactivity(self): """Test the block_actions interactivity type in Slack interactions handler.""" connector = ConnectorSlack({"token": "abc123"}, opsdroid=OpsDroid()) connector.opsdroid = amock.CoroutineMock() connector.opsdroid.parse = amock.CoroutineMock() req_ob = { "type": "block_actions", "team": { "id": "T9TK3CUKW", "domain": "example" }, "user": { "id": "UA8RXUSPL", "username": "******", "team_id": "T9TK3CUKW", }, "channel": { "id": "CBR2V3XEX", "name": "review-updates" }, "actions": [ { "action_id": "WaXA", "block_id": "=qXel", "text": { "type": "plain_text", "text": "View", "emoji": True }, "value": "click_me_123", "type": "button", "action_ts": "1548426417.840180", }, { "type": "overflow", "block_id": "B5XNP", "action_id": "BnhtF", "selected_option": { "text": { "type": "plain_text", "text": "Option 1", "emoji": True, }, "value": "value-0", }, "action_ts": "1576336883.317406", }, { "type": "datepicker", "block_id": "CAwR", "action_id": "VS+", "selected_date": "2019-12-31", "initial_date": "1990-04-28", "action_ts": "1576337318.133466", }, { "type": "multi_static_select", "block_id": "rOL", "action_id": "Cd9", "selected_options": [ { "text": { "type": "plain_text", "text": "Choice 1", "emoji": True, }, "value": "value-0", }, { "text": { "type": "plain_text", "text": "Choice 2", "emoji": True, }, "value": "value-1", }, ], "placeholder": { "type": "plain_text", "text": "Select items", "emoji": True, }, "action_ts": "1576337351.609054", }, { "type": "static_select", "block_id": "OGN1", "action_id": "4jd", "selected_option": { "text": { "type": "plain_text", "text": "Choice 2", "emoji": True, }, "value": "value-1", }, "placeholder": { "type": "plain_text", "text": "Select an item", "emoji": True, }, "action_ts": "1576337378.859991", }, ], } mock_request = amock.CoroutineMock() mock_request.post = amock.CoroutineMock() mock_request.post.return_value = {"payload": json.dumps(req_ob)} response = await connector.slack_interactions_handler(mock_request) self.assertTrue(connector.opsdroid.parse.called) self.assertEqual(connector.opsdroid.parse.call_count, len(req_ob["actions"])) self.assertEqual(type(response), aiohttp.web.Response) self.assertEqual(response.status, 200)
async def test_join_room(self): connector = ConnectorSlack({"token": "abc123"}, opsdroid=self.od) connector.slack_user.api_call = amock.CoroutineMock() await connector.send(events.JoinRoom(target="an-existing-room")) connector.slack_user.api_call.assert_called_once_with( "conversations.join", data={"channel": "an-existing-room"})
async def test_send_room_creation(self): connector = ConnectorSlack({"token": "abc123"}, opsdroid=self.od) connector.slack_user.api_call = amock.CoroutineMock() await connector.send(events.NewRoom(name="mynewroom")) connector.slack_user.api_call.assert_called_once_with( "conversations.create", data={"name": "mynewroom"})
async def test_replace_usernames(self): connector = ConnectorSlack({"api-token": "abc123"}, opsdroid=OpsDroid()) connector.lookup_username = amock.CoroutineMock() connector.lookup_username.return_value = {"name": 'user'} result = await connector.replace_usernames("Hello <@U023BECGF>!") self.assertEqual(result, "Hello user!")
def test_missing_api_key(self): """Test that creating without an API key raises an error.""" with self.assertRaises(KeyError): ConnectorSlack({}, opsdroid=OpsDroid())
def test_init(self): """Test that the connector is initialised properly.""" connector = ConnectorSlack({"api-token": "abc123"}) self.assertEqual("#general", connector.default_room) self.assertEqual("slack", connector.name)
async def test_respond(self): connector = ConnectorSlack({"api-token": "abc123"}) connector.slacker.chat.post_message = amock.CoroutineMock() await connector.respond(Message("test", "user", "room", connector)) self.assertTrue(connector.slacker.chat.post_message.called)
async def test_respond(self): connector = ConnectorSlack({"api-token": "abc123"}, opsdroid=OpsDroid()) connector.slack.api_call = amock.CoroutineMock() await connector.send(Message("test", "user", "room", connector)) self.assertTrue(connector.slack.api_call.called)