class TestConnectorSlack(unittest.TestCase): """Test the opsdroid Slack connector class.""" def setUp(self): self.loop = asyncio.new_event_loop() configure_lang({}) self.od = OpsDroid() self.od.__enter__() self.connector = ConnectorSlack({"token": "abc123"}, opsdroid=self.od) self.event_creator = slackevents.SlackEventCreator( self.connector, self.connector.slack_rtm) def tearDown(self): slack.RTMClient._callbacks = collections.defaultdict(list) del self.connector del self.event_creator self.od.__exit__(None, None, None) del self.od def test_init(self): """Test that the connector is initialised properly.""" connector = ConnectorSlack({"token": "abc123"}, opsdroid=self.od) self.assertEqual("#general", connector.default_target) self.assertEqual("slack", connector.name) self.assertEqual(10, connector.timeout) def test_missing_api_key(self): """Test that creating without an API key raises an error.""" with self.assertRaises(KeyError): ConnectorSlack({}, opsdroid=self.od)
class TestConnectorSlackAsync(asynctest.TestCase): """Test the async methods of the opsdroid Slack connector class.""" def setUp(self): self.loop = asyncio.new_event_loop() configure_lang({}) self.od = OpsDroid() self.od.__enter__() self.connector = ConnectorSlack({"token": "abc123"}, opsdroid=self.od) self.event_creator = slackevents.SlackEventCreator( self.connector, self.connector.slack_rtm) def tearDown(self): slack.RTMClient._callbacks = collections.defaultdict(list) del self.connector del self.event_creator self.od.__exit__(None, None, None) del self.od async def test_connect(self): connector = ConnectorSlack({"token": "abc123"}, opsdroid=self.od) 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_connect_auth_fail(self): connector = ConnectorSlack({"token": "abc123"}, opsdroid=self.od) opsdroid = amock.CoroutineMock() opsdroid.eventloop = self.loop connector.slack_rtm._connect_and_read = amock.Mock() connector.slack_rtm._connect_and_read.side_effect = slack.errors.SlackApiError( message="", response="") await connector.connect() self.assertLogs("_LOGGER", "error") async def test_abort_on_connection_error(self): connector = ConnectorSlack({"token": "abc123"}) connector.slack_rtm._connect_and_read = amock.CoroutineMock() connector.slack_rtm._connect_and_read.side_effect = Exception() connector.slack_rtm.stop = amock.CoroutineMock() with self.assertRaises(Exception): await connector.connect() self.assertTrue(connector.slack_rtm.stop.called) async def test_listen_loop(self): """Test that listening consumes from the socket.""" connector = ConnectorSlack({"token": "abc123"}, opsdroid=self.od) connector.listening = False await connector.listen() async def test_lookup_username(self): """Test that looking up a username works and that it caches.""" connector = ConnectorSlack({"token": "abc123"}, opsdroid=self.od) 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_respond(self): connector = ConnectorSlack({"token": "abc123"}, opsdroid=self.od) connector.slack.api_call = amock.CoroutineMock() await connector.send( events.Message( text="test", user="******", target="room", connector=connector, )) self.assertTrue(connector.slack.api_call.called) async def test_send_message_inside_thread(self): connector = ConnectorSlack({"token": "abc123"}, opsdroid=self.od) connector.slack.api_call = amock.CoroutineMock() linked_event = events.Message( text="linked text", raw_event={"thread_ts": "1582838099.000600"}) message = events.Message( text="test", user="******", target="room", connector=connector, linked_event=linked_event, event_id="1582838099.000601", ) await connector.send(message) connector.slack.api_call.assert_called_once_with( "chat.postMessage", data={ "channel": "room", "text": "test", "as_user": False, "username": "******", "icon_emoji": ":robot_face:", "thread_ts": "1582838099.000600", }, ) async def test_send_message_start_thread_is_true(self): connector = ConnectorSlack({"token": "abc123"}, opsdroid=self.od) connector.slack.api_call = amock.CoroutineMock() connector.start_thread = True event_id = "1582838099.000601" linked_event = events.Message(text="linked text", event_id=event_id, raw_event={}) message = events.Message( text="test", user="******", target="room", connector=connector, linked_event=linked_event, ) await connector.send(message) connector.slack.api_call.assert_called_once_with( "chat.postMessage", data={ "channel": "room", "text": "test", "as_user": False, "username": "******", "icon_emoji": ":robot_face:", "thread_ts": "1582838099.000601", }, ) 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_send_blocks(self): connector = ConnectorSlack({"token": "abc123"}, opsdroid=self.od) connector.slack.api_call = amock.CoroutineMock() await connector.send( slackevents.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_react(self): connector = ConnectorSlack({"token": "abc123"}, opsdroid=self.od) connector.slack.api_call = amock.CoroutineMock() prev_message = events.Message( text="test", user="******", target="room", connector=connector, raw_event={"ts": 0}, ) await prev_message.respond(events.Reaction("😀")) self.assertTrue(connector.slack.api_call) self.assertEqual(connector.slack.api_call.call_args[1]["data"]["name"], "grinning_face") 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_unknown_error(self): connector = ConnectorSlack({"token": "abc123"}, opsdroid=self.od) connector.slack.api_call = amock.CoroutineMock( side_effect=slack.errors.SlackApiError("unknown", "unknown")) with self.assertRaises(slack.errors.SlackApiError): prev_message = events.Message( text="test", user="******", target="room", connector=connector, raw_event={"ts": 0}, ) await prev_message.respond(events.Reaction("😀")) async def test_replace_usernames(self): connector = ConnectorSlack({"token": "abc123"}, opsdroid=self.od) connector.lookup_username = amock.CoroutineMock() connector.lookup_username.return_value = {"name": "user"} result = await connector.replace_usernames("Hello <@U023BECGF>!") self.assertEqual(result, "Hello user!") async def test_block_actions_interactivity(self): """Test the block_actions interactivity type in Slack interactions handler.""" connector = ConnectorSlack({"token": "abc123"}, opsdroid=self.od) 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_message_action_interactivity(self): """Test the message_action interactivity type in Slack interactions handler.""" connector = ConnectorSlack({"token": "abc123"}, opsdroid=self.od) connector.opsdroid = amock.CoroutineMock() connector.opsdroid.parse = amock.CoroutineMock() req_ob = { "token": "Nj2rfC2hU8mAfgaJLemZgO7H", "callback_id": "chirp_message", "type": "message_action", "trigger_id": "13345224609.8534564800.6f8ab1f53e13d0cd15f96106292d5536", "response_url": "https://hooks.slack.com/app-actions/T0MJR11A4/21974584944/yk1S9ndf35Q1flupVG5JbpM6", "team": { "id": "T0MJRM1A7", "domain": "pandamonium" }, "channel": { "id": "D0LFFBKLZ", "name": "cats" }, "user": { "id": "U0D15K92L", "name": "dr_maomao" }, "message": { "type": "message", "user": "******", "ts": "1516229207.000133", "text": "World's smallest big cat! <https://youtube.com/watch?v=W86cTIoMv2U>", }, } 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_view_submission_interactivity(self): """Test the view_submission interactivity type in Slack interactions handler.""" connector = ConnectorSlack({"token": "abc123"}, opsdroid=self.od) connector.opsdroid = amock.CoroutineMock() connector.opsdroid.parse = amock.CoroutineMock() req_ob = { "type": "view_submission", "team": { "id": "T0MJRM1A7", "domain": "pandamonium" }, "user": { "id": "U0D15K92L", "name": "dr_maomao" }, "view": { "id": "VNHU13V36", "type": "modal", "private_metadata": "shhh-its-secret", "callback_id": "modal-with-inputs", "state": { "values": { "multi-line": { "ml-value": { "type": "plain_text_input", "value": "This is my example inputted value", } } } }, "hash": "156663117.cd33ad1f", }, } 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_view_closed_interactivity(self): """Test the view_closed interactivity type in Slack interactions handler.""" connector = ConnectorSlack({"token": "abc123"}, opsdroid=self.od) connector.opsdroid = amock.CoroutineMock() connector.opsdroid.parse = amock.CoroutineMock() req_ob = { "type": "view_closed", "team": { "id": "TXXXXXX", "domain": "coverbands" }, "user": { "id": "UXXXXXX", "name": "dreamweaver" }, "view": { "id": "VNHU13V36", "type": "modal", "private_metadata": "shhh-its-secret", "callback_id": "modal-with-inputs", "state": { "values": { "multi-line": { "ml-value": { "type": "plain_text_input", "value": "This is my example inputted value", } } } }, "hash": "156663117.cd33ad1f", }, "api_app_id": "AXXXXXX", "is_cleared": False, } 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_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 = slackevents.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 = slackevents.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) connector = ConnectorSlack({"token": "abc123"}, opsdroid=self.od) raw_message = { "text": "Hello world", "user_id": "user_id", "user": "******", "room": "default", } message = events.Message( text="Hello world", user_id="user_id", user="******", target="default", connector=connector, raw_event=raw_message, ) self.od.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(self.od.send.called) self.assertFalse(patched_request.called) 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_send_room_name_set(self): connector = ConnectorSlack({"token": "abc123"}, opsdroid=self.od) connector.slack_user.api_call = amock.CoroutineMock() await connector.send( events.RoomName(target="an-existing-room", name="my-new-room-name")) connector.slack_user.api_call.assert_called_once_with( "conversations.rename", data={ "channel": "an-existing-room", "name": "my-new-room-name" }, ) async def test_send_user_invitation(self): connector = ConnectorSlack({"token": "abc123"}, opsdroid=self.od) connector.slack_user.api_call = amock.CoroutineMock() await connector.send( events.UserInvite(user="******", user_id="UMcU42", target="an-existing-room")) connector.slack_user.api_call.assert_called_once_with( "conversations.invite", data={ "channel": "an-existing-room", "users": "UMcU42" }, ) 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_description(self): connector = ConnectorSlack({"token": "abc123"}, opsdroid=self.od) connector.slack_user.api_call = amock.CoroutineMock() await connector.send( events.RoomDescription(target="an-existing-room", description="A new description")) connector.slack_user.api_call.assert_called_once_with( "conversations.setTopic", data={ "channel": "an-existing-room", "topic": "A new description" }, ) async def test_send_pin_added(self): connector = ConnectorSlack({"token": "abc123"}, opsdroid=self.od) connector.slack.api_call = amock.CoroutineMock() await connector.send( events.PinMessage( 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.add", data={ "channel": "an-existing-room", "timestamp": "1582838099.000600" }, ) 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" }, )
class TestEventCreatorAsync(asynctest.TestCase): def setUp(self): configure_lang({}) self.od = OpsDroid() self.od.__enter__() self.connector = ConnectorSlack({"token": "abc123"}, opsdroid=self.od) self.event_creator = slackevents.SlackEventCreator( self.connector, self.connector.slack_rtm) def tearDown(self): slack.RTMClient._callbacks = collections.defaultdict(list) del self.connector del self.event_creator self.od.__exit__(None, None, None) del self.od @property def message_event(self): return { "text": "Hello World", "user": "******", "team": "T9T6EKEEB", "source_team": "T9T6EKEEB", "user_team": "T9T6EKEEB", "channel": "C9S8JGM2R", "event_ts": "1582838099.000600", "ts": "1582838099.000600", } async def test_create_message(self): with amock.patch( "opsdroid.connector.slack.ConnectorSlack.lookup_username" ) as lookup, amock.patch("opsdroid.core.OpsDroid.parse") as parse: lookup.return_value = asyncio.Future() lookup.return_value.set_result({"name": "testuser"}) await self.connector.slack_rtm._dispatch_event( "message", self.message_event) (called_event, ), _ = parse.call_args self.assertTrue(isinstance(called_event, events.Message)) self.assertTrue(called_event.text == self.message_event["text"]) self.assertTrue(called_event.user == "testuser") self.assertTrue(called_event.user_id == self.message_event["user"]) self.assertTrue( called_event.target == self.message_event["channel"]) self.assertTrue(called_event.event_id == self.message_event["ts"]) self.assertTrue(called_event.raw_event == self.message_event) lookup.assert_called_with("U9S8JGF45") async def test_create_message_no_user(self): with amock.patch( "opsdroid.connector.slack.events.SlackEventCreator._get_user_name" ) as getuser, amock.patch("opsdroid.core.OpsDroid.parse") as parse: getuser.return_value = asyncio.Future() getuser.return_value.set_result(None) await self.connector.slack_rtm._dispatch_event( "message", self.message_event) parse.assert_not_called() @property def bot_message_event(self): return { "subtype": "bot_message", "text": "Hello", "username": "******", "bot_id": "BDATLJZKQ", "team": "T9T6EKEEB", "bot_profile": { "id": "BDATLJZKQ", "deleted": False, "name": "rss", "updated": 1539016312, "app_id": "A0F81R7U7", "team_id": "T9T6EKEEB", }, "channel": "C9S8JGM2R", "event_ts": "1582841283.001700", "ts": "1582841283.001700", } async def test_create_message_from_bot(self): # Skip this test until we have implemented bot user name lookup return with amock.patch("opsdroid.core.OpsDroid.parse") as parse: await self.connector.slack_rtm._dispatch_event( "message", self.bot_message_event) (called_event, ), _ = parse.call_args self.assertTrue(isinstance(called_event, events.Message)) self.assertTrue( called_event.text == self.bot_message_event["text"]) self.assertTrue(called_event.user == "testuser") self.assertTrue( called_event.user_id == self.bot_message_event["user"]) self.assertTrue( called_event.target == self.bot_message_event["channel"]) self.assertTrue( called_event.event_id == self.bot_message_event["ts"]) self.assertTrue(called_event.raw_event == self.bot_message_event) async def test_ignore_own_bot_message(self): self.connector.bot_id = self.bot_message_event["bot_id"] with amock.patch("opsdroid.core.OpsDroid.parse") as parse: await self.connector.slack_rtm._dispatch_event( "message", self.bot_message_event) self.assertFalse(parse.called) self.connector.bot_id = None @property def channel_created_event(self): return { "channel": { "id": "CUM24109W", "is_channel": True, "name": "test100", "name_normalized": "test100", "created": 1582843467, "creator": "U9S8JGF45", "is_shared": False, "is_org_shared": False, }, "event_ts": "1582843467.006500", } async def test_create_channel_created_event(self): with amock.patch( "opsdroid.connector.slack.ConnectorSlack.lookup_username" ) as lookup, amock.patch("opsdroid.core.OpsDroid.parse") as parse: lookup.return_value = asyncio.Future() lookup.return_value.set_result({"name": "testuser"}) await self.connector.slack_rtm._dispatch_event( "channel_created", self.channel_created_event) (called_event, ), _ = parse.call_args self.assertTrue(isinstance(called_event, events.NewRoom)) self.assertTrue(called_event.user_id == self.channel_created_event["channel"]["creator"]) self.assertTrue(called_event.user == "testuser") self.assertTrue(called_event.target == self.channel_created_event["channel"]["id"]) self.assertTrue(called_event.connector == self.connector) self.assertTrue(called_event.event_id == self.channel_created_event["event_ts"]) self.assertTrue(called_event.name == self.channel_created_event["channel"]["name"]) lookup.assert_called_with("U9S8JGF45") @property def channel_archive_event(self): return { "channel": "CULJAHTUL", "user": "******", "is_moved": 0, "event_ts": "1582843733.007500", } async def test_create_channel_archive_event(self): with amock.patch( "opsdroid.connector.slack.ConnectorSlack.lookup_username" ) as lookup, amock.patch("opsdroid.core.OpsDroid.parse") as parse: lookup.return_value = asyncio.Future() lookup.return_value.set_result({"name": "testuser"}) await self.connector.slack_rtm._dispatch_event( "channel_archive", self.channel_archive_event) (called_event, ), _ = parse.call_args self.assertTrue( isinstance(called_event, slackevents.ChannelArchived)) self.assertTrue( called_event.target == self.channel_archive_event["channel"]) self.assertTrue(called_event.connector == self.connector) self.assertTrue(called_event.event_id == self.channel_archive_event["event_ts"]) @property def channel_unarchive_event(self): return { "channel": "CULJAHTUL", "user": "******", "event_ts": "1582843808.007700", } async def test_create_channel_unarchive_event(self): with amock.patch( "opsdroid.connector.slack.ConnectorSlack.lookup_username" ) as lookup, amock.patch("opsdroid.core.OpsDroid.parse") as parse: lookup.return_value = asyncio.Future() lookup.return_value.set_result({"name": "testuser"}) await self.connector.slack_rtm._dispatch_event( "channel_unarchive", self.channel_unarchive_event) (called_event, ), _ = parse.call_args self.assertTrue( isinstance(called_event, slackevents.ChannelUnarchived)) self.assertTrue( called_event.target == self.channel_unarchive_event["channel"]) self.assertTrue(called_event.connector == self.connector) self.assertTrue(called_event.event_id == self.channel_unarchive_event["event_ts"]) @property def join_event(self): return { "user": { "id": "UV6E5JA5T", "team_id": "T9T6EKEEB", "name": "slack1", "deleted": False, "color": "99a949", "real_name": "test4", "tz": "Europe/London", "tz_label": "Greenwich Mean Time", "tz_offset": 0, "profile": { "title": "", "phone": "", "skype": "", "real_name": "test4", "real_name_normalized": "test4", "display_name": "test4", "display_name_normalized": "test4", "fields": None, "status_text": "", "status_emoji": "", "status_expiration": 0, "avatar_hash": "gaa2e6c047a5", "status_text_canonical": "", "team": "T9T6EKEEB", }, "is_admin": False, "is_owner": False, "is_primary_owner": False, "is_restricted": False, "is_ultra_restricted": False, "is_bot": False, "is_app_user": False, "updated": 1583879206, "presence": "away", }, "cache_ts": 1583879206, "event_ts": "1583879207.001200", } async def test_user_join(self): with amock.patch( "opsdroid.connector.slack.ConnectorSlack.lookup_username" ) as lookup, amock.patch("opsdroid.core.OpsDroid.parse") as parse: lookup.return_value = asyncio.Future() lookup.return_value.set_result({"name": "testuser"}) await self.connector.slack_rtm._dispatch_event( "team_join", self.join_event) lookup.assert_called_with("UV6E5JA5T") (called_event, ), _ = parse.call_args assert isinstance(called_event, events.JoinGroup) self.assertTrue(called_event.user == "testuser") self.assertTrue( called_event.user_id == self.join_event["user"]["id"]) self.assertTrue( called_event.target == self.join_event["user"]["team_id"]) self.assertTrue( called_event.event_id == self.join_event["event_ts"]) self.assertTrue(called_event.raw_event == self.join_event) @property def edit_event(self): return { "subtype": "message_changed", "hidden": True, "message": { "type": "message", "text": "Hello World", "user": "******", "team": "T9T6EKEEB", "edited": { "user": "******", "ts": "1582842709.000000" }, "ts": "1582842695.003200", "source_team": "T9T6EKEEB", "user_team": "T9T6EKEEB", }, "channel": "C9S8JGM2R", "previous_message": { "type": "message", "text": "Hi", "user": "******", "ts": "1582842695.003200", "team": "T9T6EKEEB", }, "event_ts": "1582842709.003300", "ts": "1582842709.003300", } async def test_create_message_from_edit(self): with amock.patch( "opsdroid.connector.slack.ConnectorSlack.lookup_username" ) as lookup, amock.patch("opsdroid.core.OpsDroid.parse") as parse: lookup.return_value = asyncio.Future() lookup.return_value.set_result({"name": "testuser"}) await self.connector.slack_rtm._dispatch_event( "message", self.edit_event) assert parse.called is False # lookup.assert_called_once_with("U9S8JGF45") async def test_create_event_fails(self): # The create_event method of the event creator is redundant in slack because the RTM is # doing the heavy lifting on that. Check that it fails loudly if it gets called. with self.assertRaises(NotImplementedError): await self.event_creator.create_event(self.message_event, "hello") async def test_get_user_name(self): # Check that username lookup works with a username with amock.patch( "opsdroid.connector.slack.ConnectorSlack.lookup_username" ) as lookup: lookup.return_value = asyncio.Future() lookup.return_value.set_result({"name": "testuser"}) username = await self.event_creator._get_user_name( self.message_event) assert username == "testuser" async def test_get_user_name_fails(self): # Check that username lookup works without a username userless_event = self.message_event del userless_event["user"] user_info = await self.event_creator._get_user_name(userless_event) assert user_info is None @property def channel_name_event(self): return { "type": "channel_rename", "channel": { "id": "C02ELGNBH", "name": "new_name", "created": 1360782804 }, "event_ts": "1582842709.003300", "ts": "1582842709.003300", } async def test_channel_rename(self): with amock.patch( "opsdroid.connector.slack.ConnectorSlack.lookup_username" ) as lookup, amock.patch("opsdroid.core.OpsDroid.parse") as parse: await self.connector.slack_rtm._dispatch_event( "channel_rename", self.channel_name_event) (called_event, ), _ = parse.call_args assert isinstance(called_event, events.RoomName) assert called_event.name == self.channel_name_event["channel"][ "name"] assert called_event.target == self.channel_name_event["channel"][ "id"] assert called_event.connector == self.connector assert called_event.event_id == self.channel_name_event["event_ts"] lookup.assert_not_called() @property def pin_added_event(self): return { "type": "pin_added", "user": "******", "channel_id": "C02ELGNBH", "item": self.message_event, "event_ts": "1582842709.003301", } async def test_pin_message(self): with amock.patch( "opsdroid.connector.slack.ConnectorSlack.lookup_username" ) as lookup, amock.patch("opsdroid.core.OpsDroid.parse") as parse: await self.connector.slack_rtm._dispatch_event( "pin_added", self.pin_added_event) (called_event, ), _ = parse.call_args assert isinstance(called_event, events.PinMessage) assert called_event.target == self.pin_added_event["channel_id"] assert called_event.connector == self.connector assert called_event.event_id == self.pin_added_event["event_ts"] assert called_event.linked_event == self.message_event lookup.assert_not_called() @property def pin_removed_event(self): return { "type": "pin_removed", "user": "******", "channel_id": "C02ELGNBH", "item": self.message_event, "event_ts": "1582842709.003302", } async def test_unpin_message(self): with amock.patch( "opsdroid.connector.slack.ConnectorSlack.lookup_username" ) as lookup, amock.patch("opsdroid.core.OpsDroid.parse") as parse: await self.connector.slack_rtm._dispatch_event( "pin_removed", self.pin_removed_event) (called_event, ), _ = parse.call_args assert isinstance(called_event, events.UnpinMessage) assert called_event.target == self.pin_removed_event["channel_id"] assert called_event.connector == self.connector assert called_event.event_id == self.pin_removed_event["event_ts"] assert called_event.linked_event == self.message_event lookup.assert_not_called() @property def topic_changed_event(self): return { "subtype": "channel_topic", "user": "******", "text": "<@U9S8JGF45> set the channel topic: New topic", "topic": "New topic", "team": "T9T6EKEEB", "channel": "CUTKP9FDG", "event_ts": "1587296471.000100", "ts": "1587296471.000100", } async def test_topic_change(self): with amock.patch( "opsdroid.connector.slack.ConnectorSlack.lookup_username" ) as lookup, amock.patch("opsdroid.core.OpsDroid.parse") as parse: await self.connector.slack_rtm._dispatch_event( "message", self.topic_changed_event) (called_event, ), _ = parse.call_args assert isinstance(called_event, events.RoomDescription) assert called_event.target == self.topic_changed_event["channel"] assert called_event.connector == self.connector assert called_event.event_id == self.topic_changed_event[ "event_ts"] assert called_event.description == self.topic_changed_event[ "topic"] lookup.assert_not_called()