mirror of
https://github.com/apache/superset.git
synced 2026-04-19 08:04:53 +00:00
feat: add slackv2 notification (#29264)
This commit is contained in:
committed by
GitHub
parent
c0d46eb1af
commit
6dbfe2aab9
@@ -1,16 +0,0 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
@@ -1,88 +0,0 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
import uuid
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pandas as pd
|
||||
|
||||
|
||||
@patch("superset.reports.notifications.slack.g")
|
||||
@patch("superset.reports.notifications.slack.logger")
|
||||
@patch("superset.reports.notifications.slack.get_slack_client")
|
||||
def test_send_slack(
|
||||
slack_client_mock: MagicMock,
|
||||
logger_mock: MagicMock,
|
||||
flask_global_mock: MagicMock,
|
||||
) -> None:
|
||||
# `superset.models.helpers`, a dependency of following imports,
|
||||
# requires app context
|
||||
from superset.reports.models import ReportRecipients, ReportRecipientType
|
||||
from superset.reports.notifications.base import NotificationContent
|
||||
from superset.reports.notifications.slack import SlackNotification
|
||||
|
||||
execution_id = uuid.uuid4()
|
||||
flask_global_mock.logs_context = {"execution_id": execution_id}
|
||||
slack_client_mock.return_value.conversations_list.return_value = {
|
||||
"channels": [{"name": "some_channel", "id": "123"}]
|
||||
}
|
||||
content = NotificationContent(
|
||||
name="test alert",
|
||||
header_data={
|
||||
"notification_format": "PNG",
|
||||
"notification_type": "Alert",
|
||||
"owners": [1],
|
||||
"notification_source": None,
|
||||
"chart_id": None,
|
||||
"dashboard_id": None,
|
||||
},
|
||||
embedded_data=pd.DataFrame(
|
||||
{
|
||||
"A": [1, 2, 3],
|
||||
"B": [4, 5, 6],
|
||||
"C": ["111", "222", '<a href="http://www.example.com">333</a>'],
|
||||
}
|
||||
),
|
||||
description='<p>This is <a href="#">a test</a> alert</p><br />',
|
||||
)
|
||||
|
||||
SlackNotification(
|
||||
recipient=ReportRecipients(
|
||||
type=ReportRecipientType.SLACK,
|
||||
recipient_config_json='{"target": "some_channel"}',
|
||||
),
|
||||
content=content,
|
||||
).send()
|
||||
logger_mock.info.assert_called_with(
|
||||
"Report sent to slack", extra={"execution_id": execution_id}
|
||||
)
|
||||
slack_client_mock.return_value.chat_postMessage.assert_called_with(
|
||||
channel="123",
|
||||
text="""*test alert*
|
||||
|
||||
<p>This is <a href="#">a test</a> alert</p><br />
|
||||
|
||||
<None|Explore in Superset>
|
||||
|
||||
```
|
||||
| | A | B | C |
|
||||
|---:|----:|----:|:-----------------------------------------|
|
||||
| 0 | 1 | 4 | 111 |
|
||||
| 1 | 2 | 5 | 222 |
|
||||
| 2 | 3 | 6 | <a href="http://www.example.com">333</a> |
|
||||
```
|
||||
""",
|
||||
)
|
||||
@@ -14,9 +14,14 @@
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
from unittest.mock import Mock
|
||||
|
||||
import uuid
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pandas as pd
|
||||
from slack_sdk.errors import SlackApiError
|
||||
|
||||
from superset.reports.notifications.slackv2 import SlackV2Notification
|
||||
|
||||
|
||||
def test_get_channel_with_multi_recipients() -> None:
|
||||
@@ -55,15 +60,350 @@ def test_get_channel_with_multi_recipients() -> None:
|
||||
content=content,
|
||||
)
|
||||
|
||||
client = Mock()
|
||||
client.conversations_list.return_value = {
|
||||
"channels": [
|
||||
{"name": "some_channel", "id": "23SDKE"},
|
||||
{"name": "second_channel", "id": "WD3D8KE"},
|
||||
{"name": "third_channel", "id": "223DFKE"},
|
||||
]
|
||||
result = slack_notification._get_channel()
|
||||
|
||||
assert result == "some_channel,second_channel,third_channel"
|
||||
|
||||
# Test if the recipient configuration JSON is valid when using a SlackV2 recipient type
|
||||
|
||||
|
||||
def test_valid_recipient_config_json_slackv2() -> None:
|
||||
"""
|
||||
Test if the recipient configuration JSON is valid when using a SlackV2 recipient type
|
||||
"""
|
||||
from superset.reports.models import ReportRecipients, ReportRecipientType
|
||||
from superset.reports.notifications.base import NotificationContent
|
||||
from superset.reports.notifications.slack import SlackNotification
|
||||
|
||||
content = NotificationContent(
|
||||
name="test alert",
|
||||
header_data={
|
||||
"notification_format": "PNG",
|
||||
"notification_type": "Alert",
|
||||
"owners": [1],
|
||||
"notification_source": None,
|
||||
"chart_id": None,
|
||||
"dashboard_id": None,
|
||||
},
|
||||
embedded_data=pd.DataFrame(
|
||||
{
|
||||
"A": [1, 2, 3],
|
||||
"B": [4, 5, 6],
|
||||
"C": ["111", "222", '<a href="http://www.example.com">333</a>'],
|
||||
}
|
||||
),
|
||||
description='<p>This is <a href="#">a test</a> alert</p><br />',
|
||||
)
|
||||
slack_notification = SlackNotification(
|
||||
recipient=ReportRecipients(
|
||||
type=ReportRecipientType.SLACKV2,
|
||||
recipient_config_json='{"target": "some_channel"}',
|
||||
),
|
||||
content=content,
|
||||
)
|
||||
|
||||
result = slack_notification._recipient.recipient_config_json
|
||||
|
||||
assert result == '{"target": "some_channel"}'
|
||||
|
||||
# Ensure _get_inline_files function returns the correct tuple when content has screenshots
|
||||
|
||||
|
||||
def test_get_inline_files_with_screenshots() -> None:
|
||||
"""
|
||||
Test the _get_inline_files function to ensure it will return the correct tuple
|
||||
when content has screenshots
|
||||
"""
|
||||
from superset.reports.models import ReportRecipients, ReportRecipientType
|
||||
from superset.reports.notifications.base import NotificationContent
|
||||
from superset.reports.notifications.slack import SlackNotification
|
||||
|
||||
content = NotificationContent(
|
||||
name="test alert",
|
||||
header_data={
|
||||
"notification_format": "PNG",
|
||||
"notification_type": "Alert",
|
||||
"owners": [1],
|
||||
"notification_source": None,
|
||||
"chart_id": None,
|
||||
"dashboard_id": None,
|
||||
},
|
||||
embedded_data=pd.DataFrame(
|
||||
{
|
||||
"A": [1, 2, 3],
|
||||
"B": [4, 5, 6],
|
||||
"C": ["111", "222", '<a href="http://www.example.com">333</a>'],
|
||||
}
|
||||
),
|
||||
description='<p>This is <a href="#">a test</a> alert</p><br />',
|
||||
screenshots=[b"screenshot1", b"screenshot2"],
|
||||
)
|
||||
slack_notification = SlackNotification(
|
||||
recipient=ReportRecipients(
|
||||
type=ReportRecipientType.SLACK,
|
||||
recipient_config_json='{"target": "some_channel"}',
|
||||
),
|
||||
content=content,
|
||||
)
|
||||
|
||||
result = slack_notification._get_inline_files()
|
||||
|
||||
assert result == ("png", [b"screenshot1", b"screenshot2"])
|
||||
|
||||
# Ensure _get_inline_files function returns None when content has no screenshots or csv
|
||||
|
||||
|
||||
def test_get_inline_files_with_no_screenshots_or_csv() -> None:
|
||||
"""
|
||||
Test the _get_inline_files function to ensure it will return None
|
||||
when content has no screenshots or csv
|
||||
"""
|
||||
from superset.reports.models import ReportRecipients, ReportRecipientType
|
||||
from superset.reports.notifications.base import NotificationContent
|
||||
from superset.reports.notifications.slack import SlackNotification
|
||||
|
||||
content = NotificationContent(
|
||||
name="test alert",
|
||||
header_data={
|
||||
"notification_format": "PNG",
|
||||
"notification_type": "Alert",
|
||||
"owners": [1],
|
||||
"notification_source": None,
|
||||
"chart_id": None,
|
||||
"dashboard_id": None,
|
||||
},
|
||||
embedded_data=pd.DataFrame(
|
||||
{
|
||||
"A": [1, 2, 3],
|
||||
"B": [4, 5, 6],
|
||||
"C": ["111", "222", '<a href="http://www.example.com">333</a>'],
|
||||
}
|
||||
),
|
||||
description='<p>This is <a href="#">a test</a> alert</p><br />',
|
||||
)
|
||||
slack_notification = SlackNotification(
|
||||
recipient=ReportRecipients(
|
||||
type=ReportRecipientType.SLACK,
|
||||
recipient_config_json='{"target": "some_channel"}',
|
||||
),
|
||||
content=content,
|
||||
)
|
||||
|
||||
result = slack_notification._get_inline_files()
|
||||
|
||||
assert result == (None, [])
|
||||
|
||||
|
||||
@patch("superset.reports.notifications.slackv2.g")
|
||||
@patch("superset.reports.notifications.slackv2.logger")
|
||||
@patch("superset.reports.notifications.slackv2.get_slack_client")
|
||||
def test_send_slackv2(
|
||||
slack_client_mock: MagicMock,
|
||||
logger_mock: MagicMock,
|
||||
flask_global_mock: MagicMock,
|
||||
) -> None:
|
||||
# `superset.models.helpers`, a dependency of following imports,
|
||||
# requires app context
|
||||
from superset.reports.models import ReportRecipients, ReportRecipientType
|
||||
from superset.reports.notifications.base import NotificationContent
|
||||
|
||||
execution_id = uuid.uuid4()
|
||||
flask_global_mock.logs_context = {"execution_id": execution_id}
|
||||
slack_client_mock.return_value.chat_postMessage.return_value = {"ok": True}
|
||||
content = NotificationContent(
|
||||
name="test alert",
|
||||
header_data={
|
||||
"notification_format": "PNG",
|
||||
"notification_type": "Alert",
|
||||
"owners": [1],
|
||||
"notification_source": None,
|
||||
"chart_id": None,
|
||||
"dashboard_id": None,
|
||||
},
|
||||
embedded_data=pd.DataFrame(
|
||||
{
|
||||
"A": [1, 2, 3],
|
||||
"B": [4, 5, 6],
|
||||
"C": ["111", "222", '<a href="http://www.example.com">333</a>'],
|
||||
}
|
||||
),
|
||||
description='<p>This is <a href="#">a test</a> alert</p><br />',
|
||||
)
|
||||
|
||||
notification = SlackV2Notification(
|
||||
recipient=ReportRecipients(
|
||||
type=ReportRecipientType.SLACKV2,
|
||||
recipient_config_json='{"target": "some_channel"}',
|
||||
),
|
||||
content=content,
|
||||
)
|
||||
notification.send()
|
||||
logger_mock.info.assert_called_with(
|
||||
"Report sent to slack", extra={"execution_id": execution_id}
|
||||
)
|
||||
slack_client_mock.return_value.chat_postMessage.assert_called_with(
|
||||
channel="some_channel",
|
||||
text="""*test alert*
|
||||
|
||||
<p>This is <a href="#">a test</a> alert</p><br />
|
||||
|
||||
<None|Explore in Superset>
|
||||
|
||||
```
|
||||
| | A | B | C |
|
||||
|---:|----:|----:|:-----------------------------------------|
|
||||
| 0 | 1 | 4 | 111 |
|
||||
| 1 | 2 | 5 | 222 |
|
||||
| 2 | 3 | 6 | <a href="http://www.example.com">333</a> |
|
||||
```
|
||||
""",
|
||||
)
|
||||
|
||||
|
||||
@patch("superset.reports.notifications.slack.g")
|
||||
@patch("superset.reports.notifications.slack.logger")
|
||||
@patch("superset.utils.slack.get_slack_client")
|
||||
@patch("superset.reports.notifications.slack.get_slack_client")
|
||||
def test_send_slack(
|
||||
slack_client_mock: MagicMock,
|
||||
slack_client_mock_util: MagicMock,
|
||||
logger_mock: MagicMock,
|
||||
flask_global_mock: MagicMock,
|
||||
) -> None:
|
||||
# `superset.models.helpers`, a dependency of following imports,
|
||||
# requires app context
|
||||
from superset.reports.models import ReportRecipients, ReportRecipientType
|
||||
from superset.reports.notifications.base import NotificationContent
|
||||
from superset.reports.notifications.slack import SlackNotification
|
||||
|
||||
execution_id = uuid.uuid4()
|
||||
flask_global_mock.logs_context = {"execution_id": execution_id}
|
||||
slack_client_mock.return_value.chat_postMessage.return_value = {"ok": True}
|
||||
slack_client_mock_util.return_value.conversations_list.side_effect = SlackApiError(
|
||||
"scope not found", "error"
|
||||
)
|
||||
|
||||
content = NotificationContent(
|
||||
name="test alert",
|
||||
header_data={
|
||||
"notification_format": "PNG",
|
||||
"notification_type": "Alert",
|
||||
"owners": [1],
|
||||
"notification_source": None,
|
||||
"chart_id": None,
|
||||
"dashboard_id": None,
|
||||
},
|
||||
embedded_data=pd.DataFrame(
|
||||
{
|
||||
"A": [1, 2, 3],
|
||||
"B": [4, 5, 6],
|
||||
"C": ["111", "222", '<a href="http://www.example.com">333</a>'],
|
||||
}
|
||||
),
|
||||
description='<p>This is <a href="#">a test</a> alert</p><br />',
|
||||
)
|
||||
|
||||
notification = SlackNotification(
|
||||
recipient=ReportRecipients(
|
||||
type=ReportRecipientType.SLACKV2,
|
||||
recipient_config_json='{"target": "some_channel"}',
|
||||
),
|
||||
content=content,
|
||||
)
|
||||
notification.send()
|
||||
|
||||
logger_mock.info.assert_called_with(
|
||||
"Report sent to slack", extra={"execution_id": execution_id}
|
||||
)
|
||||
slack_client_mock.return_value.chat_postMessage.assert_called_with(
|
||||
channel="some_channel",
|
||||
text="""*test alert*
|
||||
|
||||
<p>This is <a href="#">a test</a> alert</p><br />
|
||||
|
||||
<None|Explore in Superset>
|
||||
|
||||
```
|
||||
| | A | B | C |
|
||||
|---:|----:|----:|:-----------------------------------------|
|
||||
| 0 | 1 | 4 | 111 |
|
||||
| 1 | 2 | 5 | 222 |
|
||||
| 2 | 3 | 6 | <a href="http://www.example.com">333</a> |
|
||||
```
|
||||
""",
|
||||
)
|
||||
|
||||
|
||||
@patch("superset.reports.notifications.slack.g")
|
||||
@patch("superset.reports.notifications.slack.logger")
|
||||
@patch("superset.utils.slack.get_slack_client")
|
||||
@patch("superset.reports.notifications.slack.get_slack_client")
|
||||
def test_send_slack_no_feature_flag(
|
||||
slack_client_mock: MagicMock,
|
||||
slack_client_mock_util: MagicMock,
|
||||
logger_mock: MagicMock,
|
||||
flask_global_mock: MagicMock,
|
||||
) -> None:
|
||||
# `superset.models.helpers`, a dependency of following imports,
|
||||
# requires app context
|
||||
from superset.reports.models import ReportRecipients, ReportRecipientType
|
||||
from superset.reports.notifications.base import NotificationContent
|
||||
from superset.reports.notifications.slack import SlackNotification
|
||||
|
||||
execution_id = uuid.uuid4()
|
||||
flask_global_mock.logs_context = {"execution_id": execution_id}
|
||||
slack_client_mock.return_value.chat_postMessage.return_value = {"ok": True}
|
||||
# scopes are valid but the feature flag is off. It should still run Slack v1
|
||||
slack_client_mock_util.return_value.conversations_list.return_value = {
|
||||
"channels": [{"id": "foo", "name": "bar"}]
|
||||
}
|
||||
|
||||
result = slack_notification._get_channels(client)
|
||||
content = NotificationContent(
|
||||
name="test alert",
|
||||
header_data={
|
||||
"notification_format": "PNG",
|
||||
"notification_type": "Alert",
|
||||
"owners": [1],
|
||||
"notification_source": None,
|
||||
"chart_id": None,
|
||||
"dashboard_id": None,
|
||||
},
|
||||
embedded_data=pd.DataFrame(
|
||||
{
|
||||
"A": [1, 2, 3],
|
||||
"B": [4, 5, 6],
|
||||
"C": ["111", "222", '<a href="http://www.example.com">333</a>'],
|
||||
}
|
||||
),
|
||||
description='<p>This is <a href="#">a test</a> alert</p><br />',
|
||||
)
|
||||
|
||||
assert result == ["23SDKE", "WD3D8KE", "223DFKE"]
|
||||
notification = SlackNotification(
|
||||
recipient=ReportRecipients(
|
||||
type=ReportRecipientType.SLACKV2,
|
||||
recipient_config_json='{"target": "some_channel"}',
|
||||
),
|
||||
content=content,
|
||||
)
|
||||
notification.send()
|
||||
|
||||
logger_mock.info.assert_called_with(
|
||||
"Report sent to slack", extra={"execution_id": execution_id}
|
||||
)
|
||||
slack_client_mock.return_value.chat_postMessage.assert_called_with(
|
||||
channel="some_channel",
|
||||
text="""*test alert*
|
||||
|
||||
<p>This is <a href="#">a test</a> alert</p><br />
|
||||
|
||||
<None|Explore in Superset>
|
||||
|
||||
```
|
||||
| | A | B | C |
|
||||
|---:|----:|----:|:-----------------------------------------|
|
||||
| 0 | 1 | 4 | 111 |
|
||||
| 1 | 2 | 5 | 222 |
|
||||
| 2 | 3 | 6 | <a href="http://www.example.com">333</a> |
|
||||
```
|
||||
""",
|
||||
)
|
||||
|
||||
193
tests/unit_tests/utils/slack_test.py
Normal file
193
tests/unit_tests/utils/slack_test.py
Normal file
@@ -0,0 +1,193 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import pytest
|
||||
|
||||
from superset.utils.slack import get_channels_with_search
|
||||
|
||||
|
||||
class MockResponse:
|
||||
def __init__(self, data):
|
||||
self._data = data
|
||||
|
||||
@property
|
||||
def data(self):
|
||||
return self._data
|
||||
|
||||
|
||||
class TestGetChannelsWithSearch:
|
||||
# Fetch all channels when no search string is provided
|
||||
def test_fetch_all_channels_no_search_string(self, mocker):
|
||||
# Mock data
|
||||
mock_data = {
|
||||
"channels": [{"name": "general", "id": "C12345"}],
|
||||
"response_metadata": {"next_cursor": None},
|
||||
}
|
||||
|
||||
# Mock class instance with data property
|
||||
mock_response_instance = MockResponse(mock_data)
|
||||
|
||||
mock_client = mocker.Mock()
|
||||
mock_client.conversations_list.return_value = mock_response_instance
|
||||
mocker.patch("superset.utils.slack.get_slack_client", return_value=mock_client)
|
||||
|
||||
result = get_channels_with_search()
|
||||
assert result == [{"name": "general", "id": "C12345"}]
|
||||
|
||||
# Handle an empty search string gracefully
|
||||
def test_handle_empty_search_string(self, mocker):
|
||||
mock_data = {
|
||||
"channels": [{"name": "general", "id": "C12345"}],
|
||||
"response_metadata": {"next_cursor": None},
|
||||
}
|
||||
|
||||
mock_response_instance = MockResponse(mock_data)
|
||||
mock_client = mocker.Mock()
|
||||
mock_client.conversations_list.return_value = mock_response_instance
|
||||
mocker.patch("superset.utils.slack.get_slack_client", return_value=mock_client)
|
||||
|
||||
result = get_channels_with_search(search_string="")
|
||||
assert result == [{"name": "general", "id": "C12345"}]
|
||||
|
||||
def test_handle_exact_match_search_string_single_channel(self, mocker):
|
||||
# Mock data with multiple channels
|
||||
mock_data = {
|
||||
"channels": [
|
||||
{"name": "general", "id": "C12345"},
|
||||
{"name": "general2", "id": "C13454"},
|
||||
{"name": "random", "id": "C67890"},
|
||||
],
|
||||
"response_metadata": {"next_cursor": None},
|
||||
}
|
||||
|
||||
# Mock response and client setup
|
||||
mock_response_instance = MockResponse(mock_data)
|
||||
mock_client = mocker.Mock()
|
||||
mock_client.conversations_list.return_value = mock_response_instance
|
||||
mocker.patch("superset.utils.slack.get_slack_client", return_value=mock_client)
|
||||
|
||||
# Call the function with a search string that matches a single channel
|
||||
result = get_channels_with_search(search_string="general", exact_match=True)
|
||||
|
||||
# Assert that the result is a list with a single channel dictionary
|
||||
assert result == [{"name": "general", "id": "C12345"}]
|
||||
|
||||
def test_handle_exact_match_search_string_multiple_channels(self, mocker):
|
||||
mock_data = {
|
||||
"channels": [
|
||||
{"name": "general", "id": "C12345"},
|
||||
{"name": "general2", "id": "C13454"},
|
||||
{"name": "random", "id": "C67890"},
|
||||
],
|
||||
"response_metadata": {"next_cursor": None},
|
||||
}
|
||||
|
||||
mock_response_instance = MockResponse(mock_data)
|
||||
mock_client = mocker.Mock()
|
||||
mock_client.conversations_list.return_value = mock_response_instance
|
||||
mocker.patch("superset.utils.slack.get_slack_client", return_value=mock_client)
|
||||
|
||||
result = get_channels_with_search(
|
||||
search_string="general,random", exact_match=True
|
||||
)
|
||||
assert result == [
|
||||
{"name": "general", "id": "C12345"},
|
||||
{"name": "random", "id": "C67890"},
|
||||
]
|
||||
|
||||
def test_handle_loose_match_search_string_multiple_channels(self, mocker):
|
||||
mock_data = {
|
||||
"channels": [
|
||||
{"name": "general", "id": "C12345"},
|
||||
{"name": "general2", "id": "C13454"},
|
||||
{"name": "random", "id": "C67890"},
|
||||
],
|
||||
"response_metadata": {"next_cursor": None},
|
||||
}
|
||||
|
||||
mock_response_instance = MockResponse(mock_data)
|
||||
mock_client = mocker.Mock()
|
||||
mock_client.conversations_list.return_value = mock_response_instance
|
||||
mocker.patch("superset.utils.slack.get_slack_client", return_value=mock_client)
|
||||
|
||||
result = get_channels_with_search(search_string="general,random")
|
||||
assert result == [
|
||||
{"name": "general", "id": "C12345"},
|
||||
{"name": "general2", "id": "C13454"},
|
||||
{"name": "random", "id": "C67890"},
|
||||
]
|
||||
|
||||
def test_handle_slack_client_error_listing_channels(self, mocker):
|
||||
from slack_sdk.errors import SlackApiError
|
||||
|
||||
from superset.exceptions import SupersetException
|
||||
|
||||
mock_client = mocker.Mock()
|
||||
mock_client.conversations_list.side_effect = SlackApiError(
|
||||
"foo", "missing scope: channels:read"
|
||||
)
|
||||
mocker.patch("superset.utils.slack.get_slack_client", return_value=mock_client)
|
||||
|
||||
with pytest.raises(SupersetException) as ex:
|
||||
get_channels_with_search()
|
||||
|
||||
assert str(ex.value) == (
|
||||
"""Failed to list channels: foo
|
||||
The server responded with: missing scope: channels:read"""
|
||||
)
|
||||
|
||||
def test_filter_channels_by_specified_types(self, mocker):
|
||||
mock_data = {
|
||||
"channels": [
|
||||
{"name": "general", "id": "C12345", "type": "public"},
|
||||
],
|
||||
"response_metadata": {"next_cursor": None},
|
||||
}
|
||||
|
||||
mock_response_instance = MockResponse(mock_data)
|
||||
mock_client = mocker.Mock()
|
||||
mock_client.conversations_list.return_value = mock_response_instance
|
||||
mocker.patch("superset.utils.slack.get_slack_client", return_value=mock_client)
|
||||
|
||||
result = get_channels_with_search(types=["public"])
|
||||
assert result == [{"name": "general", "id": "C12345", "type": "public"}]
|
||||
|
||||
def test_handle_pagination_multiple_pages(self, mocker):
|
||||
mock_data_page1 = {
|
||||
"channels": [{"name": "general", "id": "C12345"}],
|
||||
"response_metadata": {"next_cursor": "page2"},
|
||||
}
|
||||
mock_data_page2 = {
|
||||
"channels": [{"name": "random", "id": "C67890"}],
|
||||
"response_metadata": {"next_cursor": None},
|
||||
}
|
||||
|
||||
mock_response_instance_page1 = MockResponse(mock_data_page1)
|
||||
mock_response_instance_page2 = MockResponse(mock_data_page2)
|
||||
|
||||
mock_client = mocker.Mock()
|
||||
mock_client.conversations_list.side_effect = [
|
||||
mock_response_instance_page1,
|
||||
mock_response_instance_page2,
|
||||
]
|
||||
mocker.patch("superset.utils.slack.get_slack_client", return_value=mock_client)
|
||||
|
||||
result = get_channels_with_search()
|
||||
assert result == [
|
||||
{"name": "general", "id": "C12345"},
|
||||
{"name": "random", "id": "C67890"},
|
||||
]
|
||||
Reference in New Issue
Block a user