From 47fa3f7b87d18c9a0ffdb906ca39e1d0b7e1d6e5 Mon Sep 17 00:00:00 2001 From: Adams Pierre David <57180807+adamspd@users.noreply.github.com> Date: Mon, 9 Sep 2024 22:22:17 +0200 Subject: [PATCH 1/5] Fix a few test for django-q2 --- .gitignore | 1 + appointment/tests/utils/test_db_helpers.py | 52 +++++++++++-------- .../tests/utils/test_staff_member_time.py | 23 +++++--- appointment/utils/db_helpers.py | 2 +- 4 files changed, 48 insertions(+), 30 deletions(-) diff --git a/.gitignore b/.gitignore index 11323c0..5f57b43 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ __pycache__/ local_settings.py db.sqlite3 db.sqlite3-journal +db.sqlite3.init media # If your build process includes running collectstatic, then you probably don't need or want to include staticfiles/ diff --git a/appointment/tests/utils/test_db_helpers.py b/appointment/tests/utils/test_db_helpers.py index 33418eb..46bcddc 100644 --- a/appointment/tests/utils/test_db_helpers.py +++ b/appointment/tests/utils/test_db_helpers.py @@ -3,7 +3,7 @@ import datetime from unittest import skip -from unittest.mock import MagicMock, PropertyMock, patch +from unittest.mock import ANY, MagicMock, PropertyMock, patch from django.apps import apps from django.conf import settings @@ -252,13 +252,6 @@ def get_mock_reverse(url_name, **kwargs): class ScheduleEmailReminderTest(BaseTest): - @classmethod - def setUpClass(cls): - if not DJANGO_Q_AVAILABLE: - import unittest - raise unittest.SkipTest("Django-Q is not available") - super().setUpClass() - def setUp(self): super().setUp() self.factory = RequestFactory() @@ -270,19 +263,36 @@ def tearDown(self): AppointmentRequest.objects.all().delete() super().tearDown() - def test_schedule_email_reminder_cluster_running(self): - with patch('appointment.settings.check_q_cluster', return_value=True), \ - patch('appointment.utils.db_helpers.schedule') as mock_schedule: - schedule_email_reminder(self.appointment, self.request) - mock_schedule.assert_called_once() - # Further assertions can be made here based on the arguments passed to schedule - - def test_schedule_email_reminder_cluster_not_running(self): - with patch('appointment.settings.check_q_cluster', return_value=False), \ - patch('appointment.utils.db_helpers.logger') as mock_logger: - schedule_email_reminder(self.appointment, self.request) - mock_logger.warning.assert_called_with( - "Django-Q cluster is not running. Email reminder will not be scheduled.") + @patch('appointment.utils.db_helpers.DJANGO_Q_AVAILABLE', True) + @patch('appointment.utils.db_helpers.schedule') + @patch('appointment.utils.db_helpers.logger') + @patch('appointment.utils.db_helpers.get_absolute_url_') + @patch('appointment.utils.db_helpers.reverse') + def test_schedule_email_reminder_django_q_available(self, mock_reverse, mock_get_absolute_url, mock_logger, + mock_schedule): + mock_reverse.return_value = '/reschedule' + mock_get_absolute_url.return_value = "https://test.com/reschedule" + + schedule_email_reminder(self.appointment, self.request) + + mock_logger.info.assert_called_once() + mock_schedule.assert_called_once_with( + 'appointment.tasks.send_email_reminder', + to_email=self.appointment.client.email, + name=f"reminder_{self.appointment.id_request}", + first_name=self.appointment.client.first_name, + reschedule_link="https://test.com/reschedule", + appointment_id=self.appointment.id, + schedule_type='O', # Use 'O' instead of Mock(ONCE=1) + next_run=ANY # We can't predict the exact datetime, so we use ANY + ) + + @patch('appointment.utils.db_helpers.DJANGO_Q_AVAILABLE', False) + @patch('appointment.utils.db_helpers.logger') + def test_schedule_email_reminder_django_q_not_available(self, mock_logger): + schedule_email_reminder(self.appointment, self.request) + mock_logger.warning.assert_called_once_with( + "Django-Q is not available. Email reminder will not be scheduled.") class UpdateAppointmentReminderTest(BaseTest, TestCase): diff --git a/appointment/tests/utils/test_staff_member_time.py b/appointment/tests/utils/test_staff_member_time.py index 3028fbb..17246c8 100644 --- a/appointment/tests/utils/test_staff_member_time.py +++ b/appointment/tests/utils/test_staff_member_time.py @@ -2,7 +2,7 @@ from unittest.mock import patch from django.core.cache import cache -from django.test import override_settings +from django.test import TransactionTestCase, override_settings from appointment.models import StaffMember from appointment.tests.base.base_test import BaseTest @@ -10,7 +10,7 @@ get_staff_member_end_time, get_staff_member_slot_duration, get_staff_member_start_time -class BaseStaffMemberTimeTestSetup(BaseTest): +class BaseStaffMemberTimeTestSetup(BaseTest, TransactionTestCase): """Base setup class for staff member time function tests.""" @classmethod @@ -23,6 +23,9 @@ def tearDownClass(cls): def setUp(self): super().setUp() + cache.clear() + Config.objects.all().delete() + WorkingHours.objects.all().delete() # Set staff member-specific settings self.staff_member1.slot_duration = 15 @@ -43,34 +46,38 @@ def setUp(self): def tearDown(self): super().tearDown() StaffMember.objects.all().delete() - if Config.objects.exists(): - Config.objects.all().delete() + Config.objects.all().delete() WorkingHours.objects.all().delete() cache.clear() -@patch('appointment.utils.db_helpers.APPOINTMENT_BUFFER_TIME', 59) class TestGetStaffMemberBufferTime(BaseStaffMemberTimeTestSetup): """Test suite for get_staff_member_buffer_time function.""" + @patch('appointment.utils.db_helpers.APPOINTMENT_BUFFER_TIME', 59) def test_staff_member_buffer_time_with_global_setting(self): """Test buffer time when staff member-specific setting is None.""" self.staff_member1.appointment_buffer_time = None self.staff_member1.save() - buffer_time = get_staff_member_buffer_time(self.staff_member1, datetime.date(2023, 10, 9)) + + with patch('appointment.utils.db_helpers.get_config', return_value=None): + buffer_time = get_staff_member_buffer_time(self.staff_member1, datetime.date(2023, 10, 9)) + self.assertEqual(buffer_time, 59) # Global setting + @patch('appointment.utils.db_helpers.APPOINTMENT_BUFFER_TIME', 59) def test_staff_member_buffer_time_with_staff_member_setting(self): """Test buffer time using staff member-specific setting.""" buffer_time = get_staff_member_buffer_time(self.staff_member1, datetime.date(2023, 10, 9)) self.assertEqual(buffer_time, 45) # Staff member specific setting + @patch('appointment.utils.db_helpers.APPOINTMENT_BUFFER_TIME', 59) def test_staff_member_buffer_time_with_working_hours_conflict(self): """Test buffer time when it conflicts with WorkingHours.""" - self.staff_member1.appointment_buffer_time = 120 # Set a buffer time greater than WorkingHours start time + self.staff_member1.appointment_buffer_time = 120 self.staff_member1.save() buffer_time = get_staff_member_buffer_time(self.staff_member1, datetime.date(2023, 10, 9)) - self.assertEqual(buffer_time, 120) # Should still use staff member-specific setting even if it causes conflict + self.assertEqual(buffer_time, 120) @patch('appointment.utils.db_helpers.APPOINTMENT_SLOT_DURATION', 31) diff --git a/appointment/utils/db_helpers.py b/appointment/utils/db_helpers.py index 5920b1f..e563f8a 100644 --- a/appointment/utils/db_helpers.py +++ b/appointment/utils/db_helpers.py @@ -161,7 +161,7 @@ def schedule_email_reminder(appointment, request, appointment_datetime=None): first_name=appointment.client.first_name, reschedule_link=reschedule_link, appointment_id=appointment.id, - schedule_type=Schedule.ONCE, # Use Schedule.ONCE for a one-time task + schedule_type=Schedule.ONCE, next_run=reminder_datetime) From 8901158268e70f694b877a888d34361f26abdc78 Mon Sep 17 00:00:00 2001 From: Adams Pierre David <57180807+adamspd@users.noreply.github.com> Date: Mon, 9 Sep 2024 22:30:35 +0200 Subject: [PATCH 2/5] Mock django-q2 when it's not installed in the test --- appointment/tests/utils/test_db_helpers.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/appointment/tests/utils/test_db_helpers.py b/appointment/tests/utils/test_db_helpers.py index 46bcddc..0aee419 100644 --- a/appointment/tests/utils/test_db_helpers.py +++ b/appointment/tests/utils/test_db_helpers.py @@ -3,7 +3,7 @@ import datetime from unittest import skip -from unittest.mock import ANY, MagicMock, PropertyMock, patch +from unittest.mock import ANY, MagicMock, Mock, PropertyMock, patch from django.apps import apps from django.conf import settings @@ -251,6 +251,12 @@ def get_mock_reverse(url_name, **kwargs): return reverse(url_name, **kwargs) +# Mock the django_q module +mock_django_q = Mock() +mock_django_q.Schedule = Mock() +mock_django_q.Schedule.ONCE = 'O' + + class ScheduleEmailReminderTest(BaseTest): def setUp(self): super().setUp() @@ -283,8 +289,8 @@ def test_schedule_email_reminder_django_q_available(self, mock_reverse, mock_get first_name=self.appointment.client.first_name, reschedule_link="https://test.com/reschedule", appointment_id=self.appointment.id, - schedule_type='O', # Use 'O' instead of Mock(ONCE=1) - next_run=ANY # We can't predict the exact datetime, so we use ANY + schedule_type='O', + next_run=ANY ) @patch('appointment.utils.db_helpers.DJANGO_Q_AVAILABLE', False) From 763af2fad670c93ea32c92a2bcaa8705ee5aee79 Mon Sep 17 00:00:00 2001 From: Adams Pierre David <57180807+adamspd@users.noreply.github.com> Date: Mon, 9 Sep 2024 22:31:08 +0200 Subject: [PATCH 3/5] Mock django-q2 when it's not installed in the test --- appointment/tests/utils/test_db_helpers.py | 1 + 1 file changed, 1 insertion(+) diff --git a/appointment/tests/utils/test_db_helpers.py b/appointment/tests/utils/test_db_helpers.py index 0aee419..d2ddbc7 100644 --- a/appointment/tests/utils/test_db_helpers.py +++ b/appointment/tests/utils/test_db_helpers.py @@ -257,6 +257,7 @@ def get_mock_reverse(url_name, **kwargs): mock_django_q.Schedule.ONCE = 'O' +@patch.dict('sys.modules', {'django_q': mock_django_q}) class ScheduleEmailReminderTest(BaseTest): def setUp(self): super().setUp() From 874fcec71bfbdd7313acf085fdc409dee2a904ec Mon Sep 17 00:00:00 2001 From: Adams Pierre David <57180807+adamspd@users.noreply.github.com> Date: Mon, 9 Sep 2024 22:39:29 +0200 Subject: [PATCH 4/5] django-q2 needs to be in requirements for tests to pass --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 13c871c..ac05eb7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,3 +7,4 @@ setuptools==74.1.2 requests~=2.32.3 python-dotenv==1.0.1 colorama~=0.4.6 +django-q2==1.6.2 From 5d10798e2ba6d5c880c5c246ee24d5ef659e298e Mon Sep 17 00:00:00 2001 From: Adams Pierre David <57180807+adamspd@users.noreply.github.com> Date: Mon, 9 Sep 2024 22:56:41 +0200 Subject: [PATCH 5/5] add USE_DJANGO_Q to tests.yml workflow --- .github/workflows/tests.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 76320e0..87343e5 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -12,6 +12,9 @@ on: pull_request: types: [opened, reopened] +env: + USE_DJANGO_Q: 'true' + jobs: testing: runs-on: ubuntu-20.04 @@ -37,16 +40,22 @@ jobs: coverage debug sys coverage debug config - name: Setup Database and Migrations + env: + USE_DJANGO_Q: 'true' run: | python manage.py makemigrations python manage.py migrate - name: Run Tests + env: + USE_DJANGO_Q: 'true' run: | python manage.py test appointment.tests --parallel=10 --shuffle --verbosity=2 - name: Install tblib for better error reporting if: failure() run: pip install tblib - name: Rerun failed tests with tblib + env: + USE_DJANGO_Q: 'true' if: failure() run: | python manage.py test appointment.tests --parallel=10 --shuffle --verbosity=2