Commit a7f1ea03 by Calen Pennington

fixup! Add tests of the send_recurring_nudge

parent 55dfe940
...@@ -126,7 +126,10 @@ class CourseEnrollmentFactory(DjangoModelFactory): ...@@ -126,7 +126,10 @@ class CourseEnrollmentFactory(DjangoModelFactory):
model = CourseEnrollment model = CourseEnrollment
user = factory.SubFactory(UserFactory) user = factory.SubFactory(UserFactory)
course_id = CourseKey.from_string('edX/toy/2012_Fall') course = factory.SubFactory(
'openedx.core.djangoapps.content.course_overviews.tests.factories.CourseOverviewFactory',
id=CourseKey.from_string('edX/toy/2012_Fall')
)
class CourseAccessRoleFactory(DjangoModelFactory): class CourseAccessRoleFactory(DjangoModelFactory):
......
...@@ -35,7 +35,7 @@ from xmodule.modulestore.django import modulestore ...@@ -35,7 +35,7 @@ from xmodule.modulestore.django import modulestore
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory, check_mongo_calls, check_mongo_calls_range from xmodule.modulestore.tests.factories import CourseFactory, check_mongo_calls, check_mongo_calls_range
from .models import CourseOverview, CourseOverviewImageSet, CourseOverviewImageConfig from ..models import CourseOverview, CourseOverviewImageSet, CourseOverviewImageConfig
@attr(shard=3) @attr(shard=3)
......
from factory.django import DjangoModelFactory
from ..models import CourseOverview
class CourseOverviewFactory(DjangoModelFactory):
class Meta(object):
model = CourseOverview
django_get_or_create = ('id', )
version = CourseOverview.VERSION
\ No newline at end of file
from __future__ import print_function from __future__ import print_function
import datetime import datetime
import pytz
from celery import task from celery import task
from dateutil.tz import tzutc, gettz from dateutil.tz import tzutc, gettz
...@@ -25,72 +26,59 @@ from lms.djangoapps.experiments.utils import check_and_get_upgrade_link ...@@ -25,72 +26,59 @@ from lms.djangoapps.experiments.utils import check_and_get_upgrade_link
class RecurringNudge(MessageType): class RecurringNudge(MessageType):
def __init__(self, week): def __init__(self, week, *args, **kwargs):
self.name = "RecurringNudge_Week{}".format(week) self.name = "RecurringNudge_Week{}".format(week)
super(RecurringNudge, self).__init__(*args, **kwargs)
class ScheduleStartResolver(RecipientResolver): class ScheduleStartResolver(RecipientResolver):
def __init__(self, current_date): def __init__(self, current_date):
self.current_date = current_date self.current_date = current_date.replace(hour=0, minute=0, second=0)
def send(self, week): def send(self, week):
schedule_day.delay(week, self.current_date - datetime.timedelta(days=week * 7)) _schedule_day.delay(week, self.current_date - datetime.timedelta(days=week * 7))
@task @task
def schedule_day(week, target_date): def _schedule_day(week, target_time):
for hour in range(23): for hour in range(24):
schedule_hour.delay(week, target_date, hour) _schedule_hour.delay(week, target_time + datetime.timedelta(hours=hour))
@task @task
def schedule_hour(week, target_date, hour): def _schedule_hour(week, target_time):
for minute in range(60): for minute in range(60):
schedule_minute.delay(week, target_date, hour, minute) _schedule_minute.delay(week, target_time + datetime.timedelta(minutes=minute))
@task @task
def schedule_minute(week, target_date, hour, minute): def _schedule_minute(week, target_time):
msg_type = RecurringNudge(week) msg_type = RecurringNudge(week)
for (user, language, context) in schedules_for_minute(target_date, hour, minute): for (user, language, context) in _schedules_for_minute(target_time):
msg = msg_type.personalize( msg = msg_type.personalize(
Recipient( Recipient(
user.username, user.username,
user.email, user.email,
), ),
language, language,
context context,
) )
schedule_send.delay(msg) _schedule_send.delay(msg)
@task @task
def schedule_send(msg): def _schedule_send(msg):
ace.send(msg) ace.send(msg)
def schedules_for_minute(target_date, hour, minute): def _schedules_for_minute(target_time):
schedules = Schedule.objects.select_related( schedules = Schedule.objects.select_related(
'enrollment__user__profile', 'enrollment__user__profile',
'enrollment__course', 'enrollment__course',
).prefetch_related(
Prefetch(
'enrollment__course__modes',
queryset=CourseMode.objects.filter(mode_slug=CourseMode.VERIFIED),
to_attr='verified_modes'
),
Prefetch(
'enrollment__user__preferences',
queryset=UserPreference.objects.filter(key='time_zone'),
to_attr='tzprefs'
),
).filter( ).filter(
start__year=target_date.year, start__gte=target_time,
start__month=target_date.month, start__lt=target_time + datetime.timedelta(seconds=60),
start__day=target_date.day,
start__hour=hour,
start__minute=minute,
) )
for schedule in schedules: for schedule in schedules:
...@@ -120,7 +108,10 @@ class Command(BaseCommand): ...@@ -120,7 +108,10 @@ class Command(BaseCommand):
parser.add_argument('--date', default=datetime.datetime.utcnow().date().isoformat()) parser.add_argument('--date', default=datetime.datetime.utcnow().date().isoformat())
def handle(self, *args, **options): def handle(self, *args, **options):
current_date = datetime.date(*[int(x) for x in options['date'].split('-')]) current_date = datetime.datetime(
*[int(x) for x in options['date'].split('-')],
tzinfo=pytz.UTC
)
resolver = ScheduleStartResolver(current_date) resolver = ScheduleStartResolver(current_date)
for week in (1, 2, 3, 4): for week in (1, 2, 3, 4):
resolver.send(week) resolver.send(week)
import datetime import datetime
from mock import patch from mock import patch, Mock
from unittest import TestCase import pytz
from student.tests.factories import UserFactory
from openedx.core.djangoapps.schedules.management.commands import send_recurring_nudge as nudge from openedx.core.djangoapps.schedules.management.commands import send_recurring_nudge as nudge
from openedx.core.djangolib.testing.utils import CacheIsolationTestCase
from openedx.core.djangoapps.schedules.tests.factories import ScheduleFactory
class TestSendRecurringNudge(TestCase): class TestSendRecurringNudge(CacheIsolationTestCase):
# pylint: disable=protected-access
def setUp(self):
ScheduleFactory.create(start=datetime.datetime(2017, 8, 1, 15, 34, 30, tzinfo=pytz.UTC))
ScheduleFactory.create(start=datetime.datetime(2017, 8, 1, 15, 44, 30, tzinfo=pytz.UTC))
ScheduleFactory.create(start=datetime.datetime(2017, 8, 1, 17, 34, 30, tzinfo=pytz.UTC))
ScheduleFactory.create(start=datetime.datetime(2017, 8, 2, 15, 34, 30, tzinfo=pytz.UTC))
@patch.object(nudge, 'ScheduleStartResolver') @patch.object(nudge, 'ScheduleStartResolver')
def test_handle(self, mock_resolver): def test_handle(self, mock_resolver):
test_date = datetime.date(2017, 8, 1) test_time = datetime.datetime(2017, 8, 1, tzinfo=pytz.UTC)
nudge.Command().handle(date=test_date.isoformat()) nudge.Command().handle(date='2017-08-01')
mock_resolver.assert_called_with(test_date) mock_resolver.assert_called_with(test_time)
for week in (1, 2, 3, 4): for week in (1, 2, 3, 4):
mock_resolver().send.assert_any_call(week) mock_resolver().send.assert_any_call(week)
\ No newline at end of file
@patch.object(nudge, 'ace')
@patch.object(nudge, '_schedule_day')
def test_resolver_send(self, mock_schedule_day, mock_ace):
test_time = datetime.datetime(2017, 8, 1, tzinfo=pytz.UTC)
nudge.ScheduleStartResolver(test_time).send(3)
self.assertFalse(mock_schedule_day.called)
mock_schedule_day.delay.assert_called_once_with(3, datetime.datetime(2017, 7, 11, tzinfo=pytz.UTC))
self.assertFalse(mock_ace.send.called)
@patch.object(nudge, 'ace')
@patch.object(nudge, '_schedule_hour')
def test_schedule_day(self, mock_schedule_hour, mock_ace):
test_time = datetime.datetime(2017, 8, 1, tzinfo=pytz.UTC)
nudge._schedule_day(3, test_time)
self.assertFalse(mock_schedule_hour.called)
self.assertEquals(mock_schedule_hour.delay.call_count, 24)
mock_schedule_hour.delay.assert_any_call(3, test_time)
mock_schedule_hour.delay.assert_any_call(3, test_time + datetime.timedelta(hours=23))
self.assertFalse(mock_ace.send.called)
@patch.object(nudge, 'ace')
@patch.object(nudge, '_schedule_minute')
def test_schedule_hour(self, mock_schedule_minute, mock_ace):
test_time = datetime.datetime(2017, 8, 1, 15, tzinfo=pytz.UTC)
nudge._schedule_hour(3, test_time)
self.assertFalse(mock_schedule_minute.called)
self.assertEquals(mock_schedule_minute.delay.call_count, 60)
mock_schedule_minute.delay.assert_any_call(3, test_time)
mock_schedule_minute.delay.assert_any_call(3, test_time + datetime.timedelta(minutes=59))
self.assertFalse(mock_ace.send.called)
@patch.object(nudge, 'ace')
@patch.object(nudge, '_schedule_send')
def test_schedule_minute(self, mock_schedule_send, mock_ace):
test_time = datetime.datetime(2017, 8, 1, 15, 34, tzinfo=pytz.UTC)
with self.assertNumQueries(1):
nudge._schedule_minute(3, test_time)
self.assertEqual(mock_schedule_send.delay.call_count, 1)
self.assertFalse(mock_ace.send.called)
@patch.object(nudge, 'ace')
def test_schedule_send(self, mock_ace):
mock_msg = Mock()
nudge._schedule_send(mock_msg)
mock_ace.send.assert_called_exactly_once(mock_msg)
\ No newline at end of file
...@@ -2,6 +2,7 @@ import factory ...@@ -2,6 +2,7 @@ import factory
import pytz import pytz
from openedx.core.djangoapps.schedules import models from openedx.core.djangoapps.schedules import models
from student.tests.factories import CourseEnrollmentFactory
class ScheduleFactory(factory.DjangoModelFactory): class ScheduleFactory(factory.DjangoModelFactory):
...@@ -10,3 +11,4 @@ class ScheduleFactory(factory.DjangoModelFactory): ...@@ -10,3 +11,4 @@ class ScheduleFactory(factory.DjangoModelFactory):
start = factory.Faker('future_datetime', tzinfo=pytz.UTC) start = factory.Faker('future_datetime', tzinfo=pytz.UTC)
upgrade_deadline = factory.Faker('future_datetime', tzinfo=pytz.UTC) upgrade_deadline = factory.Faker('future_datetime', tzinfo=pytz.UTC)
enrollment = factory.SubFactory(CourseEnrollmentFactory)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment