Commit 08d31e1a by Nimisha Asthagiri

Schedules: Tests for Course Update messages

parent 03e496bd
......@@ -23,15 +23,20 @@ from student.tests.factories import UserFactory
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
SITE_QUERY = 1
ORG_DEADLINE_QUERY = 1
SCHEDULES_QUERY = 1
COURSE_MODES_QUERY = 1
GLOBAL_DEADLINE_SWITCH_QUERY = 1
COMMERCE_CONFIG_QUERY = 1
NUM_QUERIES_NO_ORG_LIST = 1
SITE_QUERY = 2 # django_site, site_configuration_siteconfiguration
NUM_QUERIES_NO_MATCHING_SCHEDULES = SITE_QUERY + SCHEDULES_QUERY
SCHEDULES_QUERY = 1 # schedules_schedule
COURSE_MODES_QUERY = 1 # course_modes_coursemode
GLOBAL_DEADLINE_QUERY = 1 # courseware_dynamicupgradedeadlineconfiguration
ORG_DEADLINE_QUERY = 1 # courseware_orgdynamicupgradedeadlineconfiguration
COURSE_DEADLINE_QUERY = 1 # courseware_coursedynamicupgradedeadlineconfiguration
COMMERCE_CONFIG_QUERY = 1 # commerce_commerceconfiguration
NUM_QUERIES_NO_MATCHING_SCHEDULES = (
SITE_QUERY +
SCHEDULES_QUERY
)
NUM_QUERIES_WITH_MATCHES = (
NUM_QUERIES_NO_MATCHING_SCHEDULES +
......@@ -40,8 +45,9 @@ NUM_QUERIES_WITH_MATCHES = (
NUM_QUERIES_FIRST_MATCH = (
NUM_QUERIES_WITH_MATCHES
+ GLOBAL_DEADLINE_SWITCH_QUERY
+ GLOBAL_DEADLINE_QUERY
+ ORG_DEADLINE_QUERY
+ COURSE_DEADLINE_QUERY
+ COMMERCE_CONFIG_QUERY
)
......@@ -56,7 +62,8 @@ class ScheduleSendEmailTestBase(SharedModuleStoreTestCase):
ENABLED_CACHES = ['default']
has_course_queries = False
queries_deadline_for_each_course = False
consolidates_emails_for_learner = False
def setUp(self):
super(ScheduleSendEmailTestBase, self).setUp()
......@@ -74,7 +81,11 @@ class ScheduleSendEmailTestBase(SharedModuleStoreTestCase):
current_day = _get_datetime_beginning_of_day(datetime.datetime.now(pytz.UTC))
offset = offset or self.expected_offsets[0]
target_day = current_day + datetime.timedelta(days=offset)
return current_day, offset, target_day
if self.tested_resolver.schedule_date_field == 'upgrade_deadline':
upgrade_deadline = target_day
else:
upgrade_deadline = current_day + datetime.timedelta(days=7)
return current_day, offset, target_day, upgrade_deadline
def _get_template_overrides(self):
templates_override = deepcopy(settings.TEMPLATES)
......@@ -99,7 +110,7 @@ class ScheduleSendEmailTestBase(SharedModuleStoreTestCase):
@patch.object(tasks, 'ace')
def test_resolver_send(self, mock_ace):
current_day, offset, target_day = self._get_dates()
current_day, offset, target_day, _ = self._get_dates()
with patch.object(self.tested_task, 'apply_async') as mock_apply_async:
self.tested_task.enqueue(self.site_config.site, current_day, offset)
mock_apply_async.assert_any_call(
......@@ -117,18 +128,17 @@ class ScheduleSendEmailTestBase(SharedModuleStoreTestCase):
@patch.object(resolvers, 'set_custom_metric')
def test_schedule_bin(self, schedule_count, mock_metric, mock_ace):
with patch.object(self.tested_task, 'async_send_task') as mock_schedule_send:
current_day, offset, target_day = self._get_dates()
current_day, offset, target_day, upgrade_deadline = self._get_dates()
schedules = [
ScheduleFactory.create(
start=target_day,
upgrade_deadline=target_day,
upgrade_deadline=upgrade_deadline,
enrollment__course__self_paced=True,
) for _ in range(schedule_count)
]
bins_in_use = frozenset((self._calculate_bin_for_user(s.enrollment.user)) for s in schedules)
is_first_match = True
course_queries = len(set(s.enrollment.course.id for s in schedules)) if self.has_course_queries else 0
target_day_str = serialize(target_day)
for b in range(self.tested_task.num_bins):
......@@ -139,14 +149,12 @@ class ScheduleSendEmailTestBase(SharedModuleStoreTestCase):
expected_queries = (
# Since this is the first match, we need to cache all of the config models, so we run a
# query for each of those...
NUM_QUERIES_FIRST_MATCH + course_queries
NUM_QUERIES_FIRST_MATCH
)
is_first_match = False
else:
expected_queries = NUM_QUERIES_WITH_MATCHES
expected_queries += NUM_QUERIES_NO_ORG_LIST
with self.assertNumQueries(expected_queries, table_blacklist=WAFFLE_TABLES):
self.tested_task.apply(kwargs=dict(
site_id=self.site_config.site.id, target_day_str=target_day_str, day_offset=offset, bin_num=b,
......@@ -162,10 +170,10 @@ class ScheduleSendEmailTestBase(SharedModuleStoreTestCase):
self.assertFalse(mock_ace.send.called)
def test_no_course_overview(self):
current_day, offset, target_day = self._get_dates()
current_day, offset, target_day, upgrade_deadline = self._get_dates()
schedule = ScheduleFactory.create(
start=target_day,
upgrade_deadline=target_day,
upgrade_deadline=upgrade_deadline,
enrollment__course__self_paced=True,
)
schedule.enrollment.course_id = CourseKey.from_string('edX/toy/Not_2012_Fall')
......@@ -239,24 +247,24 @@ class ScheduleSendEmailTestBase(SharedModuleStoreTestCase):
user1 = UserFactory.create(id=self.tested_task.num_bins)
user2 = UserFactory.create(id=self.tested_task.num_bins * 2)
current_day, offset, target_day = self._get_dates()
current_day, offset, target_day, upgrade_deadline = self._get_dates()
ScheduleFactory.create(
upgrade_deadline=target_day,
upgrade_deadline=upgrade_deadline,
start=target_day,
enrollment__course__org=filtered_org,
enrollment__course__self_paced=True,
enrollment__user=user1,
)
ScheduleFactory.create(
upgrade_deadline=target_day,
upgrade_deadline=upgrade_deadline,
start=target_day,
enrollment__course__org=unfiltered_org,
enrollment__course__self_paced=True,
enrollment__user=user1,
)
ScheduleFactory.create(
upgrade_deadline=target_day,
upgrade_deadline=upgrade_deadline,
start=target_day,
enrollment__course__org=unfiltered_org,
enrollment__course__self_paced=True,
......@@ -274,11 +282,11 @@ class ScheduleSendEmailTestBase(SharedModuleStoreTestCase):
@ddt.data(True, False)
def test_course_end(self, has_course_ended):
user1 = UserFactory.create(id=self.tested_task.num_bins)
current_day, offset, target_day = self._get_dates()
current_day, offset, target_day, upgrade_deadline = self._get_dates()
schedule = ScheduleFactory.create(
start=target_day,
upgrade_deadline=target_day,
upgrade_deadline=upgrade_deadline,
enrollment__course__self_paced=True,
enrollment__user=user1,
)
......@@ -299,29 +307,31 @@ class ScheduleSendEmailTestBase(SharedModuleStoreTestCase):
self.assertTrue(mock_schedule_send.apply_async.called)
@patch.object(tasks, 'ace')
def test_multiple_enrollments(self, mock_ace):
def test_multiple_target_schedules(self, mock_ace):
user = UserFactory.create()
current_day, offset, target_day = self._get_dates()
current_day, offset, target_day, upgrade_deadline = self._get_dates()
num_courses = 3
for course_index in range(num_courses):
ScheduleFactory.create(
start=target_day,
upgrade_deadline=target_day,
upgrade_deadline=upgrade_deadline,
enrollment__course__self_paced=True,
enrollment__user=user,
enrollment__course__id=CourseKey.from_string('edX/toy/course{}'.format(course_index))
)
course_queries = num_courses if self.has_course_queries else 0
expected_query_count = NUM_QUERIES_FIRST_MATCH + course_queries + NUM_QUERIES_NO_ORG_LIST
additional_course_queries = num_courses - 1 if self.queries_deadline_for_each_course else 0
expected_query_count = NUM_QUERIES_FIRST_MATCH + additional_course_queries
with self.assertNumQueries(expected_query_count, table_blacklist=WAFFLE_TABLES):
with patch.object(self.tested_task, 'async_send_task') as mock_schedule_send:
self.tested_task.apply(kwargs=dict(
site_id=self.site_config.site.id, target_day_str=serialize(target_day), day_offset=offset,
bin_num=self._calculate_bin_for_user(user),
))
self.assertEqual(mock_schedule_send.apply_async.call_count, 1)
self.assertFalse(mock_ace.send.called)
expected_call_count = 1 if self.consolidates_emails_for_learner else num_courses
self.assertEqual(mock_schedule_send.apply_async.call_count, expected_call_count)
self.assertFalse(mock_ace.send.called)
@ddt.data(1, 10, 100)
def test_templates(self, message_count):
......@@ -330,13 +340,13 @@ class ScheduleSendEmailTestBase(SharedModuleStoreTestCase):
self.clear_caches()
def _assert_template_for_offset(self, offset, message_count):
current_day, offset, target_day = self._get_dates(offset)
current_day, offset, target_day, upgrade_deadline = self._get_dates(offset)
user = UserFactory.create()
for course_index in range(message_count):
ScheduleFactory.create(
start=target_day,
upgrade_deadline=target_day,
upgrade_deadline=upgrade_deadline,
enrollment__course__self_paced=True,
enrollment__user=user,
enrollment__course__id=CourseKey.from_string('edX/toy/course{}'.format(course_index))
......@@ -354,20 +364,20 @@ class ScheduleSendEmailTestBase(SharedModuleStoreTestCase):
with patch.object(self.tested_task, 'async_send_task') as mock_schedule_send:
mock_schedule_send.apply_async = lambda args, *_a, **_kw: sent_messages.append(args)
num_expected_queries = NUM_QUERIES_NO_ORG_LIST + NUM_QUERIES_FIRST_MATCH
if self.has_course_queries:
num_expected_queries += message_count
num_expected_queries = NUM_QUERIES_FIRST_MATCH
if self.queries_deadline_for_each_course:
num_expected_queries += (message_count - 1)
with self.assertNumQueries(num_expected_queries, table_blacklist=WAFFLE_TABLES):
self.tested_task.apply(kwargs=dict(
site_id=self.site_config.site.id, target_day_str=serialize(target_day), day_offset=offset,
bin_num=self._calculate_bin_for_user(user),
))
self.assertEqual(len(sent_messages), 1)
num_expected_messages = 1 if self.consolidates_emails_for_learner else message_count
self.assertEqual(len(sent_messages), num_expected_messages)
with self.assertNumQueries(2):
for args in sent_messages:
self.deliver_task(*args)
self.deliver_task(*sent_messages[0])
self.assertEqual(mock_channel.deliver.call_count, 1)
for (_name, (_msg, email), _kwargs) in mock_channel.deliver.mock_calls:
......
from mock import patch
from unittest import skipUnless
from django.conf import settings
from openedx.core.djangoapps.schedules import resolvers, tasks
from openedx.core.djangoapps.schedules.management.commands import send_course_update as nudge
from openedx.core.djangoapps.schedules.management.commands.tests.send_email_base import ScheduleSendEmailTestBase
from openedx.core.djangoapps.schedules.management.commands.tests.upsell_base import ScheduleUpsellTestMixin
from openedx.core.djangolib.testing.utils import skip_unless_lms
@skip_unless_lms
@skipUnless(
'openedx.core.djangoapps.schedules.apps.SchedulesConfig' in settings.INSTALLED_APPS,
"Can't test schedules if the app isn't installed",
)
class TestSendCourseUpdate(ScheduleUpsellTestMixin, ScheduleSendEmailTestBase):
__test__ = True
# pylint: disable=protected-access
tested_resolver = resolvers.CourseUpdateResolver
tested_task = tasks.ScheduleCourseUpdate
deliver_task = tasks._course_update_schedule_send
tested_command = nudge.Command
deliver_config = 'deliver_course_update'
enqueue_config = 'enqueue_course_update'
expected_offsets = xrange(-7, -77, -7)
queries_deadline_for_each_course = True
def setUp(self):
super(TestSendCourseUpdate, self).setUp()
patcher = patch('openedx.core.djangoapps.schedules.resolvers.get_week_highlights')
mock_highlights = patcher.start()
mock_highlights.return_value = ['Highlight {}'.format(num + 1) for num in range(3)]
self.addCleanup(patcher.stop)
......@@ -2,7 +2,7 @@ from unittest import skipUnless
from django.conf import settings
from openedx.core.djangoapps.schedules import tasks
from openedx.core.djangoapps.schedules import resolvers, tasks
from openedx.core.djangoapps.schedules.management.commands import send_recurring_nudge as nudge
from openedx.core.djangoapps.schedules.management.commands.tests.send_email_base import ScheduleSendEmailTestBase
from openedx.core.djangoapps.schedules.management.commands.tests.upsell_base import ScheduleUpsellTestMixin
......@@ -18,9 +18,12 @@ class TestSendRecurringNudge(ScheduleUpsellTestMixin, ScheduleSendEmailTestBase)
__test__ = True
# pylint: disable=protected-access
tested_resolver = resolvers.RecurringNudgeResolver
tested_task = tasks.ScheduleRecurringNudge
deliver_task = tasks._recurring_nudge_schedule_send
tested_command = nudge.Command
deliver_config = 'deliver_recurring_nudge'
enqueue_config = 'enqueue_recurring_nudge'
expected_offsets = (-3, -10)
consolidates_emails_for_learner = True
......@@ -5,11 +5,11 @@ import ddt
from django.conf import settings
from edx_ace import Message
from edx_ace.utils.date import serialize
from mock import Mock, patch
from mock import patch
from opaque_keys.edx.locator import CourseLocator
from course_modes.models import CourseMode
from openedx.core.djangoapps.schedules import tasks
from openedx.core.djangoapps.schedules import resolvers, tasks
from openedx.core.djangoapps.schedules.management.commands import send_upgrade_reminder as reminder
from openedx.core.djangoapps.schedules.management.commands.tests.send_email_base import ScheduleSendEmailTestBase
from openedx.core.djangoapps.schedules.tests.factories import ScheduleFactory
......@@ -27,6 +27,7 @@ LOG = logging.getLogger(__name__)
class TestUpgradeReminder(ScheduleSendEmailTestBase):
__test__ = True
tested_resolver = resolvers.UpgradeReminderResolver
tested_task = tasks.ScheduleUpgradeReminder
deliver_task = tasks._upgrade_reminder_schedule_send
tested_command = reminder.Command
......@@ -34,15 +35,16 @@ class TestUpgradeReminder(ScheduleSendEmailTestBase):
enqueue_config = 'enqueue_upgrade_reminder'
expected_offsets = (2,)
has_course_queries = True
queries_deadline_for_each_course = True
consolidates_emails_for_learner = True
@ddt.data(True, False)
@patch.object(tasks, 'ace')
def test_verified_learner(self, is_verified, mock_ace):
user = UserFactory.create(id=self.tested_task.num_bins)
current_day, offset, target_day = self._get_dates()
current_day, offset, target_day, upgrade_deadline = self._get_dates()
ScheduleFactory.create(
upgrade_deadline=target_day,
upgrade_deadline=upgrade_deadline,
enrollment__course__self_paced=True,
enrollment__user=user,
enrollment__mode=CourseMode.VERIFIED if is_verified else CourseMode.AUDIT,
......@@ -56,12 +58,12 @@ class TestUpgradeReminder(ScheduleSendEmailTestBase):
self.assertEqual(mock_ace.send.called, not is_verified)
def test_filter_out_verified_schedules(self):
current_day, offset, target_day = self._get_dates()
current_day, offset, target_day, upgrade_deadline = self._get_dates()
user = UserFactory.create()
schedules = [
ScheduleFactory.create(
upgrade_deadline=target_day,
upgrade_deadline=upgrade_deadline,
enrollment__user=user,
enrollment__course__self_paced=True,
enrollment__course__id=CourseLocator('edX', 'toy', 'Course{}'.format(i)),
......
......@@ -29,7 +29,7 @@ class ScheduleUpsellTestMixin(object):
def test_upsell(self, enable_config, testcase):
DynamicUpgradeDeadlineConfiguration.objects.create(enabled=enable_config)
current_day, offset, target_day = self._get_dates()
current_day, offset, target_day, _ = self._get_dates()
upgrade_deadline = None
if testcase.set_deadline:
upgrade_deadline = current_day + datetime.timedelta(days=testcase.deadline_offset)
......
......@@ -224,11 +224,11 @@ class InvalidContextError(Exception):
pass
class ScheduleStartResolver(BinnedSchedulesBaseResolver):
class RecurringNudgeResolver(BinnedSchedulesBaseResolver):
"""
Send a message to all users whose schedule started at ``self.current_date`` + ``day_offset``.
"""
log_prefix = 'Scheduled Nudge'
log_prefix = 'Recurring Nudge'
schedule_date_field = 'start'
num_bins = RECURRING_NUDGE_NUM_BINS
......
......@@ -147,7 +147,7 @@ class ScheduleRecurringNudge(ScheduleMessageBaseTask):
num_bins = resolvers.RECURRING_NUDGE_NUM_BINS
enqueue_config_var = 'enqueue_recurring_nudge'
log_prefix = RECURRING_NUDGE_LOG_PREFIX
resolver = resolvers.ScheduleStartResolver
resolver = resolvers.RecurringNudgeResolver
async_send_task = _recurring_nudge_schedule_send
def make_message_type(self, day_offset):
......
......@@ -4,8 +4,11 @@
Welcome to week {{ week_num }} of our {{ course_name }} course!
Here is what you can look forward to learning this week:
{{ week_summary }}
{% endblocktrans %}
{% for highlight in week_highlights %}
* {{ highlight }}
{% endfor %}
{% include "schedules/edx_ace/common/upsell_cta.txt"%}
......@@ -25,3 +25,5 @@ class ScheduleConfigFactory(factory.DjangoModelFactory):
deliver_recurring_nudge = True
enqueue_upgrade_reminder = True
deliver_upgrade_reminder = True
enqueue_course_update = True
deliver_course_update = True
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