Skip to content

Commit 7dbb7e3

Browse files
fix: Add testing, fix bug in fetch_meeting_attendance_task (#6961)
* test: Test rfc_editor_index_update_task * chore: Add docstring to test * fix: Reuse stats instead of fetching twice * test: Test fetch_meeting_attendance_task * chore: Remove outdated tasks * Revert "chore: Add docstring to test" This reverts commit 0395410. * Revert "test: Test rfc_editor_index_update_task" This reverts commit 4dd9dd1. * test: Test rfc_editor_index_update_task This time without reformatting the entire file... * chore: Remove accidentally committed fragment * test: Annotate function to satisfy mypy * chore: Remove unused imports
1 parent 4227ef5 commit 7dbb7e3

File tree

4 files changed

+112
-27
lines changed

4 files changed

+112
-27
lines changed

ietf/stats/tasks.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ def fetch_meeting_attendance_task():
1919
except RuntimeError as err:
2020
log.log(f"Error in fetch_meeting_attendance_task: {err}")
2121
else:
22-
for meeting, stats in zip(meetings, fetch_attendance_from_meetings(meetings)):
22+
for meeting, meeting_stats in zip(meetings, stats):
2323
log.log(
2424
"Fetched data for meeting {:>3}: {:4d} processed, {:4d} added, {:4d} in table".format(
25-
meeting.number, stats.processed, stats.added, stats.total
25+
meeting.number, meeting_stats.processed, meeting_stats.added, meeting_stats.total
2626
)
2727
)

ietf/stats/tests.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@
2929
from ietf.review.factories import ReviewRequestFactory, ReviewerSettingsFactory, ReviewAssignmentFactory
3030
from ietf.stats.models import MeetingRegistration, CountryAlias
3131
from ietf.stats.factories import MeetingRegistrationFactory
32-
from ietf.stats.utils import get_meeting_registration_data
32+
from ietf.stats.tasks import fetch_meeting_attendance_task
33+
from ietf.stats.utils import get_meeting_registration_data, FetchStats
3334
from ietf.utils.timezone import date_today
3435

3536

@@ -300,3 +301,20 @@ def test_get_meeting_registration_data_duplicates(self, mock_get):
300301
get_meeting_registration_data(meeting)
301302
query = MeetingRegistration.objects.all()
302303
self.assertEqual(query.count(), 2)
304+
305+
306+
class TaskTests(TestCase):
307+
@patch("ietf.stats.tasks.fetch_attendance_from_meetings")
308+
def test_fetch_meeting_attendance_task(self, mock_fetch_attendance):
309+
today = date_today()
310+
meetings = [
311+
MeetingFactory(type_id="ietf", date=today - datetime.timedelta(days=1)),
312+
MeetingFactory(type_id="ietf", date=today - datetime.timedelta(days=2)),
313+
MeetingFactory(type_id="ietf", date=today - datetime.timedelta(days=3)),
314+
]
315+
mock_fetch_attendance.return_value = [FetchStats(1,2,3), FetchStats(1,2,3)]
316+
317+
fetch_meeting_attendance_task()
318+
319+
self.assertEqual(mock_fetch_attendance.call_count, 1)
320+
self.assertCountEqual(mock_fetch_attendance.call_args[0][0], meetings[0:2])

ietf/sync/tests.py

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,12 @@
99
import mock
1010
import quopri
1111

12+
from dataclasses import dataclass
13+
1214
from django.conf import settings
1315
from django.urls import reverse as urlreverse
1416
from django.utils import timezone
17+
from django.test.utils import override_settings
1518

1619
import debug # pyflakes:ignore
1720

@@ -20,7 +23,7 @@
2023
from ietf.doc.utils import add_state_change_event
2124
from ietf.group.factories import GroupFactory
2225
from ietf.person.models import Person
23-
from ietf.sync import iana, rfceditor
26+
from ietf.sync import iana, rfceditor, tasks
2427
from ietf.utils.mail import outbox, empty_outbox
2528
from ietf.utils.test_utils import login_testing_unauthorized
2629
from ietf.utils.test_utils import TestCase
@@ -672,3 +675,90 @@ def test_rfceditor_undo(self):
672675

673676
e.content_type.model_class().objects.create(**json.loads(e.json))
674677
self.assertTrue(StateDocEvent.objects.filter(desc="First", doc=draft))
678+
679+
680+
class TaskTests(TestCase):
681+
@override_settings(
682+
RFC_EDITOR_INDEX_URL="https://rfc-editor.example.com/index/",
683+
RFC_EDITOR_ERRATA_JSON_URL="https://rfc-editor.example.com/errata/",
684+
)
685+
@mock.patch("ietf.sync.tasks.update_docs_from_rfc_index")
686+
@mock.patch("ietf.sync.tasks.parse_index")
687+
@mock.patch("ietf.sync.tasks.requests.get")
688+
def test_rfc_editor_index_update_task(
689+
self, requests_get_mock, parse_index_mock, update_docs_mock
690+
) -> None: # the annotation here prevents mypy from complaining about annotation-unchecked
691+
"""rfc_editor_index_update_task calls helpers correctly
692+
693+
This tests that data flow is as expected. Assumes the individual helpers are
694+
separately tested to function correctly.
695+
"""
696+
@dataclass
697+
class MockIndexData:
698+
"""Mock index item that claims to be a specified length"""
699+
length: int
700+
701+
def __len__(self):
702+
return self.length
703+
704+
@dataclass
705+
class MockResponse:
706+
"""Mock object that contains text and json() that claims to be a specified length"""
707+
text: str
708+
json_length: int = 0
709+
710+
def json(self):
711+
return MockIndexData(length=self.json_length)
712+
713+
# Response objects
714+
index_response = MockResponse(text="this is the index")
715+
errata_response = MockResponse(
716+
text="these are the errata", json_length=rfceditor.MIN_ERRATA_RESULTS
717+
)
718+
719+
# Test with full_index = False
720+
requests_get_mock.side_effect = (index_response, errata_response) # will step through these
721+
parse_index_mock.return_value = MockIndexData(length=rfceditor.MIN_INDEX_RESULTS)
722+
update_docs_mock.return_value = [] # not tested
723+
724+
tasks.rfc_editor_index_update_task(full_index=False)
725+
726+
# Check parse_index() call
727+
self.assertTrue(parse_index_mock.called)
728+
(parse_index_args, _) = parse_index_mock.call_args
729+
self.assertEqual(
730+
parse_index_args[0].read(), # arg is a StringIO
731+
"this is the index",
732+
"parse_index is called with the index text in a StringIO",
733+
)
734+
735+
# Check update_docs_from_rfc_index call
736+
self.assertTrue(update_docs_mock.called)
737+
(update_docs_args, update_docs_kwargs) = update_docs_mock.call_args
738+
self.assertEqual(
739+
update_docs_args, (parse_index_mock.return_value, errata_response.json())
740+
)
741+
self.assertIsNotNone(update_docs_kwargs["skip_older_than_date"])
742+
743+
# Test again with full_index = True
744+
requests_get_mock.side_effect = (index_response, errata_response) # will step through these
745+
parse_index_mock.return_value = MockIndexData(length=rfceditor.MIN_INDEX_RESULTS)
746+
update_docs_mock.return_value = [] # not tested
747+
tasks.rfc_editor_index_update_task(full_index=True)
748+
749+
# Check parse_index() call
750+
self.assertTrue(parse_index_mock.called)
751+
(parse_index_args, _) = parse_index_mock.call_args
752+
self.assertEqual(
753+
parse_index_args[0].read(), # arg is a StringIO
754+
"this is the index",
755+
"parse_index is called with the index text in a StringIO",
756+
)
757+
758+
# Check update_docs_from_rfc_index call
759+
self.assertTrue(update_docs_mock.called)
760+
(update_docs_args, update_docs_kwargs) = update_docs_mock.call_args
761+
self.assertEqual(
762+
update_docs_args, (parse_index_mock.return_value, errata_response.json())
763+
)
764+
self.assertIsNone(update_docs_kwargs["skip_older_than_date"])

ietf/utils/tasks.py

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,33 +7,10 @@
77

88
from ietf.message.utils import send_scheduled_message_from_send_queue
99
from ietf.message.models import SendQueue
10-
from ietf.review.tasks import send_review_reminders_task
11-
from ietf.stats.tasks import fetch_meeting_attendance_task
12-
from ietf.sync.tasks import rfc_editor_index_update_task
1310
from ietf.utils import log
1411
from ietf.utils.mail import log_smtp_exception, send_error_email
1512

1613

17-
@shared_task
18-
def every_15m_task():
19-
"""Queue four-times-hourly tasks for execution"""
20-
# todo decide whether we want this to be a meta-task or to individually schedule the tasks
21-
send_scheduled_mail_task.delay()
22-
# Parse the last year of RFC index data to get new RFCs. Needed until
23-
# https://github.com/ietf-tools/datatracker/issues/3734 is addressed.
24-
rfc_editor_index_update_task.delay(full_index=False)
25-
26-
27-
@shared_task
28-
def daily_task():
29-
"""Queue daily tasks for execution"""
30-
fetch_meeting_attendance_task.delay()
31-
send_review_reminders_task.delay()
32-
# Run an extended version of the rfc editor update to catch changes
33-
# with backdated timestamps
34-
rfc_editor_index_update_task.delay(full_index=True)
35-
36-
3714
@shared_task
3815
def send_scheduled_mail_task():
3916
"""Send scheduled email

0 commit comments

Comments
 (0)