diff --git a/amy/emails/forms.py b/amy/emails/forms.py index 0749ea42a..85e66509c 100644 --- a/amy/emails/forms.py +++ b/amy/emails/forms.py @@ -1,3 +1,5 @@ +from datetime import UTC, datetime + from django import forms from markdownx.fields import MarkdownxFormField @@ -87,6 +89,14 @@ class ScheduledEmailRescheduleForm(forms.Form): helper = BootstrapHelper(submit_label="Update") + def clean_scheduled_at(self): + scheduled_at = self.cleaned_data["scheduled_at"] + + if scheduled_at < datetime.now(tz=UTC): + raise forms.ValidationError("Scheduled time cannot be in the past.") + + return scheduled_at + class ScheduledEmailCancelForm(forms.Form): confirm = forms.CharField(required=False) diff --git a/amy/emails/tests/test_forms.py b/amy/emails/tests/test_forms.py new file mode 100644 index 000000000..a058ab14d --- /dev/null +++ b/amy/emails/tests/test_forms.py @@ -0,0 +1,44 @@ +from datetime import UTC, datetime +from unittest.mock import MagicMock, patch + +from django.test import TestCase + +from emails.forms import ScheduledEmailRescheduleForm + + +class TestScheduledEmailRescheduleForm(TestCase): + @patch("emails.forms.datetime", wraps=datetime) + def test_clean_scheduled_at_success(self, mock_datetime: MagicMock) -> None: + # Arrange + mock_datetime.now.return_value = datetime(2023, 1, 1, 0, 0, 0, tzinfo=UTC) + data = { + "scheduled_at_0": "2024-01-01", + "scheduled_at_1": "00:00", + } + form = ScheduledEmailRescheduleForm(data=data) + + # Act + result = form.is_valid() + + # Assert + self.assertTrue(result) + + @patch("emails.forms.datetime", wraps=datetime) + def test_clean_scheduled_at_failure(self, mock_datetime: MagicMock) -> None: + # Arrange + mock_datetime.now.return_value = datetime(2024, 1, 1, 0, 0, 0, tzinfo=UTC) + data = { + "scheduled_at_0": "2023-01-01", + "scheduled_at_1": "00:00", + } + form = ScheduledEmailRescheduleForm(data=data) + + # Act + result = form.is_valid() + + # Assert + self.assertFalse(result) + self.assertEqual( + form.errors["scheduled_at"], + ["Scheduled time cannot be in the past."], + ) diff --git a/amy/emails/tests/test_views.py b/amy/emails/tests/test_views.py index 4dd956589..3a9c6d87d 100644 --- a/amy/emails/tests/test_views.py +++ b/amy/emails/tests/test_views.py @@ -1,4 +1,5 @@ from datetime import UTC, datetime, timedelta +from unittest.mock import MagicMock, patch from django.test import RequestFactory, override_settings from django.urls import reverse @@ -504,8 +505,9 @@ def test_disallowed_email_statuses(self) -> None: class TestScheduledEmailReschedule(TestBase): + @patch("emails.forms.datetime", wraps=datetime) @override_settings(FLAGS={"EMAIL_MODULE": [("boolean", True)]}) - def test_view(self) -> None: + def test_view(self, mock_datetime: MagicMock) -> None: # Arrange super()._setUpUsersAndLogin() template = EmailTemplate.objects.create( @@ -531,6 +533,7 @@ def test_view(self) -> None: template=template, ) url = reverse("scheduledemail_reschedule", kwargs={"pk": scheduled_email.pk}) + mock_datetime.now.return_value = datetime(2022, 12, 31, 23, 59, 59, tzinfo=UTC) new_scheduled_date = datetime(2023, 1, 1, 0, 0, tzinfo=UTC) data = { "scheduled_at_0": f"{new_scheduled_date:%Y-%m-%d}",