From 0ae55b1116f9f14f52161c9d0ac86979a67867b4 Mon Sep 17 00:00:00 2001 From: "Gareth J. Greenaway" Date: Thu, 27 Oct 2022 09:40:13 -0700 Subject: [PATCH] Adding some additional tests for the slack engine. --- salt/engines/slack.py | 7 +- tests/pytests/unit/engines/test_slack.py | 251 ++++++++++++++++++++++- 2 files changed, 249 insertions(+), 9 deletions(-) diff --git a/salt/engines/slack.py b/salt/engines/slack.py index a079781f1f5d..fb69ab2417a4 100644 --- a/salt/engines/slack.py +++ b/salt/engines/slack.py @@ -233,6 +233,9 @@ def __init__(self, app_token, bot_token, trigger_string): # with the trigger string self.app.message(re.compile(trigger_pattern))(self.message_trigger) + def _run_until(self): + return True + def message_trigger(self, message): # Add the received message to the queue self.msg_queue.append(message) @@ -607,7 +610,7 @@ def just_data(m_data): self.handler ) ) # Boom! - while True: + while self._run_until(): while self.msg_queue: msg = self.msg_queue.popleft() try: @@ -816,7 +819,7 @@ def run_commands_from_slack_async( outstanding = {} # set of job_id that we need to check for - while True: + while self._run_until(): log.trace("Sleeping for interval of %s", interval) time.sleep(interval) # Drain the slack messages, up to 10 messages at a clip diff --git a/tests/pytests/unit/engines/test_slack.py b/tests/pytests/unit/engines/test_slack.py index 5fc486b9c910..b6a0df32266d 100644 --- a/tests/pytests/unit/engines/test_slack.py +++ b/tests/pytests/unit/engines/test_slack.py @@ -4,12 +4,12 @@ import pytest import salt.config -import salt.engines.slack as slack -from tests.support.mock import MagicMock, patch +import salt.engines.slack as slack_engine +from tests.support.mock import MagicMock, call, patch pytestmark = [ pytest.mark.skipif( - slack.HAS_SLACKBOLT is False, reason="The slack_bolt is not installed" + slack_engine.HAS_SLACKBOLT is False, reason="The slack_bolt is not installed" ) ] @@ -28,7 +28,7 @@ def __init__(self, *args, **kwargs): self.args = args self.kwargs = kwargs - self.client = None + self.client = MockSlackBoltAppClient() self.logger = None self.proxy = None @@ -36,9 +36,21 @@ def message(self, *args, **kwargs): return MagicMock(return_value=True) +class MockSlackBoltAppClient: + def __init__(self, *args, **kwargs): + self.args = args + self.kwargs = kwargs + + def chat_postMessage(self, *args, **kwargs): + return MagicMock(return_value=True) + + def files_upload(self, *args, **kwargs): + return MagicMock(return_value=True) + + @pytest.fixture def configure_loader_modules(): - return {slack: {}} + return {slack_engine: {}} @pytest.fixture @@ -48,7 +60,7 @@ def slack_client(): bot_token = "xoxb-xxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxx" trigger = "!" - with patch.dict(slack.__opts__, mock_opts): + with patch.dict(slack_engine.__opts__, mock_opts): with patch( "slack_bolt.App", MagicMock(autospec=True, return_value=MockSlackBoltApp()) ): @@ -56,7 +68,7 @@ def slack_client(): "slack_bolt.adapter.socket_mode.SocketModeHandler", MagicMock(autospec=True, return_value=MockSlackBoltSocketMode()), ): - slack_client = slack.SlackClient(app_token, bot_token, trigger) + slack_client = slack_engine.SlackClient(app_token, bot_token, trigger) yield slack_client @@ -123,3 +135,228 @@ def test_control_message_target(slack_client): ) assert target_commandline == _expected + + +def test_run_commands_from_slack_async(slack_client): + """ + Test slack engine: test_run_commands_from_slack_async + """ + + mock_job_status = { + "20221027001127600438": { + "data": {"minion": {"return": True, "retcode": 0, "success": True}}, + "function": "test.ping", + } + } + + message_generator = [ + { + "message_data": { + "client_msg_id": "c1d0c13d-5e78-431e-9921-4786a7d27543", + "type": "message", + "text": '!test.ping target="minion"', + "user": "U02QY11UJ", + "ts": "1666829486.542159", + "blocks": [ + { + "type": "rich_text", + "block_id": "2vdy", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "text", + "text": '!test.ping target="minion"', + } + ], + } + ], + } + ], + "team": "T02QY11UG", + "channel": "C02QY11UQ", + "event_ts": "1666829486.542159", + "channel_type": "channel", + }, + "channel": "C02QY11UQ", + "user": "U02QY11UJ", + "user_name": "garethgreenaway", + "cmdline": ["test.ping"], + "target": {"target": "minion", "tgt_type": "glob"}, + } + ] + + mock_files_upload_resp = { + "ok": True, + "file": { + "id": "F047YTDGJF9", + "created": 1666883749, + "timestamp": 1666883749, + "name": "salt-results-20221027081549173603.yaml", + "title": "salt-results-20221027081549173603", + "mimetype": "text/plain", + "filetype": "yaml", + "pretty_type": "YAML", + "user": "U0485K894PN", + "user_team": "T02QY11UG", + "editable": True, + "size": 18, + "mode": "snippet", + "is_external": False, + "external_type": "", + "is_public": True, + "public_url_shared": False, + "display_as_bot": False, + "username": "", + "url_private": "", + "url_private_download": "", + "permalink": "", + "permalink_public": "", + "edit_link": "", + "preview": "minion:\n True", + "preview_highlight": "", + "lines": 2, + "lines_more": 0, + "preview_is_truncated": False, + "comments_count": 0, + "is_starred": False, + "shares": { + "public": { + "C02QY11UQ": [ + { + "reply_users": [], + "reply_users_count": 0, + "reply_count": 0, + "ts": "1666883749.485979", + "channel_name": "general", + "team_id": "T02QY11UG", + "share_user_id": "U0485K894PN", + } + ] + } + }, + "channels": ["C02QY11UQ"], + "groups": [], + "ims": [], + "has_rich_preview": False, + "file_access": "visible", + }, + } + + patch_app_client_files_upload = patch.object( + MockSlackBoltAppClient, + "files_upload", + MagicMock(autospec=True, return_value=mock_files_upload_resp), + ) + patch_app_client_chat_postMessage = patch.object( + MockSlackBoltAppClient, + "chat_postMessage", + MagicMock(autospec=True, return_value=True), + ) + patch_slack_client_run_until = patch.object( + slack_client, "_run_until", MagicMock(autospec=True, side_effect=[True, False]) + ) + patch_slack_client_run_command_async = patch.object( + slack_client, + "run_command_async", + MagicMock(autospec=True, return_value="20221027001127600438"), + ) + patch_slack_client_get_jobs_from_runner = patch.object( + slack_client, + "get_jobs_from_runner", + MagicMock(autospec=True, return_value=mock_job_status), + ) + + upload_calls = call( + channels="C02QY11UQ", + content="minion:\n True", + filename="salt-results-20221027090136014442.yaml", + ) + + chat_postMessage_calls = [ + call( + channel="C02QY11UQ", + text="@garethgreenaway's job is submitted as salt jid 20221027001127600438", + ), + call( + channel="C02QY11UQ", + text="@garethgreenaway's job `['test.ping']` (id: 20221027001127600438) (target: {'target': 'minion', 'tgt_type': 'glob'}) returned", + ), + ] + + # + # test with control as True and fire_all as False + # + with patch_slack_client_run_until, patch_slack_client_run_command_async, patch_slack_client_get_jobs_from_runner, patch_app_client_files_upload as app_client_files_upload, patch_app_client_chat_postMessage as app_client_chat_postMessage: + slack_client.run_commands_from_slack_async( + message_generator=message_generator, + fire_all=False, + tag="salt/engines/slack", + control=True, + ) + app_client_files_upload.asser_has_calls(upload_calls) + app_client_chat_postMessage.asser_has_calls(chat_postMessage_calls) + + # + # test with control and fire_all as True + # + patch_slack_client_run_until = patch.object( + slack_client, "_run_until", MagicMock(autospec=True, side_effect=[True, False]) + ) + + mock_event_send = MagicMock(return_value=True) + patch_event_send = patch.dict( + slack_engine.__salt__, {"event.send": mock_event_send} + ) + + event_send_calls = [ + call( + "salt/engines/slack/message", + { + "message_data": { + "client_msg_id": "c1d0c13d-5e78-431e-9921-4786a7d27543", + "type": "message", + "text": '!test.ping target="minion"', + "user": "U02QY11UJ", + "ts": "1666829486.542159", + "blocks": [ + { + "type": "rich_text", + "block_id": "2vdy", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "text", + "text": '!test.ping target="minion"', + } + ], + } + ], + } + ], + "team": "T02QY11UG", + "channel": "C02QY11UQ", + "event_ts": "1666829486.542159", + "channel_type": "channel", + }, + "channel": "C02QY11UQ", + "user": "U02QY11UJ", + "user_name": "garethgreenaway", + "cmdline": ["test.ping"], + "target": {"target": "minion", "tgt_type": "glob"}, + }, + ) + ] + with patch_slack_client_run_until, patch_slack_client_run_command_async, patch_slack_client_get_jobs_from_runner, patch_event_send, patch_app_client_files_upload as app_client_files_upload, patch_app_client_chat_postMessage as app_client_chat_postMessage: + slack_client.run_commands_from_slack_async( + message_generator=message_generator, + fire_all=True, + tag="salt/engines/slack", + control=True, + ) + app_client_files_upload.asser_has_calls(upload_calls) + app_client_chat_postMessage.asser_has_calls(chat_postMessage_calls) + mock_event_send.asser_has_calls(event_send_calls)