Exemple #1
0
    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
Exemple #3
0
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()
Exemple #4
0
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
Exemple #7
0
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'])
Exemple #10
0
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"])
Exemple #11
0
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', [])
Exemple #13
0
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')
Exemple #15
0
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()
Exemple #16
0
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))
Exemple #17
0
    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("😀"))
Exemple #21
0
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
Exemple #22
0
    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
Exemple #23
0
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",
    }
Exemple #25
0
    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)
Exemple #26
0
    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)
Exemple #27
0
    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)
Exemple #28
0
    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