class TestWebClient(unittest.TestCase): """Runs integration tests with real Slack API https://github.com/slackapi/python-slackclient/issues/560 """ def setUp(self): self.logger = logging.getLogger(__name__) self.bot_token = os.environ[SLACK_SDK_TEST_BOT_TOKEN] self.sync_client: WebClient = WebClient(token=self.bot_token, run_async=False, loop=asyncio.new_event_loop()) self.async_client: WebClient = WebClient(token=self.bot_token, run_async=True) def tearDown(self): pass def test_issue_560_success(self): client = self.sync_client response = client.conversations_list(exclude_archived=1) self.assertIsNotNone(response) response = client.conversations_list(exclude_archived="true") self.assertIsNotNone(response) @pytest.mark.skipif(condition=is_not_specified(), reason="still unfixed") def test_issue_560_failure(self): client = self.sync_client response = client.conversations_list(exclude_archived=True) self.assertIsNotNone(response) @pytest.mark.skipif(condition=is_not_specified(), reason="still unfixed") @async_test async def test_issue_560_failure_async(self): client = self.async_client response = await client.conversations_list(exclude_archived=True) self.assertIsNotNone(response)
class TestWebClient(unittest.TestCase): """Runs integration tests with real Slack API""" def setUp(self): self.logger = logging.getLogger(__name__) self.org_admin_token = os.environ[ SLACK_SDK_TEST_GRID_ORG_ADMIN_USER_TOKEN] self.sync_client: WebClient = WebClient(token=self.org_admin_token) self.sync_client.retry_handlers.append( RateLimitErrorRetryHandler(max_retry_count=2)) self.async_client: AsyncWebClient = AsyncWebClient( token=self.org_admin_token) self.async_client.retry_handlers.append( AsyncRateLimitErrorRetryHandler(max_retry_count=2)) def tearDown(self): pass @pytest.mark.skipif(condition=is_not_specified(), reason="execution can take long") def test_sync(self): client = self.sync_client for response in client.admin_users_session_list(limit=1): self.assertIsNotNone(response.get("active_sessions")) @pytest.mark.skipif(condition=is_not_specified(), reason="execution can take long") @async_test async def test_async(self): client = self.async_client async for response in await client.admin_users_session_list(limit=1): self.assertIsNotNone(response.get("active_sessions"))
class TestRTMClient(unittest.TestCase): """Runs integration tests with real Slack API https://github.com/slackapi/python-slackclient/issues/530 """ def setUp(self): self.logger = logging.getLogger(__name__) def tearDown(self): # Reset the decorators by @RTMClient.run_on RTMClient._callbacks = collections.defaultdict(list) @pytest.mark.skipif(condition=is_not_specified(), reason="still unfixed") def test_issue_530(self): try: rtm_client = RTMClient(token="I am not a token", run_async=False, loop=asyncio.new_event_loop()) rtm_client.start() self.fail("Raising an error here was expected") except Exception as e: self.assertEqual( str(e), "The server responded with: {'ok': False, 'error': 'invalid_auth'}" ) finally: if not rtm_client._stopped: rtm_client.stop() @pytest.mark.skipif(condition=is_not_specified(), reason="still unfixed") @async_test async def test_issue_530_async(self): try: rtm_client = RTMClient(token="I am not a token", run_async=True) await rtm_client.start() self.fail("Raising an error here was expected") except Exception as e: self.assertEqual( str(e), "The server responded with: {'ok': False, 'error': 'invalid_auth'}" ) finally: if not rtm_client._stopped: rtm_client.stop()
class TestRTMClient(unittest.TestCase): """Runs integration tests with real Slack API https://github.com/slackapi/python-slackclient/issues/605 """ def setUp(self): self.logger = logging.getLogger(__name__) self.bot_token = os.environ[SLACK_SDK_TEST_CLASSIC_APP_BOT_TOKEN] self.channel_id = os.environ[SLACK_SDK_TEST_RTM_TEST_CHANNEL_ID] self.rtm_client = RTMClient(token=self.bot_token, run_async=False) def tearDown(self): # Reset the decorators by @RTMClient.run_on RTMClient._callbacks = collections.defaultdict(list) @pytest.mark.skipif(condition=is_not_specified(), reason="still unfixed") def test_issue_605(self): self.text = "This message was sent to verify issue #605" self.called = False @RTMClient.run_on(event="message") def process_messages(**payload): self.logger.info(payload) self.called = True def connect(): self.logger.debug("Starting RTM Client...") self.rtm_client.start() t = threading.Thread(target=connect) t.setDaemon(True) try: t.start() self.assertFalse(self.called) time.sleep(3) self.web_client = WebClient( token=self.bot_token, run_async=False, loop=asyncio.new_event_loop( ), # TODO: this doesn't work without this ) new_message = self.web_client.chat_postMessage( channel=self.channel_id, text=self.text) self.assertFalse("error" in new_message) time.sleep(5) self.assertTrue(self.called) finally: t.join(.3)
class TestRTMClient(unittest.TestCase): """Runs integration tests with real Slack API https://github.com/slackapi/python-slackclient/issues/558 """ def setUp(self): self.logger = logging.getLogger(__name__) self.bot_token = os.environ[SLACK_SDK_TEST_CLASSIC_APP_BOT_TOKEN] def tearDown(self): # Reset the decorators by @RTMClient.run_on RTMClient._callbacks = collections.defaultdict(list) @pytest.mark.skipif(condition=is_not_specified(), reason="still unfixed") @async_test async def test_issue_558(self): channel_id = os.environ[SLACK_SDK_TEST_RTM_TEST_CHANNEL_ID] text = "This message was sent by <https://slack.dev/python-slackclient/|python-slackclient>! (test_issue_558)" self.message_count, self.reaction_count = 0, 0 async def process_messages(**payload): self.logger.debug(payload) self.message_count += 1 await asyncio.sleep(10) # this blocks all handlers async def process_reactions(**payload): self.logger.debug(payload) self.reaction_count += 1 rtm = RTMClient(token=self.bot_token, run_async=True) RTMClient.on(event='message', callback=process_messages) RTMClient.on(event='reaction_added', callback=process_reactions) web_client = WebClient(token=self.bot_token, run_async=True) message = await web_client.chat_postMessage(channel=channel_id, text=text) self.assertFalse("error" in message) ts = message["ts"] await asyncio.sleep(3) # intentionally not waiting here rtm.start() await asyncio.sleep(3) try: first_reaction = await web_client.reactions_add(channel=channel_id, timestamp=ts, name="eyes") self.assertFalse("error" in first_reaction) await asyncio.sleep(2) message = await web_client.chat_postMessage(channel=channel_id, text=text) self.assertFalse("error" in message) # start blocking here # This reaction_add event won't be handled due to a bug second_reaction = await web_client.reactions_add(channel=channel_id, timestamp=ts, name="tada") self.assertFalse("error" in second_reaction) await asyncio.sleep(2) self.assertEqual(self.message_count, 1) self.assertEqual(self.reaction_count, 2) # fails finally: if not rtm._stopped: rtm.stop()
class TestRTMClient(unittest.TestCase): """Runs integration tests with real Slack API https://github.com/slackapi/python-slack-sdk/issues/611 """ def setUp(self): self.logger = logging.getLogger(__name__) self.bot_token = os.environ[SLACK_SDK_TEST_CLASSIC_APP_BOT_TOKEN] def tearDown(self): # Reset the decorators by @RTMClient.run_on RTMClient._callbacks = collections.defaultdict(list) @pytest.mark.skipif(condition=is_not_specified(), reason="To avoid rate limited errors") @async_test async def test_issue_611(self): channel_id = os.environ[SLACK_SDK_TEST_RTM_TEST_CHANNEL_ID] text = "This message was sent by <https://slack.dev/python-slackclient/|python-slackclient>! (test_issue_611)" self.message_count, self.reaction_count = 0, 0 async def process_messages(**payload): self.logger.info(payload) if "subtype" in payload["data"] and payload["data"][ "subtype"] == "message_replied": return # skip self.message_count += 1 raise Exception("something is wrong!" ) # This causes the termination of the process async def process_reactions(**payload): self.logger.info(payload) self.reaction_count += 1 rtm = RTMClient(token=self.bot_token, run_async=True) RTMClient.on(event='message', callback=process_messages) RTMClient.on(event='reaction_added', callback=process_reactions) web_client = WebClient(token=self.bot_token, run_async=True) message = await web_client.chat_postMessage(channel=channel_id, text=text) ts = message["ts"] await asyncio.sleep(3) # intentionally not waiting here rtm.start() try: await asyncio.sleep(3) first_reaction = await web_client.reactions_add(channel=channel_id, timestamp=ts, name="eyes") self.assertFalse("error" in first_reaction) await asyncio.sleep(2) should_be_ignored = await web_client.chat_postMessage( channel=channel_id, text="Hello?", thread_ts=ts) self.assertFalse("error" in should_be_ignored) await asyncio.sleep(2) second_reaction = await web_client.reactions_add( channel=channel_id, timestamp=ts, name="tada") self.assertFalse("error" in second_reaction) await asyncio.sleep(2) self.assertEqual(self.message_count, 1) self.assertEqual(self.reaction_count, 2) finally: if not rtm._stopped: rtm.stop()
class TestWebClient(unittest.TestCase): """Runs integration tests with real Slack API""" def setUp(self): if not hasattr(self, "logger"): self.logger = logging.getLogger(__name__) self.bot_token = os.environ[SLACK_SDK_TEST_BOT_TOKEN] self.async_client: WebClient = WebClient(token=self.bot_token, run_async=True) self.sync_client: WebClient = WebClient( token=self.bot_token, run_async=False, loop=asyncio.new_event_loop(), # TODO: remove this ) self.channel_id = os.environ[SLACK_SDK_TEST_WEB_TEST_CHANNEL_ID] def tearDown(self): pass # ------------------------- # api.test def test_api_test(self): response: SlackResponse = self.sync_client.api_test(foo="bar") self.assertEqual(response["args"]["foo"], "bar") @async_test async def test_api_test_async(self): response: SlackResponse = await self.async_client.api_test(foo="bar") self.assertEqual(response["args"]["foo"], "bar") # ------------------------- # auth.test def test_auth_test(self): response: SlackResponse = self.sync_client.auth_test() self.verify_auth_test_response(response) @async_test async def test_auth_test_async(self): response: SlackResponse = await self.async_client.auth_test() self.verify_auth_test_response(response) def verify_auth_test_response(self, response): self.assertIsNotNone(response["url"]) self.assertIsNotNone(response["user"]) self.assertIsNotNone(response["user_id"]) self.assertIsNotNone(response["team"]) self.assertIsNotNone(response["team_id"]) self.assertIsNotNone(response["bot_id"]) # ------------------------- # basic metadata retrieval def test_metadata_retrieval(self): client = self.sync_client auth = client.auth_test() self.assertIsNotNone(auth) bot = client.bots_info(bot=auth["bot_id"]) self.assertIsNotNone(bot) @async_test async def test_metadata_retrieval_async(self): client = self.async_client auth = await client.auth_test() self.assertIsNotNone(auth) bot = await client.bots_info(bot=auth["bot_id"]) self.assertIsNotNone(bot) # ------------------------- # basic chat operations def test_basic_chat_operations(self): client = self.sync_client auth = client.auth_test() self.assertIsNotNone(auth) subdomain = auth["team"] channel = self.channel_id message = "This message was posted by <https://slack.dev/python-slackclient/|python-slackclient>! " + \ "(integration_tests/test_web_client.py #test_chat_operations)" new_message: SlackResponse = client.chat_postMessage(channel=channel, text=message) self.assertEqual(new_message["message"]["text"], message) ts = new_message["ts"] permalink = client.chat_getPermalink(channel=channel, message_ts=ts) self.assertIsNotNone(permalink) self.assertRegex( permalink["permalink"], f"https://{subdomain}.slack.com/archives/{channel}/.+") new_reaction = client.reactions_add(channel=channel, timestamp=ts, name="eyes") self.assertIsNotNone(new_reaction) reactions = client.reactions_get(channel=channel, timestamp=ts) self.assertIsNotNone(reactions) reaction_removal = client.reactions_remove(channel=channel, timestamp=ts, name="eyes") self.assertIsNotNone(reaction_removal) thread_reply = client.chat_postMessage(channel=channel, thread_ts=ts, text="threading...") self.assertIsNotNone(thread_reply) modification = client.chat_update(channel=channel, ts=ts, text="Is this intentional?") self.assertIsNotNone(modification) reply_deletion = client.chat_delete(channel=channel, ts=thread_reply["ts"]) self.assertIsNotNone(reply_deletion) message_deletion = client.chat_delete(channel=channel, ts=ts) self.assertIsNotNone(message_deletion) @async_test async def test_basic_chat_operations_async(self): client = self.async_client auth = await client.auth_test() self.assertIsNotNone(auth) subdomain = auth["team"] channel = self.channel_id message = "This message was posted by <https://slack.dev/python-slackclient/|python-slackclient>! " + \ "(integration_tests/test_web_client.py #test_chat_operations)" new_message: SlackResponse = await client.chat_postMessage( channel=channel, text=message) self.assertEqual(new_message["message"]["text"], message) ts = new_message["ts"] permalink = await client.chat_getPermalink(channel=channel, message_ts=ts) self.assertIsNotNone(permalink) self.assertRegex( permalink["permalink"], f"https://{subdomain}.slack.com/archives/{channel}/.+") new_reaction = await client.reactions_add(channel=channel, timestamp=ts, name="eyes") self.assertIsNotNone(new_reaction) reactions = await client.reactions_get(channel=channel, timestamp=ts) self.assertIsNotNone(reactions) reaction_removal = await client.reactions_remove(channel=channel, timestamp=ts, name="eyes") self.assertIsNotNone(reaction_removal) thread_reply = await client.chat_postMessage(channel=channel, thread_ts=ts, text="threading...") self.assertIsNotNone(thread_reply) modification = await client.chat_update(channel=channel, ts=ts, text="Is this intentional?") self.assertIsNotNone(modification) reply_deletion = await client.chat_delete(channel=channel, ts=thread_reply["ts"]) self.assertIsNotNone(reply_deletion) message_deletion = await client.chat_delete(channel=channel, ts=ts) self.assertIsNotNone(message_deletion) # ------------------------- # file operations def test_uploading_text_files(self): client = self.sync_client file, filename = __file__, os.path.basename(__file__) upload = client.files_upload(channels=self.channel_id, filename=filename, file=file) self.assertIsNotNone(upload) deletion = client.files_delete(file=upload["file"]["id"]) self.assertIsNotNone(deletion) @async_test async def test_uploading_text_files_async(self): client = self.async_client file, filename = __file__, os.path.basename(__file__) upload = await client.files_upload(channels=self.channel_id, title="Good Old Slack Logo", filename=filename, file=file) self.assertIsNotNone(upload) deletion = await client.files_delete(file=upload["file"]["id"]) self.assertIsNotNone(deletion) def test_uploading_binary_files(self): client = self.sync_client current_dir = os.path.dirname(__file__) file = f"{current_dir}/../../tests/data/slack_logo.png" upload = client.files_upload(channels=self.channel_id, title="Good Old Slack Logo", filename="slack_logo.png", file=file) self.assertIsNotNone(upload) deletion = client.files_delete(file=upload["file"]["id"]) self.assertIsNotNone(deletion) def test_uploading_binary_files_as_content(self): client = self.sync_client current_dir = os.path.dirname(__file__) file = f"{current_dir}/../../tests/data/slack_logo.png" with open(file, 'rb') as f: content = f.read() upload = client.files_upload(channels=self.channel_id, title="Good Old Slack Logo", filename="slack_logo.png", content=content) self.assertIsNotNone(upload) deletion = client.files_delete(file=upload["file"]["id"]) self.assertIsNotNone(deletion) @async_test async def test_uploading_binary_files_async(self): client = self.async_client current_dir = os.path.dirname(__file__) file = f"{current_dir}/../../tests/data/slack_logo.png" upload = await client.files_upload(channels=self.channel_id, title="Good Old Slack Logo", filename="slack_logo.png", file=file) self.assertIsNotNone(upload) deletion = client.files_delete(file=upload["file"]["id"]) self.assertIsNotNone(deletion) def test_uploading_file_with_token_param(self): client = WebClient() current_dir = os.path.dirname(__file__) file = f"{current_dir}/../../tests/data/slack_logo.png" upload = client.files_upload( token=self.bot_token, channels=self.channel_id, title="Good Old Slack Logo", filename="slack_logo.png", file=file, ) self.assertIsNotNone(upload) deletion = client.files_delete( token=self.bot_token, file=upload["file"]["id"], ) self.assertIsNotNone(deletion) @async_test async def test_uploading_file_with_token_param_async(self): client = WebClient(run_async=True) current_dir = os.path.dirname(__file__) file = f"{current_dir}/../../tests/data/slack_logo.png" upload = await client.files_upload( token=self.bot_token, channels=self.channel_id, title="Good Old Slack Logo", filename="slack_logo.png", file=file, ) self.assertIsNotNone(upload) deletion = client.files_delete( token=self.bot_token, file=upload["file"]["id"], ) self.assertIsNotNone(deletion) # ------------------------- # pagination def test_pagination_with_iterator(self): client = self.sync_client fetched_count = 0 # SlackResponse is an iterator that fetches next if next_cursor is not "" for response in client.conversations_list(limit=1, exclude_archived=1, types="public_channel"): fetched_count += len(response["channels"]) if fetched_count > 1: break self.assertGreater(fetched_count, 1) def test_pagination_with_iterator_use_sync_aiohttp(self): client: WebClient = WebClient( token=self.bot_token, run_async=False, use_sync_aiohttp=True, loop=asyncio.new_event_loop(), ) fetched_count = 0 # SlackResponse is an iterator that fetches next if next_cursor is not "" for response in client.conversations_list(limit=1, exclude_archived=1, types="public_channel"): fetched_count += len(response["channels"]) if fetched_count > 1: break self.assertGreater(fetched_count, 1) @pytest.mark.skipif(condition=is_not_specified(), reason="still unfixed") @async_test async def test_pagination_with_iterator_async(self): client = self.async_client fetched_count = 0 # SlackResponse is an iterator that fetches next if next_cursor is not "" for response in await client.conversations_list( limit=1, exclude_archived=1, types="public_channel"): fetched_count += len(response["channels"]) if fetched_count > 1: break self.assertGreater(fetched_count, 1)
class TestRTMClient(unittest.TestCase): """Runs integration tests with real Slack API https://github.com/slackapi/python-slack-sdk/issues/569 """ def setUp(self): if not hasattr(self, "logger"): self.logger = logging.getLogger(__name__) self.channel_id = os.environ[SLACK_SDK_TEST_RTM_TEST_CHANNEL_ID] self.bot_token = os.environ[SLACK_SDK_TEST_CLASSIC_APP_BOT_TOKEN] if not hasattr( self, "cpu_monitor") or not TestRTMClient.cpu_monitor.is_alive(): def run_cpu_monitor(self): self.logger.debug("Starting CPU monitor in another thread...") TestRTMClient.cpu_usage = 0 while True: p = psutil.Process(os.getpid()) current_cpu_usage: float = p.cpu_percent(interval=0.5) self.logger.debug(current_cpu_usage) if current_cpu_usage > TestRTMClient.cpu_usage: TestRTMClient.cpu_usage = current_cpu_usage TestRTMClient.cpu_monitor = threading.Thread( target=run_cpu_monitor, args=[self]) TestRTMClient.cpu_monitor.daemon = True TestRTMClient.cpu_monitor.start() self.rtm_client = None self.web_client = None def tearDown(self): # Reset the decorators by @RTMClient.run_on RTMClient._callbacks = collections.defaultdict(list) # Stop the Client if hasattr(self, "rtm_client") and not self.rtm_client._stopped: self.rtm_client.stop() @pytest.mark.skipif(condition=is_not_specified(), reason="To avoid rate_limited errors") def test_cpu_usage(self): self.rtm_client = RTMClient(token=self.bot_token, run_async=False, loop=asyncio.new_event_loop()) self.web_client = WebClient(token=self.bot_token) self.call_count = 0 TestRTMClient.cpu_usage = 0 @RTMClient.run_on(event="message") def send_reply(**payload): self.logger.debug(payload) event = payload["data"] if "text" in event: if not str(event["text"]).startswith("Current CPU usage:"): web_client = payload["web_client"] for i in range(0, 3): new_message = web_client.chat_postMessage( channel=event["channel"], text= f"Current CPU usage: {TestRTMClient.cpu_usage} % (test_cpu_usage)", ) self.logger.debug(new_message) self.call_count += 1 def connect(): self.logger.debug("Starting RTM Client...") self.rtm_client.start() rtm = threading.Thread(target=connect) rtm.daemon = True rtm.start() time.sleep(5) text = "This message was sent by <https://slack.dev/python-slackclient/|python-slackclient>! (test_cpu_usage)" new_message = self.web_client.chat_postMessage(channel=self.channel_id, text=text) self.assertFalse("error" in new_message) time.sleep(5) self.assertLess(TestRTMClient.cpu_usage, 30, "Too high CPU usage detected") self.assertEqual(self.call_count, 3, "The RTM handler failed") # > self.assertLess(TestRTMClient.cpu_usage, 30, "Too high CPU usage detected") # E AssertionError: 100.2 not less than 30 : Too high CPU usage detected # # integration_tests/rtm/test_rtm_client.py:160: AssertionError @async_test async def test_cpu_usage_async(self): self.rtm_client = RTMClient(token=self.bot_token, run_async=True) self.web_client = LegacyWebClient(token=self.bot_token, run_async=True) self.call_count = 0 TestRTMClient.cpu_usage = 0 @RTMClient.run_on(event="message") async def send_reply_async(**payload): self.logger.debug(payload) event = payload["data"] if "text" in event: if not str(event["text"]).startswith("Current CPU usage:"): web_client = payload["web_client"] for i in range(0, 3): new_message = await web_client.chat_postMessage( channel=event["channel"], text= f"Current CPU usage: {TestRTMClient.cpu_usage} % (test_cpu_usage_async)", ) self.logger.debug(new_message) self.call_count += 1 # intentionally not waiting here self.rtm_client.start() await asyncio.sleep(5) text = "This message was sent by <https://slack.dev/python-slackclient/|python-slackclient>! (test_cpu_usage_async)" new_message = await self.web_client.chat_postMessage( channel=self.channel_id, text=text) self.assertFalse("error" in new_message) await asyncio.sleep(5) self.assertLess(TestRTMClient.cpu_usage, 30, "Too high CPU usage detected") self.assertEqual(self.call_count, 3, "The RTM handler failed")
class TestRTMClient(unittest.TestCase): """Runs integration tests with real Slack API https://github.com/slackapi/python-slack-sdk/issues/701 """ def setUp(self): self.logger = logging.getLogger(__name__) self.bot_token = os.environ[SLACK_SDK_TEST_CLASSIC_APP_BOT_TOKEN] def tearDown(self): # Reset the decorators by @RTMClient.run_on RTMClient._callbacks = collections.defaultdict(list) # @pytest.mark.skipif(condition=is_not_specified(), reason="to avoid rate_limited errors") @pytest.mark.skip() def test_receiving_all_messages(self): self.rtm_client = RTMClient(token=self.bot_token, loop=asyncio.new_event_loop()) self.web_client = WebClient(token=self.bot_token) self.call_count = 0 @RTMClient.run_on(event="message") def send_reply(**payload): self.logger.debug(payload) web_client, data = payload["web_client"], payload["data"] web_client.reactions_add(channel=data["channel"], timestamp=data["ts"], name="eyes") self.call_count += 1 def connect(): self.logger.debug("Starting RTM Client...") self.rtm_client.start() rtm = threading.Thread(target=connect) rtm.daemon = True rtm.start() time.sleep(3) total_num = 10 sender_completion = [] def sent_bulk_message(): for i in range(total_num): text = f"Sent by <https://slack.dev/python-slackclient/|python-slackclient>! ({i})" self.web_client.chat_postMessage(channel="#random", text=text) time.sleep(0.1) sender_completion.append(True) num_of_senders = 3 senders = [] for sender_num in range(num_of_senders): sender = threading.Thread(target=sent_bulk_message) sender.daemon = True sender.start() senders.append(sender) while len(sender_completion) < num_of_senders: time.sleep(1) expected_call_count = total_num * num_of_senders wait_seconds = 0 max_wait = 20 while self.call_count < expected_call_count and wait_seconds < max_wait: time.sleep(1) wait_seconds += 1 self.assertEqual(total_num * num_of_senders, self.call_count, "The RTM handler failed") @pytest.mark.skipif(condition=is_not_specified(), reason="to avoid rate_limited errors") @async_test async def test_receiving_all_messages_async(self): self.rtm_client = RTMClient(token=self.bot_token, run_async=True) self.web_client = WebClient(token=self.bot_token, run_async=False) self.call_count = 0 @RTMClient.run_on(event="message") async def send_reply(**payload): self.logger.debug(payload) web_client, data = payload["web_client"], payload["data"] await web_client.reactions_add(channel=data["channel"], timestamp=data["ts"], name="eyes") self.call_count += 1 # intentionally not waiting here self.rtm_client.start() await asyncio.sleep(3) total_num = 10 sender_completion = [] def sent_bulk_message(): for i in range(total_num): text = f"Sent by <https://slack.dev/python-slackclient/|python-slackclient>! ({i})" self.web_client.chat_postMessage(channel="#random", text=text) time.sleep(0.1) sender_completion.append(True) num_of_senders = 3 senders = [] for sender_num in range(num_of_senders): sender = threading.Thread(target=sent_bulk_message) sender.daemon = True sender.start() senders.append(sender) while len(sender_completion) < num_of_senders: await asyncio.sleep(1) expected_call_count = total_num * num_of_senders wait_seconds = 0 max_wait = 20 while self.call_count < expected_call_count and wait_seconds < max_wait: await asyncio.sleep(1) wait_seconds += 1 self.assertEqual(total_num * num_of_senders, self.call_count, "The RTM handler failed")
class TestWebClient(unittest.TestCase): """Runs integration tests with real Slack API https://github.com/slackapi/python-slackclient/issues/594 """ def setUp(self): self.logger = logging.getLogger(__name__) self.bot_token = os.environ[SLACK_SDK_TEST_BOT_TOKEN] self.sync_client: WebClient = WebClient(token=self.bot_token, run_async=False, loop=asyncio.new_event_loop()) self.async_client: WebClient = WebClient(token=self.bot_token, run_async=True) self.channel_id = os.environ[SLACK_SDK_TEST_WEB_TEST_CHANNEL_ID] self.user_id = os.environ[SLACK_SDK_TEST_WEB_TEST_USER_ID] def tearDown(self): pass @pytest.mark.skipif(condition=is_not_specified(), reason="still unfixed") def test_issue_594(self): client, logger = self.sync_client, self.logger external_url = "https://www.example.com/good-old-slack-logo" external_id = f"test-remote-file-{uuid4()}" current_dir = os.path.dirname(__file__) image = f"{current_dir}/../../tests/data/slack_logo.png" creation = client.files_remote_add( external_id=external_id, external_url=external_url, title="Good Old Slack Logo", indexable_file_contents="Good Old Slack Logo", preview_image=image) self.assertIsNotNone(creation) sharing = client.files_remote_share(channels=self.channel_id, external_id=external_id) self.assertIsNotNone(sharing) message = client.chat_postEphemeral( channel=self.channel_id, user="******", blocks=[{ "type": "section", "text": { "type": "mrkdwn", "text": "This is a mrkdwn section block :ghost: *this is bold*, and ~this is crossed out~, and <https://google.com|this is a link>" } }, { "type": "file", "external_id": external_id, "source": "remote", }], ) self.assertIsNotNone(message) def test_no_preview_image(self): client, logger = self.sync_client, self.logger external_url = "https://www.example.com/what-is-slack" external_id = f"test-remote-file-{uuid4()}" creation = client.files_remote_add( external_id=external_id, external_url=external_url, title="Slack (Wikipedia)", indexable_file_contents= "Slack is a proprietary business communication platform developed by Slack Technologies.", ) self.assertIsNotNone(creation) sharing = client.files_remote_share(channels=self.channel_id, external_id=external_id) self.assertIsNotNone(sharing) message = client.chat_postEphemeral( channel=self.channel_id, user="******", blocks=[{ "type": "section", "text": { "type": "mrkdwn", "text": "This is a mrkdwn section block :ghost: *this is bold*, and ~this is crossed out~, and <https://google.com|this is a link>" } }, { "type": "file", "external_id": external_id, "source": "remote", }], ) self.assertIsNotNone(message)
class TestRTMClient(unittest.TestCase): """Runs integration tests with real Slack API https://github.com/slackapi/python-slack-sdk/issues/631 """ def setUp(self): if not hasattr(self, "logger"): self.logger = logging.getLogger(__name__) self.channel_id = os.environ[SLACK_SDK_TEST_RTM_TEST_CHANNEL_ID] self.bot_token = os.environ[SLACK_SDK_TEST_CLASSIC_APP_BOT_TOKEN] def tearDown(self): # Reset the decorators by @RTMClient.run_on RTMClient._callbacks = collections.defaultdict(list) # Stop the Client if hasattr(self, "rtm_client") and not self.rtm_client._stopped: self.rtm_client.stop() @pytest.mark.skipif( condition=is_not_specified(), reason="to avoid rate_limited errors" ) def test_issue_631_sharing_event_loop(self): self.success = None self.text = "This message was sent to verify issue #631" self.rtm_client = RTMClient( token=self.bot_token, run_async=False, loop=asyncio.new_event_loop(), # TODO: this doesn't work without this ) # @RTMClient.run_on(event="message") # def send_reply(**payload): # self.logger.debug(payload) # data = payload['data'] # web_client = payload['web_client'] # web_client._event_loop = self.loop # # Maybe you will also need the following line uncommented # # web_client.run_async = True # # if self.text in data['text']: # channel_id = data['channel'] # thread_ts = data['ts'] # try: # self.success = web_client.chat_postMessage( # channel=channel_id, # text="Thanks!", # thread_ts=thread_ts # ) # except Exception as e: # # slack.rtm.client:client.py:446 When calling '#send_reply()' # # in the 'test_rtm_client' module the following error was raised: This event loop is already running # self.logger.error(traceback.format_exc()) # raise e # Solution (1) for #631 @RTMClient.run_on(event="message") def send_reply(**payload): self.logger.debug(payload) data = payload["data"] web_client = payload["web_client"] try: if "text" in data and self.text in data["text"]: channel_id = data["channel"] thread_ts = data["ts"] self.success = web_client.chat_postMessage( channel=channel_id, text="Thanks!", thread_ts=thread_ts ) except Exception as e: self.logger.error(traceback.format_exc()) raise e def connect(): self.logger.debug("Starting RTM Client...") self.rtm_client.start() t = threading.Thread(target=connect) t.daemon = True t.start() try: self.assertIsNone(self.success) time.sleep(5) self.web_client = WebClient( token=self.bot_token, run_async=False, ) new_message = self.web_client.chat_postMessage( channel=self.channel_id, text=self.text ) self.assertFalse("error" in new_message) time.sleep(5) self.assertIsNotNone(self.success) finally: t.join(0.3) # Solution (2) for #631 @pytest.mark.skipif( condition=is_not_specified(), reason="this is just for reference" ) @async_test async def test_issue_631_sharing_event_loop_async(self): self.success = None self.text = "This message was sent to verify issue #631" # To make run_async=True, the test method needs to be an async function + @async_test decorator self.rtm_client = RTMClient(token=self.bot_token, run_async=True) self.web_client = WebClient(token=self.bot_token, run_async=True) @RTMClient.run_on(event="message") async def send_reply(**payload): self.logger.debug(payload) data = payload["data"] web_client = payload["web_client"] try: if "text" in data and self.text in data["text"]: channel_id = data["channel"] thread_ts = data["ts"] self.success = await web_client.chat_postMessage( channel=channel_id, text="Thanks!", thread_ts=thread_ts ) except Exception as e: self.logger.error(traceback.format_exc()) raise e # intentionally not waiting here self.rtm_client.start() self.assertIsNone(self.success) await asyncio.sleep(5) self.web_client = WebClient( token=self.bot_token, run_async=True, # all need to be async here ) new_message = await self.web_client.chat_postMessage( channel=self.channel_id, text=self.text ) self.assertFalse("error" in new_message) await asyncio.sleep(5) self.assertIsNotNone(self.success)