Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions ietf/message/factories.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Copyright The IETF Trust 2024, All Rights Reserved
import factory

from ietf.person.models import Person
from .models import Message, SendQueue


class MessageFactory(factory.django.DjangoModelFactory):
class Meta:
model = Message

by = factory.LazyFunction(lambda: Person.objects.get(name="(System)"))
subject = factory.Faker("sentence")
to = factory.Faker("email")
frm = factory.Faker("email")
cc = factory.Faker("email")
bcc = factory.Faker("email")
body = factory.Faker("paragraph")
content_type = "text/plain"


class SendQueueFactory(factory.django.DjangoModelFactory):
class Meta:
model = SendQueue

by = factory.LazyFunction(lambda: Person.objects.get(name="(System)"))
message = factory.SubFactory(MessageFactory)
File renamed without changes.
26 changes: 24 additions & 2 deletions ietf/message/tests.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
# Copyright The IETF Trust 2013-2020, All Rights Reserved
# -*- coding: utf-8 -*-


import datetime
import mock

from smtplib import SMTPException

from django.urls import reverse as urlreverse
from django.utils import timezone

import debug # pyflakes:ignore

from ietf.group.factories import GroupFactory
from ietf.message.factories import SendQueueFactory
from ietf.message.models import Message, SendQueue
from ietf.message.tasks import send_scheduled_mail_task
from ietf.message.utils import send_scheduled_message_from_send_queue
from ietf.person.models import Person
from ietf.utils.mail import outbox, send_mail_text, send_mail_message, get_payload_text
Expand Down Expand Up @@ -128,3 +131,22 @@ def test_send_mime_announcement(self):
self.assertTrue("This is a test" in outbox[-1]["Subject"])
self.assertTrue("--NextPart" in outbox[-1].as_string())
self.assertTrue(SendQueue.objects.get(id=q.id).sent_at)


class TaskTests(TestCase):
@mock.patch("ietf.message.tasks.log_smtp_exception")
@mock.patch("ietf.message.tasks.send_scheduled_message_from_send_queue")
def test_send_scheduled_mail_task(self, mock_send_message, mock_log_smtp_exception):
not_yet_sent = SendQueueFactory()
SendQueueFactory(sent_at=timezone.now()) # already sent
send_scheduled_mail_task()
self.assertEqual(mock_send_message.call_count, 1)
self.assertEqual(mock_send_message.call_args[0], (not_yet_sent,))
self.assertFalse(mock_log_smtp_exception.called)

mock_send_message.reset_mock()
mock_send_message.side_effect = SMTPException
send_scheduled_mail_task()
self.assertEqual(mock_send_message.call_count, 1)
self.assertEqual(mock_send_message.call_args[0], (not_yet_sent,))
self.assertTrue(mock_log_smtp_exception.called)
66 changes: 66 additions & 0 deletions ietf/review/tests.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
# Copyright The IETF Trust 2019-2020, All Rights Reserved
# -*- coding: utf-8 -*-
import datetime
import mock
import debug # pyflakes:ignore

from pyquery import PyQuery

from ietf.group.factories import RoleFactory
from ietf.doc.factories import WgDraftFactory
from ietf.utils.mail import empty_outbox, get_payload_text, outbox
Expand All @@ -13,6 +15,7 @@
from .factories import ReviewAssignmentFactory, ReviewRequestFactory, ReviewerSettingsFactory
from .mailarch import hash_list_message_id
from .models import ReviewerSettings, ReviewSecretarySettings, ReviewTeamSettings, UnavailablePeriod
from .tasks import send_review_reminders_task
from .utils import (email_secretary_reminder, review_assignments_needing_secretary_reminder,
email_reviewer_reminder, review_assignments_needing_reviewer_reminder,
send_reminder_unconfirmed_assignments, send_review_reminder_overdue_assignment,
Expand Down Expand Up @@ -550,3 +553,66 @@ def test_review_add_comment(self):
# But can't have the comment we are goint to add.
self.assertContains(r, 'This is a test.')


class TaskTests(TestCase):
# hyaaa it's mockzilla
@mock.patch("ietf.review.tasks.date_today")
@mock.patch("ietf.review.tasks.review_assignments_needing_reviewer_reminder")
@mock.patch("ietf.review.tasks.email_reviewer_reminder")
@mock.patch("ietf.review.tasks.review_assignments_needing_secretary_reminder")
@mock.patch("ietf.review.tasks.email_secretary_reminder")
@mock.patch("ietf.review.tasks.send_unavailability_period_ending_reminder")
@mock.patch("ietf.review.tasks.send_reminder_all_open_reviews")
@mock.patch("ietf.review.tasks.send_review_reminder_overdue_assignment")
@mock.patch("ietf.review.tasks.send_reminder_unconfirmed_assignments")
def test_send_review_reminders_task(
self,
mock_send_reminder_unconfirmed_assignments,
mock_send_review_reminder_overdue_assignment,
mock_send_reminder_all_open_reviews,
mock_send_unavailability_period_ending_reminder,
mock_email_secretary_reminder,
mock_review_assignments_needing_secretary_reminder,
mock_email_reviewer_reminder,
mock_review_assignments_needing_reviewer_reminder,
mock_date_today,
):
"""Test that send_review_reminders calls functions correctly

Does not test individual methods, just that they are called as expected.
"""
mock_today = object()
assignment = ReviewAssignmentFactory()
secretary_role = RoleFactory(name_id="secr")

mock_date_today.return_value = mock_today
mock_review_assignments_needing_reviewer_reminder.return_value = [assignment]
mock_review_assignments_needing_secretary_reminder.return_value = [[assignment, secretary_role]]
mock_send_unavailability_period_ending_reminder.return_value = ["pretending I sent a period end reminder"]
mock_send_review_reminder_overdue_assignment.return_value = ["pretending I sent an overdue reminder"]
mock_send_reminder_all_open_reviews.return_value = ["pretending I sent an open review reminder"]
mock_send_reminder_unconfirmed_assignments.return_value = ["pretending I sent an unconfirmed reminder"]

send_review_reminders_task()

self.assertEqual(mock_review_assignments_needing_reviewer_reminder.call_count, 1)
self.assertEqual(mock_review_assignments_needing_reviewer_reminder.call_args[0], (mock_today,))
self.assertEqual(mock_email_reviewer_reminder.call_count, 1)
self.assertEqual(mock_email_reviewer_reminder.call_args[0], (assignment,))

self.assertEqual(mock_review_assignments_needing_secretary_reminder.call_count, 1)
self.assertEqual(mock_review_assignments_needing_secretary_reminder.call_args[0], (mock_today,))
self.assertEqual(mock_email_secretary_reminder.call_count, 1)
self.assertEqual(mock_email_secretary_reminder.call_args[0], (assignment, secretary_role))

self.assertEqual(mock_send_unavailability_period_ending_reminder.call_count, 1)
self.assertEqual(mock_send_unavailability_period_ending_reminder.call_args[0], (mock_today,))

self.assertEqual(mock_send_review_reminder_overdue_assignment.call_count, 1)
self.assertEqual(mock_send_review_reminder_overdue_assignment.call_args[0], (mock_today,))

self.assertEqual(mock_send_reminder_all_open_reviews.call_count, 1)
self.assertEqual(mock_send_reminder_all_open_reviews.call_args[0], (mock_today,))

self.assertEqual(mock_send_reminder_unconfirmed_assignments.call_count, 1)
self.assertEqual(mock_send_reminder_unconfirmed_assignments.call_args[0], (mock_today,))
32 changes: 30 additions & 2 deletions ietf/stats/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
from ietf.stats.models import MeetingRegistration, CountryAlias
from ietf.stats.factories import MeetingRegistrationFactory
from ietf.stats.tasks import fetch_meeting_attendance_task
from ietf.stats.utils import get_meeting_registration_data, FetchStats
from ietf.stats.utils import get_meeting_registration_data, FetchStats, fetch_attendance_from_meetings
from ietf.utils.timezone import date_today


Expand Down Expand Up @@ -302,6 +302,28 @@ def test_get_meeting_registration_data_duplicates(self, mock_get):
query = MeetingRegistration.objects.all()
self.assertEqual(query.count(), 2)

@patch("ietf.stats.utils.get_meeting_registration_data")
def test_fetch_attendance_from_meetings(self, mock_get_mtg_reg_data):
mock_meetings = [object(), object(), object()]
mock_get_mtg_reg_data.side_effect = (
(1, 2, 3),
(4, 5, 6),
(7, 8, 9),
)
stats = fetch_attendance_from_meetings(mock_meetings)
self.assertEqual(
[mock_get_mtg_reg_data.call_args_list[n][0][0] for n in range(3)],
mock_meetings,
)
self.assertEqual(
stats,
[
FetchStats(1, 2, 3),
FetchStats(4, 5, 6),
FetchStats(7, 8, 9),
]
)


class TaskTests(TestCase):
@patch("ietf.stats.tasks.fetch_attendance_from_meetings")
Expand All @@ -315,6 +337,12 @@ def test_fetch_meeting_attendance_task(self, mock_fetch_attendance):
mock_fetch_attendance.return_value = [FetchStats(1,2,3), FetchStats(1,2,3)]

fetch_meeting_attendance_task()

self.assertEqual(mock_fetch_attendance.call_count, 1)
self.assertCountEqual(mock_fetch_attendance.call_args[0][0], meetings[0:2])

# test handling of RuntimeError
mock_fetch_attendance.reset_mock()
mock_fetch_attendance.side_effect = RuntimeError
fetch_meeting_attendance_task()
self.assertTrue(mock_fetch_attendance.called)
# Good enough that we got here without raising an exception
52 changes: 47 additions & 5 deletions ietf/sync/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import datetime
import mock
import quopri
import requests

from dataclasses import dataclass

Expand All @@ -18,7 +19,7 @@

import debug # pyflakes:ignore

from ietf.doc.factories import WgDraftFactory, RfcFactory
from ietf.doc.factories import WgDraftFactory, RfcFactory, DocumentAuthorFactory
from ietf.doc.models import Document, DocEvent, DeletedEvent, DocTagName, RelatedDocument, State, StateDocEvent
from ietf.doc.utils import add_state_change_event
from ietf.group.factories import GroupFactory
Expand Down Expand Up @@ -238,6 +239,7 @@ def test_rfc_index(self):
external_url="http://my-external-url.example.com",
note="this is a note",
)
DocumentAuthorFactory.create_batch(2, document=draft_doc)
draft_doc.action_holders.add(draft_doc.ad) # not normally set, but add to be sure it's cleared

RfcFactory(rfc_number=123)
Expand Down Expand Up @@ -381,6 +383,7 @@ def test_rfc_index(self):

rfc_doc = Document.objects.filter(rfc_number=1234, type_id="rfc").first()
self.assertIsNotNone(rfc_doc, "RFC document should have been created")
self.assertEqual(rfc_doc.authors(), draft_doc.authors())
rfc_events = rfc_doc.docevent_set.all()
self.assertEqual(len(rfc_events), 8)
expected_events = [
Expand Down Expand Up @@ -715,11 +718,14 @@ def json(self):
errata_response = MockResponse(
text="these are the errata", json_length=rfceditor.MIN_ERRATA_RESULTS
)

rfc = RfcFactory()

# Test with full_index = False
requests_get_mock.side_effect = (index_response, errata_response) # will step through these
parse_index_mock.return_value = MockIndexData(length=rfceditor.MIN_INDEX_RESULTS)
update_docs_mock.return_value = [] # not tested
update_docs_mock.return_value = (
(rfc.rfc_number, ("something changed",), rfc, False),
)

tasks.rfc_editor_index_update_task(full_index=False)

Expand All @@ -741,9 +747,10 @@ def json(self):
self.assertIsNotNone(update_docs_kwargs["skip_older_than_date"])

# Test again with full_index = True
requests_get_mock.reset_mock()
parse_index_mock.reset_mock()
update_docs_mock.reset_mock()
requests_get_mock.side_effect = (index_response, errata_response) # will step through these
parse_index_mock.return_value = MockIndexData(length=rfceditor.MIN_INDEX_RESULTS)
update_docs_mock.return_value = [] # not tested
tasks.rfc_editor_index_update_task(full_index=True)

# Check parse_index() call
Expand All @@ -762,3 +769,38 @@ def json(self):
update_docs_args, (parse_index_mock.return_value, errata_response.json())
)
self.assertIsNone(update_docs_kwargs["skip_older_than_date"])

# Test error handling
requests_get_mock.reset_mock()
parse_index_mock.reset_mock()
update_docs_mock.reset_mock()
requests_get_mock.side_effect = requests.Timeout # timeout on every get()
tasks.rfc_editor_index_update_task(full_index=False)
self.assertFalse(parse_index_mock.called)
self.assertFalse(update_docs_mock.called)

requests_get_mock.reset_mock()
parse_index_mock.reset_mock()
update_docs_mock.reset_mock()
requests_get_mock.side_effect = [index_response, requests.Timeout] # timeout second get()
tasks.rfc_editor_index_update_task(full_index=False)
self.assertFalse(update_docs_mock.called)

requests_get_mock.reset_mock()
parse_index_mock.reset_mock()
update_docs_mock.reset_mock()
requests_get_mock.side_effect = [index_response, errata_response]
# feed in an index that is too short
parse_index_mock.return_value = MockIndexData(length=rfceditor.MIN_INDEX_RESULTS - 1)
tasks.rfc_editor_index_update_task(full_index=False)
self.assertTrue(parse_index_mock.called)
self.assertFalse(update_docs_mock.called)

requests_get_mock.reset_mock()
parse_index_mock.reset_mock()
update_docs_mock.reset_mock()
requests_get_mock.side_effect = [index_response, errata_response]
errata_response.json_length = rfceditor.MIN_ERRATA_RESULTS - 1 # too short
parse_index_mock.return_value = MockIndexData(length=rfceditor.MIN_INDEX_RESULTS)
tasks.rfc_editor_index_update_task(full_index=False)
self.assertFalse(update_docs_mock.called)
2 changes: 1 addition & 1 deletion ietf/utils/management/commands/periodic_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def get_or_create_crontabs(self):
def create_default_tasks(self):
PeriodicTask.objects.get_or_create(
name="Send scheduled mail",
task="ietf.utils.tasks.send_scheduled_mail_task",
task="ietf.meeting.tasks.send_scheduled_mail_task",
defaults=dict(
enabled=False,
crontab=self.crontabs["every_15m"],
Expand Down