From 720ed69fb3a255f40079b8bbb51b7aaae403f5f7 Mon Sep 17 00:00:00 2001 From: Aly Sivji <4369343+alysivji@users.noreply.github.com> Date: Thu, 1 Oct 2020 16:46:50 -0500 Subject: [PATCH] Handle Slack app_uninstalled callback event (#331) --- busy_beaver/apps/call_for_proposals/models.py | 4 +- busy_beaver/apps/github_integration/models.py | 8 +- .../slack_integration/event_subscription.py | 22 +- busy_beaver/apps/slack_integration/models.py | 13 +- .../slack_integration/oauth/oauth_flow.py | 2 - busy_beaver/apps/upcoming_events/models.py | 5 +- ...1001_18-36-02__remove_user_token_column.py | 33 +++ ...-34__add_cascade_delete_to_foreign_key_.py | 150 ++++++++++ scripts/dev/populate_database.py | 1 - tests/_utilities/factories/slack.py | 1 - .../api/test_event_subscription.py | 263 +++++++++++------- .../functional/test_install_integration.py | 57 ++-- 12 files changed, 407 insertions(+), 152 deletions(-) create mode 100644 migrations/versions/20201001_18-36-02__remove_user_token_column.py create mode 100644 migrations/versions/20201001_19-05-34__add_cascade_delete_to_foreign_key_.py diff --git a/busy_beaver/apps/call_for_proposals/models.py b/busy_beaver/apps/call_for_proposals/models.py index c8769980..e62de606 100644 --- a/busy_beaver/apps/call_for_proposals/models.py +++ b/busy_beaver/apps/call_for_proposals/models.py @@ -44,7 +44,9 @@ def __repr__(self): # pragma: no cover enabled = db.Column(db.Boolean, default=False, nullable=False) installation_id = db.Column( db.Integer, - db.ForeignKey("slack_installation.id", name="fk_installation_id"), + db.ForeignKey( + "slack_installation.id", name="fk_installation_id", ondelete="CASCADE" + ), nullable=False, ) channel = db.Column(db.String(20), nullable=False) diff --git a/busy_beaver/apps/github_integration/models.py b/busy_beaver/apps/github_integration/models.py index bb39da3e..fd3e725b 100644 --- a/busy_beaver/apps/github_integration/models.py +++ b/busy_beaver/apps/github_integration/models.py @@ -45,7 +45,9 @@ def __repr__(self): # pragma: no cover enabled = db.Column(db.Boolean, default=False, nullable=False) installation_id = db.Column( db.Integer, - db.ForeignKey("slack_installation.id", name="fk_installation_id"), + db.ForeignKey( + "slack_installation.id", name="fk_installation_id", ondelete="CASCADE" + ), nullable=False, ) channel = db.Column(db.String(20), nullable=False) @@ -80,7 +82,9 @@ def __repr__(self): # pragma: no cover config_id = db.Column( db.Integer, db.ForeignKey( - "github_summary_configuration.id", name="fk_github_summary_configuration_id" + "github_summary_configuration.id", + name="fk_github_summary_configuration_id", + ondelete="CASCADE", ), nullable=False, ) diff --git a/busy_beaver/apps/slack_integration/event_subscription.py b/busy_beaver/apps/slack_integration/event_subscription.py index 7a2f9d6e..1c268079 100644 --- a/busy_beaver/apps/slack_integration/event_subscription.py +++ b/busy_beaver/apps/slack_integration/event_subscription.py @@ -22,13 +22,13 @@ def process_event_subscription_callback(data): + logger.info("Process Event Subscription webhook", extra={"type": data["type"]}) return subscription_dispatch.emit(data["type"], default="not_found", data=data) ####################### # Subscription Handlers ####################### -@subscription_dispatch.on("app_uninstalled") # TODO @subscription_dispatch.on("not_found") @event_dispatch.on("not_found") def command_not_found(data): @@ -44,7 +44,10 @@ def url_verification_handler(data): @subscription_dispatch.on("event_callback") def event_callback_dispatcher(data): - logger.info("[Busy Beaver] Slack -- Event Callback") + logger.info( + f"Process Event Callback -- {data['event']['type']}", + extra={"event_type": data["event"]["type"]}, + ) return event_dispatch.emit(data["event"]["type"], default="not_found", data=data) @@ -135,3 +138,18 @@ def app_home_handler(data): slack = SlackClient(installation.bot_access_token) slack.display_app_home(user_id, view=app_home.to_dict()) return None + + +@event_dispatch.on("app_uninstalled") +def app_uninstalled_handler(data): + workspace_id = data["team_id"] + installation = SlackInstallation.query.filter_by(workspace_id=workspace_id).first() + + if not installation: + logger.error("Workspace not found", extra={"workspace_id": workspace_id}) + return {} + + db.session.delete(installation) + db.session.commit() + + return {} diff --git a/busy_beaver/apps/slack_integration/models.py b/busy_beaver/apps/slack_integration/models.py index 584d0a7c..525f28f6 100644 --- a/busy_beaver/apps/slack_integration/models.py +++ b/busy_beaver/apps/slack_integration/models.py @@ -16,9 +16,6 @@ def __repr__(self): # pragma: no cover return f"" # Attributes - access_token = db.Column( - EncryptedType(db.String, SECRET_KEY, AesEngine, "pkcs5"), nullable=False - ) authorizing_user_id = db.Column(db.String(300), nullable=False) bot_access_token = db.Column( EncryptedType(db.String, SECRET_KEY, AesEngine, "pkcs5"), nullable=False @@ -38,23 +35,29 @@ def __repr__(self): # pragma: no cover workspace_logo_url = db.Column(URLType, nullable=True) # Relationships + slack_users = db.relationship( + "SlackUser", back_populates="installation", lazy="select", cascade="all, delete" + ) github_summary_config = db.relationship( "GitHubSummaryConfiguration", back_populates="slack_installation", uselist=False, lazy="joined", + cascade="all, delete", ) upcoming_events_config = db.relationship( "UpcomingEventsConfiguration", back_populates="slack_installation", uselist=False, lazy="joined", + cascade="all, delete", ) cfp_config = db.relationship( "CallForProposalsConfiguration", back_populates="slack_installation", uselist=False, lazy="joined", + cascade="all, delete", ) @@ -65,7 +68,9 @@ class SlackUser(UserMixin, BaseModel): installation_id = db.Column( db.Integer, - db.ForeignKey("slack_installation.id", name="fk_installation_id"), + db.ForeignKey( + "slack_installation.id", name="fk_installation_id", ondelete="CASCADE" + ), index=True, nullable=False, ) diff --git a/busy_beaver/apps/slack_integration/oauth/oauth_flow.py b/busy_beaver/apps/slack_integration/oauth/oauth_flow.py index fef10ea5..8a65094b 100644 --- a/busy_beaver/apps/slack_integration/oauth/oauth_flow.py +++ b/busy_beaver/apps/slack_integration/oauth/oauth_flow.py @@ -16,7 +16,6 @@ # Slack Installation #################### class SlackOAuthInfo(NamedTuple): - access_token: str authorizing_user_id: str bot_access_token: str bot_user_id: str @@ -124,7 +123,6 @@ def _parse_json_response(self, code): # TODO do this with marshmallow output = {} - output["access_token"] = oauth_json["authed_user"]["access_token"] output["scope"] = oauth_json["scope"] output["authorizing_user_id"] = oauth_json["authed_user"]["id"] output["workspace_id"] = oauth_json["team"]["id"] diff --git a/busy_beaver/apps/upcoming_events/models.py b/busy_beaver/apps/upcoming_events/models.py index a4c9c3ac..47232dfb 100644 --- a/busy_beaver/apps/upcoming_events/models.py +++ b/busy_beaver/apps/upcoming_events/models.py @@ -78,7 +78,9 @@ def __repr__(self): # pragma: no cover enabled = db.Column(db.Boolean, default=False, nullable=False) installation_id = db.Column( db.Integer, - db.ForeignKey("slack_installation.id", name="fk_installation_id"), + db.ForeignKey( + "slack_installation.id", name="fk_installation_id", ondelete="CASCADE" + ), nullable=False, ) @@ -136,6 +138,7 @@ def __repr__(self): # pragma: no cover db.ForeignKey( "upcoming_events_configuration.id", name="fk_upcoming_events_configuration_id", + ondelete="CASCADE", ), nullable=False, ) diff --git a/migrations/versions/20201001_18-36-02__remove_user_token_column.py b/migrations/versions/20201001_18-36-02__remove_user_token_column.py new file mode 100644 index 00000000..6346039e --- /dev/null +++ b/migrations/versions/20201001_18-36-02__remove_user_token_column.py @@ -0,0 +1,33 @@ +"""remove user token column + +Revision ID: 1fe0aa535040 +Revises: e51108c88034 +Create Date: 2020-10-01 18:36:02.685882 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision = "1fe0aa535040" +down_revision = "e51108c88034" +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column("slack_installation", "access_token") + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column( + "slack_installation", + sa.Column( + "access_token", postgresql.BYTEA(), autoincrement=False, nullable=True + ), + ) + # ### end Alembic commands ### diff --git a/migrations/versions/20201001_19-05-34__add_cascade_delete_to_foreign_key_.py b/migrations/versions/20201001_19-05-34__add_cascade_delete_to_foreign_key_.py new file mode 100644 index 00000000..00118f1c --- /dev/null +++ b/migrations/versions/20201001_19-05-34__add_cascade_delete_to_foreign_key_.py @@ -0,0 +1,150 @@ +"""add cascade delete to foreign key relationships + +Revision ID: b99005e9733d +Revises: 1fe0aa535040 +Create Date: 2020-10-01 19:05:34.770544 + +""" +from alembic import op + +# revision identifiers, used by Alembic. +revision = "b99005e9733d" +down_revision = "1fe0aa535040" +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_constraint( + "fk_installation_id", "call_for_proposals_configuration", type_="foreignkey" + ) + op.create_foreign_key( + "fk_installation_id", + "call_for_proposals_configuration", + "slack_installation", + ["installation_id"], + ["id"], + ondelete="CASCADE", + ) + op.drop_constraint( + "fk_installation_id", "github_summary_configuration", type_="foreignkey" + ) + op.create_foreign_key( + "fk_installation_id", + "github_summary_configuration", + "slack_installation", + ["installation_id"], + ["id"], + ondelete="CASCADE", + ) + op.drop_constraint( + "fk_github_summary_configuration_id", "github_summary_user", type_="foreignkey" + ) + op.create_foreign_key( + "fk_github_summary_configuration_id", + "github_summary_user", + "github_summary_configuration", + ["config_id"], + ["id"], + ondelete="CASCADE", + ) + op.drop_constraint("fk_installation_id", "slack_user", type_="foreignkey") + op.create_foreign_key( + "fk_installation_id", + "slack_user", + "slack_installation", + ["installation_id"], + ["id"], + ondelete="CASCADE", + ) + op.drop_constraint( + "fk_installation_id", "upcoming_events_configuration", type_="foreignkey" + ) + op.create_foreign_key( + "fk_installation_id", + "upcoming_events_configuration", + "slack_installation", + ["installation_id"], + ["id"], + ondelete="CASCADE", + ) + op.drop_constraint( + "fk_upcoming_events_configuration_id", + "upcoming_events_group", + type_="foreignkey", + ) + op.create_foreign_key( + "fk_upcoming_events_configuration_id", + "upcoming_events_group", + "upcoming_events_configuration", + ["config_id"], + ["id"], + ondelete="CASCADE", + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_constraint( + "fk_upcoming_events_configuration_id", + "upcoming_events_group", + type_="foreignkey", + ) + op.create_foreign_key( + "fk_upcoming_events_configuration_id", + "upcoming_events_group", + "upcoming_events_configuration", + ["config_id"], + ["id"], + ) + op.drop_constraint( + "fk_installation_id", "upcoming_events_configuration", type_="foreignkey" + ) + op.create_foreign_key( + "fk_installation_id", + "upcoming_events_configuration", + "slack_installation", + ["installation_id"], + ["id"], + ) + op.drop_constraint("fk_installation_id", "slack_user", type_="foreignkey") + op.create_foreign_key( + "fk_installation_id", + "slack_user", + "slack_installation", + ["installation_id"], + ["id"], + ) + op.drop_constraint( + "fk_github_summary_configuration_id", "github_summary_user", type_="foreignkey" + ) + op.create_foreign_key( + "fk_github_summary_configuration_id", + "github_summary_user", + "github_summary_configuration", + ["config_id"], + ["id"], + ) + op.drop_constraint( + "fk_installation_id", "github_summary_configuration", type_="foreignkey" + ) + op.create_foreign_key( + "fk_installation_id", + "github_summary_configuration", + "slack_installation", + ["installation_id"], + ["id"], + ) + op.drop_constraint( + "fk_installation_id", "call_for_proposals_configuration", type_="foreignkey" + ) + op.create_foreign_key( + "fk_installation_id", + "call_for_proposals_configuration", + "slack_installation", + ["installation_id"], + ["id"], + ) + # ### end Alembic commands ### diff --git a/scripts/dev/populate_database.py b/scripts/dev/populate_database.py index e2e89e60..c937d05e 100644 --- a/scripts/dev/populate_database.py +++ b/scripts/dev/populate_database.py @@ -46,7 +46,6 @@ ) else: installation = SlackInstallation( - access_token="access_token", authorizing_user_id=authorizing_user_id, bot_access_token=bot_token, bot_user_id=bot_user_id, diff --git a/tests/_utilities/factories/slack.py b/tests/_utilities/factories/slack.py index b910d487..9e689bc0 100644 --- a/tests/_utilities/factories/slack.py +++ b/tests/_utilities/factories/slack.py @@ -11,7 +11,6 @@ class Meta: sqlalchemy_session_persistence = "commit" sqlalchemy_session = session - access_token = factory.Faker("uuid4") authorizing_user_id = "abc" bot_access_token = factory.Faker("uuid4") diff --git a/tests/apps/slack_integration/api/test_event_subscription.py b/tests/apps/slack_integration/api/test_event_subscription.py index 75444b53..fe334ad6 100644 --- a/tests/apps/slack_integration/api/test_event_subscription.py +++ b/tests/apps/slack_integration/api/test_event_subscription.py @@ -2,7 +2,13 @@ from busy_beaver.apps.slack_integration.blocks import AppHome from busy_beaver.apps.slack_integration.event_subscription import app_home_handler -from busy_beaver.models import SlackUser +from busy_beaver.models import ( + CallForProposalsConfiguration, + GitHubSummaryConfiguration, + SlackInstallation, + SlackUser, + UpcomingEventsConfiguration, +) from tests._utilities import FakeSlackClient pytest_plugins = ("tests._utilities.fixtures.slack",) @@ -36,7 +42,7 @@ def test_slack_callback_url_verification( assert resp.json == {"challenge": challenge_code} -class TestEventCallbackForDMs: +class TestDMEventCallback: @pytest.mark.integration def test_slack_callback_bot_message_is_ignored( self, client, session, patch_slack, create_slack_headers @@ -47,8 +53,8 @@ def test_slack_callback_bot_message_is_ignored( "busy_beaver.apps.slack_integration.event_subscription" ) data = { - "type": "unknown todo", - "event": {"type": "message", "subtype": "bot_message"}, + "type": "event_callback", + "event": {"type": "message", "subtype": "bot_message", "user": "my_id"}, } headers = create_slack_headers(100_000_000, data) @@ -96,44 +102,47 @@ def test_slack_callback_user_dms_bot_reply( assert kwargs["channel"] == channel -@pytest.mark.end2end -def test_user_joins_github_summary_channel_with_feature_enabled( - client, session, factory, patch_slack, create_slack_headers -): - # Arrange - patched_slack = patch_slack("busy_beaver.apps.slack_integration.event_subscription") - # Create installation in database - workspace_id = "TXXXXXXXXX" - authorizing_user_id = "alysivji" - bot_id = "test_bot" - channel = "busy-beaver" - installation = factory.SlackInstallation( - authorizing_user_id=authorizing_user_id, - workspace_id=workspace_id, - workspace_name="Test", - bot_user_id=bot_id, - ) - factory.GitHubSummaryConfiguration( - enabled=True, channel=channel, slack_installation=installation - ) +class TestMemberJoinedChannelEventCallback: + @pytest.mark.end2end + def test_user_joins_github_summary_channel_with_feature_enabled( + self, client, session, factory, patch_slack, create_slack_headers + ): + # Arrange + patched_slack = patch_slack( + "busy_beaver.apps.slack_integration.event_subscription" + ) + # Create installation in database + workspace_id = "TXXXXXXXXX" + authorizing_user_id = "alysivji" + bot_id = "test_bot" + channel = "busy-beaver" + installation = factory.SlackInstallation( + authorizing_user_id=authorizing_user_id, + workspace_id=workspace_id, + workspace_name="Test", + bot_user_id=bot_id, + ) + factory.GitHubSummaryConfiguration( + enabled=True, channel=channel, slack_installation=installation + ) - # Act -- event_subscription callback - data = { - "type": "event_callback", - "team_id": workspace_id, - "event": { - "type": "member_joined_channel", - "user": authorizing_user_id, - "channel": channel, - }, - } - headers = create_slack_headers(100_000_000, data) - client.post("/slack/event-subscription", headers=headers, json=data) + # Act -- event_subscription callback + data = { + "type": "event_callback", + "team_id": workspace_id, + "event": { + "type": "member_joined_channel", + "user": authorizing_user_id, + "channel": channel, + }, + } + headers = create_slack_headers(100_000_000, data) + client.post("/slack/event-subscription", headers=headers, json=data) - # Assert -- check that we send ephhermal message - args, kwargs = patched_slack.mock.call_args - assert "/busybeaver connect" in args[0] - assert kwargs["user_id"] == authorizing_user_id + # Assert -- check that we send ephhermal message + args, kwargs = patched_slack.mock.call_args + assert "/busybeaver connect" in args[0] + assert kwargs["user_id"] == authorizing_user_id @pytest.mark.end2end @@ -174,83 +183,121 @@ def test_user_joins_github_summary_channel_with_feature_disabled( assert patched_slack.mock.call_count == 0 -@pytest.mark.unit -def test_user_opens_app_home_for_first_time__shown_app_home( - client, session, factory, patch_slack, create_slack_headers -): - # Arrange - workspace_id = "TXXXXXXXXX" - user_id = "U5FTQ3QRZ" - installation = factory.SlackInstallation(workspace_id=workspace_id) - patched_slack = patch_slack("busy_beaver.apps.slack_integration.event_subscription") +class TestAppHomeOpenedEventCallback: + @pytest.mark.unit + def test_user_opens_app_home_for_first_time__shown_app_home( + self, client, session, factory, patch_slack, create_slack_headers + ): + # Arrange + workspace_id = "TXXXXXXXXX" + user_id = "U5FTQ3QRZ" + installation = factory.SlackInstallation(workspace_id=workspace_id) + patched_slack = patch_slack( + "busy_beaver.apps.slack_integration.event_subscription" + ) - # Act - data = { - "type": "event_callback", - "team_id": workspace_id, - "event": {"type": "app_home_opened", "user": user_id, "tab": "home"}, - } - app_home_handler(data) + # Act + data = { + "type": "event_callback", + "team_id": workspace_id, + "event": {"type": "app_home_opened", "user": user_id, "tab": "home"}, + } + app_home_handler(data) - # Assert -- check we send the app home send - args, kwargs = patched_slack.mock.call_args - assert args[0] == user_id - assert kwargs.get("view", {}) == AppHome().to_dict() + # Assert -- check we send the app home send + args, kwargs = patched_slack.mock.call_args + assert args[0] == user_id + assert kwargs.get("view", {}) == AppHome().to_dict() - params = {"installation_id": installation.id, "slack_id": user_id} - user = SlackUser.query.filter_by(**params).first() - assert user - assert user.app_home_opened_count == 1 + params = {"installation_id": installation.id, "slack_id": user_id} + user = SlackUser.query.filter_by(**params).first() + assert user + assert user.app_home_opened_count == 1 + @pytest.mark.unit + def test_user_opens_app_home_for_greater_than_first_time__shown_app_home( + self, client, session, factory, patch_slack, create_slack_headers + ): + # Arrange + workspace_id = "TXXXXXXXXX" + user_id = "U5FTQ3QRZ" + installation = factory.SlackInstallation(workspace_id=workspace_id) + user = factory.SlackUser(slack_id=user_id, installation=installation) + original_user_count = user.app_home_opened_count + patched_slack = patch_slack( + "busy_beaver.apps.slack_integration.event_subscription" + ) -@pytest.mark.unit -def test_user_opens_app_home_for_greater_than_first_time__shown_app_home( - client, session, factory, patch_slack, create_slack_headers -): - # Arrange - workspace_id = "TXXXXXXXXX" - user_id = "U5FTQ3QRZ" - installation = factory.SlackInstallation(workspace_id=workspace_id) - user = factory.SlackUser(slack_id=user_id, installation=installation) - original_user_count = user.app_home_opened_count - patched_slack = patch_slack("busy_beaver.apps.slack_integration.event_subscription") + # Act + data = { + "type": "event_callback", + "team_id": workspace_id, + "event": {"type": "app_home_opened", "user": user_id, "tab": "home"}, + } + app_home_handler(data) - # Act - data = { - "type": "event_callback", - "team_id": workspace_id, - "event": {"type": "app_home_opened", "user": user_id, "tab": "home"}, - } - app_home_handler(data) + # Assert -- check we send the app home send + args, kwargs = patched_slack.mock.call_args + assert args[0] == user_id + assert kwargs.get("view", {}) == AppHome().to_dict() - # Assert -- check we send the app home send - args, kwargs = patched_slack.mock.call_args - assert args[0] == user_id - assert kwargs.get("view", {}) == AppHome().to_dict() + params = {"installation_id": installation.id, "slack_id": user_id} + user = SlackUser.query.filter_by(**params).first() + assert user + assert user.app_home_opened_count == original_user_count + 1 - params = {"installation_id": installation.id, "slack_id": user_id} - user = SlackUser.query.filter_by(**params).first() - assert user - assert user.app_home_opened_count == original_user_count + 1 + @pytest.mark.unit + def test_user_opens_app_home_message_tab__does_nothing( + self, client, session, factory, patch_slack, create_slack_headers + ): + # Arrange + workspace_id = "TXXXXXXXXX" + user_id = "U5FTQ3QRZ" + factory.SlackInstallation(workspace_id=workspace_id) + patched_slack = patch_slack( + "busy_beaver.apps.slack_integration.event_subscription" + ) + # Act + data = { + "type": "event_callback", + "team_id": workspace_id, + "event": {"type": "app_home_opened", "user": user_id, "tab": "messages"}, + } + app_home_handler(data) -@pytest.mark.unit -def test_user_opens_app_home_message_tab__does_nothing( - client, session, factory, patch_slack, create_slack_headers -): - # Arrange - workspace_id = "TXXXXXXXXX" - user_id = "U5FTQ3QRZ" - factory.SlackInstallation(workspace_id=workspace_id) - patched_slack = patch_slack("busy_beaver.apps.slack_integration.event_subscription") + # Assert + assert patched_slack.mock.assert_not_called - # Act - data = { - "type": "event_callback", - "team_id": workspace_id, - "event": {"type": "app_home_opened", "user": user_id, "tab": "messages"}, - } - app_home_handler(data) - # Assert - assert patched_slack.mock.assert_not_called +class TestAppUninstalledEventCallback: + @pytest.mark.integration + def test_uninstall_application( + self, client, session, factory, create_slack_headers + ): + # Arrange + workspace_id = "abc" + installation = factory.SlackInstallation(workspace_id=workspace_id) + factory.SlackUser(installation=installation) + factory.CallForProposalsConfiguration(slack_installation=installation) + factory.GitHubSummaryConfiguration(slack_installation=installation) + factory.UpcomingEventsConfiguration(slack_installation=installation) + + data = { + "type": "event_callback", + "event": {"type": "app_uninstalled"}, + "team_id": workspace_id, + } + headers = create_slack_headers(100_000_000, data) + + # Act + resp = client.post("/slack/event-subscription", headers=headers, json=data) + + # Assert + assert resp.status_code == 200 + + assert SlackInstallation.query.count() == 0 + assert SlackUser.query.count() == 0 + assert CallForProposalsConfiguration.query.count() == 0 + assert UpcomingEventsConfiguration.query.count() == 0 + assert GitHubSummaryConfiguration.query.count() == 0 diff --git a/tests/apps/slack_integration/functional/test_install_integration.py b/tests/apps/slack_integration/functional/test_install_integration.py index c7f66f89..4675c3fa 100644 --- a/tests/apps/slack_integration/functional/test_install_integration.py +++ b/tests/apps/slack_integration/functional/test_install_integration.py @@ -26,6 +26,9 @@ def test_slack_oauth_flow_first_time_installation( patched_slack = patch_slack("busy_beaver.apps.slack_integration.oauth.workflow") authorizing_user_id = "abc" workspace_id = "T9TK3CUKW" + workspace_name = "Slack Softball Team" + bot_user_id = "U0KRQLJ9H" + scope = "app_mentions:read,channels:history" # Step 1 -- User installs app user Slack install link # Arrange @@ -35,20 +38,17 @@ def test_slack_oauth_flow_first_time_installation( SlackInstallationOAuthFlow.TOKEN_URL, match_querystring=False, json={ - "ok": True, "access_token": bot_access_token, - "token_type": "bot", - "scope": "commands,incoming-webhook", - "bot_user_id": "U0KRQLJ9H", "app_id": "A0KRD7HC3", - "team": {"name": "Slack Softball Team", "id": workspace_id}, - "enterprise": {"name": "slack-sports", "id": "E12345678"}, - "authed_user": { - "id": authorizing_user_id, - "scope": "chat:write", - "access_token": "xoxp-1234", - "token_type": "user", - }, + "authed_user": {"id": authorizing_user_id}, + "bot_user_id": bot_user_id, + "enterprise": None, + "ok": True, + "response_metadata": {"warnings": ["superfluous_charset"]}, + "scope": scope, + "team": {"name": workspace_name, "id": workspace_id}, + "token_type": "bot", + "warning": "superfluous_charset", }, ) @@ -59,12 +59,11 @@ def test_slack_oauth_flow_first_time_installation( # Assert -- confirm info in database is as expected assert response.status_code == 200 installation = SlackInstallation.query.first() - assert installation.access_token == "xoxp-1234" - assert installation.scope == "commands,incoming-webhook" - assert installation.workspace_name == "Slack Softball Team" + assert installation.scope == scope + assert installation.workspace_name == workspace_name assert installation.workspace_id == workspace_id assert installation.authorizing_user_id == authorizing_user_id - assert installation.bot_user_id == "U0KRQLJ9H" + assert installation.bot_user_id == bot_user_id assert installation.bot_access_token == bot_access_token # Assert -- message sent to user who installed @@ -84,6 +83,8 @@ def test_slack_oauth_flow_reinstallation(client, session, factory, patch_slack): workspace_id = "T9TK3CUKW" workspace_name = "Slack Softball Team" authorizing_user_id = "abc" + bot_user_id = "U0KRQLJ9H" + scope = "app_mentions:read,channels:history" installation = factory.SlackInstallation( workspace_id=workspace_id, workspace_name=workspace_name ) @@ -95,20 +96,17 @@ def test_slack_oauth_flow_reinstallation(client, session, factory, patch_slack): responses.POST, SlackInstallationOAuthFlow.TOKEN_URL, json={ - "ok": True, "access_token": bot_access_token, - "token_type": "bot", - "scope": "commands,incoming-webhook", - "bot_user_id": "U0KRQLJ9H", "app_id": "A0KRD7HC3", + "authed_user": {"id": authorizing_user_id}, + "bot_user_id": bot_user_id, + "enterprise": None, + "ok": True, + "response_metadata": {"warnings": ["superfluous_charset"]}, + "scope": scope, "team": {"name": workspace_name, "id": workspace_id}, - "enterprise": {"name": "slack-sports", "id": "E12345678"}, - "authed_user": { - "id": authorizing_user_id, - "scope": "chat:write", - "access_token": "xoxp-1234", - "token_type": "user", - }, + "token_type": "bot", + "warning": "superfluous_charset", }, ) @@ -119,12 +117,11 @@ def test_slack_oauth_flow_reinstallation(client, session, factory, patch_slack): # Assert -- information in database is as expected assert response.status_code == 200 installation = SlackInstallation.query.first() - assert installation.access_token == "xoxp-1234" - assert installation.scope == "commands,incoming-webhook" + assert installation.scope == scope assert installation.workspace_name == workspace_name assert installation.workspace_id == workspace_id assert installation.authorizing_user_id == authorizing_user_id - assert installation.bot_user_id == "U0KRQLJ9H" + assert installation.bot_user_id == bot_user_id assert installation.bot_access_token == bot_access_token # Assert -- message sent to user who installed