From 2051c909247cd40a677ee74d39bcd865e26f75fc Mon Sep 17 00:00:00 2001 From: David Ormsbee Date: Thu, 15 Dec 2016 23:47:30 -0500 Subject: [PATCH] Test Speedup: Isolate Modulestore Signals There are a number of Django Signals that are on the modulestore's SignalHandler class, such as SignalHandler.course_published. These signals can trigger very expensive processes to occur, such as course overview or block structures generation. Most of the time, the test author doesn't care about these side-effects. This commit does a few things: * Converts the signals on SignalHandler to be instances of a new SwitchedSignal class, that allows signal sending to be disabled. * Creates a SignalIsolationMixin helper similar in spirit to the CacheIsolationMixin, and adds it to the ModuleStoreIsolationMixin (and thus to ModuleStoreTestCase and SharedModuleStoreTestCase). * Converts our various tests to use this new mechanism. In some cases, this means adjusting query counts downwards because they no longer have to account for publishing listener actions. Modulestore generated signals are now muted by default during test runs. Calls to send() them will result in no-ops. You can choose to enable specific signals for a given subclass of ModuleStoreTestCase or SharedModuleStoreTestCase by specifying an ENABLED_SIGNALS class attribute, like the following example: from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase class MyPublishTestCase(ModuleStoreTestCase): ENABLED_SIGNALS = ['course_published', 'pre_publish'] You should take great care when disabling signals outside of a ModuleStoreTestCase or SharedModuleStoreTestCase, since they can leak out into other tests. Be sure to always clean up, and never disable signals outside of testing. Because signals are essentially process globals, it can have a lot of unpleasant side-effects if we start mucking around with them during live requests. Overall, this change has cut the total test execution time for edx-platform by a bit over a third, though we still spend a lot in pre-test setup during our test builds. [PERF-413] --- .../contentstore/tests/test_gating.py | 2 + .../contentstore/tests/test_import.py | 10 +- .../contentstore/tests/test_orphan.py | 4 +- .../views/tests/test_course_index.py | 4 + .../contentstore/views/tests/test_gating.py | 1 + .../djangoapps/enrollment/tests/test_views.py | 1 + .../student/tests/test_course_listing.py | 2 + common/djangoapps/student/tests/tests.py | 7 +- .../lib/xmodule/xmodule/modulestore/django.py | 111 +++++++++++-- .../xmodule/modulestore/tests/django_utils.py | 69 +++++++- lms/djangoapps/branding/tests/test_page.py | 4 + lms/djangoapps/ccx/api/v0/tests/test_views.py | 5 +- lms/djangoapps/ccx/tests/test_tasks.py | 3 +- lms/djangoapps/ccx/tests/test_utils.py | 2 + lms/djangoapps/certificates/tests/test_api.py | 2 + .../tests/test_cert_management.py | 1 + .../course_api/blocks/tests/test_api.py | 2 + lms/djangoapps/course_api/tests/test_api.py | 6 + .../course_api/tests/test_serializers.py | 2 + lms/djangoapps/course_api/tests/test_views.py | 1 + .../commands/tests/test_dump_course.py | 1 + .../courseware/tests/test_courses.py | 1 + .../courseware/tests/test_entrance_exam.py | 151 +++++++++--------- .../courseware/tests/test_microsites.py | 1 + lms/djangoapps/courseware/tests/test_views.py | 3 +- lms/djangoapps/courseware/testutils.py | 4 +- .../django_comment_client/base/tests.py | 1 + .../django_comment_client/tests/test_utils.py | 2 + .../grades/tests/integration/test_access.py | 2 + .../grades/tests/integration/test_events.py | 2 + lms/djangoapps/grades/tests/test_tasks.py | 2 + .../grades/tests/test_transformer.py | 2 + .../tests/views/test_instructor_dashboard.py | 2 +- lms/djangoapps/mobile_api/users/tests.py | 3 + lms/djangoapps/oauth2_handler/tests.py | 7 +- .../djangoapps/bookmarks/tests/test_api.py | 5 +- .../djangoapps/bookmarks/tests/test_models.py | 8 +- .../bookmarks/tests/test_services.py | 2 +- .../djangoapps/bookmarks/tests/test_tasks.py | 7 +- .../block_structure/tests/test_signals.py | 2 + .../content/course_overviews/tests.py | 2 + .../course_structures/api/v0/tests_api.py | 2 + .../course_groups/tests/test_cohorts.py | 4 +- .../credit/tests/test_verification_access.py | 2 + 44 files changed, 333 insertions(+), 124 deletions(-) diff --git a/cms/djangoapps/contentstore/tests/test_gating.py b/cms/djangoapps/contentstore/tests/test_gating.py index beacd7c240..6c46e092d0 100644 --- a/cms/djangoapps/contentstore/tests/test_gating.py +++ b/cms/djangoapps/contentstore/tests/test_gating.py @@ -13,6 +13,8 @@ class TestHandleItemDeleted(ModuleStoreTestCase, MilestonesTestCaseMixin): """ Test case for handle_score_changed django signal handler """ + ENABLED_SIGNALS = ['course_published'] + def setUp(self): """ Initial data setup diff --git a/cms/djangoapps/contentstore/tests/test_import.py b/cms/djangoapps/contentstore/tests/test_import.py index f845df6e61..e6c3e152f2 100644 --- a/cms/djangoapps/contentstore/tests/test_import.py +++ b/cms/djangoapps/contentstore/tests/test_import.py @@ -181,13 +181,13 @@ class ContentStoreImportTest(SignalDisconnectTestMixin, ModuleStoreTestCase): # we try to refresh the inheritance tree for each update_item in the import with check_exact_number_of_calls(store, 'refresh_cached_metadata_inheritance_tree', 28): - # _get_cached_metadata_inheritance_tree should be called twice (once for import, once on publish) - with check_exact_number_of_calls(store, '_get_cached_metadata_inheritance_tree', 2): + # _get_cached_metadata_inheritance_tree should be called once + with check_exact_number_of_calls(store, '_get_cached_metadata_inheritance_tree', 1): # with bulk-edit in progress, the inheritance tree should be recomputed only at the end of the import - # NOTE: On Jenkins, with memcache enabled, the number of calls here is only 1. - # Locally, without memcache, the number of calls is actually 2 (once more during the publish step) - with check_number_of_calls(store, '_compute_metadata_inheritance_tree', 2): + # NOTE: On Jenkins, with memcache enabled, the number of calls here is 1. + # Locally, without memcache, the number of calls is 1 (publish no longer counted) + with check_number_of_calls(store, '_compute_metadata_inheritance_tree', 1): self.load_test_import_course(create_if_not_present=False, module_store=store) @ddt.data(ModuleStoreEnum.Type.mongo, ModuleStoreEnum.Type.split) diff --git a/cms/djangoapps/contentstore/tests/test_orphan.py b/cms/djangoapps/contentstore/tests/test_orphan.py index 5d089bdd8f..f48d47aa50 100644 --- a/cms/djangoapps/contentstore/tests/test_orphan.py +++ b/cms/djangoapps/contentstore/tests/test_orphan.py @@ -100,8 +100,8 @@ class TestOrphan(TestOrphanBase): self.assertIn(unicode(location), orphans) @ddt.data( - (ModuleStoreEnum.Type.split, 9, 6), - (ModuleStoreEnum.Type.mongo, 34, 13), + (ModuleStoreEnum.Type.split, 9, 5), + (ModuleStoreEnum.Type.mongo, 34, 12), ) @ddt.unpack def test_delete_orphans(self, default_store, max_mongo_calls, min_mongo_calls): diff --git a/cms/djangoapps/contentstore/views/tests/test_course_index.py b/cms/djangoapps/contentstore/views/tests/test_course_index.py index 337335654a..c38daa6020 100644 --- a/cms/djangoapps/contentstore/views/tests/test_course_index.py +++ b/cms/djangoapps/contentstore/views/tests/test_course_index.py @@ -331,6 +331,8 @@ class TestCourseOutline(CourseTestCase): """ Unit tests for the course outline. """ + ENABLED_SIGNALS = ['course_published'] + def setUp(self): """ Set up the for the course outline tests. @@ -579,6 +581,8 @@ class TestCourseReIndex(CourseTestCase): """ SUCCESSFUL_RESPONSE = _("Course has been successfully reindexed.") + ENABLED_SIGNALS = ['course_published'] + def setUp(self): """ Set up the for the course outline tests. diff --git a/cms/djangoapps/contentstore/views/tests/test_gating.py b/cms/djangoapps/contentstore/views/tests/test_gating.py index 5ef6438ba2..88f5974c53 100644 --- a/cms/djangoapps/contentstore/views/tests/test_gating.py +++ b/cms/djangoapps/contentstore/views/tests/test_gating.py @@ -17,6 +17,7 @@ class TestSubsectionGating(CourseTestCase): Tests for the subsection gating feature """ MODULESTORE = TEST_DATA_SPLIT_MODULESTORE + ENABLED_SIGNALS = ['item_deleted'] def setUp(self): """ diff --git a/common/djangoapps/enrollment/tests/test_views.py b/common/djangoapps/enrollment/tests/test_views.py index 88bc30c398..44ea7dcc8f 100644 --- a/common/djangoapps/enrollment/tests/test_views.py +++ b/common/djangoapps/enrollment/tests/test_views.py @@ -142,6 +142,7 @@ class EnrollmentTest(EnrollmentTestMixin, ModuleStoreTestCase, APITestCase): OTHER_EMAIL = "jane@example.com" ENABLED_CACHES = ['default', 'mongo_metadata_inheritance', 'loc_cache'] + ENABLED_SIGNALS = ['course_published'] def setUp(self): """ Create a course and user, then log in. """ diff --git a/common/djangoapps/student/tests/test_course_listing.py b/common/djangoapps/student/tests/test_course_listing.py index 1a06f7e039..b9c8782ce2 100644 --- a/common/djangoapps/student/tests/test_course_listing.py +++ b/common/djangoapps/student/tests/test_course_listing.py @@ -29,6 +29,8 @@ class TestCourseListing(ModuleStoreTestCase, MilestonesTestCaseMixin): """ Unit tests for getting the list of courses for a logged in user """ + ENABLED_SIGNALS = ['course_deleted'] + def setUp(self): """ Add a student & teacher diff --git a/common/djangoapps/student/tests/tests.py b/common/djangoapps/student/tests/tests.py index 90da0735b9..b2ff11fcd8 100644 --- a/common/djangoapps/student/tests/tests.py +++ b/common/djangoapps/student/tests/tests.py @@ -229,6 +229,7 @@ class DashboardTest(ModuleStoreTestCase): """ Tests for dashboard utility functions """ + ENABLED_SIGNALS = ['course_published'] def setUp(self): super(DashboardTest, self).setUp() @@ -434,10 +435,11 @@ class DashboardTest(ModuleStoreTestCase): # If user has a certificate with valid linked-in config then Add Certificate to LinkedIn button # should be visible. and it has URL value with valid parameters. self.client.login(username="jack", password="test") - LinkedInAddToProfileConfiguration( + + LinkedInAddToProfileConfiguration.objects.create( company_identifier='0_mC_o2MizqdtZEmkVXjH4eYwMj4DnkCWrZP_D9', enabled=True - ).save() + ) CourseModeFactory.create( course_id=self.course.id, @@ -474,6 +476,7 @@ class DashboardTest(ModuleStoreTestCase): u'pfCertificationUrl=www.edx.org&' u'source=o' ).format(platform=quote(settings.PLATFORM_NAME.encode('utf-8'))) + self.assertContains(response, escape(expected_url)) @unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms') diff --git a/common/lib/xmodule/xmodule/modulestore/django.py b/common/lib/xmodule/xmodule/modulestore/django.py index e6179f133a..094890c194 100644 --- a/common/lib/xmodule/xmodule/modulestore/django.py +++ b/common/lib/xmodule/xmodule/modulestore/django.py @@ -57,6 +57,86 @@ log = logging.getLogger(__name__) ASSET_IGNORE_REGEX = getattr(settings, "ASSET_IGNORE_REGEX", r"(^\._.*$)|(^\.DS_Store$)|(^.*~$)") +class SwitchedSignal(django.dispatch.Signal): + """ + SwitchedSignal is like a normal Django signal, except that you can turn it + on and off. This is especially useful for tests where we want to be able to + isolate signals and disable expensive operations that are irrelevant to + what's being tested (like everything that triggers off of a course publish). + + SwitchedSignals default to being on. You should be very careful if you ever + turn one off -- the only instances of this class are shared class attributes + of `SignalHandler`. You have to make sure that you re-enable the signal when + you're done, or else you may permanently turn that signal off for that + process. I can't think of any reason you'd want to disable signals outside + of running tests. + """ + def __init__(self, name, *args, **kwargs): + """ + The `name` parameter exists only to make debugging more convenient. + + All other args are passed to the constructor for django.dispatch.Signal. + """ + super(SwitchedSignal, self).__init__(*args, **kwargs) + self.name = name + self._allow_signals = True + + def disable(self): + """ + Turn off signal sending. + + All calls to send/send_robust will no-op. + """ + self._allow_signals = False + + def enable(self): + """ + Turn on signal sending. + + Calls to send/send_robust will behave like normal Django Signals. + """ + self._allow_signals = True + + def send(self, *args, **kwargs): + """ + See `django.dispatch.Signal.send()` + + This method will no-op and return an empty list if the signal has been + disabled. + """ + log.debug( + "SwitchedSignal %s's send() called with args %s, kwargs %s - %s", + self.name, + args, + kwargs, + "ALLOW" if self._allow_signals else "BLOCK" + ) + if self._allow_signals: + return super(SwitchedSignal, self).send(*args, **kwargs) + return [] + + def send_robust(self, *args, **kwargs): + """ + See `django.dispatch.Signal.send_robust()` + + This method will no-op and return an empty list if the signal has been + disabled. + """ + log.debug( + "SwitchedSignal %s's send_robust() called with args %s, kwargs %s - %s", + self.name, + args, + kwargs, + "ALLOW" if self._allow_signals else "BLOCK" + ) + if self._allow_signals: + return super(SwitchedSignal, self).send_robust(*args, **kwargs) + return [] + + def __repr__(self): + return u"SwitchedSignal('{}')".format(self.name) + + class SignalHandler(object): """ This class is to allow the modulestores to emit signals that can be caught @@ -88,23 +168,34 @@ class SignalHandler(object): almost no work. Its main job is to kick off the celery task that will do the actual work. """ - pre_publish = django.dispatch.Signal(providing_args=["course_key"]) - course_published = django.dispatch.Signal(providing_args=["course_key"]) - course_deleted = django.dispatch.Signal(providing_args=["course_key"]) - library_updated = django.dispatch.Signal(providing_args=["library_key"]) - item_deleted = django.dispatch.Signal(providing_args=["usage_key", "user_id"]) + + # If you add a new signal, please don't forget to add it to the _mapping + # as well. + pre_publish = SwitchedSignal("pre_publish", providing_args=["course_key"]) + course_published = SwitchedSignal("course_published", providing_args=["course_key"]) + course_deleted = SwitchedSignal("course_deleted", providing_args=["course_key"]) + library_updated = SwitchedSignal("library_updated", providing_args=["library_key"]) + item_deleted = SwitchedSignal("item_deleted", providing_args=["usage_key", "user_id"]) _mapping = { - "pre_publish": pre_publish, - "course_published": course_published, - "course_deleted": course_deleted, - "library_updated": library_updated, - "item_deleted": item_deleted, + signal.name: signal + for signal + in [pre_publish, course_published, course_deleted, library_updated, item_deleted] } def __init__(self, modulestore_class): self.modulestore_class = modulestore_class + @classmethod + def all_signals(cls): + """Return a list with all our signals in it.""" + return cls._mapping.values() + + @classmethod + def signal_by_name(cls, signal_name): + """Given a signal name, return the appropriate signal.""" + return cls._mapping[signal_name] + def send(self, signal_name, **kwargs): """ Send the signal to the receivers. diff --git a/common/lib/xmodule/xmodule/modulestore/tests/django_utils.py b/common/lib/xmodule/xmodule/modulestore/tests/django_utils.py index c80317d4d7..870e342c6d 100644 --- a/common/lib/xmodule/xmodule/modulestore/tests/django_utils.py +++ b/common/lib/xmodule/xmodule/modulestore/tests/django_utils.py @@ -194,7 +194,46 @@ TEST_DATA_SPLIT_MODULESTORE = functools.partial( ) -class ModuleStoreIsolationMixin(CacheIsolationMixin): +class SignalIsolationMixin(object): + """ + Simple utility mixin class to toggle ModuleStore signals on and off. This + class operates on `SwitchedSignal` objects on the modulestore's + `SignalHandler`. + """ + @classmethod + def disable_all_signals(cls): + """Disable all signals in the modulestore's SignalHandler.""" + for signal in SignalHandler.all_signals(): + signal.disable() + + @classmethod + def enable_all_signals(cls): + """Enable all signals in the modulestore's SignalHandler.""" + for signal in SignalHandler.all_signals(): + signal.enable() + + @classmethod + def enable_signals_by_name(cls, *signal_names): + """ + Enable specific signals in the modulestore's SignalHandler. + + Arguments: + signal_names (list of `str`): Names of signals to enable. + """ + for signal_name in signal_names: + try: + signal = SignalHandler.signal_by_name(signal_name) + except KeyError: + all_signal_names = sorted(s.name for s in SignalHandler.all_signals()) + err_msg = ( + "You tried to enable signal '{}', but I don't recognize that " + "signal name. Did you mean one of these?: {}" + ) + raise ValueError(err_msg.format(signal_name, all_signal_names)) + signal.enable() + + +class ModuleStoreIsolationMixin(CacheIsolationMixin, SignalIsolationMixin): """ A mixin to be used by TestCases that want to isolate their use of the Modulestore. @@ -205,6 +244,8 @@ class ModuleStoreIsolationMixin(CacheIsolationMixin): MODULESTORE = + ENABLED_SIGNALS = ['course_published'] + def my_test(self): self.start_modulestore_isolation() self.addCleanup(self.end_modulestore_isolation) @@ -213,10 +254,21 @@ class ModuleStoreIsolationMixin(CacheIsolationMixin): ... """ - MODULESTORE = functools.partial(mixed_store_config, mkdtemp_clean(), {}) CONTENTSTORE = functools.partial(contentstore_config) ENABLED_CACHES = ['default', 'mongo_metadata_inheritance', 'loc_cache'] + + # List of modulestore signals enabled for this test. Defaults to an empty + # list. The list of signals available is found on the SignalHandler class, + # in /common/lib/xmodule/xmodule/modulestore/django.py + # + # You must use the signal itself, and not its name. So for example: + # + # class MyPublishTestCase(ModuleStoreTestCase): + # ENABLED_SIGNALS = ['course_published', 'pre_publish'] + # + ENABLED_SIGNALS = [] + __settings_overrides = [] __old_modulestores = [] __old_contentstores = [] @@ -228,6 +280,8 @@ class ModuleStoreIsolationMixin(CacheIsolationMixin): :py:meth:`end_modulestore_isolation` is called, this modulestore will be flushed (all content will be deleted). """ + cls.disable_all_signals() + cls.enable_signals_by_name(*cls.ENABLED_SIGNALS) cls.start_cache_isolation() override = override_settings( MODULESTORE=cls.MODULESTORE(), @@ -256,6 +310,7 @@ class ModuleStoreIsolationMixin(CacheIsolationMixin): assert settings.MODULESTORE == cls.__old_modulestores.pop() assert settings.CONTENTSTORE == cls.__old_contentstores.pop() cls.end_cache_isolation() + cls.enable_all_signals() class SharedModuleStoreTestCase(ModuleStoreIsolationMixin, CacheIsolationTestCase): @@ -384,6 +439,14 @@ class ModuleStoreTestCase(ModuleStoreIsolationMixin, TestCase): # Tell Django to clean out all databases, not just default multi_db = True + @classmethod + def setUpClass(cls): + super(ModuleStoreTestCase, cls).setUpClass() + + @classmethod + def tearDownClass(cls): + super(ModuleStoreTestCase, cls).tearDownClass() + def setUp(self): """ Creates a test User if `self.CREATE_USER` is True. @@ -400,8 +463,6 @@ class ModuleStoreTestCase(ModuleStoreIsolationMixin, TestCase): super(ModuleStoreTestCase, self).setUp() - SignalHandler.course_published.disconnect(trigger_update_xblocks_cache_task) - self.store = modulestore() uname = 'testuser' diff --git a/lms/djangoapps/branding/tests/test_page.py b/lms/djangoapps/branding/tests/test_page.py index 89b109c50a..9db971d3b8 100644 --- a/lms/djangoapps/branding/tests/test_page.py +++ b/lms/djangoapps/branding/tests/test_page.py @@ -117,6 +117,8 @@ class PreRequisiteCourseCatalog(ModuleStoreTestCase, LoginEnrollmentTestCase, Mi Test to simulate and verify fix for disappearing courses in course catalog when using pre-requisite courses """ + ENABLED_SIGNALS = ['course_published'] + @patch.dict(settings.FEATURES, {'ENABLE_PREREQUISITE_COURSES': True}) def test_course_with_prereq(self): """ @@ -160,6 +162,8 @@ class IndexPageCourseCardsSortingTests(ModuleStoreTestCase): """ Test for Index page course cards sorting """ + ENABLED_SIGNALS = ['course_published'] + def setUp(self): super(IndexPageCourseCardsSortingTests, self).setUp() self.starting_later = CourseFactory.create( diff --git a/lms/djangoapps/ccx/api/v0/tests/test_views.py b/lms/djangoapps/ccx/api/v0/tests/test_views.py index 47f3da1d31..3060d8eb15 100644 --- a/lms/djangoapps/ccx/api/v0/tests/test_views.py +++ b/lms/djangoapps/ccx/api/v0/tests/test_views.py @@ -52,7 +52,6 @@ from student.roles import ( ) from student.tests.factories import AdminFactory - USER_PASSWORD = 'test' AUTH_ATTRS = ('auth', 'auth_header_oauth2_provider') @@ -183,6 +182,8 @@ class CcxListTest(CcxRestApiTest): """ Test for the CCX REST APIs """ + ENABLED_SIGNALS = ['course_published'] + @classmethod def setUpClass(cls): super(CcxListTest, cls).setUpClass() @@ -892,6 +893,8 @@ class CcxDetailTest(CcxRestApiTest): """ Test for the CCX REST APIs """ + ENABLED_SIGNALS = ['course_published'] + def setUp(self): """ Set up tests diff --git a/lms/djangoapps/ccx/tests/test_tasks.py b/lms/djangoapps/ccx/tests/test_tasks.py index b3a45387c0..95e3030873 100644 --- a/lms/djangoapps/ccx/tests/test_tasks.py +++ b/lms/djangoapps/ccx/tests/test_tasks.py @@ -25,9 +25,10 @@ from lms.djangoapps.ccx.tasks import send_ccx_course_published class TestSendCCXCoursePublished(ModuleStoreTestCase): """unit tests for the send ccx course published task """ - MODULESTORE = TEST_DATA_SPLIT_MODULESTORE + ENABLED_SIGNALS = ['course_published'] + def setUp(self): """ Set up tests diff --git a/lms/djangoapps/ccx/tests/test_utils.py b/lms/djangoapps/ccx/tests/test_utils.py index 50af4df918..21ca542fea 100644 --- a/lms/djangoapps/ccx/tests/test_utils.py +++ b/lms/djangoapps/ccx/tests/test_utils.py @@ -75,6 +75,8 @@ class TestGetCourseChapters(CcxTestCase): """ Tests for the `get_course_chapters` util function """ + ENABLED_SIGNALS = ['course_published'] + def setUp(self): """ Set up tests diff --git a/lms/djangoapps/certificates/tests/test_api.py b/lms/djangoapps/certificates/tests/test_api.py index 81843cdd82..c1bfb50ac5 100644 --- a/lms/djangoapps/certificates/tests/test_api.py +++ b/lms/djangoapps/certificates/tests/test_api.py @@ -88,6 +88,7 @@ class WebCertificateTestMixin(object): @attr(shard=1) class CertificateDownloadableStatusTests(WebCertificateTestMixin, ModuleStoreTestCase): """Tests for the `certificate_downloadable_status` helper function. """ + ENABLED_SIGNALS = ['course_published'] def setUp(self): super(CertificateDownloadableStatusTests, self).setUp() @@ -457,6 +458,7 @@ class GenerateUserCertificatesTest(EventTestMixin, WebCertificateTestMixin, Modu """Tests for generating certificates for students. """ ERROR_REASON = "Kaboom!" + ENABLED_SIGNALS = ['course_published'] def setUp(self): # pylint: disable=arguments-differ super(GenerateUserCertificatesTest, self).setUp('certificates.api.tracker') diff --git a/lms/djangoapps/certificates/tests/test_cert_management.py b/lms/djangoapps/certificates/tests/test_cert_management.py index 1a119d75bd..3a8849d1ab 100644 --- a/lms/djangoapps/certificates/tests/test_cert_management.py +++ b/lms/djangoapps/certificates/tests/test_cert_management.py @@ -69,6 +69,7 @@ class CertificateManagementTest(ModuleStoreTestCase): @ddt.ddt class ResubmitErrorCertificatesTest(CertificateManagementTest): """Tests for the resubmit_error_certificates management command. """ + ENABLED_SIGNALS = ['course_published'] @ddt.data(CourseMode.HONOR, CourseMode.VERIFIED) def test_resubmit_error_certificate(self, mode): diff --git a/lms/djangoapps/course_api/blocks/tests/test_api.py b/lms/djangoapps/course_api/blocks/tests/test_api.py index 6273edbf9b..c36531e59b 100644 --- a/lms/djangoapps/course_api/blocks/tests/test_api.py +++ b/lms/djangoapps/course_api/blocks/tests/test_api.py @@ -105,6 +105,8 @@ class TestGetBlocksQueryCounts(SharedModuleStoreTestCase): """ Tests query counts for the get_blocks function. """ + ENABLED_SIGNALS = ['course_published'] + def setUp(self): super(TestGetBlocksQueryCounts, self).setUp() diff --git a/lms/djangoapps/course_api/tests/test_api.py b/lms/djangoapps/course_api/tests/test_api.py index d8e375f291..b8b7afd559 100644 --- a/lms/djangoapps/course_api/tests/test_api.py +++ b/lms/djangoapps/course_api/tests/test_api.py @@ -38,6 +38,8 @@ class CourseDetailTestMixin(CourseApiTestMixin): """ Common functionality for course_detail tests """ + ENABLED_SIGNALS = ['course_published'] + def _make_api_call(self, requesting_user, target_user, course_key): """ Call the `course_detail` api endpoint to get information on the course @@ -109,6 +111,7 @@ class TestGetCourseList(CourseListTestMixin, SharedModuleStoreTestCase): """ Test the behavior of the `list_courses` api function. """ + ENABLED_SIGNALS = ['course_published'] @classmethod def setUpClass(cls): @@ -150,6 +153,7 @@ class TestGetCourseListMultipleCourses(CourseListTestMixin, ModuleStoreTestCase) Test the behavior of the `list_courses` api function (with tests that modify the courseware). """ + ENABLED_SIGNALS = ['course_published'] def setUp(self): super(TestGetCourseListMultipleCourses, self).setUp() @@ -207,6 +211,8 @@ class TestGetCourseListExtras(CourseListTestMixin, ModuleStoreTestCase): Tests of course_list api function that require alternative configurations of created courses. """ + ENABLED_SIGNALS = ['course_published'] + @classmethod def setUpClass(cls): super(TestGetCourseListExtras, cls).setUpClass() diff --git a/lms/djangoapps/course_api/tests/test_serializers.py b/lms/djangoapps/course_api/tests/test_serializers.py index 928a81e5cb..b1d1e76bb6 100644 --- a/lms/djangoapps/course_api/tests/test_serializers.py +++ b/lms/djangoapps/course_api/tests/test_serializers.py @@ -31,6 +31,8 @@ class TestCourseSerializer(CourseApiFactoryMixin, ModuleStoreTestCase): maxDiff = 5000 # long enough to show mismatched dicts, in case of error serializer_class = CourseSerializer + ENABLED_SIGNALS = ['course_published'] + def setUp(self): super(TestCourseSerializer, self).setUp() self.staff_user = self.create_user('staff', is_staff=True) diff --git a/lms/djangoapps/course_api/tests/test_views.py b/lms/djangoapps/course_api/tests/test_views.py index bb88688349..6a357194ee 100644 --- a/lms/djangoapps/course_api/tests/test_views.py +++ b/lms/djangoapps/course_api/tests/test_views.py @@ -101,6 +101,7 @@ class CourseListViewTestCaseMultipleCourses(CourseApiTestViewMixin, ModuleStoreT Test responses returned from CourseListView (with tests that modify the courseware). """ + ENABLED_SIGNALS = ['course_published'] def setUp(self): super(CourseListViewTestCaseMultipleCourses, self).setUp() diff --git a/lms/djangoapps/courseware/management/commands/tests/test_dump_course.py b/lms/djangoapps/courseware/management/commands/tests/test_dump_course.py index 40af376df2..aee2febcec 100644 --- a/lms/djangoapps/courseware/management/commands/tests/test_dump_course.py +++ b/lms/djangoapps/courseware/management/commands/tests/test_dump_course.py @@ -38,6 +38,7 @@ class CommandsTestBase(SharedModuleStoreTestCase): """ __test__ = False url_name = '2012_Fall' + ENABLED_SIGNALS = ['course_published'] @classmethod def setUpClass(cls): diff --git a/lms/djangoapps/courseware/tests/test_courses.py b/lms/djangoapps/courseware/tests/test_courses.py index 94c1a96c1d..db961ed503 100644 --- a/lms/djangoapps/courseware/tests/test_courses.py +++ b/lms/djangoapps/courseware/tests/test_courses.py @@ -47,6 +47,7 @@ TEST_DATA_DIR = settings.COMMON_TEST_DATA_ROOT @ddt.ddt class CoursesTest(ModuleStoreTestCase): """Test methods related to fetching courses.""" + ENABLED_SIGNALS = ['course_published'] @override_settings(CMS_BASE=CMS_BASE_TEST) def test_get_cms_course_block_link(self): diff --git a/lms/djangoapps/courseware/tests/test_entrance_exam.py b/lms/djangoapps/courseware/tests/test_entrance_exam.py index b59c225b1d..8282c37e2f 100644 --- a/lms/djangoapps/courseware/tests/test_entrance_exam.py +++ b/lms/djangoapps/courseware/tests/test_entrance_exam.py @@ -58,81 +58,82 @@ class EntranceExamTestCases(LoginEnrollmentTestCase, ModuleStoreTestCase, Milest 'entrance_exam_enabled': True, } ) - self.chapter = ItemFactory.create( - parent=self.course, - display_name='Overview' - ) - self.welcome = ItemFactory.create( - parent=self.chapter, - display_name='Welcome' - ) - ItemFactory.create( - parent=self.course, - category='chapter', - display_name="Week 1" - ) - self.chapter_subsection = ItemFactory.create( - parent=self.chapter, - category='sequential', - display_name="Lesson 1" - ) - chapter_vertical = ItemFactory.create( - parent=self.chapter_subsection, - category='vertical', - display_name='Lesson 1 Vertical - Unit 1' - ) - ItemFactory.create( - parent=chapter_vertical, - category="problem", - display_name="Problem - Unit 1 Problem 1" - ) - ItemFactory.create( - parent=chapter_vertical, - category="problem", - display_name="Problem - Unit 1 Problem 2" - ) + with self.store.bulk_operations(self.course.id): + self.chapter = ItemFactory.create( + parent=self.course, + display_name='Overview' + ) + self.welcome = ItemFactory.create( + parent=self.chapter, + display_name='Welcome' + ) + ItemFactory.create( + parent=self.course, + category='chapter', + display_name="Week 1" + ) + self.chapter_subsection = ItemFactory.create( + parent=self.chapter, + category='sequential', + display_name="Lesson 1" + ) + chapter_vertical = ItemFactory.create( + parent=self.chapter_subsection, + category='vertical', + display_name='Lesson 1 Vertical - Unit 1' + ) + ItemFactory.create( + parent=chapter_vertical, + category="problem", + display_name="Problem - Unit 1 Problem 1" + ) + ItemFactory.create( + parent=chapter_vertical, + category="problem", + display_name="Problem - Unit 1 Problem 2" + ) - ItemFactory.create( - category="instructor", - parent=self.course, - data="Instructor Tab", - display_name="Instructor" - ) - self.entrance_exam = ItemFactory.create( - parent=self.course, - category="chapter", - display_name="Entrance Exam Section - Chapter 1", - is_entrance_exam=True, - in_entrance_exam=True - ) - self.exam_1 = ItemFactory.create( - parent=self.entrance_exam, - category='sequential', - display_name="Exam Sequential - Subsection 1", - graded=True, - in_entrance_exam=True - ) - subsection = ItemFactory.create( - parent=self.exam_1, - category='vertical', - display_name='Exam Vertical - Unit 1' - ) - problem_xml = MultipleChoiceResponseXMLFactory().build_xml( - question_text='The correct answer is Choice 3', - choices=[False, False, True, False], - choice_names=['choice_0', 'choice_1', 'choice_2', 'choice_3'] - ) - self.problem_1 = ItemFactory.create( - parent=subsection, - category="problem", - display_name="Exam Problem - Problem 1", - data=problem_xml - ) - self.problem_2 = ItemFactory.create( - parent=subsection, - category="problem", - display_name="Exam Problem - Problem 2" - ) + ItemFactory.create( + category="instructor", + parent=self.course, + data="Instructor Tab", + display_name="Instructor" + ) + self.entrance_exam = ItemFactory.create( + parent=self.course, + category="chapter", + display_name="Entrance Exam Section - Chapter 1", + is_entrance_exam=True, + in_entrance_exam=True + ) + self.exam_1 = ItemFactory.create( + parent=self.entrance_exam, + category='sequential', + display_name="Exam Sequential - Subsection 1", + graded=True, + in_entrance_exam=True + ) + subsection = ItemFactory.create( + parent=self.exam_1, + category='vertical', + display_name='Exam Vertical - Unit 1' + ) + problem_xml = MultipleChoiceResponseXMLFactory().build_xml( + question_text='The correct answer is Choice 3', + choices=[False, False, True, False], + choice_names=['choice_0', 'choice_1', 'choice_2', 'choice_3'] + ) + self.problem_1 = ItemFactory.create( + parent=subsection, + category="problem", + display_name="Exam Problem - Problem 1", + data=problem_xml + ) + self.problem_2 = ItemFactory.create( + parent=subsection, + category="problem", + display_name="Exam Problem - Problem 2" + ) add_entrance_exam_milestone(self.course, self.entrance_exam) @@ -295,7 +296,7 @@ class EntranceExamTestCases(LoginEnrollmentTestCase, ModuleStoreTestCase, Milest """ # One query is for getting the list of disabled XBlocks (which is # then stored in the request). - with self.assertNumQueries(2): + with self.assertNumQueries(1): exam_score = get_entrance_exam_score(self.request, self.course) self.assertEqual(exam_score, 0) diff --git a/lms/djangoapps/courseware/tests/test_microsites.py b/lms/djangoapps/courseware/tests/test_microsites.py index 10c353841f..3c49d5da88 100644 --- a/lms/djangoapps/courseware/tests/test_microsites.py +++ b/lms/djangoapps/courseware/tests/test_microsites.py @@ -21,6 +21,7 @@ class TestSites(SharedModuleStoreTestCase, LoginEnrollmentTestCase): """ STUDENT_INFO = [('view@test.com', 'foo'), ('view2@test.com', 'foo')] + ENABLED_SIGNALS = ['course_published'] @classmethod def setUpClass(cls): diff --git a/lms/djangoapps/courseware/tests/test_views.py b/lms/djangoapps/courseware/tests/test_views.py index f491be579d..e2d0d60804 100644 --- a/lms/djangoapps/courseware/tests/test_views.py +++ b/lms/djangoapps/courseware/tests/test_views.py @@ -1138,6 +1138,7 @@ class ProgressPageTests(ModuleStoreTestCase): """ ENABLED_CACHES = ['default', 'mongo_modulestore_inheritance', 'loc_cache'] + ENABLED_SIGNALS = ['course_published'] def setUp(self): super(ProgressPageTests, self).setUp() @@ -2032,7 +2033,6 @@ class TestRenderXBlock(RenderXBlockTestMixin, ModuleStoreTestCase): This class overrides the get_response method, which is used by the tests defined in RenderXBlockTestMixin. """ - def setUp(self): reload_django_url_config() super(TestRenderXBlock, self).setUp() @@ -2060,7 +2060,6 @@ class TestRenderXBlockSelfPaced(TestRenderXBlock): Test rendering XBlocks for a self-paced course. Relies on the query count assertions in the tests defined by RenderXBlockMixin. """ - def setUp(self): super(TestRenderXBlockSelfPaced, self).setUp() SelfPacedConfiguration(enabled=True).save() diff --git a/lms/djangoapps/courseware/testutils.py b/lms/djangoapps/courseware/testutils.py index 43fde1eadf..1ef13ad45e 100644 --- a/lms/djangoapps/courseware/testutils.py +++ b/lms/djangoapps/courseware/testutils.py @@ -145,9 +145,9 @@ class RenderXBlockTestMixin(object): return response @ddt.data( - ('vertical_block', ModuleStoreEnum.Type.mongo, 11), + ('vertical_block', ModuleStoreEnum.Type.mongo, 10), ('vertical_block', ModuleStoreEnum.Type.split, 6), - ('html_block', ModuleStoreEnum.Type.mongo, 12), + ('html_block', ModuleStoreEnum.Type.mongo, 11), ('html_block', ModuleStoreEnum.Type.split, 6), ) @ddt.unpack diff --git a/lms/djangoapps/django_comment_client/base/tests.py b/lms/djangoapps/django_comment_client/base/tests.py index 236c2e3b8d..720b249753 100644 --- a/lms/djangoapps/django_comment_client/base/tests.py +++ b/lms/djangoapps/django_comment_client/base/tests.py @@ -356,6 +356,7 @@ class ViewsQueryCountTestCase( CREATE_USER = False ENABLED_CACHES = ['default', 'mongo_metadata_inheritance', 'loc_cache'] + ENABLED_SIGNALS = ['course_published'] @patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True}) def setUp(self): diff --git a/lms/djangoapps/django_comment_client/tests/test_utils.py b/lms/djangoapps/django_comment_client/tests/test_utils.py index ad23b133b5..f0abdca7c5 100644 --- a/lms/djangoapps/django_comment_client/tests/test_utils.py +++ b/lms/djangoapps/django_comment_client/tests/test_utils.py @@ -210,6 +210,8 @@ class CachedDiscussionIdMapTestCase(ModuleStoreTestCase): """ Tests that using the cache of discussion id mappings has the same behavior as searching through the course. """ + ENABLED_SIGNALS = ['course_published'] + def setUp(self): super(CachedDiscussionIdMapTestCase, self).setUp() diff --git a/lms/djangoapps/grades/tests/integration/test_access.py b/lms/djangoapps/grades/tests/integration/test_access.py index cd7c8e2cbb..d73d94fd42 100644 --- a/lms/djangoapps/grades/tests/integration/test_access.py +++ b/lms/djangoapps/grades/tests/integration/test_access.py @@ -22,6 +22,8 @@ class GradesAccessIntegrationTest(ProblemSubmissionTestMixin, SharedModuleStoreT """ Tests integration between grading and block access. """ + ENABLED_SIGNALS = ['course_published'] + @classmethod def setUpClass(cls): super(GradesAccessIntegrationTest, cls).setUpClass() diff --git a/lms/djangoapps/grades/tests/integration/test_events.py b/lms/djangoapps/grades/tests/integration/test_events.py index b6ce701130..11c4f36a08 100644 --- a/lms/djangoapps/grades/tests/integration/test_events.py +++ b/lms/djangoapps/grades/tests/integration/test_events.py @@ -26,6 +26,8 @@ class GradesEventIntegrationTest(ProblemSubmissionTestMixin, SharedModuleStoreTe Tests integration between the eventing in various layers of the grading infrastructure. """ + ENABLED_SIGNALS = ['course_published'] + @classmethod def reset_course(cls): """ diff --git a/lms/djangoapps/grades/tests/test_tasks.py b/lms/djangoapps/grades/tests/test_tasks.py index 6a3d6a69b0..6d6dc57a6b 100644 --- a/lms/djangoapps/grades/tests/test_tasks.py +++ b/lms/djangoapps/grades/tests/test_tasks.py @@ -37,6 +37,8 @@ class RecalculateSubsectionGradeTest(ModuleStoreTestCase): """ Ensures that the recalculate subsection grade task functions as expected when run. """ + ENABLED_SIGNALS = ['course_published', 'pre_publish'] + def setUp(self): super(RecalculateSubsectionGradeTest, self).setUp() self.user = UserFactory() diff --git a/lms/djangoapps/grades/tests/test_transformer.py b/lms/djangoapps/grades/tests/test_transformer.py index 076fc58987..2863a64f13 100644 --- a/lms/djangoapps/grades/tests/test_transformer.py +++ b/lms/djangoapps/grades/tests/test_transformer.py @@ -29,6 +29,8 @@ class GradesTransformerTestCase(CourseStructureTestCase): TRANSFORMER_CLASS_TO_TEST = GradesTransformer + ENABLED_SIGNALS = ['course_published'] + problem_metadata = { u'graded': True, u'weight': 1, diff --git a/lms/djangoapps/instructor/tests/views/test_instructor_dashboard.py b/lms/djangoapps/instructor/tests/views/test_instructor_dashboard.py index 7fdf99fe9b..361fa7eaf4 100644 --- a/lms/djangoapps/instructor/tests/views/test_instructor_dashboard.py +++ b/lms/djangoapps/instructor/tests/views/test_instructor_dashboard.py @@ -412,6 +412,6 @@ class TestInstructorDashboardPerformance(ModuleStoreTestCase, LoginEnrollmentTes # check MongoDB calls count url = reverse('spoc_gradebook', kwargs={'course_id': self.course.id}) - with check_mongo_calls(7): + with check_mongo_calls(9): response = self.client.get(url) self.assertEqual(response.status_code, 200) diff --git a/lms/djangoapps/mobile_api/users/tests.py b/lms/djangoapps/mobile_api/users/tests.py index b283b5c484..ad58a7839f 100644 --- a/lms/djangoapps/mobile_api/users/tests.py +++ b/lms/djangoapps/mobile_api/users/tests.py @@ -87,6 +87,7 @@ class TestUserEnrollmentApi(UrlResetMixin, MobileAPITestCase, MobileAuthUserTest NEXT_WEEK = datetime.datetime.now(pytz.UTC) + datetime.timedelta(days=7) LAST_WEEK = datetime.datetime.now(pytz.UTC) - datetime.timedelta(days=7) ADVERTISED_START = "Spring 2016" + ENABLED_SIGNALS = ['course_published'] @patch.dict(settings.FEATURES, {"ENABLE_DISCUSSION_SERVICE": True}) def setUp(self, *args, **kwargs): @@ -468,6 +469,8 @@ class TestCourseEnrollmentSerializer(MobileAPITestCase, MilestonesTestCaseMixin) """ Test the course enrollment serializer """ + ENABLED_SIGNALS = ['course_published'] + def setUp(self): super(TestCourseEnrollmentSerializer, self).setUp() self.login_and_enroll() diff --git a/lms/djangoapps/oauth2_handler/tests.py b/lms/djangoapps/oauth2_handler/tests.py index 96a7b3d4c8..878dadcb6a 100644 --- a/lms/djangoapps/oauth2_handler/tests.py +++ b/lms/djangoapps/oauth2_handler/tests.py @@ -2,15 +2,15 @@ from django.core.cache import cache from django.test.utils import override_settings -from xmodule.modulestore.tests.factories import (check_mongo_calls, CourseFactory) +from openedx.core.djangoapps.lang_pref import LANGUAGE_KEY +from openedx.core.djangoapps.user_api.preferences.api import set_user_preference from student.models import anonymous_id_for_user from student.models import UserProfile from student.roles import (CourseInstructorRole, CourseStaffRole, GlobalStaff, OrgInstructorRole, OrgStaffRole) from student.tests.factories import UserFactory, UserProfileFactory -from openedx.core.djangoapps.lang_pref import LANGUAGE_KEY -from openedx.core.djangoapps.user_api.preferences.api import set_user_preference from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase +from xmodule.modulestore.tests.factories import (check_mongo_calls, CourseFactory) # Will also run default tests for IDTokens and UserInfo @@ -19,6 +19,7 @@ from edx_oauth2_provider.tests import IDTokenTestCase, UserInfoTestCase class BaseTestMixin(ModuleStoreTestCase): profile = None + ENABLED_SIGNALS = ['course_published'] def setUp(self): super(BaseTestMixin, self).setUp() diff --git a/openedx/core/djangoapps/bookmarks/tests/test_api.py b/openedx/core/djangoapps/bookmarks/tests/test_api.py index bb8705306b..0fb68f91c6 100644 --- a/openedx/core/djangoapps/bookmarks/tests/test_api.py +++ b/openedx/core/djangoapps/bookmarks/tests/test_api.py @@ -43,7 +43,6 @@ class BookmarksAPITests(BookmarkApiEventTestMixin, BookmarksTestsBase): """ These tests cover the parts of the API methods. """ - def setUp(self): super(BookmarksAPITests, self).setUp() @@ -123,7 +122,7 @@ class BookmarksAPITests(BookmarkApiEventTestMixin, BookmarksTestsBase): """ self.assertEqual(len(api.get_bookmarks(user=self.user, course_key=self.course.id)), 2) - with self.assertNumQueries(10): + with self.assertNumQueries(9): bookmark_data = api.create_bookmark(user=self.user, usage_key=self.vertical_2.location) self.assert_bookmark_event_emitted( @@ -144,7 +143,7 @@ class BookmarksAPITests(BookmarkApiEventTestMixin, BookmarksTestsBase): """ self.assertEqual(len(api.get_bookmarks(user=self.user, course_key=self.course.id)), 2) - with self.assertNumQueries(10): + with self.assertNumQueries(9): bookmark_data = api.create_bookmark(user=self.user, usage_key=self.vertical_2.location) self.assert_bookmark_event_emitted( diff --git a/openedx/core/djangoapps/bookmarks/tests/test_models.py b/openedx/core/djangoapps/bookmarks/tests/test_models.py index cdc9ab3d26..163419b639 100644 --- a/openedx/core/djangoapps/bookmarks/tests/test_models.py +++ b/openedx/core/djangoapps/bookmarks/tests/test_models.py @@ -253,10 +253,10 @@ class BookmarkModelTests(BookmarksTestsBase): @ddt.data( (ModuleStoreEnum.Type.mongo, 'course', [], 3), - (ModuleStoreEnum.Type.mongo, 'chapter_1', [], 4), - (ModuleStoreEnum.Type.mongo, 'sequential_1', ['chapter_1'], 6), - (ModuleStoreEnum.Type.mongo, 'vertical_1', ['chapter_1', 'sequential_1'], 8), - (ModuleStoreEnum.Type.mongo, 'html_1', ['chapter_1', 'sequential_2', 'vertical_2'], 10), + (ModuleStoreEnum.Type.mongo, 'chapter_1', [], 3), + (ModuleStoreEnum.Type.mongo, 'sequential_1', ['chapter_1'], 4), + (ModuleStoreEnum.Type.mongo, 'vertical_1', ['chapter_1', 'sequential_1'], 6), + (ModuleStoreEnum.Type.mongo, 'html_1', ['chapter_1', 'sequential_2', 'vertical_2'], 7), (ModuleStoreEnum.Type.split, 'course', [], 3), (ModuleStoreEnum.Type.split, 'chapter_1', [], 2), (ModuleStoreEnum.Type.split, 'sequential_1', ['chapter_1'], 2), diff --git a/openedx/core/djangoapps/bookmarks/tests/test_services.py b/openedx/core/djangoapps/bookmarks/tests/test_services.py index 341803cbdf..da70d72abe 100644 --- a/openedx/core/djangoapps/bookmarks/tests/test_services.py +++ b/openedx/core/djangoapps/bookmarks/tests/test_services.py @@ -68,7 +68,7 @@ class BookmarksServiceTests(BookmarksTestsBase): self.bookmark_service.set_bookmarked(usage_key=UsageKey.from_string("i4x://ed/ed/ed/interactive")) ) - with self.assertNumQueries(10): + with self.assertNumQueries(9): self.assertTrue(self.bookmark_service.set_bookmarked(usage_key=self.vertical_2.location)) def test_unset_bookmarked(self): diff --git a/openedx/core/djangoapps/bookmarks/tests/test_tasks.py b/openedx/core/djangoapps/bookmarks/tests/test_tasks.py index ac84d56dfe..e757154a16 100644 --- a/openedx/core/djangoapps/bookmarks/tests/test_tasks.py +++ b/openedx/core/djangoapps/bookmarks/tests/test_tasks.py @@ -20,6 +20,7 @@ class XBlockCacheTaskTests(BookmarksTestsBase): """ Test the XBlockCache model. """ + def setUp(self): super(XBlockCacheTaskTests, self).setUp() @@ -157,12 +158,6 @@ class XBlockCacheTaskTests(BookmarksTestsBase): """ course = getattr(self, course_attr) - if settings.ROOT_URLCONF == 'lms.urls': - # When the tests run under LMS, there is a certificates course_published - # signal handler that runs and causes the number of queries to be one more - # (due to the check for disabled_xblocks in django.py). - expected_sql_queries += 1 - with self.assertNumQueries(expected_sql_queries): _update_xblocks_cache(course.id) diff --git a/openedx/core/djangoapps/content/block_structure/tests/test_signals.py b/openedx/core/djangoapps/content/block_structure/tests/test_signals.py index ba9871e92b..e2a4843559 100644 --- a/openedx/core/djangoapps/content/block_structure/tests/test_signals.py +++ b/openedx/core/djangoapps/content/block_structure/tests/test_signals.py @@ -19,6 +19,8 @@ class CourseBlocksSignalTest(ModuleStoreTestCase): """ Tests for the Course Blocks signal """ + ENABLED_SIGNALS = ['course_deleted', 'course_published'] + def setUp(self): super(CourseBlocksSignalTest, self).setUp() self.course = CourseFactory.create() diff --git a/openedx/core/djangoapps/content/course_overviews/tests.py b/openedx/core/djangoapps/content/course_overviews/tests.py index 99c9918f5b..a8dff4fe9f 100644 --- a/openedx/core/djangoapps/content/course_overviews/tests.py +++ b/openedx/core/djangoapps/content/course_overviews/tests.py @@ -52,6 +52,8 @@ class CourseOverviewTestCase(ModuleStoreTestCase): COURSE_OVERVIEW_TABS = {'courseware', 'info', 'textbooks', 'discussion', 'wiki', 'progress'} + ENABLED_SIGNALS = ['course_deleted', 'course_published'] + def check_course_overview_against_course(self, course): """ Compares a CourseOverview object against its corresponding diff --git a/openedx/core/djangoapps/content/course_structures/api/v0/tests_api.py b/openedx/core/djangoapps/content/course_structures/api/v0/tests_api.py index ce1093dbea..d61d7b87e1 100644 --- a/openedx/core/djangoapps/content/course_structures/api/v0/tests_api.py +++ b/openedx/core/djangoapps/content/course_structures/api/v0/tests_api.py @@ -18,6 +18,8 @@ class CourseStructureApiTests(ModuleStoreTestCase): ENABLED_CACHES = ['default', 'mongo_metadata_inheritance', 'loc_cache'] + ENABLED_SIGNALS = ['course_published'] + def setUp(self): """ Test setup diff --git a/openedx/core/djangoapps/course_groups/tests/test_cohorts.py b/openedx/core/djangoapps/course_groups/tests/test_cohorts.py index 854774e2e9..4c32f45dc6 100644 --- a/openedx/core/djangoapps/course_groups/tests/test_cohorts.py +++ b/openedx/core/djangoapps/course_groups/tests/test_cohorts.py @@ -263,8 +263,8 @@ class TestCohorts(ModuleStoreTestCase): ) @ddt.data( - (True, 3), - (False, 7), + (True, 2), + (False, 6), ) @ddt.unpack def test_get_cohort_sql_queries(self, use_cached, num_sql_queries): diff --git a/openedx/core/djangoapps/credit/tests/test_verification_access.py b/openedx/core/djangoapps/credit/tests/test_verification_access.py index 4d8779d2c5..763d2fed2d 100644 --- a/openedx/core/djangoapps/credit/tests/test_verification_access.py +++ b/openedx/core/djangoapps/credit/tests/test_verification_access.py @@ -240,6 +240,8 @@ class WriteOnPublishTest(ModuleStoreTestCase): """ MODULESTORE = TEST_DATA_SPLIT_MODULESTORE + ENABLED_SIGNALS = ['course_published', 'pre_publish'] + @patch.dict(settings.FEATURES, {"ENABLE_COURSEWARE_INDEX": False}) def setUp(self): super(WriteOnPublishTest, self).setUp()