From 5a60fc1db6a5c81c347bfc24da755f30652de9d8 Mon Sep 17 00:00:00 2001 From: Matt Tuchfarber Date: Mon, 3 Dec 2018 11:39:28 -0500 Subject: [PATCH] Add tests to verify access denied message deduplication works --- .../content_type_gating/tests/test_access.py | 251 +++++++++++++++++- 1 file changed, 245 insertions(+), 6 deletions(-) diff --git a/openedx/features/content_type_gating/tests/test_access.py b/openedx/features/content_type_gating/tests/test_access.py index dc7f2d4512..913dd60b47 100644 --- a/openedx/features/content_type_gating/tests/test_access.py +++ b/openedx/features/content_type_gating/tests/test_access.py @@ -14,7 +14,6 @@ from course_modes.tests.factories import CourseModeFactory from lms.djangoapps.courseware.module_render import load_single_xblock from openedx.core.djangoapps.user_api.tests.factories import UserCourseTagFactory from openedx.core.djangoapps.util.testing import TestConditionalContent -from openedx.core.djangoapps.waffle_utils.testutils import override_waffle_flag from openedx.core.lib.url_utils import quote_slashes from openedx.features.content_type_gating.partitions import CONTENT_GATING_PARTITION_ID from openedx.features.content_type_gating.models import ContentTypeGatingConfig @@ -26,20 +25,18 @@ from student.tests.factories import ( UserFactory, TEST_PASSWORD ) -from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase +from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase, SharedModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory @patch("crum.get_current_request") -def _assert_block_is_gated(mock_get_current_request, block, is_gated, user_id, course, request_factory): +def _get_fragment_from_block(block, user_id, course, request_factory, mock_get_current_request): """ - Asserts that a block in a specific course is gated for a specific user + Returns the rendered fragment of a block Arguments: block: some sort of xblock descriptor, must implement .scope_ids.usage_id - is_gated (bool): if True, this user is expected to be gated from this block user_id (int): id of user course_id (CourseLocator): id of course - view_name (str): type of view for the block, if not set will default to 'student_view' """ fake_request = request_factory.get('') mock_get_current_request.return_value = fake_request @@ -59,12 +56,38 @@ def _assert_block_is_gated(mock_get_current_request, block, is_gated, user_id, c # Attempt to render the block, this should return different fragments if the content is gated or not. frag = runtime.render(problem_block, 'student_view') + return frag + + +def _assert_block_is_gated(block, is_gated, user_id, course, request_factory): + """ + Asserts that a block in a specific course is gated for a specific user + Arguments: + block: some sort of xblock descriptor, must implement .scope_ids.usage_id + is_gated (bool): if True, this user is expected to be gated from this block + user_id (int): id of user + course_id (CourseLocator): id of course + """ + frag = _get_fragment_from_block(block, user_id, course, request_factory) if is_gated: assert 'content-paywall' in frag.content else: assert 'content-paywall' not in frag.content +def _assert_block_is_empty(block, user_id, course, request_factory): + """ + Asserts that a block in a specific course is empty for a specific user + Arguments: + block: some sort of xblock descriptor, must implement .scope_ids.usage_id + is_gated (bool): if True, this user is expected to be gated from this block + user_id (int): id of user + course_id (CourseLocator): id of course + """ + frag = _get_fragment_from_block(block, user_id, course, request_factory) + assert frag.content == u'' + + @ddt.ddt @override_settings(FIELD_OVERRIDE_PROVIDERS=( 'openedx.features.content_type_gating.field_override.ContentTypeGatingFieldOverride', @@ -505,3 +528,219 @@ class TestConditionalContentAccess(TestConditionalContent): is_gated=False, request_factory=self.factory, ) + + +@override_settings(FIELD_OVERRIDE_PROVIDERS=( + 'openedx.features.content_type_gating.field_override.ContentTypeGatingFieldOverride', +)) +class TestMessageDeduplication(ModuleStoreTestCase): + """ + Tests to verify that access denied messages isn't shown if multiple items in a row are denied. + Expected results: + - 0 items denied => No access denied messages + - 1+ items in a row denied => 1 access denied message + - If page has accessible content between access denied blocks, show both blocks. + + NOTE: This uses `_assert_block_is_gated` to verify that the message is being shown (as that's + how it's currently tested). If that method changes to use something other than the template + message, this method's checks will need to be updated. + """ + + def setUp(self): + super(TestMessageDeduplication, self).setUp() + + self.user = UserFactory.create() + self.request_factory = RequestFactory() + ContentTypeGatingConfig.objects.create(enabled=True, enabled_as_of=date(2018, 1, 1)) + + def _create_course(self): + course = CourseFactory.create(run='test', display_name='test') + CourseModeFactory.create(course_id=course.id, mode_slug='audit') + CourseModeFactory.create(course_id=course.id, mode_slug='verified') + blocks_dict = {} + with self.store.bulk_operations(course.id): + blocks_dict['chapter'] = ItemFactory.create( + parent=course, + category='chapter', + display_name='Week 1' + ) + blocks_dict['sequential'] = ItemFactory.create( + parent=blocks_dict['chapter'], + category='sequential', + display_name='Lesson 1' + ) + blocks_dict['vertical'] = ItemFactory.create( + parent=blocks_dict['sequential'], + category='vertical', + display_name='Lesson 1 Vertical - Unit 1' + ) + return { + 'course': course, + 'blocks': blocks_dict, + } + + def test_single_denied(self): + ''' Single graded problem should show error ''' + course = self._create_course() + blocks_dict = course['blocks'] + CourseEnrollmentFactory.create( + user=self.user, + course_id=course['course'].id, + mode='audit' + ) + blocks_dict['graded_1'] = ItemFactory.create( + parent=blocks_dict['vertical'], + category='problem', + display_name='graded_problem', + graded=True, + ) + _assert_block_is_gated( + block=blocks_dict['graded_1'], + user_id=self.user.id, + course=course['course'], + is_gated=True, + request_factory=self.request_factory, + ) + + def test_double_denied(self): + ''' First graded problem should show message, second shouldn't ''' + course = self._create_course() + blocks_dict = course['blocks'] + blocks_dict['graded_1'] = ItemFactory.create( + parent=blocks_dict['vertical'], + category='problem', + display_name='graded_problem', + graded=True, + ) + blocks_dict['graded_2'] = ItemFactory.create( + parent=blocks_dict['vertical'], + category='problem', + display_name='graded_problem', + graded=True, + ) + CourseEnrollmentFactory.create( + user=self.user, + course_id=course['course'].id, + mode='audit' + ) + _assert_block_is_gated( + block=blocks_dict['graded_1'], + user_id=self.user.id, + course=course['course'], + is_gated=True, + request_factory=self.request_factory, + ) + _assert_block_is_empty( + block=blocks_dict['graded_2'], + user_id=self.user.id, + course=course['course'], + request_factory=self.request_factory, + ) + + def test_many_denied(self): + ''' First graded problem should show message, all that follow shouldn't ''' + course = self._create_course() + blocks_dict = course['blocks'] + blocks_dict['graded_1'] = ItemFactory.create( + parent=blocks_dict['vertical'], + category='problem', + display_name='graded_problem', + graded=True, + ) + blocks_dict['graded_2'] = ItemFactory.create( + parent=blocks_dict['vertical'], + category='problem', + display_name='graded_problem', + graded=True, + ) + blocks_dict['graded_3'] = ItemFactory.create( + parent=blocks_dict['vertical'], + category='problem', + display_name='graded_problem', + graded=True, + ) + blocks_dict['graded_4'] = ItemFactory.create( + parent=blocks_dict['vertical'], + category='problem', + display_name='graded_problem', + graded=True, + ) + CourseEnrollmentFactory.create( + user=self.user, + course_id=course['course'].id, + mode='audit' + ) + _assert_block_is_gated( + block=blocks_dict['graded_1'], + user_id=self.user.id, + course=course['course'], + is_gated=True, + request_factory=self.request_factory, + ) + _assert_block_is_empty( + block=blocks_dict['graded_2'], + user_id=self.user.id, + course=course['course'], + request_factory=self.request_factory, + ) + _assert_block_is_empty( + block=blocks_dict['graded_3'], + user_id=self.user.id, + course=course['course'], + request_factory=self.request_factory, + ) + _assert_block_is_empty( + block=blocks_dict['graded_4'], + user_id=self.user.id, + course=course['course'], + request_factory=self.request_factory, + ) + + def test_alternate_denied(self): + ''' Multiple graded content with ungraded between it should show message on either end ''' + course = self._create_course() + blocks_dict = course['blocks'] + blocks_dict['graded_1'] = ItemFactory.create( + parent=blocks_dict['vertical'], + category='problem', + display_name='graded_problem', + graded=True, + ) + blocks_dict['ungraded_2'] = ItemFactory.create( + parent=blocks_dict['vertical'], + category='problem', + display_name='ungraded_problem', + graded=False, + ) + blocks_dict['graded_3'] = ItemFactory.create( + parent=blocks_dict['vertical'], + category='problem', + display_name='graded_problem', + graded=True, + ) + CourseEnrollmentFactory.create( + user=self.user, + course_id=course['course'].id, + mode='audit' + ) + _assert_block_is_gated( + block=blocks_dict['graded_1'], + user_id=self.user.id, + course=course['course'], + is_gated=True, + request_factory=self.request_factory, + ) + _assert_block_is_gated( + block=blocks_dict['ungraded_2'], + user_id=self.user.id, + course=course['course'], + is_gated=False, + request_factory=self.request_factory, + ) + _assert_block_is_gated( + block=blocks_dict['graded_3'], + user_id=self.user.id, + course=course['course'], + is_gated=True, + request_factory=self.request_factory, + )