From c55dbb2c61fe14520e642a1f06cf2fde838ff8cb Mon Sep 17 00:00:00 2001 From: Andy Armstrong Date: Fri, 27 Oct 2017 12:44:05 -0400 Subject: [PATCH] Allow Waffle flags to work when outside a request context --- .../courseware/tests/test_course_tools.py | 6 ++++ .../core/djangoapps/waffle_utils/__init__.py | 14 +++++++-- .../waffle_utils/tests/test_init.py | 29 ++++++++++++++++--- .../waffle_utils/tests/test_testutils.py | 6 ++++ 4 files changed, 49 insertions(+), 6 deletions(-) diff --git a/lms/djangoapps/courseware/tests/test_course_tools.py b/lms/djangoapps/courseware/tests/test_course_tools.py index cbe9ff7f69..8060e1e4e4 100644 --- a/lms/djangoapps/courseware/tests/test_course_tools.py +++ b/lms/djangoapps/courseware/tests/test_course_tools.py @@ -1,3 +1,8 @@ +""" +Unit tests for course tools. +""" + +import crum import datetime from mock import patch @@ -59,6 +64,7 @@ class VerifiedUpgradeToolTest(SharedModuleStoreTestCase): ) self.request = RequestFactory().request() self.request.user = self.enrollment.user + crum.set_current_request(self.request) def test_tool_visible(self): self.assertTrue(VerifiedUpgradeTool().is_enabled(self.request, self.course.id)) diff --git a/openedx/core/djangoapps/waffle_utils/__init__.py b/openedx/core/djangoapps/waffle_utils/__init__.py index 90b4a2d320..3439f845bb 100644 --- a/openedx/core/djangoapps/waffle_utils/__init__.py +++ b/openedx/core/djangoapps/waffle_utils/__init__.py @@ -45,6 +45,7 @@ To test WaffleSwitchNamespace, use the provided context managers. For example: ... """ +import crum import logging from abc import ABCMeta from contextlib import contextmanager @@ -54,7 +55,6 @@ from opaque_keys.edx.keys import CourseKey from waffle import flag_is_active, switch_is_active from request_cache import get_cache as get_request_cache -from request_cache import get_request log = logging.getLogger(__name__) @@ -256,7 +256,17 @@ class WaffleFlagNamespace(WaffleNamespace): value = flag_undefined_default if value is None: - value = flag_is_active(get_request(), namespaced_flag_name) + request = crum.get_current_request() + if request: + value = flag_is_active(request, namespaced_flag_name) + else: + log.warn(u"%sFlag '%s' accessed without a request", self.log_prefix, namespaced_flag_name) + # Return the default value if not in a request context. + # Note: this skips the cache as the value might be different + # in a normal request context. This case seems to occur when + # a page redirects to a 404. In this case, we'll just return + # the default value. + return bool(flag_undefined_default) self._cached_flags[namespaced_flag_name] = value return value diff --git a/openedx/core/djangoapps/waffle_utils/tests/test_init.py b/openedx/core/djangoapps/waffle_utils/tests/test_init.py index 2732b83611..7336b3938f 100644 --- a/openedx/core/djangoapps/waffle_utils/tests/test_init.py +++ b/openedx/core/djangoapps/waffle_utils/tests/test_init.py @@ -1,8 +1,10 @@ """ Tests for waffle utils features. """ +import crum import ddt from django.test import TestCase +from django.test.client import RequestFactory from mock import patch from opaque_keys.edx.keys import CourseKey from request_cache.middleware import RequestCache @@ -27,6 +29,12 @@ class TestCourseWaffleFlag(TestCase): TEST_NAMESPACE = WaffleFlagNamespace(NAMESPACE_NAME) TEST_COURSE_FLAG = CourseWaffleFlag(TEST_NAMESPACE, FLAG_NAME) + def setUp(self): + super(TestCourseWaffleFlag, self).setUp() + request = RequestFactory().request() + crum.set_current_request(request) + RequestCache.clear_request_cache() + @ddt.data( {'course_override': WaffleFlagCourseOverrideModel.ALL_CHOICES.on, 'waffle_enabled': False, 'result': True}, {'course_override': WaffleFlagCourseOverrideModel.ALL_CHOICES.off, 'waffle_enabled': True, 'result': False}, @@ -38,8 +46,6 @@ class TestCourseWaffleFlag(TestCase): Tests various combinations of a flag being set in waffle and overridden for a course. """ - RequestCache.clear_request_cache() - with patch.object(WaffleFlagCourseOverrideModel, 'override_value', return_value=data['course_override']): with override_flag(self.NAMESPACED_FLAG_NAME, active=data['waffle_enabled']): # check twice to test that the result is properly cached @@ -70,8 +76,6 @@ class TestCourseWaffleFlag(TestCase): """ Test flag with various defaults provided for undefined waffle flags. """ - RequestCache.clear_request_cache() - test_course_flag = CourseWaffleFlag( self.TEST_NAMESPACE, self.FLAG_NAME, @@ -91,3 +95,20 @@ class TestCourseWaffleFlag(TestCase): self.NAMESPACED_FLAG_NAME, self.TEST_COURSE_KEY ) + + @ddt.data( + {'flag_undefined_default': None, 'result': False}, + {'flag_undefined_default': False, 'result': False}, + {'flag_undefined_default': True, 'result': True}, + ) + def test_without_request(self, data): + """ + Test the flag behavior when outside a request context. + """ + crum.set_current_request(None) + test_course_flag = CourseWaffleFlag( + self.TEST_NAMESPACE, + self.FLAG_NAME, + flag_undefined_default=data['flag_undefined_default'] + ) + self.assertEqual(test_course_flag.is_enabled(self.TEST_COURSE_KEY), data['result']) diff --git a/openedx/core/djangoapps/waffle_utils/tests/test_testutils.py b/openedx/core/djangoapps/waffle_utils/tests/test_testutils.py index e04ae6ed2d..107278a1f1 100644 --- a/openedx/core/djangoapps/waffle_utils/tests/test_testutils.py +++ b/openedx/core/djangoapps/waffle_utils/tests/test_testutils.py @@ -1,7 +1,11 @@ """ Tests for waffle utils test utilities. """ + +import crum + from django.test import TestCase +from django.test.client import RequestFactory from opaque_keys.edx.keys import CourseKey from request_cache.middleware import RequestCache @@ -25,6 +29,8 @@ class OverrideWaffleFlagTests(TestCase): def setUp(self): super(OverrideWaffleFlagTests, self).setUp() + request = RequestFactory().request() + crum.set_current_request(request) RequestCache.clear_request_cache() @override_waffle_flag(TEST_COURSE_FLAG, True)