# What this PR does - Stops writing `SlackMessage.organization` + removes references to this field. [As we discussed](https://raintank-corp.slack.com/archives/C083TU81TCH/p1733315887463279?thread_ts=1733311105.095309&cid=C083TU81TCH), we do not need this field on this model/table, `SlackMessage._slack_team_identity` is sufficient (`organization` will be dropped in a separate PR) - Adds a data migration script which: - drops orphaned `SlackMessage` records; ie. ones which, even after the [`engine/apps/slack/migrations/0007_migrate_slackmessage_channel_id.py`](https://github.com/grafana/oncall/blob/dev/engine/apps/slack/migrations/0007_migrate_slackmessage_channel_id.py) migration, still don't have a `SlackMessage.channel` id filled in (we discussed + agreed on dropping these records [here](https://raintank-corp.slack.com/archives/C083TU81TCH/p1733329914516859?thread_ts=1733311105.095309&cid=C083TU81TCH)) - fills in empty `SlackMessage.slack_team_identity` values (from `slack_message.channel.slack_team_identity`) ### Other notes On the `organization` topic. We store records in `SlackMessage` for two purposes (AFAICT), and in both cases, we have references back to the `organization`: - alert groups - `slack_message.alert_group.channel.organization` - shift swap requests - `shift_swap_request.schedule.organization` ## 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.
224 lines
7.9 KiB
Python
224 lines
7.9 KiB
Python
from unittest.mock import patch
|
|
|
|
import pytest
|
|
from django.utils import timezone
|
|
|
|
from apps.slack.models import SlackChannel, SlackMessage
|
|
from apps.slack.scenarios import slack_channel as slack_channel_scenarios
|
|
|
|
|
|
@pytest.mark.django_db
|
|
class TestSlackChannelCreatedOrRenamedEventStep:
|
|
def test_process_scenario_channel_created(
|
|
self,
|
|
make_organization_and_user_with_slack_identities,
|
|
) -> None:
|
|
(
|
|
organization,
|
|
user,
|
|
slack_team_identity,
|
|
slack_user_identity,
|
|
) = make_organization_and_user_with_slack_identities()
|
|
slack_channel_id = "C12345678"
|
|
channel_name = "new-channel"
|
|
payload = {
|
|
"event": {
|
|
"channel": {
|
|
"id": slack_channel_id,
|
|
"name": channel_name,
|
|
}
|
|
}
|
|
}
|
|
|
|
# Ensure the SlackChannel does not exist
|
|
assert not SlackChannel.objects.filter(
|
|
slack_id=slack_channel_id,
|
|
slack_team_identity=slack_team_identity,
|
|
).exists()
|
|
|
|
step = slack_channel_scenarios.SlackChannelCreatedOrRenamedEventStep(slack_team_identity, organization, user)
|
|
step.process_scenario(slack_user_identity, slack_team_identity, payload)
|
|
|
|
# Now the SlackChannel should exist with correct data
|
|
slack_channel = SlackChannel.objects.get(
|
|
slack_id=slack_channel_id,
|
|
slack_team_identity=slack_team_identity,
|
|
)
|
|
assert slack_channel.name == channel_name
|
|
assert slack_channel.last_populated == timezone.now().date()
|
|
|
|
def test_process_scenario_channel_renamed(
|
|
self,
|
|
make_organization_and_user_with_slack_identities,
|
|
make_slack_channel,
|
|
) -> None:
|
|
(
|
|
organization,
|
|
user,
|
|
slack_team_identity,
|
|
slack_user_identity,
|
|
) = make_organization_and_user_with_slack_identities()
|
|
slack_channel = make_slack_channel(slack_team_identity)
|
|
slack_channel_id = slack_channel.slack_id
|
|
new_name = "renamed-channel"
|
|
payload = {
|
|
"event": {
|
|
"channel": {
|
|
"id": slack_channel_id,
|
|
"name": new_name,
|
|
}
|
|
}
|
|
}
|
|
|
|
step = slack_channel_scenarios.SlackChannelCreatedOrRenamedEventStep(slack_team_identity, organization, user)
|
|
step.process_scenario(slack_user_identity, slack_team_identity, payload)
|
|
|
|
slack_channel.refresh_from_db()
|
|
assert slack_channel.name == new_name
|
|
assert slack_channel.last_populated == timezone.now().date()
|
|
|
|
|
|
@pytest.mark.django_db
|
|
class TestSlackChannelDeletedEventStep:
|
|
def test_process_scenario_channel_deleted(
|
|
self,
|
|
make_organization_and_user_with_slack_identities,
|
|
make_slack_channel,
|
|
make_slack_message,
|
|
) -> None:
|
|
(
|
|
organization,
|
|
user,
|
|
slack_team_identity,
|
|
slack_user_identity,
|
|
) = make_organization_and_user_with_slack_identities()
|
|
slack_channel = make_slack_channel(slack_team_identity)
|
|
make_slack_message(slack_channel)
|
|
slack_channel_id = slack_channel.slack_id
|
|
|
|
# Ensure the SlackChannel exists
|
|
assert SlackChannel.objects.filter(
|
|
slack_id=slack_channel_id,
|
|
slack_team_identity=slack_team_identity,
|
|
).exists()
|
|
|
|
assert SlackMessage.objects.count() == 1
|
|
|
|
step = slack_channel_scenarios.SlackChannelDeletedEventStep(slack_team_identity, organization, user)
|
|
step.process_scenario(slack_user_identity, slack_team_identity, {"event": {"channel": slack_channel_id}})
|
|
|
|
# Now the SlackChannel should not exist
|
|
assert not SlackChannel.objects.filter(
|
|
slack_id=slack_channel_id,
|
|
slack_team_identity=slack_team_identity,
|
|
).exists()
|
|
|
|
# Slack messages should be cascade deleted when their channel is deleted
|
|
assert SlackMessage.objects.count() == 0
|
|
|
|
def test_process_scenario_channel_does_not_exist(
|
|
self,
|
|
make_organization_and_user_with_slack_identities,
|
|
) -> None:
|
|
(
|
|
organization,
|
|
user,
|
|
slack_team_identity,
|
|
slack_user_identity,
|
|
) = make_organization_and_user_with_slack_identities()
|
|
slack_channel_id = "C12345678"
|
|
|
|
# Ensure the SlackChannel does not exist
|
|
assert not SlackChannel.objects.filter(
|
|
slack_id=slack_channel_id,
|
|
slack_team_identity=slack_team_identity,
|
|
).exists()
|
|
|
|
step = slack_channel_scenarios.SlackChannelDeletedEventStep(slack_team_identity, organization, user)
|
|
# The step should not raise an exception even if the channel does not exist
|
|
step.process_scenario(slack_user_identity, slack_team_identity, {"event": {"channel": slack_channel_id}})
|
|
|
|
# Still, the SlackChannel does not exist
|
|
assert not SlackChannel.objects.filter(
|
|
slack_id=slack_channel_id,
|
|
slack_team_identity=slack_team_identity,
|
|
).exists()
|
|
|
|
|
|
@pytest.mark.django_db
|
|
class TestSlackChannelArchivedEventStep:
|
|
@patch("apps.slack.scenarios.slack_channel.clean_slack_channel_leftovers")
|
|
def test_process_scenario(
|
|
self,
|
|
mock_clean_slack_channel_leftovers,
|
|
make_organization_and_user_with_slack_identities,
|
|
make_slack_channel,
|
|
) -> None:
|
|
(
|
|
organization,
|
|
user,
|
|
slack_team_identity,
|
|
slack_user_identity,
|
|
) = make_organization_and_user_with_slack_identities()
|
|
slack_channel = make_slack_channel(slack_team_identity)
|
|
slack_channel_id = slack_channel.slack_id
|
|
|
|
assert slack_channel.is_archived is False
|
|
|
|
step = slack_channel_scenarios.SlackChannelArchivedEventStep(slack_team_identity, organization, user)
|
|
step.process_scenario(slack_user_identity, slack_team_identity, {"event": {"channel": slack_channel_id}})
|
|
|
|
slack_channel.refresh_from_db()
|
|
|
|
assert slack_channel.is_archived is True
|
|
mock_clean_slack_channel_leftovers.apply_async.assert_called_once_with(
|
|
(slack_team_identity.id, slack_channel_id)
|
|
)
|
|
|
|
|
|
@pytest.mark.django_db
|
|
class TestSlackChannelUnarchivedEventStep:
|
|
def test_process_scenario_channel_unarchived(
|
|
self,
|
|
make_organization_and_user_with_slack_identities,
|
|
make_slack_channel,
|
|
) -> None:
|
|
(
|
|
organization,
|
|
user,
|
|
slack_team_identity,
|
|
slack_user_identity,
|
|
) = make_organization_and_user_with_slack_identities()
|
|
slack_channel = make_slack_channel(slack_team_identity, is_archived=True)
|
|
slack_channel_id = slack_channel.slack_id
|
|
|
|
assert slack_channel.is_archived is True
|
|
|
|
step = slack_channel_scenarios.SlackChannelUnarchivedEventStep(slack_team_identity, organization, user)
|
|
step.process_scenario(slack_user_identity, slack_team_identity, {"event": {"channel": slack_channel_id}})
|
|
|
|
slack_channel.refresh_from_db()
|
|
assert slack_channel.is_archived is False
|
|
|
|
def test_process_scenario_channel_already_unarchived(
|
|
self,
|
|
make_organization_and_user_with_slack_identities,
|
|
make_slack_channel,
|
|
) -> None:
|
|
(
|
|
organization,
|
|
user,
|
|
slack_team_identity,
|
|
slack_user_identity,
|
|
) = make_organization_and_user_with_slack_identities()
|
|
slack_channel = make_slack_channel(slack_team_identity, is_archived=False)
|
|
slack_channel_id = slack_channel.slack_id
|
|
|
|
assert slack_channel.is_archived is False
|
|
|
|
step = slack_channel_scenarios.SlackChannelUnarchivedEventStep(slack_team_identity, organization, user)
|
|
step.process_scenario(slack_user_identity, slack_team_identity, {"event": {"channel": slack_channel_id}})
|
|
|
|
slack_channel.refresh_from_db()
|
|
# Ensure that is_archived remains False
|
|
assert slack_channel.is_archived is False
|