afterhours-shift-manager/tests/slack_bot/test_admin_modals.py

190 lines
7.3 KiB
Python
Raw Permalink Normal View History

"""Tests for the admin Block Kit modals — opening (views.open), submitting
(view_submission), shared validation, and non-admin rejection (the IDOR case:
a modal can be opened from Home, so the surface is never trusted)."""
from unittest.mock import MagicMock
import pytest
from freezegun import freeze_time
ADMIN = "U_ADMIN"
INTRUDER = "U_INTRUDER"
# 2026-06-01 is a Monday → night shift; 18:00 ET is inside the night window.
MON_NIGHT = "2026-06-01 18:00:00"
@pytest.fixture
def admin_schedule(schedule, seed):
seed.config(admin_users=[ADMIN])
return schedule
@pytest.fixture
def ack():
return MagicMock(name="ack")
def _override_view(date="2026-12-25", ext="114", shift="night"):
return {
"state": {
"values": {
"date": {"date": {"selected_date": date}},
"extension": {"extension": {"selected_option": {"value": ext}}},
"shift_type": {"shift_type": {"selected_option": {"value": shift}}},
}
}
}
def _holiday_view(date="2026-12-25", slots="2", multiplier="", label="Christmas"):
return {
"state": {
"values": {
"date": {"date": {"selected_date": date}},
"slots": {"slots": {"value": slots}},
"multiplier": {"multiplier": {"value": multiplier or None}},
"label": {"label": {"value": label}},
}
}
}
def _body(user_id=ADMIN, trigger_id="T123"):
return {"user": {"id": user_id}, "trigger_id": trigger_id}
class TestOpenModals:
def test_admin_opens_override_modal(
self, slackbot_app, admin_schedule, seed, client
):
seed.roster("114", "Alice")
slackbot_app.open_override_modal(_body(), client, admin_schedule)
view = client.views_open.call_args.kwargs["view"]
assert view["callback_id"] == "admin_override_submit"
def test_admin_opens_holiday_modal(self, slackbot_app, admin_schedule, client):
slackbot_app.open_holiday_add_modal(_body(), client, admin_schedule)
view = client.views_open.call_args.kwargs["view"]
assert view["callback_id"] == "admin_holiday_add_submit"
def test_non_admin_cannot_open_override(self, slackbot_app, admin_schedule, client):
slackbot_app.open_override_modal(_body(INTRUDER), client, admin_schedule)
client.views_open.assert_not_called()
def test_non_admin_cannot_open_holiday(self, slackbot_app, admin_schedule, client):
slackbot_app.open_holiday_add_modal(_body(INTRUDER), client, admin_schedule)
client.views_open.assert_not_called()
class TestOverrideSubmission:
def test_happy_path(self, slackbot_app, admin_schedule, seed, ack, client):
seed.roster("114", "Alice")
slackbot_app.handle_override_submission(
ack, _body(), _override_view(), client, admin_schedule, None
)
assert admin_schedule.get_override("2026-12-25")["extension"] == "114"
ack.assert_called_once_with()
assert client.chat_postMessage.call_args.kwargs["channel"] == ADMIN
@freeze_time(MON_NIGHT)
def test_same_day_repoints_and_confirms(
self, slackbot_app, admin_schedule, seed, ack, client, routing_spy
):
seed.roster("114", "Alice")
slackbot_app.handle_override_submission(
ack,
_body(),
_override_view(date="2026-06-01"),
client,
admin_schedule,
None,
)
routing_spy.assert_called_once_with("114")
assert "repointed" in client.chat_postMessage.call_args.kwargs["text"].lower()
def test_unknown_extension_surfaces_field_error(
self, slackbot_app, admin_schedule, ack, client
):
slackbot_app.handle_override_submission(
ack, _body(), _override_view(ext="999"), client, admin_schedule, None
)
kwargs = ack.call_args.kwargs
assert kwargs["response_action"] == "errors"
assert "extension" in kwargs["errors"]
assert admin_schedule.get_override("2026-12-25") is None
def test_non_admin_rejected(self, slackbot_app, admin_schedule, seed, ack, client):
seed.roster("114", "Alice")
slackbot_app.handle_override_submission(
ack, _body(INTRUDER), _override_view(), client, admin_schedule, None
)
assert ack.call_args.kwargs["response_action"] == "errors"
assert admin_schedule.get_override("2026-12-25") is None
@freeze_time("2026-06-01 12:00:00")
class TestHolidaySubmission:
def test_happy_path(self, slackbot_app, admin_schedule, ack, client, monkeypatch):
monkeypatch.setattr(slackbot_app, "_activate_holiday_inline", MagicMock())
slackbot_app.handle_holiday_add_submission(
ack, _body(), _holiday_view(multiplier="2"), client, admin_schedule, "C_T"
)
holiday = admin_schedule.get_holiday("2026-12-25")
assert holiday["slots"] == 2
assert holiday["label"] == "Christmas"
assert float(holiday["multiplier"]) == 2.0
ack.assert_called_once_with()
assert client.chat_postMessage.call_args.kwargs["channel"] == ADMIN
def test_default_multiplier_when_blank(
self, slackbot_app, admin_schedule, ack, client, monkeypatch
):
monkeypatch.setattr(slackbot_app, "_activate_holiday_inline", MagicMock())
slackbot_app.handle_holiday_add_submission(
ack, _body(), _holiday_view(), client, admin_schedule, None
)
assert float(admin_schedule.get_holiday("2026-12-25")["multiplier"]) == 1.5
def test_past_date_surfaces_field_error(
self, slackbot_app, admin_schedule, ack, client
):
slackbot_app.handle_holiday_add_submission(
ack, _body(), _holiday_view(date="2026-01-01"), client, admin_schedule, None
)
errors = ack.call_args.kwargs["errors"]
assert "date" in errors and "past" in errors["date"].lower()
assert admin_schedule.get_holiday("2026-01-01") is None
def test_bad_slots_surfaces_field_error(
self, slackbot_app, admin_schedule, ack, client
):
slackbot_app.handle_holiday_add_submission(
ack, _body(), _holiday_view(slots="lots"), client, admin_schedule, None
)
assert "slots" in ack.call_args.kwargs["errors"]
assert admin_schedule.get_holiday("2026-12-25") is None
def test_missing_label_surfaces_field_error(
self, slackbot_app, admin_schedule, ack, client
):
slackbot_app.handle_holiday_add_submission(
ack, _body(), _holiday_view(label=""), client, admin_schedule, None
)
assert "label" in ack.call_args.kwargs["errors"]
def test_bad_multiplier_surfaces_field_error(
self, slackbot_app, admin_schedule, ack, client
):
slackbot_app.handle_holiday_add_submission(
ack, _body(), _holiday_view(multiplier="huge"), client, admin_schedule, None
)
assert "multiplier" in ack.call_args.kwargs["errors"]
def test_non_admin_rejected(self, slackbot_app, admin_schedule, ack, client):
slackbot_app.handle_holiday_add_submission(
ack, _body(INTRUDER), _holiday_view(), client, admin_schedule, None
)
assert ack.call_args.kwargs["response_action"] == "errors"
assert admin_schedule.get_holiday("2026-12-25") is None