def test_not_exists_user_id(self): # APIにアクセスしたくないため、モックで例外を投げている # ユニットテストとしては意味がないが、仕様記載の意味で記載しておく self.mock_method.side_effect = SlackApiError("a", "b") with pytest.raises(SlackApiError): get_user_name("NOT_EXISTS_USER_ID")
async def _perform_http_request( self, *, body: Dict[str, Any], headers: Dict[str, str]) -> WebhookResponse: """Performs an HTTP request and parses the response. :param url: a complete URL to send data (e.g., https://hooks.slack.com/XXX) :param body: request body data :param headers: complete set of request headers :return: API response """ body = json.dumps(body) headers["Content-Type"] = "application/json;charset=utf-8" if self.logger.level <= logging.DEBUG: self.logger.debug( f"Sending a request - url: {self.url}, body: {body}, headers: {headers}" ) session: Optional[ClientSession] = None use_running_session = self.session and not self.session.closed if use_running_session: session = self.session else: session = aiohttp.ClientSession( timeout=aiohttp.ClientTimeout(total=self.timeout), auth=self.auth, trust_env=self.trust_env_in_session, ) resp: WebhookResponse try: request_kwargs = { "headers": headers, "data": body, "ssl": self.ssl, "proxy": self.proxy, } async with session.request("POST", self.url, **request_kwargs) as res: response_body = {} try: response_body = await res.text() except aiohttp.ContentTypeError: self.logger.debug( f"No response data returned from the following API call: {self.url}." ) except json.decoder.JSONDecodeError as e: message = f"Failed to parse the response body: {str(e)}" raise SlackApiError(message, res) resp = WebhookResponse( url=self.url, status_code=res.status, body=response_body, headers=res.headers, ) _debug_log_response(self.logger, resp) finally: if not use_running_session: await session.close() return resp
def test_chat_post_message_raises_other(mocker, slack_api): slack_api.client.channel = "test" err_resp = new_slack_response({"ok": False, "error": "no_text"}) slack_api.mock_slack_client.return_value.chat_postMessage.side_effect = ( SlackApiError("error", err_resp) ) with pytest.raises(SlackApiError): slack_api.client.chat_post_message("foo") slack_api.mock_slack_client.return_value.chat_postMessage.assert_called_once()
def test_get_user_id_by_name_user_not_found(slack_api): """ Check that UserNotFoundException will be raised under expected conditions. """ slack_api.mock_slack_client.return_value.users_lookupByEmail.side_effect = ( SlackApiError("Some error message", {"error": "users_not_found"}) ) with pytest.raises(UserNotFoundException): slack_api.client.get_user_id_by_name("someuser", "redhat.com")
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 = SlackApiError( message="", response="") await connector.connect() self.assertLogs("_LOGGER", "error")
def test_get_invalid_permissions_icon_url(mock_webclient): class FakeResponse: data = {"error": "missing_scope", "needed": "users:read"} fake_error = SlackApiError("message", FakeResponse()) mock_webclient().users_list.side_effect = fake_error manager = SlackMessageManager(sender_token="Fake", sender_name="test_invalid_bot") url = manager._get_icon_url() assert url is None
def test_get_user_id_by_name_reraise(slack_api): """ Check that SlackApiError is re-raised when not otherwise handled as a user not found error. """ slack_api.mock_slack_client.return_value.users_lookupByEmail.side_effect = ( SlackApiError("Some error message", {"error": "internal_error"}) ) with pytest.raises(SlackApiError): slack_api.client.get_user_id_by_name("someuser", "redhat.com")
def test_get_user_id_by_name_user_not_found(get_config_mock, slack_api): """ Check that UserNotFoundException will be raised under expected conditions. """ get_config_mock.return_value = {'smtp': {'mail_address': 'redhat.com'}} slack_api.mock_slack_client.return_value\ .users_lookupByEmail.side_effect = \ SlackApiError('Some error message', {'error': 'users_not_found'}) with pytest.raises(UserNotFoundException): slack_api.client.get_user_id_by_name('someuser')
def test_update_usergroups_users_raise(slack_api): """ Any errors other than invalid_users should result in an exception being raised. """ slack_api.mock_slack_client.return_value.usergroups_users_update \ .side_effect = SlackApiError('Some error message', {'error': 'internal_error'}) with pytest.raises(SlackApiError): slack_api.client.update_usergroup_users('ABCD', ['USERA'])
def test_update_usergroups_users_raise(slack_api): """ Any errors other than invalid_users should result in an exception being raised. """ slack_api.mock_slack_client.return_value.usergroups_users_update.side_effect = ( SlackApiError("Some error message", {"error": "internal_error"}) ) with pytest.raises(SlackApiError): slack_api.client.update_usergroup_users("ABCD", ["USERA"])
def test_update_usergroups_users_empty_no_raise(mocker, slack_api): """ invalid_users errors shouldn't be raised because providing an empty list is actually removing users from the usergroup. """ mocker.patch.object(SlackApi, "get_random_deleted_user", autospec=True) slack_api.mock_slack_client.return_value.usergroups_users_update.side_effect = ( SlackApiError("Some error message", {"error": "invalid_users"}) ) slack_api.client.update_usergroup_users("ABCD", [])
def test_update_usergroups_users_empty_no_raise(mocker, slack_api): """ invalid_users errors shouldn't be raised because providing an empty list is actually removing users from the usergroup. """ mocker.patch.object(SlackApi, 'get_random_deleted_user', autospec=True) slack_api.mock_slack_client.return_value.usergroups_users_update\ .side_effect = SlackApiError('Some error message', {'error': 'invalid_users'}) slack_api.client.update_usergroup_users('ABCD', [])
async def with_retry_coro(): data = {"ok": False, "error": "ratelimited"} response = AsyncSlackResponse(client=None, http_verb=None, api_url=None, req_args=None, data=data, headers={"Retry-After": retry_time}, status_code=429) raise SlackApiError("The request to the Slack API failed.", response)
def test_get_user_id_by_name_reraise(get_config_mock, slack_api): """ Check that SlackApiError is re-raised when not otherwise handled as a user not found error. """ get_config_mock.return_value = {'smtp': {'mail_address': 'redhat.com'}} slack_api.mock_slack_client.return_value\ .users_lookupByEmail.side_effect = \ SlackApiError('Some error message', {'error': 'internal_error'}) with pytest.raises(SlackApiError): slack_api.client.get_user_id_by_name('someuser')
def test_chat_post_message_channel_not_found(mocker, slack_api): slack_api.client.channel = "test" mock_join = mocker.patch( "reconcile.utils.slack_api.SlackApi.join_channel", autospec=True ) nf_resp = new_slack_response({"ok": False, "error": "not_in_channel"}) slack_api.mock_slack_client.return_value.chat_postMessage.side_effect = [ SlackApiError("error", nf_resp), None, ] slack_api.client.chat_post_message("foo") assert slack_api.mock_slack_client.return_value.chat_postMessage.call_count == 2 mock_join.assert_called_once()
def get_rate_limited_slack_response_error(): return SlackApiError( 'ratelimited', SlackResponse(data={ 'ok': False, 'error': 'ratelimited' }, client=None, headers={'retry-after': '0'}, req_args=None, api_url="", http_verb="", status_code=400))
def test_call_with_failure(self, slack_client_class_mock): slack_client_mock = mock.Mock() slack_client_class_mock.return_value = slack_client_mock expected_exception = SlackApiError(message='foo', response='bar') slack_client_mock.api_call = mock.Mock(side_effect=expected_exception) test_token = 'test_token' test_slack_conn_id = 'test_slack_conn_id' slack_hook = SlackHook(token=test_token, slack_conn_id=test_slack_conn_id) test_method = 'test_method' test_api_params = {'key1': 'value1', 'key2': 'value2'} with pytest.raises(SlackApiError): slack_hook.call(test_method, test_api_params)
def test_get_invalid_unknown_slack_error_icon_url(mock_webclient): class FakeResponse: data = {"error": "unknown", "needed": "unknown"} fake_error = SlackApiError("mocked error", FakeResponse()) mock_webclient().users_list.side_effect = fake_error manager = SlackMessageManager(sender_token="Fake", sender_name="test_invalid_bot") with pytest.raises(SlackApiError) as excinfo: manager._get_icon_url() assert excinfo.value.response.data == { "error": "unknown", "needed": "unknown" }
async def test_react_invalid_name(self): connector = ConnectorSlack({"token": "abc123"}, opsdroid=self.od) connector.slack.api_call = amock.CoroutineMock( side_effect=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=SlackApiError("unknown", "unknown")) with self.assertRaises(SlackApiError): prev_message = events.Message( text="test", user="******", target="room", connector=connector, raw_event={"ts": 0}, ) await prev_message.respond(events.Reaction("😀"))
def collect_channel_info(channel): channel_id = channel["id"] try: info_response: Dict = bot.app.client.conversations_info( channel=channel_id, include_num_members=True ) if not info_response["ok"]: raise SlackApiError("converstation.info error", info_response) history_response: Dict = bot.app.client.conversations_history( channel=channel_id, limit=1 ) if not history_response["ok"]: raise SlackApiError("conversation.history error", history_response) except SlackApiError as exc: logging.error(exc) return "I am truly sorry but something went wrong ;(" channel_info: Dict = info_response["channel"] channel_history: Dict = history_response["messages"][0] latest_ts = channel_history.get("ts") latest_type = channel_history.get("type") if latest_ts: info = Channel( channel["id"], channel["name"], channel_info["purpose"]["value"], channel_info["num_members"], float(latest_ts), latest_type, ) return info return None
def post_image(self, file): channel_name = self.SLACK_CHANNEL_NAME try: response = self.client.files_upload( channels=channel_name, file=file, ) if not response["ok"]: raise SlackApiError("슬랙 전송 실패") return True except SlackApiError: return False
def test_send_slack_failure(mock_send, db): mock_send.side_effect = SlackApiError(None, None) slack_user_id = 'still not a real slack user ID' message = fake.paragraph(nb_sentences=10) nm.send_notification(slack_user_id, message, NotificationChannel.SLACK) notification = db.query(Notification).filter( Notification.recipient == slack_user_id).first() assert notification.status == NotificationStatus.FAILED assert notification.sent_date is None db.delete(notification) db.commit()
def test_post_error(mocker, log_output): mock = mocker.patch("services.slack.client", autospec=True) mock.chat_postMessage.side_effect = SlackApiError( message="an error", response={"error": "an error occurred"}) slack.post("text", "channel") mock.chat_postMessage.assert_called_once_with(channel="channel", text="text") # check we logged the slack failure assert len(log_output.entries) == 1, log_output.entries assert log_output.entries[0] == { "exc_info": True, "event": "Failed to notify slack", "log_level": "error", }
def test_logging_when_bad_api_key(self, mock_logger, mock_postmessage): mock_postmessage.side_effect = SlackApiError("my_slack_error", {"error": "invalid_auth"}) slack_manager = self._create_slack_manager() slack_manager.publish_contribution(self.contribution) # Org slack errors are warnings mock_logger.warning.assert_called() warning_args = mock_logger.warning.call_args.args[0] self.assertIn(self.org.name, warning_args) self.assertNotIn("HubSlackIntegration", warning_args) self.assertIn("invalid token", warning_args) # Hub slack errors are errors mock_logger.error.assert_called() error_args = mock_logger.error.call_args.args[0] self.assertIn("HubSlackIntegration", error_args) self.assertIn("invalid token", error_args)
def test_log_info_when_generic_slack_error(self, mock_warn, mock_postmessage): error_message = "some_other_error" mock_postmessage.side_effect = SlackApiError("my_slack_error", {"error": error_message}) slack_manager = self._create_slack_manager() slack_manager.publish_contribution(self.contribution) # 2 Hub warnings first... hub_warning = mock_warn.call_args_list[0] hub_warning = hub_warning.args[0] self.assertIn("Generic SlackApiError", hub_warning) self.assertIn(error_message, hub_warning) # ... then an org warning. org_warning = mock_warn.call_args_list[2] org_warning = org_warning.args[0] self.assertIn( f'Generic SlackApiError for Org "{self.org.name}" to channel "{ORG_CHANNEL}"', org_warning) self.assertIn(error_message, org_warning)
def test_logging_when_bad_channel_name(self, mock_logger, mock_postmessage): mock_postmessage.side_effect = SlackApiError( "my_slack_error", {"error": "channel_not_found"}) slack_manager = self._create_slack_manager() slack_manager.publish_contribution(self.contribution) # Org slack errors are warnings mock_logger.warning.assert_called() for warning_args in mock_logger.warning.call_args.args: self.assertNotIn("HubSlackIntegration", warning_args) self.assertIn( f'No such channel "{ORG_CHANNEL}" for {self.org.name}', warning_args) # Hub slack errors are errors mock_logger.error.assert_called() error_args = mock_logger.error.call_args.args[0] self.assertIn("HubSlackIntegration", error_args) self.assertIn( f'No such channel "{self.org_channel_name}" for HubSlackIntegration', error_args)
def test_leaderboard_exceptions(self, web_client, _) -> None: """Basic testing of the leaderboard method's ability to handle exceptions Arguments: web_client -- Mocked out version of the WebClient class. Populated by the @patch decorator """ web_client.side_effect = SlackApiError("test error", None) with open(self.karma_file_path, "w", encoding="utf-8") as json_file: json.dump( [{ "name": "foobar", "pluses": 9000, "minuses": 9000 }], json_file, ) bot = KarmaBot(token=os.environ.get("SLACK_BOT_TOKEN"), ) msg, user_text, thing_text = bot.display_karma_leaderboards() assert not msg and not user_text and not thing_text self.cleanup()
from fastapi.testclient import TestClient from slack_sdk.errors import SlackApiError from moto import mock_dynamodb2 from app import app client = TestClient(app) class MockResponse: def __init__(self, data: Any = None): self.data = data @mock.patch('slack_sdk.web.base_client.BaseClient.api_call', mock.Mock(side_effect=SlackApiError('', MockResponse()))) def test_authorize_invalid_code(): response = client.get('/v1/authorize/', params={'code': 'invalid_code'}) assert response.status_code == HTTPStatus.BAD_REQUEST def mock_oauth_v2_access_response(*args, **kwargs): data = { 'ok': True, 'team': { 'id': 'T0000000000', 'name': 'example_name' }, 'access_token': 'xoxb-0000000000000-0000000000000-aaaaaaaaaaaaaaaaaaaaaaaa', 'app_id': 'example_app_id'
async def _request_with_session( *, current_session: Optional[ClientSession], timeout: int, logger: Logger, http_verb: str, api_url: str, req_args: dict, ) -> Dict[str, any]: """Submit the HTTP request with the running session or a new session. Returns: A dictionary of the response data. """ session = None use_running_session = current_session and not current_session.closed if use_running_session: session = current_session else: session = aiohttp.ClientSession( timeout=aiohttp.ClientTimeout(total=timeout), auth=req_args.pop("auth", None), ) if logger.level <= logging.DEBUG: def convert_params(values: dict) -> dict: if not values or not isinstance(values, dict): return {} return { k: ("(bytes)" if isinstance(v, bytes) else v) for k, v in values.items() } headers = { k: "(redacted)" if k.lower() == "authorization" else v for k, v in req_args.get("headers", {}).items() } logger.debug(f"Sending a request - url: {http_verb} {api_url}, " f"params: {convert_params(req_args.get('params'))}, " f"files: {convert_params(req_args.get('files'))}, " f"data: {convert_params(req_args.get('data'))}, " f"json: {convert_params(req_args.get('json'))}, " f"proxy: {convert_params(req_args.get('proxy'))}, " f"headers: {headers}") response = None try: async with session.request(http_verb, api_url, **req_args) as res: data: Union[dict, bytes] = {} if res.content_type == "application/gzip": # admin.analytics.getFile data = await res.read() else: try: data = await res.json() except aiohttp.ContentTypeError: logger.debug( f"No response data returned from the following API call: {api_url}." ) except json.decoder.JSONDecodeError as e: message = f"Failed to parse the response body: {str(e)}" raise SlackApiError(message, res) response = { "data": data, "headers": res.headers, "status_code": res.status, } finally: if not use_running_session: await session.close() return response