import datetime from unittest.mock import patch import pytest import pytz from django.utils import timezone from apps.schedules import exceptions from apps.schedules.models import CustomOnCallShift, ShiftSwapRequest from apps.user_management.models import User ROTATION_START = datetime.datetime(2150, 8, 29, 0, 0, 0, 0, tzinfo=pytz.UTC) @pytest.mark.django_db def test_soft_delete(shift_swap_request_setup): ssr, _, _ = shift_swap_request_setup() assert ssr.deleted_at is None with patch("apps.schedules.models.shift_swap_request.refresh_ical_final_schedule") as mock_refresh_final: ssr.delete() ssr.refresh_from_db() assert ssr.deleted_at is not None mock_refresh_final.apply_async.assert_called_with((ssr.schedule.pk,)) assert ShiftSwapRequest.objects.all().count() == 0 assert ShiftSwapRequest.objects_with_deleted.all().count() == 1 @pytest.mark.django_db def test_status_open(shift_swap_request_setup) -> None: ssr, _, _ = shift_swap_request_setup() assert ssr.status == ShiftSwapRequest.Statuses.OPEN @pytest.mark.django_db def test_status_taken(shift_swap_request_setup) -> None: ssr, _, benefactor = shift_swap_request_setup() assert ssr.status == ShiftSwapRequest.Statuses.OPEN assert ssr.is_taken is False ssr.benefactor = benefactor ssr.save() assert ssr.status == ShiftSwapRequest.Statuses.TAKEN assert ssr.is_taken is True # taken in the past it's still taken now = timezone.now() ssr.swap_start = now - timezone.timedelta(days=2) ssr.save() assert ssr.status == ShiftSwapRequest.Statuses.TAKEN assert ssr.is_taken is True assert ssr.is_past_due is False @pytest.mark.django_db def test_status_past_due(shift_swap_request_setup) -> None: ssr, _, _ = shift_swap_request_setup() assert ssr.status == ShiftSwapRequest.Statuses.OPEN assert ssr.is_past_due is False ssr.swap_start = ssr.swap_start - datetime.timedelta(days=5) ssr.save() assert ssr.status == ShiftSwapRequest.Statuses.PAST_DUE assert ssr.is_past_due is True @pytest.mark.django_db def test_status_deleted(shift_swap_request_setup) -> None: ssr, _, _ = shift_swap_request_setup() assert ssr.status == ShiftSwapRequest.Statuses.OPEN assert ssr.is_deleted is False ssr.delete() assert ssr.status == ShiftSwapRequest.Statuses.DELETED assert ssr.is_deleted is True @patch("apps.schedules.tasks.shift_swaps.update_shift_swap_request_message") @patch("apps.schedules.tasks.shift_swaps.notify_beneficiary_about_taken_shift_swap_request") @patch("apps.schedules.models.shift_swap_request.refresh_ical_final_schedule") @pytest.mark.django_db def test_take( mock_refresh_final, mock_notify_beneficiary_about_taken_shift_swap_request, mock_update_shift_swap_request_message, shift_swap_request_setup, ) -> None: ssr, _, benefactor = shift_swap_request_setup() original_updated_at = ssr.updated_at ssr.take(benefactor) assert ssr.benefactor == benefactor assert ssr.updated_at != original_updated_at mock_update_shift_swap_request_message.apply_async.assert_called_once_with((ssr.pk,)) mock_notify_beneficiary_about_taken_shift_swap_request.apply_async.assert_called_once_with((ssr.pk,)) # final schedule refresh was triggered mock_refresh_final.apply_async.assert_called_with((ssr.schedule.pk,)) @pytest.mark.django_db def test_take_only_works_for_open_requests(shift_swap_request_setup) -> None: # already taken ssr, _, benefactor = shift_swap_request_setup() ssr.benefactor = benefactor ssr.save() assert ssr.status == ShiftSwapRequest.Statuses.TAKEN with pytest.raises(exceptions.ShiftSwapRequestNotOpenForTaking): ssr.take(benefactor) # past due ssr, _, benefactor = shift_swap_request_setup() ssr.swap_start = ssr.swap_start - datetime.timedelta(days=5) ssr.save() assert ssr.status == ShiftSwapRequest.Statuses.PAST_DUE with pytest.raises(exceptions.ShiftSwapRequestNotOpenForTaking): ssr.take(benefactor) # deleted ssr, _, benefactor = shift_swap_request_setup() ssr.delete() assert ssr.status == ShiftSwapRequest.Statuses.DELETED with pytest.raises(exceptions.ShiftSwapRequestNotOpenForTaking): ssr.take(benefactor) @pytest.mark.django_db def test_take_own_ssr(shift_swap_request_setup) -> None: ssr, beneficiary, _ = shift_swap_request_setup() with pytest.raises(exceptions.BeneficiaryCannotTakeOwnShiftSwapRequest): ssr.take(beneficiary) @pytest.mark.skip( "Skipping as flaky based on time of day that the test runs. Example failure here https://github.com/grafana/oncall/actions/runs/5747168275/job/15577755519?pr=2725#step:5:1005" ) @pytest.mark.django_db def test_related_shifts(shift_swap_request_setup, make_on_call_shift) -> None: ssr, beneficiary, _ = shift_swap_request_setup() schedule = ssr.schedule organization = schedule.organization user = beneficiary today = timezone.now().replace(hour=0, minute=0, second=0, microsecond=0) start = today + timezone.timedelta(days=2) duration = timezone.timedelta(hours=8) data = { "start": start, "rotation_start": start, "duration": duration, "priority_level": 1, "frequency": CustomOnCallShift.FREQUENCY_DAILY, "schedule": schedule, } on_call_shift = make_on_call_shift( organization=organization, shift_type=CustomOnCallShift.TYPE_ROLLING_USERS_EVENT, **data ) on_call_shift.add_rolling_users([[user]]) events = ssr.shifts() expected = [ # start, end, user, swap request ID (start, start + duration, user.public_primary_key, ssr.public_primary_key), ] returned_events = [(e["start"], e["end"], e["users"][0]["pk"], e["users"][0]["swap_request"]["pk"]) for e in events] assert returned_events == expected @pytest.mark.django_db def test_possible_benefactors(shift_swap_request_setup) -> None: ssr, beneficiary, benefactor = shift_swap_request_setup() with patch.object(ssr.schedule, "related_users") as mock_related_users: mock_related_users.return_value = User.objects.filter(pk__in=[beneficiary.pk, benefactor.pk]) assert list(ssr.possible_benefactors) == [benefactor]