oncall-engine/engine/apps/slack/tests/test_installation.py
Vadim Stepanov 87d7982250
Unified Slack app reinstall (#4682)
# What this PR does

Adds a button to reinstall the Slack app to migrate to Unified Slack
App. Also adds backend support for this.

<img width="1204" alt="Screenshot 2024-07-18 at 18 33 08"
src="https://github.com/user-attachments/assets/a326b4a2-fc65-4b88-98c0-2955e3717e3a">


## Which issue(s) this PR closes

Related to https://github.com/grafana/oncall-gateway/issues/252

<!--
*Note*: if you have more than one GitHub issue that this PR closes, be
sure to preface
each issue link with a [closing
keyword](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/using-keywords-in-issues-and-pull-requests#linking-a-pull-request-to-an-issue).
This ensures that the issue(s) are auto-closed once the PR has been
merged.
-->

## Checklist

- [x] Unit, integration, and e2e (if applicable) tests updated
- [x] Documentation added (or `pr:no public docs` PR label added if not
required)
- [x] Added the relevant release notes label (see labels prefixed w/
`release:`). These labels dictate how your PR will
    show up in the autogenerated release notes.
2024-07-19 11:53:06 +00:00

155 lines
6.1 KiB
Python

# Response example from Slack docs https://api.slack.com/methods/oauth.v2.access#examples
from unittest.mock import patch
import pytest
from apps.slack.client import SlackClient
from apps.slack.installation import SlackInstallationExc, install_slack_integration, uninstall_slack_integration
from common.constants.slack_auth import SLACK_OAUTH_ACCESS_RESPONSE
users_profile_get_response = {
"ok": True,
"user": {
"id": "W012A3CDE",
"team_id": "T012AB3C4",
"name": "spengler",
"deleted": False,
"color": "9f69e7",
"real_name": "Egon Spengler",
"tz": "America/Los_Angeles",
"tz_label": "Pacific Daylight Time",
"tz_offset": -25200,
"profile": {
"avatar_hash": "ge3b51ca72de",
"status_text": "Print is dead",
"status_emoji": ":books:",
"real_name": "Egon Spengler",
"display_name": "spengler",
"real_name_normalized": "Egon Spengler",
"display_name_normalized": "spengler",
"email": "spengler@ghostbusters.example.com",
"image_original": "https://.../avatar/e3b51ca72dee4ef87916ae2b9240df50.jpg",
"image_24": "https://.../avatar/e3b51ca72dee4ef87916ae2b9240df50.jpg",
"image_32": "https://.../avatar/e3b51ca72dee4ef87916ae2b9240df50.jpg",
"image_48": "https://.../avatar/e3b51ca72dee4ef87916ae2b9240df50.jpg",
"image_72": "https://.../avatar/e3b51ca72dee4ef87916ae2b9240df50.jpg",
"image_192": "https://.../avatar/e3b51ca72dee4ef87916ae2b9240df50.jpg",
"image_512": "https://.../avatar/e3b51ca72dee4ef87916ae2b9240df50.jpg",
"team": "T012AB3C4",
},
"is_admin": True,
"is_owner": False,
"is_primary_owner": False,
"is_restricted": False,
"is_ultra_restricted": False,
"is_bot": False,
"updated": 1502138686,
"is_app_user": False,
"has_2fa": False,
},
}
@patch("apps.slack.tasks.populate_slack_channels_for_team.apply_async", return_value=None)
@patch("apps.slack.tasks.populate_slack_usergroups_for_team.apply_async", return_value=None)
@patch.object(SlackClient, "users_info", return_value=users_profile_get_response)
@pytest.mark.django_db
def test_install_slack_integration(
mock_populate_slack_channels_for_team,
mock_populate_slack_usergroups_for_team,
mock_users_info,
make_organization_and_user,
):
organization, user = make_organization_and_user()
install_slack_integration(organization, user, SLACK_OAUTH_ACCESS_RESPONSE)
assert organization.slack_team_identity is not None
# test that two most important fields are set: id of slack workspace and api acess token
assert organization.slack_team_identity.slack_id == SLACK_OAUTH_ACCESS_RESPONSE["team"]["id"]
assert organization.slack_team_identity.bot_access_token == SLACK_OAUTH_ACCESS_RESPONSE["access_token"]
# install_slack_integration links instgallers's slack profile to OnCall
assert user.slack_user_identity is not None
# assert that installer slack profile is linked to OnCall user
assert user.slack_user_identity.slack_id == SLACK_OAUTH_ACCESS_RESPONSE["authed_user"]["id"]
# assert that we populated user's profile info
assert user.slack_user_identity.cached_slack_login == users_profile_get_response["user"]["name"]
# assert that we ran task for fetching data from slack
assert mock_populate_slack_channels_for_team.called
assert mock_populate_slack_usergroups_for_team.called
@pytest.mark.django_db
def test_install_slack_integration_raises_exception_for_existing_integration(
make_organization_and_user, make_slack_team_identity
):
slack_team_identity = make_slack_team_identity()
organization, user = make_organization_and_user()
organization.slack_team_identity = slack_team_identity
organization.save()
with pytest.raises(SlackInstallationExc):
install_slack_integration(organization, user, SLACK_OAUTH_ACCESS_RESPONSE)
@pytest.mark.django_db
def test_install_slack_integration_legacy(settings, make_organization_and_user, make_slack_team_identity):
settings.UNIFIED_SLACK_APP_ENABLED = True
slack_team_identity = make_slack_team_identity(
slack_id=SLACK_OAUTH_ACCESS_RESPONSE["team"]["id"], _unified_slack_app_installed=False
)
organization, user = make_organization_and_user()
organization.slack_team_identity = slack_team_identity
organization.save()
install_slack_integration(organization, user, SLACK_OAUTH_ACCESS_RESPONSE)
slack_team_identity.refresh_from_db()
assert slack_team_identity.needs_reinstall is False
# raises exception if organization already re-installed the app
with pytest.raises(SlackInstallationExc):
install_slack_integration(organization, user, SLACK_OAUTH_ACCESS_RESPONSE)
@patch("apps.slack.tasks.clean_slack_integration_leftovers.apply_async", return_value=None)
@pytest.mark.django_db
def test_uninstall_slack_integration(
mock_clean_slack_integration_leftovers,
make_organization_and_user,
make_slack_team_identity,
make_slack_user_identity,
):
slack_team_identity = make_slack_team_identity()
organization, user = make_organization_and_user()
organization.slack_team_identity = slack_team_identity
organization.save()
organization.refresh_from_db()
slack_user_identity = make_slack_user_identity(slack_team_identity=slack_team_identity)
user.slack_user_identity = slack_user_identity
user.save()
user.refresh_from_db()
uninstall_slack_integration(organization, user)
organization.refresh_from_db()
user.refresh_from_db()
assert organization.slack_team_identity is None
assert user.slack_user_identity is None
# assert that we ran task for fetching data from slack
assert mock_clean_slack_integration_leftovers.called
@pytest.mark.django_db
def test_uninstall_slack_integration_raises_exception_for_non_existing_integration(
make_organization_and_user,
):
organization, user = make_organization_and_user()
with pytest.raises(SlackInstallationExc):
uninstall_slack_integration(organization, user)