"""Tests for slack-bot _handle_drop, including the 24h drop lock (#84).""" from freezegun import freeze_time # Monday 2026-06-01 08:00 ET. MON_0800 = "2026-06-01 12:00:00" # Monday 2026-06-01 18:00 ET — past 17:00 (so tomorrow's night shift is <24h away). MON_1800 = "2026-06-01 22:00:00" # Friday 2026-06-05. FRI_0600 = "2026-06-05 10:00:00" # 06:00 ET FRI_0900 = "2026-06-05 13:00:00" # 09:00 ET @freeze_time(MON_0800) def test_drop_outside_24h_marks_open( slackbot_app, schedule, seed, respond, client, routing_spy, text_of ): # Tomorrow's night shift starts 06-02 17:00; now is 06-01 08:00 → ~33h away. seed.roster("114", "Alice", slack_user_id="U_ALICE") seed.weekly("Tuesday", "114", "Alice") slackbot_app._handle_drop( respond, schedule, "U_ALICE", "drop tomorrow", "C1", client, None ) assert schedule.get_override("2026-06-02")["extension"] == "OPEN" assert "dropped" in text_of(respond) client.chat_postMessage.assert_called_once() routing_spy.assert_not_called() @freeze_time(MON_0800) def test_drop_today_blocked( slackbot_app, schedule, seed, respond, client, routing_spy, text_of ): # A same-day shift is always inside the 24h lock. seed.roster("114", "Alice", slack_user_id="U_ALICE") seed.weekly("Monday", "114", "Alice") slackbot_app._handle_drop( respond, schedule, "U_ALICE", "drop today", "C1", client, None ) assert "under 24 hours" in text_of(respond) assert schedule.get_override("2026-06-01") is None # not opened routing_spy.assert_not_called() client.chat_postMessage.assert_not_called() @freeze_time(MON_1800) def test_drop_blocked_once_within_24h_of_start( slackbot_app, schedule, seed, respond, client, text_of ): # At 06-01 18:00, tomorrow's 06-02 17:00 shift is <24h away. seed.roster("114", "Alice", slack_user_id="U_ALICE") seed.weekly("Tuesday", "114", "Alice") slackbot_app._handle_drop( respond, schedule, "U_ALICE", "drop tomorrow", "C1", client, None ) assert "under 24 hours" in text_of(respond) assert schedule.get_override("2026-06-02") is None @freeze_time(FRI_0600) def test_drop_weekend_day_shift_outside_24h( slackbot_app, schedule, seed, respond, client, text_of ): # Saturday day shift starts 06-06 08:00; now Fri 06:00 → >24h. seed.roster("200", "Alice", slack_user_id="U_ALICE") seed.weekly("Saturday", "200", "Alice", shift_type="day") slackbot_app._handle_drop( respond, schedule, "U_ALICE", "drop saturday", "C1", client, None ) assert "dropped" in text_of(respond) assert schedule.get_override("2026-06-06", "day")["extension"] == "OPEN" @freeze_time(FRI_0900) def test_drop_weekend_day_shift_within_24h( slackbot_app, schedule, seed, respond, client, text_of ): # Now Fri 09:00 → Saturday 08:00 day shift is <24h away. seed.roster("200", "Alice", slack_user_id="U_ALICE") seed.weekly("Saturday", "200", "Alice", shift_type="day") slackbot_app._handle_drop( respond, schedule, "U_ALICE", "drop saturday", "C1", client, None ) assert "under 24 hours" in text_of(respond) assert schedule.get_override("2026-06-06", "day") is None @freeze_time(MON_0800) def test_admin_open_bypasses_24h_lock( slackbot_app, schedule, seed, respond, client, routing_spy, text_of ): # Admins can still open a same-day shift — the lock only applies to user drop. seed.weekly("Monday", "114", "Alice") slackbot_app._handle_admin( respond, schedule, "U_ADMIN", "admin open today", True, client, None ) assert schedule.get_override("2026-06-01")["extension"] == "OPEN" assert "marked as open" in text_of(respond).lower() @freeze_time(MON_0800) def test_drop_not_your_shift(slackbot_app, schedule, seed, respond, client, text_of): seed.roster("114", "Alice", slack_user_id="U_ALICE") seed.weekly("Monday", "115", "Bob") # Bob is on shift, not Alice slackbot_app._handle_drop( respond, schedule, "U_ALICE", "drop today", "C1", client, None ) assert "not your shift" in text_of(respond).lower() @freeze_time(MON_0800) def test_drop_past_date(slackbot_app, schedule, seed, respond, client, text_of): seed.roster("114", "Alice", slack_user_id="U_ALICE") seed.weekly("Monday", "114", "Alice") slackbot_app._handle_drop( respond, schedule, "U_ALICE", "drop 2026-05-01", "C1", client, None ) assert "past" in text_of(respond).lower() @freeze_time(MON_0800) def test_drop_unregistered(slackbot_app, schedule, respond, client, text_of): slackbot_app._handle_drop( respond, schedule, "U_NOBODY", "drop today", "C1", client, None ) assert "not registered" in text_of(respond).lower() @freeze_time(MON_0800) def test_drop_bad_date(slackbot_app, schedule, seed, respond, client, text_of): seed.roster("114", "Alice", slack_user_id="U_ALICE") seed.weekly("Monday", "114", "Alice") slackbot_app._handle_drop( respond, schedule, "U_ALICE", "drop notaday", "C1", client, None ) assert "couldn't parse" in text_of(respond).lower() @freeze_time(MON_0800) def test_drop_usage(slackbot_app, schedule, respond, client, text_of): slackbot_app._handle_drop(respond, schedule, "U_ALICE", "drop", "C1", client, None) assert "Usage" in text_of(respond)