diff --git a/requirements-dev.in b/requirements-dev.in index 43f459d08a6a..de08d461f9ea 100644 --- a/requirements-dev.in +++ b/requirements-dev.in @@ -3,10 +3,12 @@ black click coverage devtools +easydict factory-boy faker ipython pre-commit pytest==7.4.4 +pytest-mock ruff vulture diff --git a/requirements-dev.txt b/requirements-dev.txt index bb6513b4f131..2a7cdc2655f2 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -26,6 +26,8 @@ devtools==0.12.2 # via -r requirements-dev.in distlib==0.3.9 # via virtualenv +easydict==1.13 + # via -r requirements-dev.in executing==2.1.0 # via # devtools @@ -82,6 +84,8 @@ pygments==2.18.0 # ipython pytest==7.4.4 # via -r requirements-dev.in +pytest-mock==3.14.0 + # via -r requirements-dev.in python-dateutil==2.9.0.post0 # via faker pyyaml==6.0.2 diff --git a/src/dispatch/plugins/dispatch_slack/incident/interactive.py b/src/dispatch/plugins/dispatch_slack/incident/interactive.py index 33068ffda671..fec7300c6d26 100644 --- a/src/dispatch/plugins/dispatch_slack/incident/interactive.py +++ b/src/dispatch/plugins/dispatch_slack/incident/interactive.py @@ -455,6 +455,8 @@ def handle_list_incidents_command( # Don't add a divider if we are at the last incident if idx != len(open_incidents): blocks.extend([Divider()]) + else: + blocks.append(Section(text="No incidents found.")) modal = Modal( title="Incident List", diff --git a/tests/conftest.py b/tests/conftest.py index 5ef624e5f258..9ed53311b229 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,4 +1,7 @@ import pytest + +from easydict import EasyDict +from slack_sdk.web.client import WebClient from sqlalchemy_utils import drop_database, database_exists from starlette.config import environ from fastapi.testclient import TestClient @@ -707,3 +710,12 @@ def cost_model_activity(session): @pytest.fixture def service_feedback(session): return ServiceFeedbackFactory() + + +@pytest.fixture() +def mock_slack_client(mocker): + mocks = EasyDict() + + mocks.views_open = mocker.patch.object(WebClient, "views_open") + + return mocks diff --git a/tests/factories.py b/tests/factories.py index cfc84a74c89f..1130e35e6f1d 100644 --- a/tests/factories.py +++ b/tests/factories.py @@ -532,6 +532,7 @@ class ParticipantFactory(BaseFactory): added_reason = Sequence(lambda n: f"added_reason{n}") after_hours_notification = Faker().pybool() user_conversation_id = FuzzyText() + individual = SubFactory(IndividualContactFactory) class Meta: """Factory Configuration.""" @@ -546,14 +547,6 @@ def incident(self, create, extracted, **kwargs): if extracted: self.incident_id = extracted.id - @post_generation - def individual_contact(self, create, extracted, **kwargs): - if not create: - return - - if extracted: - self.individual_contact_id = extracted.id - @post_generation def team(self, create, extracted, **kwargs): if not create: @@ -941,6 +934,7 @@ class IncidentFactory(BaseFactory): incident_priority = SubFactory(IncidentPriorityFactory) incident_severity = SubFactory(IncidentSeverityFactory) project = SubFactory(ProjectFactory) + commander = SubFactory(ParticipantFactory) conversation = SubFactory(ConversationFactory) visibility = Visibility.open @@ -957,6 +951,7 @@ def participants(self, create, extracted, **kwargs): if extracted: for participant in extracted: self.participants.append(participant) + self.participants.append(self.commander) @post_generation def tags(self, create, extracted, **kwargs): diff --git a/tests/plugins/test_dispatch_slack_incident_interactive.py b/tests/plugins/test_dispatch_slack_incident_interactive.py new file mode 100644 index 000000000000..c712be1a9677 --- /dev/null +++ b/tests/plugins/test_dispatch_slack_incident_interactive.py @@ -0,0 +1,78 @@ +def test_configure(): + """Test that we can configure the plugin.""" + from dispatch.plugins.dispatch_slack.incident.interactive import ( + configure, + ) + from easydict import EasyDict + + config = EasyDict( + { + "api_bot_token": "xoxb-12345", + "socket_mode_app_token": "xapp-12345", + "signing_secret": "test-123", + "app_user_slug": "test", + "ban_threads": True, + "timeline_event_reaction": "stopwatch", + "slack_command_tasks": "/dispatch-list-tasks", + "slack_command_list_my_tasks": "/dispatch-list-my-tasks", + "slack_command_list_participants": "/dispatch-list-participants", + "slack_command_assign_role": "/dispatch-assign-role", + "slack_command_update_incident": "/dispatch-update-incident", + "slack_command_update_participant": "/dispatch-update-participant", + "slack_command_engage_oncall": "/dispatch-engage-oncall", + "slack_command_list_resource": "/dispatch-list-resources", + "slack_command_report_incident": "/dispatch-report-incident", + "slack_command_report_tactical": "/dispatch-report-tactical", + "slack_command_report_executive": "/dispatch-report-executive", + "slack_command_update_notifications_group": "/dispatch-notifications-group", + "slack_command_add_timeline_event": "/dispatch-add-timeline-event", + "slack_command_list_incidents": "/dispatch-list-incidents", + "slack_command_run_workflow": "/dispatch-run-workflow", + "slack_command_list_workflow": "/dispatch-list-workflows", + "slack_command_list_tasks": "/dispatch-list-tasks", + "slack_command_create_task": "/dispatch-create-task", + } + ) + + configure(config) + + +def test_handle_tag_search_action(session, incident): + from dispatch.plugins.dispatch_slack.incident.interactive import ( + handle_tag_search_action, + ) + from slack_bolt import Ack + + bolt_context = {"subject": incident} + payload = {"value": "payload"} + + handle_tag_search_action(ack=Ack(), payload=payload, context=bolt_context, db_session=session) + + +def test_handle_list_incidents_command(session, incident, mock_slack_client): + """Test that we can handle the list incidents command.""" + from dispatch.plugins.dispatch_slack.incident.interactive import ( + handle_list_incidents_command, + ) + from slack_bolt import Ack + from dispatch.plugins.dispatch_slack.models import SubjectMetadata, IncidentSubjects + + subject = SubjectMetadata( + type=IncidentSubjects.incident, + id=incident.id, + organization_slug=incident.project.slug, + project_id=incident.project.id, + ) + + bolt_context = {"subject": subject, "db_session": session} + body = {"trigger_id": "trigger_id"} + payload = {"value": "payload"} + + handle_list_incidents_command( + ack=Ack(), + body=body, + payload=payload, + context=bolt_context, + db_session=session, + client=mock_slack_client, + )