From 61f4ceca5b1482cd0ef4b02b873dbbce6c3db88e Mon Sep 17 00:00:00 2001 From: Nimisha Asthagiri Date: Sun, 3 Dec 2017 21:46:22 -0500 Subject: [PATCH] Schedules: Emulate HTTP request needs to include host information --- .../commands/tests/send_email_base.py | 21 +++++-- .../commands/tests/test_send_course_update.py | 57 +++++++++++++++++-- .../tests/test_send_recurring_nudge.py | 13 +++-- .../tests/test_send_upgrade_reminder.py | 13 +++-- openedx/core/djangoapps/schedules/tasks.py | 2 +- openedx/core/lib/celery/task_utils.py | 7 ++- 6 files changed, 89 insertions(+), 24 deletions(-) diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/send_email_base.py b/openedx/core/djangoapps/schedules/management/commands/tests/send_email_base.py index 7ff7cb3f80..781b43c276 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/send_email_base.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/send_email_base.py @@ -24,7 +24,7 @@ from openedx.core.djangoapps.schedules.tests.factories import ScheduleConfigFact from openedx.core.djangoapps.site_configuration.tests.factories import SiteConfigurationFactory, SiteFactory from openedx.core.djangoapps.theming.tests.test_util import with_comprehensive_theme from openedx.core.djangoapps.waffle_utils.testutils import WAFFLE_TABLES -from openedx.core.djangolib.testing.utils import CacheIsolationTestCase, FilteredQueryCountMixin +from openedx.core.djangolib.testing.utils import FilteredQueryCountMixin from student.models import CourseEnrollment from student.tests.factories import UserFactory @@ -75,7 +75,7 @@ ExperienceTest = namedtuple('ExperienceTest', 'experience offset email_sent') @ddt.ddt @freeze_time('2017-08-01 00:00:00', tz_offset=0, tick=True) -class ScheduleSendEmailTestBase(FilteredQueryCountMixin, CacheIsolationTestCase): +class ScheduleSendEmailTestMixin(FilteredQueryCountMixin): __test__ = False @@ -85,7 +85,7 @@ class ScheduleSendEmailTestBase(FilteredQueryCountMixin, CacheIsolationTestCase) consolidates_emails_for_learner = False def setUp(self): - super(ScheduleSendEmailTestBase, self).setUp() + super(ScheduleSendEmailTestMixin, self).setUp() site = SiteFactory.create() self.site_config = SiteConfigurationFactory.create(site=site) @@ -132,6 +132,15 @@ class ScheduleSendEmailTestBase(FilteredQueryCountMixin, CacheIsolationTestCase) self._courses_with_verified_modes.add(course_id) return schedule + def _update_schedule_config(self, schedule_config_kwargs): + """ + Updates the schedule config model by making sure the new entry + has a later timestamp. + """ + later_time = datetime.datetime.now(pytz.UTC) + datetime.timedelta(minutes=1) + with freeze_time(later_time): + ScheduleConfigFactory.create(**schedule_config_kwargs) + def test_command_task_binding(self): self.assertEqual(self.command.async_send_task, self.task) @@ -239,9 +248,9 @@ class ScheduleSendEmailTestBase(FilteredQueryCountMixin, CacheIsolationTestCase) 'site': self.site_config.site, self.deliver_config: is_enabled, } - mock_message.from_string.return_value.recipient.username = user.username - ScheduleConfigFactory.create(**schedule_config_kwargs) + self._update_schedule_config(schedule_config_kwargs) + mock_message.from_string.return_value.recipient.username = user.username mock_msg = Mock() self.deliver_task(self.site_config.site.id, mock_msg) if is_enabled: @@ -255,7 +264,7 @@ class ScheduleSendEmailTestBase(FilteredQueryCountMixin, CacheIsolationTestCase) 'site': self.site_config.site, self.enqueue_config: is_enabled, } - ScheduleConfigFactory.create(**schedule_config_kwargs) + self._update_schedule_config(schedule_config_kwargs) current_datetime = datetime.datetime(2017, 8, 1, tzinfo=pytz.UTC) with patch.object(self.task, 'apply_async') as mock_apply_async: diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_course_update.py b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_course_update.py index b4f3a5cd64..973f139741 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/test_send_course_update.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/test_send_course_update.py @@ -1,18 +1,28 @@ +""" +Tests for send_course_update management command. +""" +# pylint: disable=no-member import ddt -from mock import patch +from mock import patch, _is_started from unittest import skipUnless from django.conf import settings +from edx_ace.utils.date import serialize from openedx.core.djangoapps.schedules import resolvers, tasks +from openedx.core.djangoapps.schedules.config import COURSE_UPDATE_WAFFLE_FLAG from openedx.core.djangoapps.schedules.management.commands import send_course_update as nudge from openedx.core.djangoapps.schedules.management.commands.tests.send_email_base import ( - ScheduleSendEmailTestBase, + ScheduleSendEmailTestMixin, ExperienceTest ) from openedx.core.djangoapps.schedules.management.commands.tests.upsell_base import ScheduleUpsellTestMixin from openedx.core.djangoapps.schedules.models import ScheduleExperience from openedx.core.djangolib.testing.utils import skip_unless_lms +from openedx.core.djangoapps.waffle_utils.testutils import override_waffle_flag +from student.tests.factories import CourseEnrollmentFactory +from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase +from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory @ddt.ddt @@ -21,7 +31,7 @@ from openedx.core.djangolib.testing.utils import skip_unless_lms 'openedx.core.djangoapps.schedules.apps.SchedulesConfig' in settings.INSTALLED_APPS, "Can't test schedules if the app isn't installed", ) -class TestSendCourseUpdate(ScheduleUpsellTestMixin, ScheduleSendEmailTestBase): +class TestSendCourseUpdate(ScheduleUpsellTestMixin, ScheduleSendEmailTestMixin, ModuleStoreTestCase): __test__ = True # pylint: disable=protected-access @@ -38,10 +48,18 @@ class TestSendCourseUpdate(ScheduleUpsellTestMixin, ScheduleSendEmailTestBase): def setUp(self): super(TestSendCourseUpdate, self).setUp() - patcher = patch('openedx.core.djangoapps.schedules.resolvers.get_week_highlights') - mock_highlights = patcher.start() + self.highlights_patcher = patch('openedx.core.djangoapps.schedules.resolvers.get_week_highlights') + mock_highlights = self.highlights_patcher.start() mock_highlights.return_value = ['Highlight {}'.format(num + 1) for num in range(3)] - self.addCleanup(patcher.stop) + self.addCleanup(self.stop_highlights_patcher) + + def stop_highlights_patcher(self): + """ + Stops the patcher for the get_week_highlights method + if the patch is still in progress. + """ + if _is_started(self.highlights_patcher): + self.highlights_patcher.stop() @ddt.data( ExperienceTest(experience=ScheduleExperience.EXPERIENCES.default, offset=expected_offsets[0], email_sent=False), @@ -50,3 +68,30 @@ class TestSendCourseUpdate(ScheduleUpsellTestMixin, ScheduleSendEmailTestBase): ) def test_schedule_in_different_experience(self, test_config): self._check_if_email_sent_for_experience(test_config) + + @override_waffle_flag(COURSE_UPDATE_WAFFLE_FLAG, True) + @patch('openedx.core.djangoapps.schedules.signals.get_current_site') + def test_with_course_data(self, mock_get_current_site): + self.highlights_patcher.stop() + mock_get_current_site.return_value = self.site_config.site + + course = CourseFactory(highlights_enabled_for_messaging=True, self_paced=True) + with self.store.bulk_operations(course.id): + ItemFactory.create(parent=course, category='chapter', highlights=[u'highlights']) + + enrollment = CourseEnrollmentFactory(course_id=course.id, user=self.user, mode=u'audit') + self.assertEqual(enrollment.schedule.get_experience_type(), ScheduleExperience.EXPERIENCES.course_updates) + + _, offset, target_day, _ = self._get_dates(offset=self.expected_offsets[0]) + enrollment.schedule.start = target_day + enrollment.schedule.save() + + with patch.object(tasks, 'ace') as mock_ace: + self.task.apply(kwargs=dict( # pylint: disable=no-value-for-parameter + site_id=self.site_config.site.id, + target_day_str=serialize(target_day), + day_offset=offset, + bin_num=self._calculate_bin_for_user(enrollment.user), + )) + + self.assertTrue(mock_ace.send.called) 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 6a152cb24d..81797aad7c 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,3 +1,6 @@ +""" +Tests for send_recurring_nudge management command. +""" from unittest import skipUnless import ddt @@ -5,11 +8,13 @@ from django.conf import settings from openedx.core.djangoapps.schedules import resolvers, tasks from openedx.core.djangoapps.schedules.management.commands import send_recurring_nudge as nudge -from openedx.core.djangoapps.schedules.management.commands.tests.send_email_base import ScheduleSendEmailTestBase, \ - ExperienceTest +from openedx.core.djangoapps.schedules.management.commands.tests.send_email_base import ( + ScheduleSendEmailTestMixin, + ExperienceTest, +) from openedx.core.djangoapps.schedules.management.commands.tests.upsell_base import ScheduleUpsellTestMixin from openedx.core.djangoapps.schedules.models import ScheduleExperience -from openedx.core.djangolib.testing.utils import skip_unless_lms +from openedx.core.djangolib.testing.utils import skip_unless_lms, CacheIsolationTestCase @ddt.ddt @@ -18,7 +23,7 @@ from openedx.core.djangolib.testing.utils import skip_unless_lms 'openedx.core.djangoapps.schedules.apps.SchedulesConfig' in settings.INSTALLED_APPS, "Can't test schedules if the app isn't installed", ) -class TestSendRecurringNudge(ScheduleUpsellTestMixin, ScheduleSendEmailTestBase): +class TestSendRecurringNudge(ScheduleUpsellTestMixin, ScheduleSendEmailTestMixin, CacheIsolationTestCase): __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 6c00d3a97d..bd37d75895 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,3 +1,6 @@ +""" +Tests for send_upgrade_reminder management command. +""" import logging from unittest import skipUnless @@ -11,10 +14,12 @@ from opaque_keys.edx.locator import CourseLocator from course_modes.models import CourseMode from openedx.core.djangoapps.schedules import resolvers, tasks from openedx.core.djangoapps.schedules.management.commands import send_upgrade_reminder as reminder -from openedx.core.djangoapps.schedules.management.commands.tests.send_email_base import ScheduleSendEmailTestBase, \ - ExperienceTest +from openedx.core.djangoapps.schedules.management.commands.tests.send_email_base import ( + ScheduleSendEmailTestMixin, + ExperienceTest, +) from openedx.core.djangoapps.schedules.models import ScheduleExperience -from openedx.core.djangolib.testing.utils import skip_unless_lms +from openedx.core.djangolib.testing.utils import skip_unless_lms, CacheIsolationTestCase from student.tests.factories import UserFactory @@ -25,7 +30,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(ScheduleSendEmailTestBase): +class TestUpgradeReminder(ScheduleSendEmailTestMixin, CacheIsolationTestCase): __test__ = True resolver = resolvers.UpgradeReminderResolver diff --git a/openedx/core/djangoapps/schedules/tasks.py b/openedx/core/djangoapps/schedules/tasks.py index 0794f6ab6a..dda2237dd1 100644 --- a/openedx/core/djangoapps/schedules/tasks.py +++ b/openedx/core/djangoapps/schedules/tasks.py @@ -99,9 +99,9 @@ class ScheduleMessageBaseTask(Task): def run( self, site_id, target_day_str, day_offset, bin_num, override_recipient_email=None, ): - msg_type = self.make_message_type(day_offset) site = Site.objects.select_related('configuration').get(id=site_id) with emulate_http_request(site=site): + msg_type = self.make_message_type(day_offset) _annotate_for_monitoring(msg_type, site, bin_num, target_day_str, day_offset) return self.resolver( self.async_send_task, diff --git a/openedx/core/lib/celery/task_utils.py b/openedx/core/lib/celery/task_utils.py index 4937d847e3..1d8eb19a61 100644 --- a/openedx/core/lib/celery/task_utils.py +++ b/openedx/core/lib/celery/task_utils.py @@ -1,8 +1,9 @@ from contextlib import contextmanager from crum import CurrentRequestUserMiddleware -from django.http import HttpRequest, HttpResponse from openedx.core.djangoapps.theming.middleware import CurrentSiteThemeMiddleware +from django.http import HttpResponse +from request_cache import get_request_or_stub @contextmanager @@ -24,9 +25,9 @@ def emulate_http_request(site=None, user=None, middleware_classes=None): middleware_classes (list): A list of classes that implement Django's middleware interface. Defaults to [CurrentRequestUserMiddleware, CurrentSiteThemeMiddleware] if None. """ - request = HttpRequest() - request.user = user + request = get_request_or_stub() request.site = site + request.user = user # TODO: define the default middleware_classes in settings.py middleware_classes = middleware_classes or [