| # 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"}, |
| ] |