""" Unit tests for gating.signals module """ from mock import patch from nose.plugins.attrib import attr from ddt import ddt, data, unpack from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from courseware.tests.helpers import LoginEnrollmentTestCase from milestones import api as milestones_api from milestones.tests.utils import MilestonesTestCaseMixin from openedx.core.lib.gating import api as gating_api from gating.api import _get_xblock_parent, evaluate_prerequisite class GatingTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase): """ Base TestCase class for setting up a basic course structure and testing the gating feature """ def setUp(self): """ Initial data setup """ super(GatingTestCase, self).setUp() # Patch Milestones feature flag self.settings_patcher = patch.dict('django.conf.settings.FEATURES', {'MILESTONES_APP': True}) self.settings_patcher.start() # create course self.course = CourseFactory.create( org='edX', number='EDX101', run='EDX101_RUN1', display_name='edX 101' ) self.course.enable_subsection_gating = True self.course.save() self.store.update_item(self.course, 0) # create chapter self.chapter1 = ItemFactory.create( parent_location=self.course.location, category='chapter', display_name='untitled chapter 1' ) # create sequentials self.seq1 = ItemFactory.create( parent_location=self.chapter1.location, category='sequential', display_name='untitled sequential 1' ) self.seq2 = ItemFactory.create( parent_location=self.chapter1.location, category='sequential', display_name='untitled sequential 2' ) # create vertical self.vert1 = ItemFactory.create( parent_location=self.seq1.location, category='vertical', display_name='untitled vertical 1' ) # create problem self.prob1 = ItemFactory.create( parent_location=self.vert1.location, category='problem', display_name='untitled problem 1' ) # create orphan self.prob2 = ItemFactory.create( parent_location=self.course.location, category='problem', display_name='untitled problem 2' ) def tearDown(self): """ Tear down initial setup """ self.settings_patcher.stop() super(GatingTestCase, self).tearDown() class TestGetXBlockParent(GatingTestCase): """ Tests for the get_xblock_parent function """ def test_get_direct_parent(self): """ Test test_get_direct_parent """ result = _get_xblock_parent(self.vert1) self.assertEqual(result.location, self.seq1.location) def test_get_parent_with_category(self): """ Test test_get_parent_of_category """ result = _get_xblock_parent(self.vert1, 'sequential') self.assertEqual(result.location, self.seq1.location) result = _get_xblock_parent(self.vert1, 'chapter') self.assertEqual(result.location, self.chapter1.location) def test_get_parent_none(self): """ Test test_get_parent_none """ result = _get_xblock_parent(self.vert1, 'unit') self.assertIsNone(result) @attr('shard_3') @ddt class TestEvaluatePrerequisite(GatingTestCase, MilestonesTestCaseMixin): """ Tests for the evaluate_prerequisite function """ def setUp(self): super(TestEvaluatePrerequisite, self).setUp() self.user_dict = {'id': self.user.id} self.prereq_milestone = None def _setup_gating_milestone(self, min_score): """ Setup a gating milestone for testing """ gating_api.add_prerequisite(self.course.id, self.seq1.location) gating_api.set_required_content(self.course.id, self.seq2.location, self.seq1.location, min_score) self.prereq_milestone = gating_api.get_gating_milestone(self.course.id, self.seq1.location, 'fulfills') @patch('courseware.grades.get_module_score') @data((.5, True), (1, True), (0, False)) @unpack def test_min_score_achieved(self, module_score, result, mock_module_score): """ Test test_min_score_achieved """ self._setup_gating_milestone(50) mock_module_score.return_value = module_score evaluate_prerequisite(self.course, self.prob1.location, self.user.id) self.assertEqual(milestones_api.user_has_milestone(self.user_dict, self.prereq_milestone), result) @patch('gating.api.log.warning') @patch('courseware.grades.get_module_score') @data((.5, False), (1, True)) @unpack def test_invalid_min_score(self, module_score, result, mock_module_score, mock_log): """ Test test_invalid_min_score """ self._setup_gating_milestone(None) mock_module_score.return_value = module_score evaluate_prerequisite(self.course, self.prob1.location, self.user.id) self.assertEqual(milestones_api.user_has_milestone(self.user_dict, self.prereq_milestone), result) self.assertTrue(mock_log.called) @patch('courseware.grades.get_module_score') def test_orphaned_xblock(self, mock_module_score): """ Test test_orphaned_xblock """ evaluate_prerequisite(self.course, self.prob2.location, self.user.id) self.assertFalse(mock_module_score.called) @patch('courseware.grades.get_module_score') def test_no_prerequisites(self, mock_module_score): """ Test test_no_prerequisites """ evaluate_prerequisite(self.course, self.prob1.location, self.user.id) self.assertFalse(mock_module_score.called) @patch('courseware.grades.get_module_score') def test_no_gated_content(self, mock_module_score): """ Test test_no_gated_content """ # Setup gating milestones data gating_api.add_prerequisite(self.course.id, self.seq1.location) evaluate_prerequisite(self.course, self.prob1.location, self.user.id) self.assertFalse(mock_module_score.called)