From 5be19e22c2999b9eaa11206cfde22075f6266338 Mon Sep 17 00:00:00 2001 From: Calen Pennington Date: Wed, 25 Oct 2017 10:05:33 -0400 Subject: [PATCH 01/26] Induce a base class for schedule email management command tests --- .../management/commands/tests/test_send_recurring_nudge.py | 3 ++- .../management/commands/tests/test_send_upgrade_reminder.py | 3 ++- .../djangoapps/schedules/management/commands/tests/tools.py | 4 ++++ 3 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 openedx/core/djangoapps/schedules/management/commands/tests/tools.py diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py index aadaf75916..254e4d46ab 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py @@ -21,6 +21,7 @@ from courseware.models import DynamicUpgradeDeadlineConfiguration from openedx.core.djangoapps.content.course_overviews.tests.factories import CourseOverviewFactory 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.tools import ScheduleBaseEmailTestBase from openedx.core.djangoapps.schedules.tests.factories import ScheduleConfigFactory, ScheduleFactory from openedx.core.djangoapps.site_configuration.tests.factories import SiteConfigurationFactory, SiteFactory from openedx.core.djangoapps.waffle_utils.testutils import WAFFLE_TABLES @@ -45,7 +46,7 @@ NUM_COURSE_MODES_QUERIES = 1 @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 TestSendRecurringNudge(FilteredQueryCountMixin, CacheIsolationTestCase): +class TestSendRecurringNudge(ScheduleBaseEmailTestBase): # pylint: disable=protected-access ENABLED_CACHES = ['default'] diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py index 6fb9963c84..ebd496abf9 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py @@ -22,6 +22,7 @@ from courseware.models import DynamicUpgradeDeadlineConfiguration from openedx.core.djangoapps.content.course_overviews.models import CourseOverview 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.tools import ScheduleBaseEmailTestBase from openedx.core.djangoapps.schedules.tests.factories import ScheduleConfigFactory, ScheduleFactory from openedx.core.djangoapps.site_configuration.tests.factories import SiteConfigurationFactory, SiteFactory from openedx.core.djangoapps.waffle_utils.testutils import WAFFLE_TABLES @@ -59,7 +60,7 @@ LOG = logging.getLogger(__name__) @skipUnless('openedx.core.djangoapps.schedules.apps.SchedulesConfig' in settings.INSTALLED_APPS, "Can't test schedules if the app isn't installed") @freeze_time('2017-08-01 00:00:00', tz_offset=0, tick=True) -class TestUpgradeReminder(SharedModuleStoreTestCase): +class TestUpgradeReminder(ScheduleBaseEmailTestBase, SharedModuleStoreTestCase): ENABLED_CACHES = ['default'] diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/tools.py b/openedx/core/djangoapps/schedules/management/commands/tests/tools.py new file mode 100644 index 0000000000..e83526df76 --- /dev/null +++ b/openedx/core/djangoapps/schedules/management/commands/tests/tools.py @@ -0,0 +1,4 @@ +from openedx.core.djangolib.testing.utils import CacheIsolationTestCase, FilteredQueryCountMixin + +class ScheduleBaseEmailTestBase(FilteredQueryCountMixin, CacheIsolationTestCase): + pass From b774e0a82e1af0f438d4e1d20835281be6b3e9b8 Mon Sep 17 00:00:00 2001 From: Calen Pennington Date: Wed, 25 Oct 2017 10:09:44 -0400 Subject: [PATCH 02/26] Move ENABLED_CACHES into schedule test base class --- .../management/commands/tests/test_send_recurring_nudge.py | 2 -- .../management/commands/tests/test_send_upgrade_reminder.py | 2 -- .../djangoapps/schedules/management/commands/tests/tools.py | 3 ++- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py index 254e4d46ab..3b7e3832b9 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py @@ -49,8 +49,6 @@ NUM_COURSE_MODES_QUERIES = 1 class TestSendRecurringNudge(ScheduleBaseEmailTestBase): # pylint: disable=protected-access - ENABLED_CACHES = ['default'] - def setUp(self): super(TestSendRecurringNudge, self).setUp() diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py index ebd496abf9..0f76b6190b 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py @@ -62,8 +62,6 @@ LOG = logging.getLogger(__name__) @freeze_time('2017-08-01 00:00:00', tz_offset=0, tick=True) class TestUpgradeReminder(ScheduleBaseEmailTestBase, SharedModuleStoreTestCase): - ENABLED_CACHES = ['default'] - @classmethod def setUpClass(cls): super(TestUpgradeReminder, cls).setUpClass() diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/tools.py b/openedx/core/djangoapps/schedules/management/commands/tests/tools.py index e83526df76..faeb95a051 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/tools.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/tools.py @@ -1,4 +1,5 @@ from openedx.core.djangolib.testing.utils import CacheIsolationTestCase, FilteredQueryCountMixin class ScheduleBaseEmailTestBase(FilteredQueryCountMixin, CacheIsolationTestCase): - pass + + ENABLED_CACHES = ['default'] From 9b4c0540a1b5b4a9fd8ada903724052eca176e2b Mon Sep 17 00:00:00 2001 From: Calen Pennington Date: Wed, 25 Oct 2017 10:35:14 -0400 Subject: [PATCH 03/26] Move task being tested into a class attribute --- .../tests/test_send_recurring_nudge.py | 43 ++++++++++--------- .../tests/test_send_upgrade_reminder.py | 36 ++++++++-------- 2 files changed, 41 insertions(+), 38 deletions(-) diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py index 3b7e3832b9..6c5a00c776 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py @@ -48,6 +48,7 @@ NUM_COURSE_MODES_QUERIES = 1 "Can't test schedules if the app isn't installed") class TestSendRecurringNudge(ScheduleBaseEmailTestBase): # pylint: disable=protected-access + tested_task = tasks.ScheduleRecurringNudge def setUp(self): super(TestSendRecurringNudge, self).setUp() @@ -77,8 +78,8 @@ class TestSendRecurringNudge(ScheduleBaseEmailTestBase): @patch.object(tasks, 'ace') def test_resolver_send(self, mock_ace): current_day = datetime.datetime(2017, 8, 1, tzinfo=pytz.UTC) - with patch.object(tasks.ScheduleRecurringNudge, 'apply_async') as mock_apply_async: - tasks.ScheduleRecurringNudge.enqueue(self.site_config.site, current_day, -3) + with patch.object(self.tested_task, 'apply_async') as mock_apply_async: + self.tested_task.enqueue(self.site_config.site, current_day, -3) test_day = current_day + datetime.timedelta(days=-3) mock_apply_async.assert_any_call( (self.site_config.site.id, serialize(test_day), -3, 0, None), @@ -92,7 +93,7 @@ class TestSendRecurringNudge(ScheduleBaseEmailTestBase): @ddt.data(1, 10, 100) @patch.object(tasks, 'ace') - @patch.object(tasks.ScheduleRecurringNudge, 'async_send_task') + @patch.object(tested_task, 'async_send_task') def test_schedule_bin(self, schedule_count, mock_schedule_send, mock_ace): schedules = [ ScheduleFactory.create( @@ -113,13 +114,13 @@ class TestSendRecurringNudge(ScheduleBaseEmailTestBase): with self.assertNumQueries(expected_queries, table_blacklist=WAFFLE_TABLES): - tasks.ScheduleRecurringNudge.apply(kwargs=dict( + self.tested_task.apply(kwargs=dict( site_id=self.site_config.site.id, target_day_str=test_datetime_str, day_offset=-3, bin_num=b, )) self.assertEqual(mock_schedule_send.apply_async.call_count, schedule_count) self.assertFalse(mock_ace.send.called) - @patch.object(tasks.ScheduleRecurringNudge, 'async_send_task') + @patch.object(tested_task, 'async_send_task') def test_no_course_overview(self, mock_schedule_send): schedule = ScheduleFactory.create( start=datetime.datetime(2017, 8, 3, 20, 34, 30, tzinfo=pytz.UTC), @@ -132,7 +133,7 @@ class TestSendRecurringNudge(ScheduleBaseEmailTestBase): test_datetime_str = serialize(test_datetime) for b in range(resolvers.RECURRING_NUDGE_NUM_BINS): with self.assertNumQueries(NUM_QUERIES_NO_MATCHING_SCHEDULES + NUM_QUERIES_NO_ORG_LIST, table_blacklist=WAFFLE_TABLES): - tasks.ScheduleRecurringNudge.apply(kwargs=dict( + self.tested_task.apply(kwargs=dict( site_id=self.site_config.site.id, target_day_str=test_datetime_str, day_offset=-3, bin_num=b )) @@ -144,7 +145,7 @@ class TestSendRecurringNudge(ScheduleBaseEmailTestBase): # is null. self.assertEqual(mock_schedule_send.apply_async.call_count, 0) - @patch.object(tasks.ScheduleRecurringNudge, 'async_send_task') + @patch.object(tested_task, 'async_send_task') def test_send_after_course_end(self, mock_schedule_send): user1 = UserFactory.create(id=resolvers.RECURRING_NUDGE_NUM_BINS) @@ -162,7 +163,7 @@ class TestSendRecurringNudge(ScheduleBaseEmailTestBase): test_datetime = datetime.datetime(2017, 8, 3, 20, tzinfo=pytz.UTC) test_datetime_str = serialize(test_datetime) - tasks.ScheduleRecurringNudge.apply(kwargs=dict( + self.tested_task.apply(kwargs=dict( site_id=self.site_config.site.id, target_day_str=test_datetime_str, day_offset=-3, bin_num=0, )) @@ -182,7 +183,7 @@ class TestSendRecurringNudge(ScheduleBaseEmailTestBase): ScheduleConfigFactory.create(site=self.site_config.site, enqueue_recurring_nudge=False) current_datetime = datetime.datetime(2017, 8, 1, tzinfo=pytz.UTC) - tasks.ScheduleRecurringNudge.enqueue( + self.tested_task.enqueue( self.site_config.site, current_datetime, 3 @@ -191,7 +192,7 @@ class TestSendRecurringNudge(ScheduleBaseEmailTestBase): self.assertFalse(mock_ace.send.called) @patch.object(tasks, 'ace') - @patch.object(tasks.ScheduleRecurringNudge, 'async_send_task') + @patch.object(tested_task, 'async_send_task') @ddt.data( ((['filtered_org'], [], 1)), (([], ['filtered_org'], 2)) @@ -233,7 +234,7 @@ class TestSendRecurringNudge(ScheduleBaseEmailTestBase): expected_queries += NUM_QUERIES_NO_ORG_LIST with self.assertNumQueries(expected_queries, table_blacklist=WAFFLE_TABLES): - tasks.ScheduleRecurringNudge.apply(kwargs=dict( + self.tested_task.apply(kwargs=dict( site_id=this_config.site.id, target_day_str=test_datetime_str, day_offset=-3, bin_num=0 )) @@ -241,7 +242,7 @@ class TestSendRecurringNudge(ScheduleBaseEmailTestBase): self.assertFalse(mock_ace.send.called) @patch.object(tasks, 'ace') - @patch.object(tasks.ScheduleRecurringNudge, 'async_send_task') + @patch.object(tested_task, 'async_send_task') def test_multiple_enrollments(self, mock_schedule_send, mock_ace): user = UserFactory.create() schedules = [ @@ -256,7 +257,7 @@ class TestSendRecurringNudge(ScheduleBaseEmailTestBase): test_datetime = datetime.datetime(2017, 8, 3, 19, 44, 30, tzinfo=pytz.UTC) test_datetime_str = serialize(test_datetime) with self.assertNumQueries(NUM_QUERIES_WITH_MATCHES + NUM_QUERIES_NO_ORG_LIST, table_blacklist=WAFFLE_TABLES): - tasks.ScheduleRecurringNudge.apply(kwargs=dict( + self.tested_task.apply(kwargs=dict( site_id=self.site_config.site.id, target_day_str=test_datetime_str, day_offset=-3, bin_num=user.id % resolvers.RECURRING_NUDGE_NUM_BINS, )) @@ -290,11 +291,11 @@ class TestSendRecurringNudge(ScheduleBaseEmailTestBase): sent_messages = [] with self.settings(TEMPLATES=self._get_template_overrides()): - with patch.object(tasks.ScheduleRecurringNudge, 'async_send_task') as mock_schedule_send: + 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) with self.assertNumQueries(NUM_QUERIES_WITH_MATCHES + NUM_QUERIES_NO_ORG_LIST, table_blacklist=WAFFLE_TABLES): - tasks.ScheduleRecurringNudge.apply(kwargs=dict( + self.tested_task.apply(kwargs=dict( site_id=self.site_config.site.id, target_day_str=test_datetime_str, day_offset=day, bin_num=self._calculate_bin_for_user(user), )) @@ -343,8 +344,8 @@ class TestSendRecurringNudge(ScheduleBaseEmailTestBase): user, schedule.enrollment.course.org ] - sent_messages = self._stub_sender_and_collect_sent_messages(bin_task=tasks.ScheduleRecurringNudge, - stubbed_send_task=patch.object(tasks.ScheduleRecurringNudge, 'async_send_task'), + sent_messages = self._stub_sender_and_collect_sent_messages(bin_task=self.tested_task, + stubbed_send_task=patch.object(self.tested_task, 'async_send_task'), bin_task_params=bin_task_parameters) self.assertEqual(len(sent_messages), 1) @@ -375,8 +376,8 @@ class TestSendRecurringNudge(ScheduleBaseEmailTestBase): user, schedule.enrollment.course.org ] - sent_messages = self._stub_sender_and_collect_sent_messages(bin_task=tasks.ScheduleRecurringNudge, - stubbed_send_task=patch.object(tasks.ScheduleRecurringNudge, 'async_send_task'), + sent_messages = self._stub_sender_and_collect_sent_messages(bin_task=self.tested_task, + stubbed_send_task=patch.object(self.tested_task, 'async_send_task'), bin_task_params=bin_task_parameters) self.assertEqual(len(sent_messages), 1) @@ -414,8 +415,8 @@ class TestSendRecurringNudge(ScheduleBaseEmailTestBase): user, schedule.enrollment.course.org ] - sent_messages = self._stub_sender_and_collect_sent_messages(bin_task=tasks.ScheduleRecurringNudge, - stubbed_send_task=patch.object(tasks.ScheduleRecurringNudge, 'async_send_task'), + sent_messages = self._stub_sender_and_collect_sent_messages(bin_task=self.tested_task, + stubbed_send_task=patch.object(self.tested_task, 'async_send_task'), bin_task_params=bin_task_parameters) self.assertEqual(len(sent_messages), 1) diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py index 0f76b6190b..d14913740c 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py @@ -62,6 +62,8 @@ LOG = logging.getLogger(__name__) @freeze_time('2017-08-01 00:00:00', tz_offset=0, tick=True) class TestUpgradeReminder(ScheduleBaseEmailTestBase, SharedModuleStoreTestCase): + tested_task = tasks.ScheduleUpgradeReminder + @classmethod def setUpClass(cls): super(TestUpgradeReminder, cls).setUpClass() @@ -111,8 +113,8 @@ class TestUpgradeReminder(ScheduleBaseEmailTestBase, SharedModuleStoreTestCase): test_day = current_day + datetime.timedelta(days=2) ScheduleFactory.create(upgrade_deadline=datetime.datetime(2017, 8, 3, 15, 34, 30, tzinfo=pytz.UTC)) - with patch.object(tasks.ScheduleUpgradeReminder, 'apply_async') as mock_apply_async: - tasks.ScheduleUpgradeReminder.enqueue(self.site_config.site, current_day, 2) + with patch.object(self.tested_task, 'apply_async') as mock_apply_async: + self.tested_task.enqueue(self.site_config.site, current_day, 2) mock_apply_async.assert_any_call( (self.site_config.site.id, serialize(test_day), 2, 0, None), retry=False, @@ -125,7 +127,7 @@ class TestUpgradeReminder(ScheduleBaseEmailTestBase, SharedModuleStoreTestCase): @ddt.data(1, 10, 100) @patch.object(tasks, 'ace') - @patch.object(tasks.ScheduleUpgradeReminder, 'async_send_task') + @patch.object(tested_task, 'async_send_task') def test_schedule_bin(self, schedule_count, mock_schedule_send, mock_ace): upgrade_deadline = datetime.datetime.now(pytz.UTC) + datetime.timedelta(days=2) schedules = [ @@ -160,14 +162,14 @@ class TestUpgradeReminder(ScheduleBaseEmailTestBase, SharedModuleStoreTestCase): expected_queries += NUM_QUERIES_NO_ORG_LIST with self.assertNumQueries(expected_queries, table_blacklist=WAFFLE_TABLES): - tasks.ScheduleUpgradeReminder.apply(kwargs=dict( + self.tested_task.apply(kwargs=dict( site_id=self.site_config.site.id, target_day_str=test_datetime_str, day_offset=2, bin_num=b, )) self.assertEqual(mock_schedule_send.apply_async.call_count, schedule_count) self.assertFalse(mock_ace.send.called) - @patch.object(tasks.ScheduleUpgradeReminder, 'async_send_task') + @patch.object(tested_task, 'async_send_task') def test_no_course_overview(self, mock_schedule_send): schedule = ScheduleFactory.create( @@ -181,7 +183,7 @@ class TestUpgradeReminder(ScheduleBaseEmailTestBase, SharedModuleStoreTestCase): for b in range(resolvers.UPGRADE_REMINDER_NUM_BINS): with self.assertNumQueries(NUM_QUERIES_NO_MATCHING_SCHEDULES + NUM_QUERIES_NO_ORG_LIST, table_blacklist=WAFFLE_TABLES): - tasks.ScheduleUpgradeReminder.apply(kwargs=dict( + self.tested_task.apply(kwargs=dict( site_id=self.site_config.site.id, target_day_str=test_datetime_str, day_offset=2, bin_num=b, )) @@ -202,12 +204,12 @@ class TestUpgradeReminder(ScheduleBaseEmailTestBase, SharedModuleStoreTestCase): self.assertFalse(mock_ace.send.called) @patch.object(tasks, 'ace') - @patch.object(tasks.ScheduleUpgradeReminder, 'apply_async') + @patch.object(tested_task, 'apply_async') def test_enqueue_disabled(self, mock_ace, mock_apply_async): ScheduleConfigFactory.create(site=self.site_config.site, enqueue_upgrade_reminder=False) current_day = datetime.datetime(2017, 8, 1, tzinfo=pytz.UTC) - tasks.ScheduleUpgradeReminder.enqueue( + self.tested_task.enqueue( self.site_config.site, current_day, day_offset=3, @@ -216,7 +218,7 @@ class TestUpgradeReminder(ScheduleBaseEmailTestBase, SharedModuleStoreTestCase): self.assertFalse(mock_ace.send.called) @patch.object(tasks, 'ace') - @patch.object(tasks.ScheduleUpgradeReminder, 'async_send_task') + @patch.object(tested_task, 'async_send_task') @ddt.data( ((['filtered_org'], [], 1)), (([], ['filtered_org'], 2)) @@ -263,7 +265,7 @@ class TestUpgradeReminder(ScheduleBaseEmailTestBase, SharedModuleStoreTestCase): expected_queries += NUM_QUERIES_NO_ORG_LIST with self.assertNumQueries(expected_queries, table_blacklist=WAFFLE_TABLES): - tasks.ScheduleUpgradeReminder.apply(kwargs=dict( + self.tested_task.apply(kwargs=dict( site_id=this_config.site.id, target_day_str=test_datetime_str, day_offset=-3, bin_num=0 )) @@ -271,7 +273,7 @@ class TestUpgradeReminder(ScheduleBaseEmailTestBase, SharedModuleStoreTestCase): self.assertFalse(mock_ace.send.called) @patch.object(tasks, 'ace') - @patch.object(tasks.ScheduleUpgradeReminder, 'async_send_task') + @patch.object(tested_task, 'async_send_task') def test_multiple_enrollments(self, mock_schedule_send, mock_ace): user = UserFactory.create() schedules = [ @@ -293,7 +295,7 @@ class TestUpgradeReminder(ScheduleBaseEmailTestBase, SharedModuleStoreTestCase): NUM_QUERIES_FIRST_MATCH + course_switch_queries + org_switch_queries + NUM_QUERIES_NO_ORG_LIST ) with self.assertNumQueries(expected_query_count, table_blacklist=WAFFLE_TABLES): - tasks.ScheduleUpgradeReminder.apply(kwargs=dict( + self.tested_task.apply(kwargs=dict( site_id=self.site_config.site.id, target_day_str=test_datetime_str, day_offset=2, bin_num=self._calculate_bin_for_user(user), )) @@ -340,7 +342,7 @@ class TestUpgradeReminder(ScheduleBaseEmailTestBase, SharedModuleStoreTestCase): sent_messages = [] with self.settings(TEMPLATES=self._get_template_overrides()): - with patch.object(tasks.ScheduleUpgradeReminder, 'async_send_task') as mock_schedule_send: + 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) # we execute one query per course to see if it's opted out of dynamic upgrade deadlines @@ -349,7 +351,7 @@ class TestUpgradeReminder(ScheduleBaseEmailTestBase, SharedModuleStoreTestCase): ) with self.assertNumQueries(num_expected_queries, table_blacklist=WAFFLE_TABLES): - tasks.ScheduleUpgradeReminder.apply(kwargs=dict( + self.tested_task.apply(kwargs=dict( site_id=self.site_config.site.id, target_day_str=test_datetime_str, day_offset=2, bin_num=self._calculate_bin_for_user(user), )) @@ -388,7 +390,7 @@ class TestUpgradeReminder(ScheduleBaseEmailTestBase, SharedModuleStoreTestCase): ) test_datetime_str = serialize(datetime.datetime.now(pytz.UTC)) - tasks.ScheduleUpgradeReminder.delay( + self.tested_task.delay( self.site_config.site.id, target_day_str=test_datetime_str, day_offset=2, bin_num=0, org_list=[self.course.org], ) @@ -424,10 +426,10 @@ class TestUpgradeReminder(ScheduleBaseEmailTestBase, SharedModuleStoreTestCase): test_datetime_str = serialize(test_datetime) sent_messages = [] - with patch.object(tasks.ScheduleUpgradeReminder, 'async_send_task') as mock_schedule_send: + 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[1]) - tasks.ScheduleUpgradeReminder.apply(kwargs=dict( + self.tested_task.apply(kwargs=dict( site_id=self.site_config.site.id, target_day_str=test_datetime_str, day_offset=2, bin_num=self._calculate_bin_for_user(user), )) From 3696f5f5fe249b3b45dcf4c52124848419ab30bd Mon Sep 17 00:00:00 2001 From: Calen Pennington Date: Wed, 25 Oct 2017 11:03:38 -0400 Subject: [PATCH 04/26] Remove redundent ScheduleFactory calls in setUp for schedule email tests --- .../management/commands/tests/test_send_recurring_nudge.py | 4 ---- .../management/commands/tests/test_send_upgrade_reminder.py | 4 ---- 2 files changed, 8 deletions(-) diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py index 6c5a00c776..6b3657e360 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py @@ -53,10 +53,6 @@ class TestSendRecurringNudge(ScheduleBaseEmailTestBase): def setUp(self): super(TestSendRecurringNudge, self).setUp() - 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)) - site = SiteFactory.create() self.site_config = SiteConfigurationFactory.create(site=site) ScheduleConfigFactory.create(site=self.site_config.site) diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py index d14913740c..a84b0916e2 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py @@ -86,10 +86,6 @@ class TestUpgradeReminder(ScheduleBaseEmailTestBase, SharedModuleStoreTestCase): expiration_datetime=datetime.datetime.now(pytz.UTC) + datetime.timedelta(days=30), ) - ScheduleFactory.create(upgrade_deadline=datetime.datetime(2017, 8, 1, 15, 44, 30, tzinfo=pytz.UTC)) - ScheduleFactory.create(upgrade_deadline=datetime.datetime(2017, 8, 1, 17, 34, 30, tzinfo=pytz.UTC)) - ScheduleFactory.create(upgrade_deadline=datetime.datetime(2017, 8, 2, 15, 34, 30, tzinfo=pytz.UTC)) - site = SiteFactory.create() self.site_config = SiteConfigurationFactory.create(site=site) ScheduleConfigFactory.create(site=self.site_config.site) From c68883f00942ce154c231c54e5e9ad1ea12bfb49 Mon Sep 17 00:00:00 2001 From: Calen Pennington Date: Wed, 25 Oct 2017 11:11:26 -0400 Subject: [PATCH 05/26] Move setUp into the baseclass for schedule email command tests --- .../commands/tests/test_send_recurring_nudge.py | 11 +---------- .../commands/tests/test_send_upgrade_reminder.py | 9 +-------- .../schedules/management/commands/tests/tools.py | 13 +++++++++++++ 3 files changed, 15 insertions(+), 18 deletions(-) diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py index 6b3657e360..37abeabdf2 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py @@ -23,7 +23,7 @@ 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.tools import ScheduleBaseEmailTestBase from openedx.core.djangoapps.schedules.tests.factories import ScheduleConfigFactory, ScheduleFactory -from openedx.core.djangoapps.site_configuration.tests.factories import SiteConfigurationFactory, SiteFactory +from openedx.core.djangoapps.site_configuration.tests.factories import SiteConfigurationFactory from openedx.core.djangoapps.waffle_utils.testutils import WAFFLE_TABLES from openedx.core.djangolib.testing.utils import CacheIsolationTestCase, skip_unless_lms, FilteredQueryCountMixin from student.tests.factories import UserFactory @@ -50,15 +50,6 @@ class TestSendRecurringNudge(ScheduleBaseEmailTestBase): # pylint: disable=protected-access tested_task = tasks.ScheduleRecurringNudge - def setUp(self): - super(TestSendRecurringNudge, self).setUp() - - site = SiteFactory.create() - self.site_config = SiteConfigurationFactory.create(site=site) - ScheduleConfigFactory.create(site=self.site_config.site) - - DynamicUpgradeDeadlineConfiguration.objects.create(enabled=True) - @patch.object(nudge.Command, 'async_send_task') def test_handle(self, mock_send): test_day = datetime.datetime(2017, 8, 1, tzinfo=pytz.UTC) diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py index a84b0916e2..9a91b1712b 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py @@ -18,13 +18,12 @@ from opaque_keys.edx.locator import CourseLocator from course_modes.models import CourseMode from course_modes.tests.factories import CourseModeFactory -from courseware.models import DynamicUpgradeDeadlineConfiguration from openedx.core.djangoapps.content.course_overviews.models import CourseOverview 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.tools import ScheduleBaseEmailTestBase from openedx.core.djangoapps.schedules.tests.factories import ScheduleConfigFactory, ScheduleFactory -from openedx.core.djangoapps.site_configuration.tests.factories import SiteConfigurationFactory, SiteFactory +from openedx.core.djangoapps.site_configuration.tests.factories import SiteConfigurationFactory from openedx.core.djangoapps.waffle_utils.testutils import WAFFLE_TABLES from openedx.core.djangolib.testing.utils import skip_unless_lms from student.tests.factories import UserFactory @@ -86,12 +85,6 @@ class TestUpgradeReminder(ScheduleBaseEmailTestBase, SharedModuleStoreTestCase): expiration_datetime=datetime.datetime.now(pytz.UTC) + datetime.timedelta(days=30), ) - site = SiteFactory.create() - self.site_config = SiteConfigurationFactory.create(site=site) - ScheduleConfigFactory.create(site=self.site_config.site) - - DynamicUpgradeDeadlineConfiguration.objects.create(enabled=True) - @patch.object(reminder.Command, 'async_send_task') def test_handle(self, mock_send): test_day = datetime.datetime(2017, 8, 1, tzinfo=pytz.UTC) diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/tools.py b/openedx/core/djangoapps/schedules/management/commands/tests/tools.py index faeb95a051..cb0248da5a 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/tools.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/tools.py @@ -1,5 +1,18 @@ +from courseware.models import DynamicUpgradeDeadlineConfiguration from openedx.core.djangolib.testing.utils import CacheIsolationTestCase, FilteredQueryCountMixin +from openedx.core.djangoapps.site_configuration.tests.factories import SiteConfigurationFactory, SiteFactory +from openedx.core.djangoapps.schedules.tests.factories import ScheduleConfigFactory + class ScheduleBaseEmailTestBase(FilteredQueryCountMixin, CacheIsolationTestCase): ENABLED_CACHES = ['default'] + + def setUp(self): + super(ScheduleBaseEmailTestBase, self).setUp() + + site = SiteFactory.create() + self.site_config = SiteConfigurationFactory.create(site=site) + ScheduleConfigFactory.create(site=self.site_config.site) + + DynamicUpgradeDeadlineConfiguration.objects.create(enabled=True) From 8afe1ffe17091aed3fe8e53d9498997bf6018010 Mon Sep 17 00:00:00 2001 From: Calen Pennington Date: Wed, 25 Oct 2017 11:27:40 -0400 Subject: [PATCH 06/26] Move schedule management command log_prefix to a class attribute --- .../schedules/management/commands/send_course_update.py | 5 +---- .../schedules/management/commands/send_recurring_nudge.py | 5 +---- .../schedules/management/commands/send_upgrade_reminder.py | 5 +---- 3 files changed, 3 insertions(+), 12 deletions(-) diff --git a/openedx/core/djangoapps/schedules/management/commands/send_course_update.py b/openedx/core/djangoapps/schedules/management/commands/send_course_update.py index 2bfa393e29..fa8e0d230e 100644 --- a/openedx/core/djangoapps/schedules/management/commands/send_course_update.py +++ b/openedx/core/djangoapps/schedules/management/commands/send_course_update.py @@ -4,10 +4,7 @@ from openedx.core.djangoapps.schedules.tasks import ScheduleCourseUpdate class Command(SendEmailBaseCommand): async_send_task = ScheduleCourseUpdate - - def __init__(self, *args, **kwargs): - super(Command, self).__init__(*args, **kwargs) - self.log_prefix = 'Upgrade Reminder' + log_prefix = 'Course Update' def send_emails(self, *args, **kwargs): for day_offset in xrange(-7, -77, -7): diff --git a/openedx/core/djangoapps/schedules/management/commands/send_recurring_nudge.py b/openedx/core/djangoapps/schedules/management/commands/send_recurring_nudge.py index b5732e20b9..373039f9db 100644 --- a/openedx/core/djangoapps/schedules/management/commands/send_recurring_nudge.py +++ b/openedx/core/djangoapps/schedules/management/commands/send_recurring_nudge.py @@ -4,10 +4,7 @@ from openedx.core.djangoapps.schedules.tasks import ScheduleRecurringNudge class Command(SendEmailBaseCommand): async_send_task = ScheduleRecurringNudge - - def __init__(self, *args, **kwargs): - super(Command, self).__init__(*args, **kwargs) - self.log_prefix = 'Scheduled Nudge' + log_prefix = 'Scheduled Nudge' def send_emails(self, *args, **kwargs): for day_offset in (-3, -10): diff --git a/openedx/core/djangoapps/schedules/management/commands/send_upgrade_reminder.py b/openedx/core/djangoapps/schedules/management/commands/send_upgrade_reminder.py index 58a6b46a96..6e717d3b9e 100644 --- a/openedx/core/djangoapps/schedules/management/commands/send_upgrade_reminder.py +++ b/openedx/core/djangoapps/schedules/management/commands/send_upgrade_reminder.py @@ -4,10 +4,7 @@ from openedx.core.djangoapps.schedules.tasks import ScheduleUpgradeReminder class Command(SendEmailBaseCommand): async_send_task = ScheduleUpgradeReminder - - def __init__(self, *args, **kwargs): - super(Command, self).__init__(*args, **kwargs) - self.log_prefix = 'Upgrade Reminder' + log_prefix = 'Upgrade Reminder' def send_emails(self, *args, **kwargs): self.enqueue(2, *args, **kwargs) From c4249c6bc55529810e4c0fa7212ebd275375da01 Mon Sep 17 00:00:00 2001 From: Calen Pennington Date: Wed, 25 Oct 2017 11:55:27 -0400 Subject: [PATCH 07/26] Move day-offsets into class attributes of the schedule management commands --- .../schedules/management/commands/__init__.py | 8 +++++--- .../management/commands/send_course_update.py | 5 +---- .../management/commands/send_recurring_nudge.py | 5 +---- .../commands/send_upgrade_reminder.py | 4 +--- .../management/commands/tests/test_base.py | 17 ++++++++++++++++- 5 files changed, 24 insertions(+), 15 deletions(-) diff --git a/openedx/core/djangoapps/schedules/management/commands/__init__.py b/openedx/core/djangoapps/schedules/management/commands/__init__.py index 8c71e049d6..ed7b737386 100644 --- a/openedx/core/djangoapps/schedules/management/commands/__init__.py +++ b/openedx/core/djangoapps/schedules/management/commands/__init__.py @@ -9,6 +9,7 @@ from openedx.core.djangoapps.schedules.utils import PrefixedDebugLoggerMixin class SendEmailBaseCommand(PrefixedDebugLoggerMixin, BaseCommand): async_send_task = None # define in subclass + offsets = () # define in subclass def add_arguments(self, parser): parser.add_argument( @@ -37,9 +38,6 @@ class SendEmailBaseCommand(PrefixedDebugLoggerMixin, BaseCommand): override_recipient_email = options.get('override_recipient_email') self.send_emails(site, current_date, override_recipient_email) - def send_emails(self, *args, **kwargs): - raise NotImplementedError - def enqueue(self, day_offset, site, current_date, override_recipient_email=None): self.async_send_task.enqueue( site, @@ -47,3 +45,7 @@ class SendEmailBaseCommand(PrefixedDebugLoggerMixin, BaseCommand): day_offset, override_recipient_email, ) + + def send_emails(self, *args, **kwargs): + for offset in self.offsets: + self.enqueue(offset, *args, **kwargs) diff --git a/openedx/core/djangoapps/schedules/management/commands/send_course_update.py b/openedx/core/djangoapps/schedules/management/commands/send_course_update.py index fa8e0d230e..87da49ba36 100644 --- a/openedx/core/djangoapps/schedules/management/commands/send_course_update.py +++ b/openedx/core/djangoapps/schedules/management/commands/send_course_update.py @@ -5,7 +5,4 @@ from openedx.core.djangoapps.schedules.tasks import ScheduleCourseUpdate class Command(SendEmailBaseCommand): async_send_task = ScheduleCourseUpdate log_prefix = 'Course Update' - - def send_emails(self, *args, **kwargs): - for day_offset in xrange(-7, -77, -7): - self.enqueue(day_offset, *args, **kwargs) + offsets = xrange(-7, -77, -7) diff --git a/openedx/core/djangoapps/schedules/management/commands/send_recurring_nudge.py b/openedx/core/djangoapps/schedules/management/commands/send_recurring_nudge.py index 373039f9db..c1c9cf521d 100644 --- a/openedx/core/djangoapps/schedules/management/commands/send_recurring_nudge.py +++ b/openedx/core/djangoapps/schedules/management/commands/send_recurring_nudge.py @@ -5,7 +5,4 @@ from openedx.core.djangoapps.schedules.tasks import ScheduleRecurringNudge class Command(SendEmailBaseCommand): async_send_task = ScheduleRecurringNudge log_prefix = 'Scheduled Nudge' - - def send_emails(self, *args, **kwargs): - for day_offset in (-3, -10): - self.enqueue(day_offset, *args, **kwargs) + offsets = (-3, -10) diff --git a/openedx/core/djangoapps/schedules/management/commands/send_upgrade_reminder.py b/openedx/core/djangoapps/schedules/management/commands/send_upgrade_reminder.py index 6e717d3b9e..d8d0a6156d 100644 --- a/openedx/core/djangoapps/schedules/management/commands/send_upgrade_reminder.py +++ b/openedx/core/djangoapps/schedules/management/commands/send_upgrade_reminder.py @@ -5,6 +5,4 @@ from openedx.core.djangoapps.schedules.tasks import ScheduleUpgradeReminder class Command(SendEmailBaseCommand): async_send_task = ScheduleUpgradeReminder log_prefix = 'Upgrade Reminder' - - def send_emails(self, *args, **kwargs): - self.enqueue(2, *args, **kwargs) + offsets = (2,) diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/test_base.py b/openedx/core/djangoapps/schedules/management/commands/tests/test_base.py index 3a53dbbd8a..ff60ed1fd5 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/test_base.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/test_base.py @@ -4,7 +4,7 @@ from unittest import skipUnless import ddt import pytz from django.conf import settings -from mock import patch +from mock import patch, DEFAULT, Mock from openedx.core.djangoapps.schedules.management.commands import SendEmailBaseCommand from openedx.core.djangoapps.site_configuration.tests.factories import SiteFactory, SiteConfigurationFactory @@ -29,3 +29,18 @@ class TestSendEmailBaseCommand(CacheIsolationTestCase): datetime.datetime(2017, 9, 29, tzinfo=pytz.UTC), None ) + + def test_send_emails(self): + with patch.multiple( + self.command, + offsets=(1, 3, 5), + enqueue=DEFAULT, + ): + arg = Mock(name='arg') + kwarg = Mock(name='kwarg') + self.command.send_emails(arg, kwarg=kwarg) + self.assertFalse(arg.called) + self.assertFalse(kwarg.called) + + for offset in self.command.offsets: + self.command.enqueue.assert_any_call(offset, arg, kwarg=kwarg) From f2ea6f664e23e9915668e348ea1a84241b7fc77f Mon Sep 17 00:00:00 2001 From: Calen Pennington Date: Wed, 25 Oct 2017 12:11:11 -0400 Subject: [PATCH 08/26] Move the tested_command up to a class attribute, and make sure that it matches with the tested_task --- .../management/commands/tests/test_send_recurring_nudge.py | 5 +++-- .../management/commands/tests/test_send_upgrade_reminder.py | 6 ++++-- .../djangoapps/schedules/management/commands/tests/tools.py | 5 +++++ 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py index 37abeabdf2..520e02a859 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py @@ -49,11 +49,12 @@ NUM_COURSE_MODES_QUERIES = 1 class TestSendRecurringNudge(ScheduleBaseEmailTestBase): # pylint: disable=protected-access tested_task = tasks.ScheduleRecurringNudge + tested_command = nudge.Command - @patch.object(nudge.Command, 'async_send_task') + @patch.object(tested_command, 'async_send_task') def test_handle(self, mock_send): test_day = datetime.datetime(2017, 8, 1, tzinfo=pytz.UTC) - nudge.Command().handle(date='2017-08-01', site_domain_name=self.site_config.site.domain) + self.tested_command().handle(date='2017-08-01', site_domain_name=self.site_config.site.domain) for day in (-3, -10): mock_send.enqueue.assert_any_call( self.site_config.site, diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py index 9a91b1712b..dfff5b9ab3 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py @@ -60,8 +60,10 @@ LOG = logging.getLogger(__name__) "Can't test schedules if the app isn't installed") @freeze_time('2017-08-01 00:00:00', tz_offset=0, tick=True) class TestUpgradeReminder(ScheduleBaseEmailTestBase, SharedModuleStoreTestCase): + __test__ = True tested_task = tasks.ScheduleUpgradeReminder + tested_command = reminder.Command @classmethod def setUpClass(cls): @@ -85,10 +87,10 @@ class TestUpgradeReminder(ScheduleBaseEmailTestBase, SharedModuleStoreTestCase): expiration_datetime=datetime.datetime.now(pytz.UTC) + datetime.timedelta(days=30), ) - @patch.object(reminder.Command, 'async_send_task') + @patch.object(tested_command, 'async_send_task') def test_handle(self, mock_send): test_day = datetime.datetime(2017, 8, 1, tzinfo=pytz.UTC) - reminder.Command().handle(date='2017-08-01', site_domain_name=self.site_config.site.domain) + self.tested_command().handle(date='2017-08-01', site_domain_name=self.site_config.site.domain) mock_send.enqueue.assert_called_with( self.site_config.site, test_day, diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/tools.py b/openedx/core/djangoapps/schedules/management/commands/tests/tools.py index cb0248da5a..447d1ea83b 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/tools.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/tools.py @@ -6,6 +6,8 @@ from openedx.core.djangoapps.schedules.tests.factories import ScheduleConfigFact class ScheduleBaseEmailTestBase(FilteredQueryCountMixin, CacheIsolationTestCase): + __test__ = False + ENABLED_CACHES = ['default'] def setUp(self): @@ -16,3 +18,6 @@ class ScheduleBaseEmailTestBase(FilteredQueryCountMixin, CacheIsolationTestCase) ScheduleConfigFactory.create(site=self.site_config.site) DynamicUpgradeDeadlineConfiguration.objects.create(enabled=True) + + def test_command_task_binding(self): + self.assertEqual(self.tested_command.async_send_task, self.tested_task) From 77a2638d686d594623de09b5e6c84d8cebcb78db Mon Sep 17 00:00:00 2001 From: Calen Pennington Date: Wed, 25 Oct 2017 12:53:57 -0400 Subject: [PATCH 09/26] Move test_handle into the base class for schedule management command tests --- .../tests/test_send_recurring_nudge.py | 15 +++------------ .../tests/test_send_upgrade_reminder.py | 12 +----------- .../management/commands/tests/tools.py | 18 ++++++++++++++++++ 3 files changed, 22 insertions(+), 23 deletions(-) diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py index 520e02a859..244ad1aae5 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py @@ -47,21 +47,12 @@ NUM_COURSE_MODES_QUERIES = 1 @skipUnless('openedx.core.djangoapps.schedules.apps.SchedulesConfig' in settings.INSTALLED_APPS, "Can't test schedules if the app isn't installed") class TestSendRecurringNudge(ScheduleBaseEmailTestBase): + __test__ = True + # pylint: disable=protected-access tested_task = tasks.ScheduleRecurringNudge tested_command = nudge.Command - - @patch.object(tested_command, 'async_send_task') - def test_handle(self, mock_send): - test_day = datetime.datetime(2017, 8, 1, tzinfo=pytz.UTC) - self.tested_command().handle(date='2017-08-01', site_domain_name=self.site_config.site.domain) - for day in (-3, -10): - mock_send.enqueue.assert_any_call( - self.site_config.site, - test_day, - day, - None - ) + expected_offsets = (-3, -10) @patch.object(tasks, 'ace') def test_resolver_send(self, mock_ace): diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py index dfff5b9ab3..85759bdb5d 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py @@ -64,6 +64,7 @@ class TestUpgradeReminder(ScheduleBaseEmailTestBase, SharedModuleStoreTestCase): tested_task = tasks.ScheduleUpgradeReminder tested_command = reminder.Command + expected_offsets = (2,) @classmethod def setUpClass(cls): @@ -87,17 +88,6 @@ class TestUpgradeReminder(ScheduleBaseEmailTestBase, SharedModuleStoreTestCase): expiration_datetime=datetime.datetime.now(pytz.UTC) + datetime.timedelta(days=30), ) - @patch.object(tested_command, 'async_send_task') - def test_handle(self, mock_send): - test_day = datetime.datetime(2017, 8, 1, tzinfo=pytz.UTC) - self.tested_command().handle(date='2017-08-01', site_domain_name=self.site_config.site.domain) - mock_send.enqueue.assert_called_with( - self.site_config.site, - test_day, - 2, - None - ) - @patch.object(tasks, 'ace') def test_resolver_send(self, mock_ace): current_day = datetime.datetime(2017, 8, 1, tzinfo=pytz.UTC) diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/tools.py b/openedx/core/djangoapps/schedules/management/commands/tests/tools.py index 447d1ea83b..18abc343e4 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/tools.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/tools.py @@ -1,3 +1,8 @@ +import datetime + +from mock import patch +import pytz + from courseware.models import DynamicUpgradeDeadlineConfiguration from openedx.core.djangolib.testing.utils import CacheIsolationTestCase, FilteredQueryCountMixin from openedx.core.djangoapps.site_configuration.tests.factories import SiteConfigurationFactory, SiteFactory @@ -21,3 +26,16 @@ class ScheduleBaseEmailTestBase(FilteredQueryCountMixin, CacheIsolationTestCase) def test_command_task_binding(self): self.assertEqual(self.tested_command.async_send_task, self.tested_task) + + def test_handle(self): + with patch.object(self.tested_command, 'async_send_task') as mock_send: + test_day = datetime.datetime(2017, 8, 1, tzinfo=pytz.UTC) + self.tested_command().handle(date='2017-08-01', site_domain_name=self.site_config.site.domain) + + for offset in self.expected_offsets: + mock_send.enqueue.assert_any_call( + self.site_config.site, + test_day, + offset, + None + ) From c4302df041ac781e3497f67dfd1258ad4b32e759 Mon Sep 17 00:00:00 2001 From: Calen Pennington Date: Wed, 25 Oct 2017 13:07:56 -0400 Subject: [PATCH 10/26] Move test_resolver_send into the schedule management command base test class --- .../tests/test_send_recurring_nudge.py | 15 ------------- .../tests/test_send_upgrade_reminder.py | 18 ---------------- .../management/commands/tests/tools.py | 21 +++++++++++++++++++ 3 files changed, 21 insertions(+), 33 deletions(-) diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py index 244ad1aae5..516c5115eb 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py @@ -54,21 +54,6 @@ class TestSendRecurringNudge(ScheduleBaseEmailTestBase): tested_command = nudge.Command expected_offsets = (-3, -10) - @patch.object(tasks, 'ace') - def test_resolver_send(self, mock_ace): - current_day = datetime.datetime(2017, 8, 1, tzinfo=pytz.UTC) - with patch.object(self.tested_task, 'apply_async') as mock_apply_async: - self.tested_task.enqueue(self.site_config.site, current_day, -3) - test_day = current_day + datetime.timedelta(days=-3) - mock_apply_async.assert_any_call( - (self.site_config.site.id, serialize(test_day), -3, 0, None), - retry=False, - ) - mock_apply_async.assert_any_call( - (self.site_config.site.id, serialize(test_day), -3, resolvers.RECURRING_NUDGE_NUM_BINS - 1, None), - retry=False, - ) - self.assertFalse(mock_ace.send.called) @ddt.data(1, 10, 100) @patch.object(tasks, 'ace') diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py index 85759bdb5d..fe5d214d4c 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py @@ -88,24 +88,6 @@ class TestUpgradeReminder(ScheduleBaseEmailTestBase, SharedModuleStoreTestCase): expiration_datetime=datetime.datetime.now(pytz.UTC) + datetime.timedelta(days=30), ) - @patch.object(tasks, 'ace') - def test_resolver_send(self, mock_ace): - current_day = datetime.datetime(2017, 8, 1, tzinfo=pytz.UTC) - test_day = current_day + datetime.timedelta(days=2) - ScheduleFactory.create(upgrade_deadline=datetime.datetime(2017, 8, 3, 15, 34, 30, tzinfo=pytz.UTC)) - - with patch.object(self.tested_task, 'apply_async') as mock_apply_async: - self.tested_task.enqueue(self.site_config.site, current_day, 2) - mock_apply_async.assert_any_call( - (self.site_config.site.id, serialize(test_day), 2, 0, None), - retry=False, - ) - mock_apply_async.assert_any_call( - (self.site_config.site.id, serialize(test_day), 2, resolvers.UPGRADE_REMINDER_NUM_BINS - 1, None), - retry=False, - ) - self.assertFalse(mock_ace.send.called) - @ddt.data(1, 10, 100) @patch.object(tasks, 'ace') @patch.object(tested_task, 'async_send_task') diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/tools.py b/openedx/core/djangoapps/schedules/management/commands/tests/tools.py index 18abc343e4..bec32811f0 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/tools.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/tools.py @@ -1,14 +1,17 @@ import datetime +from edx_ace.utils.date import serialize from mock import patch import pytz from courseware.models import DynamicUpgradeDeadlineConfiguration from openedx.core.djangolib.testing.utils import CacheIsolationTestCase, FilteredQueryCountMixin from openedx.core.djangoapps.site_configuration.tests.factories import SiteConfigurationFactory, SiteFactory +from openedx.core.djangoapps.schedules import tasks from openedx.core.djangoapps.schedules.tests.factories import ScheduleConfigFactory + class ScheduleBaseEmailTestBase(FilteredQueryCountMixin, CacheIsolationTestCase): __test__ = False @@ -39,3 +42,21 @@ class ScheduleBaseEmailTestBase(FilteredQueryCountMixin, CacheIsolationTestCase) offset, None ) + + @patch.object(tasks, 'ace') + def test_resolver_send(self, mock_ace): + current_day = datetime.datetime(2017, 8, 1, tzinfo=pytz.UTC) + offset = self.expected_offsets[0] + target_day = current_day + datetime.timedelta(days=offset) + + 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( + (self.site_config.site.id, serialize(target_day), offset, 0, None), + retry=False, + ) + mock_apply_async.assert_any_call( + (self.site_config.site.id, serialize(target_day), offset, self.tested_task.num_bins - 1, None), + retry=False, + ) + self.assertFalse(mock_ace.send.called) From e5f84b3dd9c2978430decc99887ad7bbe334f339 Mon Sep 17 00:00:00 2001 From: Calen Pennington Date: Wed, 25 Oct 2017 14:06:58 -0400 Subject: [PATCH 11/26] Move test_schedule_bin into the schedules management command base test class --- .../tests/test_send_recurring_nudge.py | 30 ----- .../tests/test_send_upgrade_reminder.py | 64 +---------- .../management/commands/tests/tools.py | 106 +++++++++++++++++- 3 files changed, 103 insertions(+), 97 deletions(-) diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py index 516c5115eb..329333f1f0 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py @@ -54,36 +54,6 @@ class TestSendRecurringNudge(ScheduleBaseEmailTestBase): tested_command = nudge.Command expected_offsets = (-3, -10) - - @ddt.data(1, 10, 100) - @patch.object(tasks, 'ace') - @patch.object(tested_task, 'async_send_task') - def test_schedule_bin(self, schedule_count, mock_schedule_send, mock_ace): - schedules = [ - ScheduleFactory.create( - start=datetime.datetime(2017, 8, 3, 18, 44, 30, tzinfo=pytz.UTC), - enrollment__course__id=CourseLocator('edX', 'toy', 'Bin') - ) for i in range(schedule_count) - ] - - bins_in_use = frozenset((s.enrollment.user.id % resolvers.RECURRING_NUDGE_NUM_BINS) for s in schedules) - - test_datetime = datetime.datetime(2017, 8, 3, 18, tzinfo=pytz.UTC) - test_datetime_str = serialize(test_datetime) - for b in range(resolvers.RECURRING_NUDGE_NUM_BINS): - expected_queries = NUM_QUERIES_NO_MATCHING_SCHEDULES + NUM_QUERIES_NO_ORG_LIST - if b in bins_in_use: - # to fetch course modes for valid schedules - expected_queries += NUM_COURSE_MODES_QUERIES - - 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=test_datetime_str, day_offset=-3, bin_num=b, - )) - self.assertEqual(mock_schedule_send.apply_async.call_count, schedule_count) - self.assertFalse(mock_ace.send.called) - @patch.object(tested_task, 'async_send_task') def test_no_course_overview(self, mock_schedule_send): schedule = ScheduleFactory.create( diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py index fe5d214d4c..ed721df4bd 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py @@ -8,7 +8,6 @@ import ddt import pytz from django.conf import settings from edx_ace import Message -from freezegun import freeze_time from edx_ace.channel import ChannelType from edx_ace.test_utils import StubPolicy, patch_channels, patch_policies from edx_ace.utils.date import serialize @@ -18,7 +17,6 @@ from opaque_keys.edx.locator import CourseLocator from course_modes.models import CourseMode from course_modes.tests.factories import CourseModeFactory -from openedx.core.djangoapps.content.course_overviews.models import CourseOverview 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.tools import ScheduleBaseEmailTestBase @@ -27,8 +25,6 @@ from openedx.core.djangoapps.site_configuration.tests.factories import SiteConfi from openedx.core.djangoapps.waffle_utils.testutils import WAFFLE_TABLES from openedx.core.djangolib.testing.utils import skip_unless_lms from student.tests.factories import UserFactory -from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase -from xmodule.modulestore.tests.factories import CourseFactory SITE_QUERY = 1 @@ -58,26 +54,14 @@ LOG = logging.getLogger(__name__) @skip_unless_lms @skipUnless('openedx.core.djangoapps.schedules.apps.SchedulesConfig' in settings.INSTALLED_APPS, "Can't test schedules if the app isn't installed") -@freeze_time('2017-08-01 00:00:00', tz_offset=0, tick=True) -class TestUpgradeReminder(ScheduleBaseEmailTestBase, SharedModuleStoreTestCase): +class TestUpgradeReminder(ScheduleBaseEmailTestBase): __test__ = True tested_task = tasks.ScheduleUpgradeReminder tested_command = reminder.Command expected_offsets = (2,) - @classmethod - def setUpClass(cls): - super(TestUpgradeReminder, cls).setUpClass() - - cls.course = CourseFactory.create( - org='edX', - number='test', - display_name='Test Course', - self_paced=True, - start=datetime.datetime.now(pytz.UTC) - datetime.timedelta(days=30), - ) - cls.course_overview = CourseOverview.get_from_id(cls.course.id) + has_course_queries = True def setUp(self): super(TestUpgradeReminder, self).setUp() @@ -88,50 +72,6 @@ class TestUpgradeReminder(ScheduleBaseEmailTestBase, SharedModuleStoreTestCase): expiration_datetime=datetime.datetime.now(pytz.UTC) + datetime.timedelta(days=30), ) - @ddt.data(1, 10, 100) - @patch.object(tasks, 'ace') - @patch.object(tested_task, 'async_send_task') - def test_schedule_bin(self, schedule_count, mock_schedule_send, mock_ace): - upgrade_deadline = datetime.datetime.now(pytz.UTC) + datetime.timedelta(days=2) - schedules = [ - ScheduleFactory.create( - upgrade_deadline=upgrade_deadline, - enrollment__course=self.course_overview, - ) for i 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_switch_queries = len(set(s.enrollment.course.id for s in schedules)) - org_switch_queries = len(set(s.enrollment.course.id.org for s in schedules)) - test_datetime = upgrade_deadline - test_datetime_str = serialize(test_datetime) - - for b in range(resolvers.UPGRADE_REMINDER_NUM_BINS): - LOG.debug('Running bin %d', b) - expected_queries = NUM_QUERIES_NO_MATCHING_SCHEDULES - if b in bins_in_use: - if is_first_match: - 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_switch_queries + org_switch_queries - ) - 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=test_datetime_str, day_offset=2, bin_num=b, - )) - - self.assertEqual(mock_schedule_send.apply_async.call_count, schedule_count) - self.assertFalse(mock_ace.send.called) - @patch.object(tested_task, 'async_send_task') def test_no_course_overview(self, mock_schedule_send): diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/tools.py b/openedx/core/djangoapps/schedules/management/commands/tests/tools.py index bec32811f0..27fffd8eea 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/tools.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/tools.py @@ -1,23 +1,70 @@ import datetime +import ddt +import logging -from edx_ace.utils.date import serialize +from freezegun import freeze_time from mock import patch import pytz from courseware.models import DynamicUpgradeDeadlineConfiguration -from openedx.core.djangolib.testing.utils import CacheIsolationTestCase, FilteredQueryCountMixin +from edx_ace.utils.date import serialize +from opaque_keys.edx.locator import CourseLocator +from openedx.core.djangoapps.content.course_overviews.models import CourseOverview from openedx.core.djangoapps.site_configuration.tests.factories import SiteConfigurationFactory, SiteFactory -from openedx.core.djangoapps.schedules import tasks -from openedx.core.djangoapps.schedules.tests.factories import ScheduleConfigFactory +from openedx.core.djangoapps.schedules import resolvers, tasks +from openedx.core.djangoapps.schedules.tests.factories import ScheduleConfigFactory, ScheduleFactory +from openedx.core.djangoapps.waffle_utils.testutils import WAFFLE_TABLES +from openedx.core.djangolib.testing.utils import CacheIsolationTestCase, FilteredQueryCountMixin +from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase +from xmodule.modulestore.tests.factories import CourseFactory +SITE_QUERY = 1 +SCHEDULES_QUERY = 1 +COURSE_MODES_QUERY = 1 +GLOBAL_DEADLINE_SWITCH_QUERY = 1 +COMMERCE_CONFIG_QUERY = 1 +NUM_QUERIES_NO_ORG_LIST = 1 -class ScheduleBaseEmailTestBase(FilteredQueryCountMixin, CacheIsolationTestCase): +NUM_QUERIES_NO_MATCHING_SCHEDULES = SITE_QUERY + SCHEDULES_QUERY + +NUM_QUERIES_WITH_MATCHES = ( + NUM_QUERIES_NO_MATCHING_SCHEDULES + + COURSE_MODES_QUERY +) + +NUM_QUERIES_FIRST_MATCH = ( + NUM_QUERIES_WITH_MATCHES + + GLOBAL_DEADLINE_SWITCH_QUERY + + COMMERCE_CONFIG_QUERY +) + +LOG = logging.getLogger(__name__) + + +@ddt.ddt +@freeze_time('2017-08-01 00:00:00', tz_offset=0, tick=True) +class ScheduleBaseEmailTestBase(SharedModuleStoreTestCase): __test__ = False ENABLED_CACHES = ['default'] + has_course_queries = False + + @classmethod + def setUpClass(cls): + super(ScheduleBaseEmailTestBase, cls).setUpClass() + + cls.course = CourseFactory.create( + org='edX', + number='test', + display_name='Test Course', + self_paced=True, + start=datetime.datetime.now(pytz.UTC) - datetime.timedelta(days=30), + ) + cls.course_overview = CourseOverview.get_from_id(cls.course.id) + def setUp(self): super(ScheduleBaseEmailTestBase, self).setUp() @@ -60,3 +107,52 @@ class ScheduleBaseEmailTestBase(FilteredQueryCountMixin, CacheIsolationTestCase) retry=False, ) self.assertFalse(mock_ace.send.called) + + @ddt.data(1, 10, 100) + @patch.object(tasks, 'ace') + @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() + schedules = [ + ScheduleFactory.create( + start=target_day, + upgrade_deadline=target_day, + 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): + LOG.debug('Running bin %d', b) + expected_queries = NUM_QUERIES_NO_MATCHING_SCHEDULES + if b in bins_in_use: + if is_first_match: + 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 + ) + 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, + )) + + num_schedules = mock_metric.call_args[0][1] + if b in bins_in_use: + self.assertGreater(num_schedules, 0) + else: + self.assertEqual(num_schedules, 0) + + self.assertEqual(mock_schedule_send.apply_async.call_count, schedule_count) + self.assertFalse(mock_ace.send.called) From eac5f21cc117ad5613ba318976a66f7b555ca427 Mon Sep 17 00:00:00 2001 From: Nimisha Asthagiri Date: Wed, 25 Oct 2017 18:16:47 -0400 Subject: [PATCH 12/26] Move test_no_course_overview into the schedules management command base test class --- .../tests/test_send_recurring_nudge.py | 25 ---------------- .../tests/test_send_upgrade_reminder.py | 26 ---------------- .../management/commands/tests/tools.py | 30 ++++++++++++++++++- 3 files changed, 29 insertions(+), 52 deletions(-) diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py index 329333f1f0..7070cfb9f8 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py @@ -54,31 +54,6 @@ class TestSendRecurringNudge(ScheduleBaseEmailTestBase): tested_command = nudge.Command expected_offsets = (-3, -10) - @patch.object(tested_task, 'async_send_task') - def test_no_course_overview(self, mock_schedule_send): - schedule = ScheduleFactory.create( - start=datetime.datetime(2017, 8, 3, 20, 34, 30, tzinfo=pytz.UTC), - enrollment__user=UserFactory.create(), - ) - schedule.enrollment.course_id = CourseKey.from_string('edX/toy/Not_2012_Fall') - schedule.enrollment.save() - - test_datetime = datetime.datetime(2017, 8, 3, 20, tzinfo=pytz.UTC) - test_datetime_str = serialize(test_datetime) - for b in range(resolvers.RECURRING_NUDGE_NUM_BINS): - with self.assertNumQueries(NUM_QUERIES_NO_MATCHING_SCHEDULES + NUM_QUERIES_NO_ORG_LIST, table_blacklist=WAFFLE_TABLES): - self.tested_task.apply(kwargs=dict( - site_id=self.site_config.site.id, target_day_str=test_datetime_str, day_offset=-3, bin_num=b - )) - - # There is no database constraint that enforces that enrollment.course_id points - # to a valid CourseOverview object. However, in that case, schedules isn't going - # to attempt to address it, and will instead simply skip those users. - # This happens 'transparently' because django generates an inner-join between - # enrollment and course_overview, and thus will skip any rows where course_overview - # is null. - self.assertEqual(mock_schedule_send.apply_async.call_count, 0) - @patch.object(tested_task, 'async_send_task') def test_send_after_course_end(self, mock_schedule_send): user1 = UserFactory.create(id=resolvers.RECURRING_NUDGE_NUM_BINS) diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py index ed721df4bd..5a2aa45c8d 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py @@ -72,32 +72,6 @@ class TestUpgradeReminder(ScheduleBaseEmailTestBase): expiration_datetime=datetime.datetime.now(pytz.UTC) + datetime.timedelta(days=30), ) - @patch.object(tested_task, 'async_send_task') - def test_no_course_overview(self, mock_schedule_send): - - schedule = ScheduleFactory.create( - upgrade_deadline=datetime.datetime(2017, 8, 3, 20, 34, 30, tzinfo=pytz.UTC), - ) - schedule.enrollment.course_id = CourseKey.from_string('edX/toy/Not_2012_Fall') - schedule.enrollment.save() - - test_datetime = datetime.datetime(2017, 8, 3, 20, tzinfo=pytz.UTC) - test_datetime_str = serialize(test_datetime) - for b in range(resolvers.UPGRADE_REMINDER_NUM_BINS): - - with self.assertNumQueries(NUM_QUERIES_NO_MATCHING_SCHEDULES + NUM_QUERIES_NO_ORG_LIST, table_blacklist=WAFFLE_TABLES): - self.tested_task.apply(kwargs=dict( - site_id=self.site_config.site.id, target_day_str=test_datetime_str, day_offset=2, bin_num=b, - )) - - # There is no database constraint that enforces that enrollment.course_id points - # to a valid CourseOverview object. However, in that case, schedules isn't going - # to attempt to address it, and will instead simply skip those users. - # This happens 'transparently' because django generates an inner-join between - # enrollment and course_overview, and thus will skip any rows where course_overview - # is null. - self.assertEqual(mock_schedule_send.apply_async.call_count, 0) - @patch.object(tasks, 'ace') def test_delivery_disabled(self, mock_ace): ScheduleConfigFactory.create(site=self.site_config.site, deliver_upgrade_reminder=False) diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/tools.py b/openedx/core/djangoapps/schedules/management/commands/tests/tools.py index 27fffd8eea..2e13d9d1fa 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/tools.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/tools.py @@ -8,7 +8,7 @@ import pytz from courseware.models import DynamicUpgradeDeadlineConfiguration from edx_ace.utils.date import serialize -from opaque_keys.edx.locator import CourseLocator +from opaque_keys.edx.keys import CourseKey from openedx.core.djangoapps.content.course_overviews.models import CourseOverview from openedx.core.djangoapps.site_configuration.tests.factories import SiteConfigurationFactory, SiteFactory from openedx.core.djangoapps.schedules import resolvers, tasks @@ -156,3 +156,31 @@ class ScheduleBaseEmailTestBase(SharedModuleStoreTestCase): self.assertEqual(mock_schedule_send.apply_async.call_count, schedule_count) self.assertFalse(mock_ace.send.called) + + def test_no_course_overview(self): + current_day, offset, target_day = self._get_dates() + schedule = ScheduleFactory.create( + start=target_day, + upgrade_deadline=target_day, + enrollment__course__self_paced=True, + ) + schedule.enrollment.course_id = CourseKey.from_string('edX/toy/Not_2012_Fall') + schedule.enrollment.save() + + with patch.object(self.tested_task, 'async_send_task') as mock_schedule_send: + for b in range(self.tested_task.num_bins): + self.tested_task.apply(kwargs=dict( + site_id=self.site_config.site.id, + target_day_str=serialize(target_day), + day_offset=offset, + bin_num=b, + )) + + # There is no database constraint that enforces that enrollment.course_id points + # to a valid CourseOverview object. However, in that case, schedules isn't going + # to attempt to address it, and will instead simply skip those users. + # This happens 'transparently' because django generates an inner-join between + # enrollment and course_overview, and thus will skip any rows where course_overview + # is null. + self.assertEqual(mock_schedule_send.apply_async.call_count, 0) + From 2adbac03f8f6112a58531fbc4d1f89b7cfebf0b0 Mon Sep 17 00:00:00 2001 From: Nimisha Asthagiri Date: Thu, 26 Oct 2017 08:57:51 -0400 Subject: [PATCH 13/26] fixup! Move test_resolver_send into the schedule management command base class --- .../management/commands/tests/tools.py | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/tools.py b/openedx/core/djangoapps/schedules/management/commands/tests/tools.py index 2e13d9d1fa..2aa459209f 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/tools.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/tools.py @@ -92,21 +92,18 @@ class ScheduleBaseEmailTestBase(SharedModuleStoreTestCase): @patch.object(tasks, 'ace') def test_resolver_send(self, mock_ace): - current_day = datetime.datetime(2017, 8, 1, tzinfo=pytz.UTC) - offset = self.expected_offsets[0] - target_day = current_day + datetime.timedelta(days=offset) - + 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( - (self.site_config.site.id, serialize(target_day), offset, 0, None), - retry=False, - ) - mock_apply_async.assert_any_call( - (self.site_config.site.id, serialize(target_day), offset, self.tested_task.num_bins - 1, None), - retry=False, - ) - self.assertFalse(mock_ace.send.called) + mock_apply_async.assert_any_call( + (self.site_config.site.id, serialize(target_day), offset, 0, None), + retry=False, + ) + mock_apply_async.assert_any_call( + (self.site_config.site.id, serialize(target_day), offset, self.tested_task.num_bins - 1, None), + retry=False, + ) + self.assertFalse(mock_ace.send.called) @ddt.data(1, 10, 100) @patch.object(tasks, 'ace') From 9e99489a4605cffacbb36521aa34b98a3ff98684 Mon Sep 17 00:00:00 2001 From: Nimisha Asthagiri Date: Thu, 26 Oct 2017 09:00:28 -0400 Subject: [PATCH 14/26] Move test_deliver_config into the schedules management command base test class --- .../tests/test_send_recurring_nudge.py | 10 ++-------- .../tests/test_send_upgrade_reminder.py | 10 ++-------- .../management/commands/tests/tools.py | 18 +++++++++++++++++- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py index 7070cfb9f8..78a79861c0 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py @@ -51,7 +51,9 @@ class TestSendRecurringNudge(ScheduleBaseEmailTestBase): # pylint: disable=protected-access tested_task = tasks.ScheduleRecurringNudge + deliver_task = tasks._recurring_nudge_schedule_send tested_command = nudge.Command + deliver_config = 'deliver_recurring_nudge' expected_offsets = (-3, -10) @patch.object(tested_task, 'async_send_task') @@ -78,14 +80,6 @@ class TestSendRecurringNudge(ScheduleBaseEmailTestBase): self.assertFalse(mock_schedule_send.apply_async.called) - @patch.object(tasks, 'ace') - def test_delivery_disabled(self, mock_ace): - ScheduleConfigFactory.create(site=self.site_config.site, deliver_recurring_nudge=False) - - mock_msg = Mock() - tasks._recurring_nudge_schedule_send(self.site_config.site.id, mock_msg) - self.assertFalse(mock_ace.send.called) - @patch.object(tasks, 'ace') @patch.object(tasks.ScheduleUpgradeReminder, 'apply_async') def test_enqueue_disabled(self, mock_ace, mock_apply_async): diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py index 5a2aa45c8d..f104b1144d 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py @@ -58,7 +58,9 @@ class TestUpgradeReminder(ScheduleBaseEmailTestBase): __test__ = True tested_task = tasks.ScheduleUpgradeReminder + deliver_task = tasks._upgrade_reminder_schedule_send tested_command = reminder.Command + deliver_config = 'deliver_upgrade_reminder' expected_offsets = (2,) has_course_queries = True @@ -72,14 +74,6 @@ class TestUpgradeReminder(ScheduleBaseEmailTestBase): expiration_datetime=datetime.datetime.now(pytz.UTC) + datetime.timedelta(days=30), ) - @patch.object(tasks, 'ace') - def test_delivery_disabled(self, mock_ace): - ScheduleConfigFactory.create(site=self.site_config.site, deliver_upgrade_reminder=False) - - mock_msg = Mock() - tasks._upgrade_reminder_schedule_send(self.site_config.site.id, mock_msg) - self.assertFalse(mock_ace.send.called) - @patch.object(tasks, 'ace') @patch.object(tested_task, 'apply_async') def test_enqueue_disabled(self, mock_ace, mock_apply_async): diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/tools.py b/openedx/core/djangoapps/schedules/management/commands/tests/tools.py index 2aa459209f..8ec2f4cb14 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/tools.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/tools.py @@ -3,7 +3,7 @@ import ddt import logging from freezegun import freeze_time -from mock import patch +from mock import Mock, patch import pytz from courseware.models import DynamicUpgradeDeadlineConfiguration @@ -181,3 +181,19 @@ class ScheduleBaseEmailTestBase(SharedModuleStoreTestCase): # is null. self.assertEqual(mock_schedule_send.apply_async.call_count, 0) + @ddt.data(True, False) + @patch.object(tasks, 'ace') + @patch.object(tasks, 'Message') + def test_deliver_config(self, is_enabled, mock_message, mock_ace): + schedule_config_kwargs = { + 'site': self.site_config.site, + self.deliver_config: is_enabled, + } + ScheduleConfigFactory.create(**schedule_config_kwargs) + + mock_msg = Mock() + self.deliver_task(self.site_config.site.id, mock_msg) + if is_enabled: + self.assertTrue(mock_ace.send.called) + else: + self.assertFalse(mock_ace.send.called) From 81da729af7da7a5d3bcac8e256f0934bda644b80 Mon Sep 17 00:00:00 2001 From: Nimisha Asthagiri Date: Thu, 26 Oct 2017 09:01:24 -0400 Subject: [PATCH 15/26] Move test_enqeue_config into the schedules management command base test class --- .../commands/tests/test_send_recurring_nudge.py | 15 +-------------- .../tests/test_send_upgrade_reminder.py | 15 +-------------- .../management/commands/tests/tools.py | 17 +++++++++++++++++ 3 files changed, 19 insertions(+), 28 deletions(-) diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py index 78a79861c0..6f4bc5bf20 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py @@ -54,6 +54,7 @@ class TestSendRecurringNudge(ScheduleBaseEmailTestBase): 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) @patch.object(tested_task, 'async_send_task') @@ -80,20 +81,6 @@ class TestSendRecurringNudge(ScheduleBaseEmailTestBase): self.assertFalse(mock_schedule_send.apply_async.called) - @patch.object(tasks, 'ace') - @patch.object(tasks.ScheduleUpgradeReminder, 'apply_async') - def test_enqueue_disabled(self, mock_ace, mock_apply_async): - ScheduleConfigFactory.create(site=self.site_config.site, enqueue_recurring_nudge=False) - - current_datetime = datetime.datetime(2017, 8, 1, tzinfo=pytz.UTC) - self.tested_task.enqueue( - self.site_config.site, - current_datetime, - 3 - ) - self.assertFalse(mock_apply_async.called) - self.assertFalse(mock_ace.send.called) - @patch.object(tasks, 'ace') @patch.object(tested_task, 'async_send_task') @ddt.data( diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py index f104b1144d..113dd7692a 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py @@ -61,6 +61,7 @@ class TestUpgradeReminder(ScheduleBaseEmailTestBase): deliver_task = tasks._upgrade_reminder_schedule_send tested_command = reminder.Command deliver_config = 'deliver_upgrade_reminder' + enqueue_config = 'enqueue_upgrade_reminder' expected_offsets = (2,) has_course_queries = True @@ -74,20 +75,6 @@ class TestUpgradeReminder(ScheduleBaseEmailTestBase): expiration_datetime=datetime.datetime.now(pytz.UTC) + datetime.timedelta(days=30), ) - @patch.object(tasks, 'ace') - @patch.object(tested_task, 'apply_async') - def test_enqueue_disabled(self, mock_ace, mock_apply_async): - ScheduleConfigFactory.create(site=self.site_config.site, enqueue_upgrade_reminder=False) - - current_day = datetime.datetime(2017, 8, 1, tzinfo=pytz.UTC) - self.tested_task.enqueue( - self.site_config.site, - current_day, - day_offset=3, - ) - self.assertFalse(mock_apply_async.called) - self.assertFalse(mock_ace.send.called) - @patch.object(tasks, 'ace') @patch.object(tested_task, 'async_send_task') @ddt.data( diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/tools.py b/openedx/core/djangoapps/schedules/management/commands/tests/tools.py index 8ec2f4cb14..d6a4d1089f 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/tools.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/tools.py @@ -197,3 +197,20 @@ class ScheduleBaseEmailTestBase(SharedModuleStoreTestCase): self.assertTrue(mock_ace.send.called) else: self.assertFalse(mock_ace.send.called) + + @ddt.data(True, False) + def test_enqueue_config(self, is_enabled): + schedule_config_kwargs = { + 'site': self.site_config.site, + self.enqueue_config: is_enabled, + } + ScheduleConfigFactory.create(**schedule_config_kwargs) + + current_datetime = datetime.datetime(2017, 8, 1, tzinfo=pytz.UTC) + with patch.object(self.tested_task, 'apply_async') as mock_apply_async: + self.tested_task.enqueue(self.site_config.site, current_datetime, 3) + + if is_enabled: + self.assertTrue(mock_apply_async.called) + else: + self.assertFalse(mock_apply_async.called) From 1bb23a135062697f52c78ba66af0788ee9f873a2 Mon Sep 17 00:00:00 2001 From: Nimisha Asthagiri Date: Thu, 26 Oct 2017 09:02:58 -0400 Subject: [PATCH 16/26] Move test_site_config into the schedules management command base test class --- .../tests/test_send_recurring_nudge.py | 50 ----------------- .../tests/test_send_upgrade_reminder.py | 55 ------------------- .../management/commands/tests/tools.py | 50 +++++++++++++++++ 3 files changed, 50 insertions(+), 105 deletions(-) diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py index 6f4bc5bf20..b63273fec7 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py @@ -81,56 +81,6 @@ class TestSendRecurringNudge(ScheduleBaseEmailTestBase): self.assertFalse(mock_schedule_send.apply_async.called) - @patch.object(tasks, 'ace') - @patch.object(tested_task, 'async_send_task') - @ddt.data( - ((['filtered_org'], [], 1)), - (([], ['filtered_org'], 2)) - ) - @ddt.unpack - def test_site_config(self, this_org_list, other_org_list, expected_message_count, mock_schedule_send, mock_ace): - filtered_org = 'filtered_org' - unfiltered_org = 'unfiltered_org' - this_config = SiteConfigurationFactory.create(values={'course_org_filter': this_org_list}) - other_config = SiteConfigurationFactory.create(values={'course_org_filter': other_org_list}) - - for config in (this_config, other_config): - ScheduleConfigFactory.create(site=config.site) - - user1 = UserFactory.create(id=resolvers.RECURRING_NUDGE_NUM_BINS) - user2 = UserFactory.create(id=resolvers.RECURRING_NUDGE_NUM_BINS * 2) - - ScheduleFactory.create( - start=datetime.datetime(2017, 8, 3, 17, 44, 30, tzinfo=pytz.UTC), - enrollment__course__org=filtered_org, - enrollment__user=user1, - ) - ScheduleFactory.create( - start=datetime.datetime(2017, 8, 3, 17, 44, 30, tzinfo=pytz.UTC), - enrollment__course__org=unfiltered_org, - enrollment__user=user1, - ) - ScheduleFactory.create( - start=datetime.datetime(2017, 8, 3, 17, 44, 30, tzinfo=pytz.UTC), - enrollment__course__org=unfiltered_org, - enrollment__user=user2, - ) - - test_datetime = datetime.datetime(2017, 8, 3, 17, tzinfo=pytz.UTC) - test_datetime_str = serialize(test_datetime) - - expected_queries = NUM_QUERIES_WITH_MATCHES - if not this_org_list: - expected_queries += NUM_QUERIES_NO_ORG_LIST - - with self.assertNumQueries(expected_queries, table_blacklist=WAFFLE_TABLES): - self.tested_task.apply(kwargs=dict( - site_id=this_config.site.id, target_day_str=test_datetime_str, day_offset=-3, bin_num=0 - )) - - self.assertEqual(mock_schedule_send.apply_async.call_count, expected_message_count) - self.assertFalse(mock_ace.send.called) - @patch.object(tasks, 'ace') @patch.object(tested_task, 'async_send_task') def test_multiple_enrollments(self, mock_schedule_send, mock_ace): diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py index 113dd7692a..2e6ab215f3 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py @@ -75,61 +75,6 @@ class TestUpgradeReminder(ScheduleBaseEmailTestBase): expiration_datetime=datetime.datetime.now(pytz.UTC) + datetime.timedelta(days=30), ) - @patch.object(tasks, 'ace') - @patch.object(tested_task, 'async_send_task') - @ddt.data( - ((['filtered_org'], [], 1)), - (([], ['filtered_org'], 2)) - ) - @ddt.unpack - def test_site_config(self, this_org_list, other_org_list, expected_message_count, mock_schedule_send, mock_ace): - filtered_org = 'filtered_org' - unfiltered_org = 'unfiltered_org' - this_config = SiteConfigurationFactory.create(values={'course_org_filter': this_org_list}) - other_config = SiteConfigurationFactory.create(values={'course_org_filter': other_org_list}) - - for config in (this_config, other_config): - ScheduleConfigFactory.create(site=config.site) - - user1 = UserFactory.create(id=resolvers.UPGRADE_REMINDER_NUM_BINS) - user2 = UserFactory.create(id=resolvers.UPGRADE_REMINDER_NUM_BINS * 2) - - ScheduleFactory.create( - upgrade_deadline=datetime.datetime(2017, 8, 3, 17, 44, 30, tzinfo=pytz.UTC), - enrollment__course__org=filtered_org, - enrollment__course__self_paced=True, - enrollment__user=user1, - ) - ScheduleFactory.create( - upgrade_deadline=datetime.datetime(2017, 8, 3, 17, 44, 30, tzinfo=pytz.UTC), - enrollment__course__org=unfiltered_org, - enrollment__course__self_paced=True, - enrollment__user=user1, - ) - ScheduleFactory.create( - upgrade_deadline=datetime.datetime(2017, 8, 3, 17, 44, 30, tzinfo=pytz.UTC), - enrollment__course__org=unfiltered_org, - enrollment__course__self_paced=True, - enrollment__user=user2, - ) - - test_datetime = datetime.datetime(2017, 8, 3, 17, tzinfo=pytz.UTC) - test_datetime_str = serialize(test_datetime) - - course_switch_queries = 1 - org_switch_queries = 1 - expected_queries = NUM_QUERIES_FIRST_MATCH + course_switch_queries + org_switch_queries - if not this_org_list: - expected_queries += NUM_QUERIES_NO_ORG_LIST - - with self.assertNumQueries(expected_queries, table_blacklist=WAFFLE_TABLES): - self.tested_task.apply(kwargs=dict( - site_id=this_config.site.id, target_day_str=test_datetime_str, day_offset=-3, bin_num=0 - )) - - self.assertEqual(mock_schedule_send.apply_async.call_count, expected_message_count) - self.assertFalse(mock_ace.send.called) - @patch.object(tasks, 'ace') @patch.object(tested_task, 'async_send_task') def test_multiple_enrollments(self, mock_schedule_send, mock_ace): diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/tools.py b/openedx/core/djangoapps/schedules/management/commands/tests/tools.py index d6a4d1089f..111baaf94d 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/tools.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/tools.py @@ -15,6 +15,7 @@ from openedx.core.djangoapps.schedules import resolvers, tasks from openedx.core.djangoapps.schedules.tests.factories import ScheduleConfigFactory, ScheduleFactory from openedx.core.djangoapps.waffle_utils.testutils import WAFFLE_TABLES from openedx.core.djangolib.testing.utils import CacheIsolationTestCase, FilteredQueryCountMixin +from student.tests.factories import UserFactory from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory @@ -214,3 +215,52 @@ class ScheduleBaseEmailTestBase(SharedModuleStoreTestCase): self.assertTrue(mock_apply_async.called) else: self.assertFalse(mock_apply_async.called) + + @patch.object(tasks, 'ace') + @ddt.data( + ((['filtered_org'], [], 1)), + (([], ['filtered_org'], 2)) + ) + @ddt.unpack + def test_site_config(self, this_org_list, other_org_list, expected_message_count, mock_ace): + filtered_org = 'filtered_org' + unfiltered_org = 'unfiltered_org' + this_config = SiteConfigurationFactory.create(values={'course_org_filter': this_org_list}) + other_config = SiteConfigurationFactory.create(values={'course_org_filter': other_org_list}) + + for config in (this_config, other_config): + ScheduleConfigFactory.create(site=config.site) + + 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() + + ScheduleFactory.create( + upgrade_deadline=target_day, + start=target_day, + enrollment__course__org=filtered_org, + enrollment__course__self_paced=True, + enrollment__user=user1, + ) + ScheduleFactory.create( + upgrade_deadline=target_day, + start=target_day, + enrollment__course__org=unfiltered_org, + enrollment__course__self_paced=True, + enrollment__user=user1, + ) + ScheduleFactory.create( + upgrade_deadline=target_day, + start=target_day, + enrollment__course__org=unfiltered_org, + enrollment__course__self_paced=True, + enrollment__user=user2, + ) + + with patch.object(self.tested_task, 'async_send_task') as mock_schedule_send: + self.tested_task.apply(kwargs=dict( + site_id=this_config.site.id, target_day_str=serialize(target_day), day_offset=offset, bin_num=0 + )) + + self.assertEqual(mock_schedule_send.apply_async.call_count, expected_message_count) + self.assertFalse(mock_ace.send.called) From b852025ab3b6b701eb824aed32d1770e46bbcc58 Mon Sep 17 00:00:00 2001 From: Nimisha Asthagiri Date: Thu, 26 Oct 2017 09:04:10 -0400 Subject: [PATCH 17/26] Move test_course_end into the schedules management command base test class --- .../tests/test_send_recurring_nudge.py | 24 ----------------- .../management/commands/tests/tools.py | 27 +++++++++++++++++++ 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py index b63273fec7..e1c033ad48 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py @@ -57,30 +57,6 @@ class TestSendRecurringNudge(ScheduleBaseEmailTestBase): enqueue_config = 'enqueue_recurring_nudge' expected_offsets = (-3, -10) - @patch.object(tested_task, 'async_send_task') - def test_send_after_course_end(self, mock_schedule_send): - user1 = UserFactory.create(id=resolvers.RECURRING_NUDGE_NUM_BINS) - - schedule_start = datetime.datetime(2017, 8, 3, 20, 34, 30, tzinfo=pytz.UTC) - day_command_is_run = schedule_start + datetime.timedelta(days=3) - schedule = ScheduleFactory.create( - start=schedule_start, - enrollment__user=user1, - ) - - schedule.enrollment.course.start = schedule_start - datetime.timedelta(days=30) - schedule.enrollment.course.end = day_command_is_run - datetime.timedelta(days=1) - schedule.enrollment.course.save() - - test_datetime = datetime.datetime(2017, 8, 3, 20, tzinfo=pytz.UTC) - test_datetime_str = serialize(test_datetime) - - self.tested_task.apply(kwargs=dict( - site_id=self.site_config.site.id, target_day_str=test_datetime_str, day_offset=-3, bin_num=0, - )) - - self.assertFalse(mock_schedule_send.apply_async.called) - @patch.object(tasks, 'ace') @patch.object(tested_task, 'async_send_task') def test_multiple_enrollments(self, mock_schedule_send, mock_ace): diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/tools.py b/openedx/core/djangoapps/schedules/management/commands/tests/tools.py index 111baaf94d..a8b1d31879 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/tools.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/tools.py @@ -264,3 +264,30 @@ class ScheduleBaseEmailTestBase(SharedModuleStoreTestCase): self.assertEqual(mock_schedule_send.apply_async.call_count, expected_message_count) self.assertFalse(mock_ace.send.called) + + @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() + + schedule = ScheduleFactory.create( + start=target_day, + upgrade_deadline=target_day, + enrollment__course__self_paced=True, + enrollment__user=user1, + ) + + schedule.enrollment.course.start = current_day - datetime.timedelta(days=30) + end_date_offset = -2 if has_course_ended else 2 + schedule.enrollment.course.end = current_day + datetime.timedelta(days=end_date_offset) + schedule.enrollment.course.save() + + 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=0, + )) + + if has_course_ended: + self.assertFalse(mock_schedule_send.apply_async.called) + else: + self.assertTrue(mock_schedule_send.apply_async.called) From 1ad8cd01f529a6808f7f01d0bbe2d9337ca43e9b Mon Sep 17 00:00:00 2001 From: Nimisha Asthagiri Date: Thu, 26 Oct 2017 09:04:58 -0400 Subject: [PATCH 18/26] Move test_multiple_enrollments into the schedules management command base test class --- .../tests/test_send_recurring_nudge.py | 23 -------------- .../tests/test_send_upgrade_reminder.py | 30 ------------------- .../management/commands/tests/tools.py | 25 ++++++++++++++++ 3 files changed, 25 insertions(+), 53 deletions(-) diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py index e1c033ad48..50398a0287 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py @@ -57,29 +57,6 @@ class TestSendRecurringNudge(ScheduleBaseEmailTestBase): enqueue_config = 'enqueue_recurring_nudge' expected_offsets = (-3, -10) - @patch.object(tasks, 'ace') - @patch.object(tested_task, 'async_send_task') - def test_multiple_enrollments(self, mock_schedule_send, mock_ace): - user = UserFactory.create() - schedules = [ - ScheduleFactory.create( - start=datetime.datetime(2017, 8, 3, 19, 44, 30, tzinfo=pytz.UTC), - enrollment__user=user, - enrollment__course__id=CourseLocator('edX', 'toy', 'Course{}'.format(course_num)) - ) - for course_num in (1, 2, 3) - ] - - test_datetime = datetime.datetime(2017, 8, 3, 19, 44, 30, tzinfo=pytz.UTC) - test_datetime_str = serialize(test_datetime) - with self.assertNumQueries(NUM_QUERIES_WITH_MATCHES + NUM_QUERIES_NO_ORG_LIST, table_blacklist=WAFFLE_TABLES): - self.tested_task.apply(kwargs=dict( - site_id=self.site_config.site.id, target_day_str=test_datetime_str, day_offset=-3, - bin_num=user.id % resolvers.RECURRING_NUDGE_NUM_BINS, - )) - self.assertEqual(mock_schedule_send.apply_async.call_count, 1) - self.assertFalse(mock_ace.send.called) - @ddt.data(*itertools.product((1, 10, 100), (-3, -10))) @ddt.unpack def test_templates(self, message_count, day): diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py index 2e6ab215f3..b73894f64d 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py @@ -75,36 +75,6 @@ class TestUpgradeReminder(ScheduleBaseEmailTestBase): expiration_datetime=datetime.datetime.now(pytz.UTC) + datetime.timedelta(days=30), ) - @patch.object(tasks, 'ace') - @patch.object(tested_task, 'async_send_task') - def test_multiple_enrollments(self, mock_schedule_send, mock_ace): - user = UserFactory.create() - schedules = [ - ScheduleFactory.create( - upgrade_deadline=datetime.datetime(2017, 8, 3, 19, 44, 30, tzinfo=pytz.UTC), - enrollment__user=user, - enrollment__course__self_paced=True, - enrollment__course__id=CourseLocator('edX', 'toy', 'Course{}'.format(course_num)) - ) - for course_num in (1, 2, 3) - ] - - course_switch_queries = len(set(s.enrollment.course.id for s in schedules)) - org_switch_queries = len(set(s.enrollment.course.id.org for s in schedules)) - - test_datetime = datetime.datetime(2017, 8, 3, 19, 44, 30, tzinfo=pytz.UTC) - test_datetime_str = serialize(test_datetime) - expected_query_count = ( - NUM_QUERIES_FIRST_MATCH + course_switch_queries + org_switch_queries + NUM_QUERIES_NO_ORG_LIST - ) - with self.assertNumQueries(expected_query_count, table_blacklist=WAFFLE_TABLES): - self.tested_task.apply(kwargs=dict( - site_id=self.site_config.site.id, target_day_str=test_datetime_str, day_offset=2, - bin_num=self._calculate_bin_for_user(user), - )) - self.assertEqual(mock_schedule_send.apply_async.call_count, 1) - self.assertFalse(mock_ace.send.called) - @ddt.data(1, 10, 100) def test_templates(self, message_count): now = datetime.datetime.now(pytz.UTC) diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/tools.py b/openedx/core/djangoapps/schedules/management/commands/tests/tools.py index a8b1d31879..8d1f6b9be1 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/tools.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/tools.py @@ -291,3 +291,28 @@ class ScheduleBaseEmailTestBase(SharedModuleStoreTestCase): self.assertFalse(mock_schedule_send.apply_async.called) else: self.assertTrue(mock_schedule_send.apply_async.called) + + @patch.object(tasks, 'ace') + def test_multiple_enrollments(self, mock_ace): + user = UserFactory.create() + current_day, offset, target_day = self._get_dates() + num_courses = 3 + for course_index in range(num_courses): + ScheduleFactory.create( + start=target_day, + upgrade_deadline=target_day, + 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 + 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) From 82978e516c44dd66b9519a53767a90209b5ed9ec Mon Sep 17 00:00:00 2001 From: Nimisha Asthagiri Date: Thu, 26 Oct 2017 09:07:18 -0400 Subject: [PATCH 19/26] Move test_template into the schedules management command base test classs --- .../tests/test_send_recurring_nudge.py | 60 -------------- .../tests/test_send_upgrade_reminder.py | 78 ------------------- .../management/commands/tests/tools.py | 63 +++++++++++++++ 3 files changed, 63 insertions(+), 138 deletions(-) diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py index 50398a0287..14aa3598c5 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py @@ -1,14 +1,10 @@ import datetime import itertools -from copy import deepcopy from unittest import skipUnless -import attr import ddt import pytz from django.conf import settings -from edx_ace.channel import ChannelType -from edx_ace.test_utils import StubPolicy, patch_channels, patch_policies from edx_ace.utils.date import serialize from edx_ace.message import Message from mock import Mock, patch @@ -57,57 +53,6 @@ class TestSendRecurringNudge(ScheduleBaseEmailTestBase): enqueue_config = 'enqueue_recurring_nudge' expected_offsets = (-3, -10) - @ddt.data(*itertools.product((1, 10, 100), (-3, -10))) - @ddt.unpack - def test_templates(self, message_count, day): - - user = UserFactory.create() - schedules = [ - ScheduleFactory.create( - start=datetime.datetime(2017, 8, 3, 19, 44, 30, tzinfo=pytz.UTC), - enrollment__user=user, - enrollment__course__id=CourseLocator('edX', 'toy', 'Course{}'.format(course_num)) - ) - for course_num in range(message_count) - ] - - test_datetime = datetime.datetime(2017, 8, 3, 19, tzinfo=pytz.UTC) - test_datetime_str = serialize(test_datetime) - - patch_policies(self, [StubPolicy([ChannelType.PUSH])]) - mock_channel = Mock( - name='test_channel', - channel_type=ChannelType.EMAIL - ) - patch_channels(self, [mock_channel]) - - sent_messages = [] - - with self.settings(TEMPLATES=self._get_template_overrides()): - 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) - - with self.assertNumQueries(NUM_QUERIES_WITH_MATCHES + NUM_QUERIES_NO_ORG_LIST, table_blacklist=WAFFLE_TABLES): - self.tested_task.apply(kwargs=dict( - site_id=self.site_config.site.id, target_day_str=test_datetime_str, day_offset=day, - bin_num=self._calculate_bin_for_user(user), - )) - - self.assertEqual(len(sent_messages), 1) - - # Load the site - # Check the schedule config - with self.assertNumQueries(2): - for args in sent_messages: - tasks._recurring_nudge_schedule_send(*args) - - self.assertEqual(mock_channel.deliver.call_count, 1) - for (_name, (_msg, email), _kwargs) in mock_channel.deliver.mock_calls: - for template in attr.astuple(email): - self.assertNotIn("TEMPLATE WARNING", template) - self.assertNotIn("{{", template) - self.assertNotIn("}}", template) - def test_user_in_course_with_verified_coursemode_receives_upsell(self): user = UserFactory.create() course_id = CourseLocator('edX', 'toy', 'Course1') @@ -233,11 +178,6 @@ class TestSendRecurringNudge(ScheduleBaseEmailTestBase): return sent_messages - def _get_template_overrides(self): - templates_override = deepcopy(settings.TEMPLATES) - templates_override[0]['OPTIONS']['string_if_invalid'] = "TEMPLATE WARNING - MISSING VARIABLE [%s]" - return templates_override - def _calculate_bin_for_user(self, user): return user.id % resolvers.RECURRING_NUDGE_NUM_BINS diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py index b73894f64d..5d8c10de88 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py @@ -1,15 +1,11 @@ import datetime -from copy import deepcopy import logging from unittest import skipUnless -import attr import ddt import pytz from django.conf import settings from edx_ace import Message -from edx_ace.channel import ChannelType -from edx_ace.test_utils import StubPolicy, patch_channels, patch_policies from edx_ace.utils.date import serialize from mock import Mock, patch from opaque_keys.edx.keys import CourseKey @@ -75,80 +71,6 @@ class TestUpgradeReminder(ScheduleBaseEmailTestBase): expiration_datetime=datetime.datetime.now(pytz.UTC) + datetime.timedelta(days=30), ) - @ddt.data(1, 10, 100) - def test_templates(self, message_count): - now = datetime.datetime.now(pytz.UTC) - future_datetime = now + datetime.timedelta(days=21) - - user = UserFactory.create() - schedules = [ - ScheduleFactory.create( - upgrade_deadline=future_datetime, - enrollment__user=user, - enrollment__course__self_paced=True, - enrollment__course__end=future_datetime + datetime.timedelta(days=30), - enrollment__course__id=CourseLocator('edX', 'toy', 'Course{}'.format(course_num)) - ) - for course_num in range(message_count) - ] - - for schedule in schedules: - CourseModeFactory( - course_id=schedule.enrollment.course.id, - mode_slug=CourseMode.VERIFIED, - expiration_datetime=future_datetime - ) - - course_switch_queries = len(set(s.enrollment.course.id for s in schedules)) - org_switch_queries = len(set(s.enrollment.course.id.org for s in schedules)) - - test_datetime = future_datetime - test_datetime_str = serialize(test_datetime) - - patch_policies(self, [StubPolicy([ChannelType.PUSH])]) - mock_channel = Mock( - name='test_channel', - channel_type=ChannelType.EMAIL - ) - patch_channels(self, [mock_channel]) - - sent_messages = [] - - with self.settings(TEMPLATES=self._get_template_overrides()): - 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) - - # we execute one query per course to see if it's opted out of dynamic upgrade deadlines - num_expected_queries = ( - NUM_QUERIES_FIRST_MATCH + NUM_QUERIES_NO_ORG_LIST + course_switch_queries + org_switch_queries - ) - - 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=test_datetime_str, day_offset=2, - bin_num=self._calculate_bin_for_user(user), - )) - - self.assertEqual(len(sent_messages), 1) - - # Load the site (which we query per message sent) - # Check the schedule config - with self.assertNumQueries(2): - for args in sent_messages: - tasks._upgrade_reminder_schedule_send(*args) - - self.assertEqual(mock_channel.deliver.call_count, 1) - for (_name, (_msg, email), _kwargs) in mock_channel.deliver.mock_calls: - for template in attr.astuple(email): - self.assertNotIn("TEMPLATE WARNING", template) - self.assertNotIn("{{", template) - self.assertNotIn("}}", template) - - def _get_template_overrides(self): - templates_override = deepcopy(settings.TEMPLATES) - templates_override[0]['OPTIONS']['string_if_invalid'] = "TEMPLATE WARNING - MISSING VARIABLE [%s]" - return templates_override - def _calculate_bin_for_user(self, user): return user.id % resolvers.UPGRADE_REMINDER_NUM_BINS diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/tools.py b/openedx/core/djangoapps/schedules/management/commands/tests/tools.py index 8d1f6b9be1..0f8e55c0a4 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/tools.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/tools.py @@ -1,13 +1,18 @@ +from copy import deepcopy import datetime import ddt import logging +import attr +from django.conf import settings from freezegun import freeze_time from mock import Mock, patch import pytz from courseware.models import DynamicUpgradeDeadlineConfiguration +from edx_ace.channel import ChannelType from edx_ace.utils.date import serialize +from edx_ace.test_utils import StubPolicy, patch_channels, patch_policies from opaque_keys.edx.keys import CourseKey from openedx.core.djangoapps.content.course_overviews.models import CourseOverview from openedx.core.djangoapps.site_configuration.tests.factories import SiteConfigurationFactory, SiteFactory @@ -75,6 +80,11 @@ class ScheduleBaseEmailTestBase(SharedModuleStoreTestCase): DynamicUpgradeDeadlineConfiguration.objects.create(enabled=True) + def _get_template_overrides(self): + templates_override = deepcopy(settings.TEMPLATES) + templates_override[0]['OPTIONS']['string_if_invalid'] = "TEMPLATE WARNING - MISSING VARIABLE [%s]" + return templates_override + def test_command_task_binding(self): self.assertEqual(self.tested_command.async_send_task, self.tested_task) @@ -316,3 +326,56 @@ class ScheduleBaseEmailTestBase(SharedModuleStoreTestCase): )) self.assertEqual(mock_schedule_send.apply_async.call_count, 1) self.assertFalse(mock_ace.send.called) + + @ddt.data(1, 10, 100) + def test_templates(self, message_count): + for offset in self.expected_offsets: + self._assert_template_for_offset(offset, message_count) + self.clear_caches() + + def _assert_template_for_offset(self, offset, message_count): + current_day, offset, target_day = self._get_dates(offset) + + user = UserFactory.create() + for course_index in range(message_count): + ScheduleFactory.create( + start=target_day, + upgrade_deadline=target_day, + enrollment__course__self_paced=True, + enrollment__user=user, + enrollment__course__id=CourseKey.from_string('edX/toy/course{}'.format(course_index)) + ) + + patch_policies(self, [StubPolicy([ChannelType.PUSH])]) + mock_channel = Mock( + name='test_channel', + channel_type=ChannelType.EMAIL + ) + patch_channels(self, [mock_channel]) + + sent_messages = [] + with self.settings(TEMPLATES=self._get_template_overrides()): + 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 + + 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) + + with self.assertNumQueries(2): + for args in sent_messages: + self.deliver_task(*args) + + self.assertEqual(mock_channel.deliver.call_count, 1) + for (_name, (_msg, email), _kwargs) in mock_channel.deliver.mock_calls: + for template in attr.astuple(email): + self.assertNotIn("TEMPLATE WARNING", template) + self.assertNotIn("{{", template) + self.assertNotIn("}}", template) From 346a2bcf2a1a09c54a69da2ef365922602b0863e Mon Sep 17 00:00:00 2001 From: Nimisha Asthagiri Date: Thu, 26 Oct 2017 09:11:03 -0400 Subject: [PATCH 20/26] Move _calculate_bin_for_user into the schedules management command base test class --- .../management/commands/tests/test_send_recurring_nudge.py | 3 --- .../management/commands/tests/test_send_upgrade_reminder.py | 3 --- .../djangoapps/schedules/management/commands/tests/tools.py | 3 +++ 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py index 14aa3598c5..8c9f5e1f35 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py @@ -178,9 +178,6 @@ class TestSendRecurringNudge(ScheduleBaseEmailTestBase): return sent_messages - def _calculate_bin_for_user(self, user): - return user.id % resolvers.RECURRING_NUDGE_NUM_BINS - def _contains_upsell_attribute(self, msg_attr): msg = Message.from_string(msg_attr) tmp = msg.context["show_upsell"] diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py index 5d8c10de88..1ae9e64647 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py @@ -71,9 +71,6 @@ class TestUpgradeReminder(ScheduleBaseEmailTestBase): expiration_datetime=datetime.datetime.now(pytz.UTC) + datetime.timedelta(days=30), ) - def _calculate_bin_for_user(self, user): - return user.id % resolvers.UPGRADE_REMINDER_NUM_BINS - @patch.object(tasks, '_upgrade_reminder_schedule_send') def test_dont_send_to_verified_learner(self, mock_schedule_send): upgrade_deadline = datetime.datetime.now(pytz.UTC) + datetime.timedelta(days=2) diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/tools.py b/openedx/core/djangoapps/schedules/management/commands/tests/tools.py index 0f8e55c0a4..bb6ad3f20c 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/tools.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/tools.py @@ -80,6 +80,9 @@ class ScheduleBaseEmailTestBase(SharedModuleStoreTestCase): DynamicUpgradeDeadlineConfiguration.objects.create(enabled=True) + def _calculate_bin_for_user(self, user): + return user.id % self.tested_task.num_bins + def _get_template_overrides(self): templates_override = deepcopy(settings.TEMPLATES) templates_override[0]['OPTIONS']['string_if_invalid'] = "TEMPLATE WARNING - MISSING VARIABLE [%s]" From a72df1f9d533feebd075ea0845c1f9c29bfe9dc5 Mon Sep 17 00:00:00 2001 From: Nimisha Asthagiri Date: Thu, 26 Oct 2017 09:13:29 -0400 Subject: [PATCH 21/26] Add _get_dates helper method to the schedules management command base test class --- .../schedules/management/commands/tests/tools.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/tools.py b/openedx/core/djangoapps/schedules/management/commands/tests/tools.py index bb6ad3f20c..cab728af0b 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/tools.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/tools.py @@ -17,6 +17,7 @@ from opaque_keys.edx.keys import CourseKey from openedx.core.djangoapps.content.course_overviews.models import CourseOverview from openedx.core.djangoapps.site_configuration.tests.factories import SiteConfigurationFactory, SiteFactory from openedx.core.djangoapps.schedules import resolvers, tasks +from openedx.core.djangoapps.schedules.resolvers import _get_datetime_beginning_of_day from openedx.core.djangoapps.schedules.tests.factories import ScheduleConfigFactory, ScheduleFactory from openedx.core.djangoapps.waffle_utils.testutils import WAFFLE_TABLES from openedx.core.djangolib.testing.utils import CacheIsolationTestCase, FilteredQueryCountMixin @@ -83,6 +84,12 @@ class ScheduleBaseEmailTestBase(SharedModuleStoreTestCase): def _calculate_bin_for_user(self, user): return user.id % self.tested_task.num_bins + def _get_dates(self, offset=None): + 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 + def _get_template_overrides(self): templates_override = deepcopy(settings.TEMPLATES) templates_override[0]['OPTIONS']['string_if_invalid'] = "TEMPLATE WARNING - MISSING VARIABLE [%s]" From 74307e45e8a4ace84de0747a20e76dbec82dfb52 Mon Sep 17 00:00:00 2001 From: Nimisha Asthagiri Date: Thu, 26 Oct 2017 09:14:57 -0400 Subject: [PATCH 22/26] Remove query constants from schedules management command test subclasses --- .../tests/test_send_recurring_nudge.py | 13 ------------ .../tests/test_send_upgrade_reminder.py | 20 ------------------- 2 files changed, 33 deletions(-) diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py index 8c9f5e1f35..9f080f6e1f 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py @@ -25,19 +25,6 @@ from openedx.core.djangolib.testing.utils import CacheIsolationTestCase, skip_un from student.tests.factories import UserFactory -# 1) Load the current django site -# 2) Query the schedules to find all of the template context information -NUM_QUERIES_NO_MATCHING_SCHEDULES = 2 - -# 3) Query all course modes for all courses in returned schedules -NUM_QUERIES_WITH_MATCHES = NUM_QUERIES_NO_MATCHING_SCHEDULES + 1 - -# 4) Load the non-matching site configurations -NUM_QUERIES_NO_ORG_LIST = 1 - -NUM_COURSE_MODES_QUERIES = 1 - - @ddt.ddt @skip_unless_lms @skipUnless('openedx.core.djangoapps.schedules.apps.SchedulesConfig' in settings.INSTALLED_APPS, diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py index 1ae9e64647..2ff4f59cf4 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py @@ -23,26 +23,6 @@ from openedx.core.djangolib.testing.utils import skip_unless_lms from student.tests.factories import UserFactory -SITE_QUERY = 1 -SCHEDULES_QUERY = 1 -COURSE_MODES_QUERY = 1 -GLOBAL_DEADLINE_SWITCH_QUERY = 1 -COMMERCE_CONFIG_QUERY = 1 -NUM_QUERIES_NO_ORG_LIST = 1 - -NUM_QUERIES_NO_MATCHING_SCHEDULES = SITE_QUERY + SCHEDULES_QUERY - -NUM_QUERIES_WITH_MATCHES = ( - NUM_QUERIES_NO_MATCHING_SCHEDULES + - COURSE_MODES_QUERY -) - -NUM_QUERIES_FIRST_MATCH = ( - NUM_QUERIES_WITH_MATCHES - + GLOBAL_DEADLINE_SWITCH_QUERY - + COMMERCE_CONFIG_QUERY -) - LOG = logging.getLogger(__name__) From 185f35b17a6b1daade6e59b03295cef6ef501d62 Mon Sep 17 00:00:00 2001 From: Nimisha Asthagiri Date: Thu, 26 Oct 2017 09:16:21 -0400 Subject: [PATCH 23/26] Optimize remaining tests in schedule management command TestUpgradeReminder class --- .../tests/test_send_upgrade_reminder.py | 48 +++++++------------ 1 file changed, 18 insertions(+), 30 deletions(-) diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py index 2ff4f59cf4..387dc67b18 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py @@ -51,58 +51,46 @@ class TestUpgradeReminder(ScheduleBaseEmailTestBase): expiration_datetime=datetime.datetime.now(pytz.UTC) + datetime.timedelta(days=30), ) - @patch.object(tasks, '_upgrade_reminder_schedule_send') - def test_dont_send_to_verified_learner(self, mock_schedule_send): - upgrade_deadline = datetime.datetime.now(pytz.UTC) + datetime.timedelta(days=2) + @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() ScheduleFactory.create( - upgrade_deadline=upgrade_deadline, - enrollment__user=UserFactory.create(id=resolvers.UPGRADE_REMINDER_NUM_BINS), - enrollment__course=self.course_overview, - enrollment__mode=CourseMode.VERIFIED, - ) - test_datetime_str = serialize(datetime.datetime.now(pytz.UTC)) - - self.tested_task.delay( - self.site_config.site.id, target_day_str=test_datetime_str, day_offset=2, bin_num=0, - org_list=[self.course.org], + upgrade_deadline=target_day, + enrollment__course__self_paced=True, + enrollment__user=user, + enrollment__mode=CourseMode.VERIFIED if is_verified else CourseMode.AUDIT, ) - self.assertFalse(mock_schedule_send.called) - self.assertFalse(mock_schedule_send.apply_async.called) + 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_ace.send.called, not is_verified) def test_filter_out_verified_schedules(self): - now = datetime.datetime.now(pytz.UTC) - future_datetime = now + datetime.timedelta(days=21) + current_day, offset, target_day = self._get_dates() user = UserFactory.create() schedules = [ ScheduleFactory.create( - upgrade_deadline=future_datetime, + upgrade_deadline=target_day, enrollment__user=user, enrollment__course__self_paced=True, - enrollment__course__end=future_datetime + datetime.timedelta(days=30), enrollment__course__id=CourseLocator('edX', 'toy', 'Course{}'.format(i)), enrollment__mode=CourseMode.VERIFIED if i in (0, 3) else CourseMode.AUDIT, ) for i in range(5) ] - for schedule in schedules: - CourseModeFactory( - course_id=schedule.enrollment.course.id, - mode_slug=CourseMode.VERIFIED, - expiration_datetime=future_datetime - ) - - test_datetime = future_datetime - test_datetime_str = serialize(test_datetime) - sent_messages = [] 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[1]) self.tested_task.apply(kwargs=dict( - site_id=self.site_config.site.id, target_day_str=test_datetime_str, day_offset=2, + site_id=self.site_config.site.id, target_day_str=serialize(target_day), day_offset=offset, bin_num=self._calculate_bin_for_user(user), )) From f1ceb86fa9f0907c19c2a08f352a9ef3018acc71 Mon Sep 17 00:00:00 2001 From: Nimisha Asthagiri Date: Thu, 26 Oct 2017 09:19:47 -0400 Subject: [PATCH 24/26] Remove unneeded imports/etc from schedules management command test classes --- .../tests/test_send_recurring_nudge.py | 16 +++++----------- .../tests/test_send_upgrade_reminder.py | 19 ++----------------- .../management/commands/tests/tools.py | 16 ---------------- 3 files changed, 7 insertions(+), 44 deletions(-) diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py index 9f080f6e1f..f99bc47c0c 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py @@ -1,27 +1,22 @@ import datetime -import itertools from unittest import skipUnless import ddt -import pytz from django.conf import settings from edx_ace.utils.date import serialize from edx_ace.message import Message -from mock import Mock, patch -from opaque_keys.edx.keys import CourseKey +from mock import patch from opaque_keys.edx.locator import CourseLocator +import pytz from course_modes.models import CourseMode from course_modes.tests.factories import CourseModeFactory from courseware.models import DynamicUpgradeDeadlineConfiguration -from openedx.core.djangoapps.content.course_overviews.tests.factories import CourseOverviewFactory -from openedx.core.djangoapps.schedules import resolvers, tasks +from openedx.core.djangoapps.schedules import tasks from openedx.core.djangoapps.schedules.management.commands import send_recurring_nudge as nudge from openedx.core.djangoapps.schedules.management.commands.tests.tools import ScheduleBaseEmailTestBase -from openedx.core.djangoapps.schedules.tests.factories import ScheduleConfigFactory, ScheduleFactory -from openedx.core.djangoapps.site_configuration.tests.factories import SiteConfigurationFactory -from openedx.core.djangoapps.waffle_utils.testutils import WAFFLE_TABLES -from openedx.core.djangolib.testing.utils import CacheIsolationTestCase, skip_unless_lms, FilteredQueryCountMixin +from openedx.core.djangoapps.schedules.tests.factories import ScheduleFactory +from openedx.core.djangolib.testing.utils import skip_unless_lms from student.tests.factories import UserFactory @@ -167,5 +162,4 @@ class TestSendRecurringNudge(ScheduleBaseEmailTestBase): def _contains_upsell_attribute(self, msg_attr): msg = Message.from_string(msg_attr) - tmp = msg.context["show_upsell"] return msg.context["show_upsell"] diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py index 387dc67b18..1a01c2748c 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py @@ -1,24 +1,18 @@ -import datetime import logging from unittest import skipUnless import ddt -import pytz from django.conf import settings from edx_ace import Message from edx_ace.utils.date import serialize from mock import Mock, patch -from opaque_keys.edx.keys import CourseKey from opaque_keys.edx.locator import CourseLocator from course_modes.models import CourseMode -from course_modes.tests.factories import CourseModeFactory -from openedx.core.djangoapps.schedules import resolvers, tasks +from openedx.core.djangoapps.schedules import tasks from openedx.core.djangoapps.schedules.management.commands import send_upgrade_reminder as reminder from openedx.core.djangoapps.schedules.management.commands.tests.tools import ScheduleBaseEmailTestBase -from openedx.core.djangoapps.schedules.tests.factories import ScheduleConfigFactory, ScheduleFactory -from openedx.core.djangoapps.site_configuration.tests.factories import SiteConfigurationFactory -from openedx.core.djangoapps.waffle_utils.testutils import WAFFLE_TABLES +from openedx.core.djangoapps.schedules.tests.factories import ScheduleFactory from openedx.core.djangolib.testing.utils import skip_unless_lms from student.tests.factories import UserFactory @@ -42,15 +36,6 @@ class TestUpgradeReminder(ScheduleBaseEmailTestBase): has_course_queries = True - def setUp(self): - super(TestUpgradeReminder, self).setUp() - - CourseModeFactory( - course_id=self.course.id, - mode_slug=CourseMode.VERIFIED, - expiration_datetime=datetime.datetime.now(pytz.UTC) + datetime.timedelta(days=30), - ) - @ddt.data(True, False) @patch.object(tasks, 'ace') def test_verified_learner(self, is_verified, mock_ace): diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/tools.py b/openedx/core/djangoapps/schedules/management/commands/tests/tools.py index cab728af0b..4b282cd7e7 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/tools.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/tools.py @@ -14,16 +14,13 @@ from edx_ace.channel import ChannelType from edx_ace.utils.date import serialize from edx_ace.test_utils import StubPolicy, patch_channels, patch_policies from opaque_keys.edx.keys import CourseKey -from openedx.core.djangoapps.content.course_overviews.models import CourseOverview from openedx.core.djangoapps.site_configuration.tests.factories import SiteConfigurationFactory, SiteFactory from openedx.core.djangoapps.schedules import resolvers, tasks from openedx.core.djangoapps.schedules.resolvers import _get_datetime_beginning_of_day from openedx.core.djangoapps.schedules.tests.factories import ScheduleConfigFactory, ScheduleFactory from openedx.core.djangoapps.waffle_utils.testutils import WAFFLE_TABLES -from openedx.core.djangolib.testing.utils import CacheIsolationTestCase, FilteredQueryCountMixin from student.tests.factories import UserFactory from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase -from xmodule.modulestore.tests.factories import CourseFactory SITE_QUERY = 1 @@ -59,19 +56,6 @@ class ScheduleBaseEmailTestBase(SharedModuleStoreTestCase): has_course_queries = False - @classmethod - def setUpClass(cls): - super(ScheduleBaseEmailTestBase, cls).setUpClass() - - cls.course = CourseFactory.create( - org='edX', - number='test', - display_name='Test Course', - self_paced=True, - start=datetime.datetime.now(pytz.UTC) - datetime.timedelta(days=30), - ) - cls.course_overview = CourseOverview.get_from_id(cls.course.id) - def setUp(self): super(ScheduleBaseEmailTestBase, self).setUp() From ea8da6737ec3e858c29440eab1290c6566197bed Mon Sep 17 00:00:00 2001 From: Nimisha Asthagiri Date: Thu, 26 Oct 2017 18:08:46 -0400 Subject: [PATCH 25/26] Update tests to support org queries --- .../djangoapps/schedules/management/commands/tests/tools.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/tools.py b/openedx/core/djangoapps/schedules/management/commands/tests/tools.py index 4b282cd7e7..81c8499a14 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/tools.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/tools.py @@ -24,6 +24,7 @@ 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 @@ -40,6 +41,7 @@ NUM_QUERIES_WITH_MATCHES = ( NUM_QUERIES_FIRST_MATCH = ( NUM_QUERIES_WITH_MATCHES + GLOBAL_DEADLINE_SWITCH_QUERY + + ORG_DEADLINE_QUERY + COMMERCE_CONFIG_QUERY ) From b461ce0c27672df717e04899446351f2be987802 Mon Sep 17 00:00:00 2001 From: Nimisha Asthagiri Date: Thu, 26 Oct 2017 18:54:50 -0400 Subject: [PATCH 26/26] Rename test files and class name --- .../commands/tests/{tools.py => send_email_base.py} | 4 ++-- .../tests/{test_base.py => test_send_email_base_command.py} | 0 .../management/commands/tests/test_send_recurring_nudge.py | 4 ++-- .../management/commands/tests/test_send_upgrade_reminder.py | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) rename openedx/core/djangoapps/schedules/management/commands/tests/{tools.py => send_email_base.py} (99%) rename openedx/core/djangoapps/schedules/management/commands/tests/{test_base.py => test_send_email_base_command.py} (100%) diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/tools.py b/openedx/core/djangoapps/schedules/management/commands/tests/send_email_base.py similarity index 99% rename from openedx/core/djangoapps/schedules/management/commands/tests/tools.py rename to openedx/core/djangoapps/schedules/management/commands/tests/send_email_base.py index 81c8499a14..718a80b8e3 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/tools.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/send_email_base.py @@ -50,7 +50,7 @@ LOG = logging.getLogger(__name__) @ddt.ddt @freeze_time('2017-08-01 00:00:00', tz_offset=0, tick=True) -class ScheduleBaseEmailTestBase(SharedModuleStoreTestCase): +class ScheduleSendEmailTestBase(SharedModuleStoreTestCase): __test__ = False @@ -59,7 +59,7 @@ class ScheduleBaseEmailTestBase(SharedModuleStoreTestCase): has_course_queries = False def setUp(self): - super(ScheduleBaseEmailTestBase, self).setUp() + super(ScheduleSendEmailTestBase, self).setUp() site = SiteFactory.create() self.site_config = SiteConfigurationFactory.create(site=site) diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/test_base.py b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_email_base_command.py similarity index 100% rename from openedx/core/djangoapps/schedules/management/commands/tests/test_base.py rename to openedx/core/djangoapps/schedules/management/commands/tests/test_send_email_base_command.py diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py index f99bc47c0c..b3bfa73175 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_recurring_nudge.py @@ -14,7 +14,7 @@ from course_modes.tests.factories import CourseModeFactory from courseware.models import DynamicUpgradeDeadlineConfiguration from openedx.core.djangoapps.schedules import tasks from openedx.core.djangoapps.schedules.management.commands import send_recurring_nudge as nudge -from openedx.core.djangoapps.schedules.management.commands.tests.tools import ScheduleBaseEmailTestBase +from openedx.core.djangoapps.schedules.management.commands.tests.send_email_base import ScheduleSendEmailTestBase from openedx.core.djangoapps.schedules.tests.factories import ScheduleFactory from openedx.core.djangolib.testing.utils import skip_unless_lms from student.tests.factories import UserFactory @@ -24,7 +24,7 @@ from student.tests.factories import UserFactory @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 TestSendRecurringNudge(ScheduleBaseEmailTestBase): +class TestSendRecurringNudge(ScheduleSendEmailTestBase): __test__ = True # pylint: disable=protected-access diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py index 1a01c2748c..4993dbe18c 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_upgrade_reminder.py @@ -11,7 +11,7 @@ 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.management.commands import send_upgrade_reminder as reminder -from openedx.core.djangoapps.schedules.management.commands.tests.tools import ScheduleBaseEmailTestBase +from openedx.core.djangoapps.schedules.management.commands.tests.send_email_base import ScheduleSendEmailTestBase from openedx.core.djangoapps.schedules.tests.factories import ScheduleFactory from openedx.core.djangolib.testing.utils import skip_unless_lms from student.tests.factories import UserFactory @@ -24,7 +24,7 @@ LOG = logging.getLogger(__name__) @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 TestUpgradeReminder(ScheduleBaseEmailTestBase): +class TestUpgradeReminder(ScheduleSendEmailTestBase): __test__ = True tested_task = tasks.ScheduleUpgradeReminder