class EntranceExamHandlerTests(CourseTestCase, MilestonesTestCaseMixin): """ Base test class for create, save, and delete """ def setUp(self): """ Shared scaffolding for individual test runs """ super(EntranceExamHandlerTests, self).setUp() self.course_key = self.course.id self.usage_key = self.course.location self.course_url = '/course/{}'.format(six.text_type(self.course.id)) self.exam_url = '/course/{}/entrance_exam/'.format( six.text_type(self.course.id)) self.milestone_relationship_types = milestones_helpers.get_milestone_relationship_types( ) def test_entrance_exam_milestone_addition(self): """ Unit Test: test addition of entrance exam milestone content """ parent_locator = six.text_type(self.course.location) created_block = create_xblock(parent_locator=parent_locator, user=self.user, category='chapter', display_name=('Entrance Exam'), is_entrance_exam=True) add_entrance_exam_milestone(self.course.id, created_block) content_milestones = milestones_helpers.get_course_content_milestones( six.text_type(self.course.id), six.text_type(created_block.location), self.milestone_relationship_types['FULFILLS']) self.assertTrue(len(content_milestones)) self.assertEqual( len(milestones_helpers.get_course_milestones(self.course.id)), 1) def test_entrance_exam_milestone_removal(self): """ Unit Test: test removal of entrance exam milestone content """ parent_locator = six.text_type(self.course.location) created_block = create_xblock(parent_locator=parent_locator, user=self.user, category='chapter', display_name=('Entrance Exam'), is_entrance_exam=True) add_entrance_exam_milestone(self.course.id, created_block) content_milestones = milestones_helpers.get_course_content_milestones( six.text_type(self.course.id), six.text_type(created_block.location), self.milestone_relationship_types['FULFILLS']) self.assertEqual(len(content_milestones), 1) user = UserFactory() request = RequestFactory().request() request.user = user remove_entrance_exam_milestone_reference(request, self.course.id) content_milestones = milestones_helpers.get_course_content_milestones( six.text_type(self.course.id), six.text_type(created_block.location), self.milestone_relationship_types['FULFILLS']) self.assertEqual(len(content_milestones), 0) def test_contentstore_views_entrance_exam_post(self): """ Unit Test: test_contentstore_views_entrance_exam_post """ resp = self.client.post(self.exam_url, {}, http_accept='application/json') self.assertEqual(resp.status_code, 201) resp = self.client.get(self.exam_url) self.assertEqual(resp.status_code, 200) # Reload the test course now that the exam module has been added self.course = modulestore().get_course(self.course.id) metadata = CourseMetadata.fetch_all(self.course) self.assertTrue(metadata['entrance_exam_enabled']) self.assertIsNotNone(metadata['entrance_exam_minimum_score_pct']) self.assertIsNotNone(metadata['entrance_exam_id']['value']) self.assertTrue( len( milestones_helpers.get_course_milestones( six.text_type(self.course.id)))) content_milestones = milestones_helpers.get_course_content_milestones( six.text_type(self.course.id), metadata['entrance_exam_id']['value'], self.milestone_relationship_types['FULFILLS']) self.assertTrue(len(content_milestones)) def test_contentstore_views_entrance_exam_post_new_sequential_confirm_grader( self): """ Unit Test: test_contentstore_views_entrance_exam_post """ resp = self.client.post(self.exam_url, {}, http_accept='application/json') self.assertEqual(resp.status_code, 201) resp = self.client.get(self.exam_url) self.assertEqual(resp.status_code, 200) # Reload the test course now that the exam module has been added self.course = modulestore().get_course(self.course.id) # Add a new child sequential to the exam module # Confirm that the grader type is 'Entrance Exam' chapter_locator_string = json.loads( resp.content.decode('utf-8')).get('locator') # chapter_locator = UsageKey.from_string(chapter_locator_string) seq_data = { 'category': "sequential", 'display_name': "Entrance Exam Subsection", 'parent_locator': chapter_locator_string, } resp = self.client.ajax_post(reverse_url('xblock_handler'), seq_data) seq_locator_string = json.loads( resp.content.decode('utf-8')).get('locator') seq_locator = UsageKey.from_string(seq_locator_string) section_grader_type = CourseGradingModel.get_section_grader_type( seq_locator) self.assertEqual(GRADER_TYPES['ENTRANCE_EXAM'], section_grader_type['graderType']) def test_contentstore_views_entrance_exam_get(self): """ Unit Test: test_contentstore_views_entrance_exam_get """ resp = self.client.post(self.exam_url, { 'entrance_exam_minimum_score_pct': settings.ENTRANCE_EXAM_MIN_SCORE_PCT }, http_accept='application/json') self.assertEqual(resp.status_code, 201) resp = self.client.get(self.exam_url) self.assertEqual(resp.status_code, 200) def test_contentstore_views_entrance_exam_delete(self): """ Unit Test: test_contentstore_views_entrance_exam_delete """ resp = self.client.post(self.exam_url, {}, http_accept='application/json') self.assertEqual(resp.status_code, 201) resp = self.client.get(self.exam_url) self.assertEqual(resp.status_code, 200) resp = self.client.delete(self.exam_url) self.assertEqual(resp.status_code, 204) resp = self.client.get(self.exam_url) self.assertEqual(resp.status_code, 404) user = User.objects.create( username='******', email='*****@*****.**', is_active=True, ) user.set_password('test') user.save() milestones = milestones_helpers.get_course_milestones( six.text_type(self.course_key)) self.assertEqual(len(milestones), 1) milestone_key = '{}.{}'.format(milestones[0]['namespace'], milestones[0]['name']) paths = milestones_helpers.get_course_milestones_fulfillment_paths( six.text_type(self.course_key), milestones_helpers.serialize_user(user)) # What we have now is a course milestone requirement and no valid fulfillment # paths for the specified user. The LMS is going to have to ignore this situation, # because we can't confidently prevent it from occuring at some point in the future. # milestone_key_1 = self.assertEqual(len(paths[milestone_key]), 0) # Re-adding an entrance exam to the course should fix the missing link # It wipes out any old entrance exam artifacts and inserts a new exam course chapter/module resp = self.client.post(self.exam_url, {}, http_accept='application/json') self.assertEqual(resp.status_code, 201) resp = self.client.get(self.exam_url) self.assertEqual(resp.status_code, 200) # Confirm that we have only one Entrance Exam grader after re-adding the exam (validates SOL-475) graders = CourseGradingModel.fetch(self.course_key).graders count = 0 for grader in graders: if grader['type'] == GRADER_TYPES['ENTRANCE_EXAM']: count += 1 self.assertEqual(count, 1) def test_contentstore_views_entrance_exam_delete_bogus_course(self): """ Unit Test: test_contentstore_views_entrance_exam_delete_bogus_course """ resp = self.client.delete('/course/bad/course/key/entrance_exam') self.assertEqual(resp.status_code, 400) def test_contentstore_views_entrance_exam_get_bogus_course(self): """ Unit Test: test_contentstore_views_entrance_exam_get_bogus_course """ resp = self.client.get('/course/bad/course/key/entrance_exam') self.assertEqual(resp.status_code, 400) def test_contentstore_views_entrance_exam_get_bogus_exam(self): """ Unit Test: test_contentstore_views_entrance_exam_get_bogus_exam """ resp = self.client.post(self.exam_url, {'entrance_exam_minimum_score_pct': '50'}, http_accept='application/json') self.assertEqual(resp.status_code, 201) resp = self.client.get(self.exam_url) self.assertEqual(resp.status_code, 200) self.course = modulestore().get_course(self.course.id) # Should raise an ItemNotFoundError and return a 404 updated_metadata = { 'entrance_exam_id': 'i4x://org.4/course_4/chapter/ed7c4c6a4d68409998e2c8554c4629d1' } CourseMetadata.update_from_dict( updated_metadata, self.course, self.user, ) self.course = modulestore().get_course(self.course.id) resp = self.client.get(self.exam_url) self.assertEqual(resp.status_code, 404) # Should raise an InvalidKeyError and return a 404 updated_metadata = {'entrance_exam_id': '123afsdfsad90f87'} CourseMetadata.update_from_dict( updated_metadata, self.course, self.user, ) self.course = modulestore().get_course(self.course.id) resp = self.client.get(self.exam_url) self.assertEqual(resp.status_code, 404) def test_contentstore_views_entrance_exam_post_bogus_course(self): """ Unit Test: test_contentstore_views_entrance_exam_post_bogus_course """ resp = self.client.post('/course/bad/course/key/entrance_exam', {}, http_accept='application/json') self.assertEqual(resp.status_code, 400) def test_contentstore_views_entrance_exam_post_invalid_http_accept(self): """ Unit Test: test_contentstore_views_entrance_exam_post_invalid_http_accept """ resp = self.client.post('/course/bad/course/key/entrance_exam', {}, http_accept='text/html') self.assertEqual(resp.status_code, 400) def test_contentstore_views_entrance_exam_get_invalid_user(self): """ Unit Test: test_contentstore_views_entrance_exam_get_invalid_user """ user = User.objects.create( username='******', email='*****@*****.**', is_active=True, ) user.set_password('test') user.save() self.client = AjaxEnabledTestClient() self.client.login(username='******', password='******') resp = self.client.get(self.exam_url) self.assertEqual(resp.status_code, 403) def test_contentstore_views_entrance_exam_unsupported_method(self): """ Unit Test: test_contentstore_views_entrance_exam_unsupported_method """ resp = self.client.put(self.exam_url) self.assertEqual(resp.status_code, 405) def test_entrance_exam_view_direct_missing_score_setting(self): """ Unit Test: test_entrance_exam_view_direct_missing_score_setting """ user = UserFactory() user.is_staff = True request = RequestFactory() request.user = user resp = create_entrance_exam(request, self.course.id, None) self.assertEqual(resp.status_code, 201) @patch.dict('django.conf.settings.FEATURES', {'ENTRANCE_EXAMS': False}) def test_entrance_exam_feature_flag_gating(self): user = UserFactory() user.is_staff = True request = RequestFactory() request.user = user resp = self.client.get(self.exam_url) self.assertEqual(resp.status_code, 400) resp = create_entrance_exam(request, self.course.id, None) self.assertEqual(resp.status_code, 400) resp = delete_entrance_exam(request, self.course.id) self.assertEqual(resp.status_code, 400) # No return, so we'll just ensure no exception is thrown update_entrance_exam(request, self.course.id, {})
class EntranceExamHandlerTests(CourseTestCase, MilestonesTestCaseMixin): """ Base test class for create, save, and delete """ def setUp(self): """ Shared scaffolding for individual test runs """ super(EntranceExamHandlerTests, self).setUp() self.course_key = self.course.id self.usage_key = self.course.location self.course_url = '/course/{}'.format(unicode(self.course.id)) self.exam_url = '/course/{}/entrance_exam/'.format(unicode(self.course.id)) self.milestone_relationship_types = milestones_helpers.get_milestone_relationship_types() def test_entrance_exam_milestone_addition(self): """ Unit Test: test addition of entrance exam milestone content """ parent_locator = unicode(self.course.location) created_block = create_xblock( parent_locator=parent_locator, user=self.user, category='chapter', display_name=('Entrance Exam'), is_entrance_exam=True ) add_entrance_exam_milestone(self.course.id, created_block) content_milestones = milestones_helpers.get_course_content_milestones( unicode(self.course.id), unicode(created_block.location), self.milestone_relationship_types['FULFILLS'] ) self.assertTrue(len(content_milestones)) self.assertEqual(len(milestones_helpers.get_course_milestones(self.course.id)), 1) def test_entrance_exam_milestone_removal(self): """ Unit Test: test removal of entrance exam milestone content """ parent_locator = unicode(self.course.location) created_block = create_xblock( parent_locator=parent_locator, user=self.user, category='chapter', display_name=('Entrance Exam'), is_entrance_exam=True ) add_entrance_exam_milestone(self.course.id, created_block) content_milestones = milestones_helpers.get_course_content_milestones( unicode(self.course.id), unicode(created_block.location), self.milestone_relationship_types['FULFILLS'] ) self.assertEqual(len(content_milestones), 1) user = UserFactory() request = RequestFactory().request() request.user = user remove_entrance_exam_milestone_reference(request, self.course.id) content_milestones = milestones_helpers.get_course_content_milestones( unicode(self.course.id), unicode(created_block.location), self.milestone_relationship_types['FULFILLS'] ) self.assertEqual(len(content_milestones), 0) def test_contentstore_views_entrance_exam_post(self): """ Unit Test: test_contentstore_views_entrance_exam_post """ resp = self.client.post(self.exam_url, {}, http_accept='application/json') self.assertEqual(resp.status_code, 201) resp = self.client.get(self.exam_url) self.assertEqual(resp.status_code, 200) # Reload the test course now that the exam module has been added self.course = modulestore().get_course(self.course.id) metadata = CourseMetadata.fetch_all(self.course) self.assertTrue(metadata['entrance_exam_enabled']) self.assertIsNotNone(metadata['entrance_exam_minimum_score_pct']) self.assertIsNotNone(metadata['entrance_exam_id']['value']) self.assertTrue(len(milestones_helpers.get_course_milestones(unicode(self.course.id)))) content_milestones = milestones_helpers.get_course_content_milestones( unicode(self.course.id), metadata['entrance_exam_id']['value'], self.milestone_relationship_types['FULFILLS'] ) self.assertTrue(len(content_milestones)) def test_contentstore_views_entrance_exam_post_new_sequential_confirm_grader(self): """ Unit Test: test_contentstore_views_entrance_exam_post """ resp = self.client.post(self.exam_url, {}, http_accept='application/json') self.assertEqual(resp.status_code, 201) resp = self.client.get(self.exam_url) self.assertEqual(resp.status_code, 200) # Reload the test course now that the exam module has been added self.course = modulestore().get_course(self.course.id) # Add a new child sequential to the exam module # Confirm that the grader type is 'Entrance Exam' chapter_locator_string = json.loads(resp.content).get('locator') # chapter_locator = UsageKey.from_string(chapter_locator_string) seq_data = { 'category': "sequential", 'display_name': "Entrance Exam Subsection", 'parent_locator': chapter_locator_string, } resp = self.client.ajax_post(reverse_url('xblock_handler'), seq_data) seq_locator_string = json.loads(resp.content).get('locator') seq_locator = UsageKey.from_string(seq_locator_string) section_grader_type = CourseGradingModel.get_section_grader_type(seq_locator) self.assertEqual(GRADER_TYPES['ENTRANCE_EXAM'], section_grader_type['graderType']) def test_contentstore_views_entrance_exam_get(self): """ Unit Test: test_contentstore_views_entrance_exam_get """ resp = self.client.post( self.exam_url, {'entrance_exam_minimum_score_pct': settings.ENTRANCE_EXAM_MIN_SCORE_PCT}, http_accept='application/json' ) self.assertEqual(resp.status_code, 201) resp = self.client.get(self.exam_url) self.assertEqual(resp.status_code, 200) def test_contentstore_views_entrance_exam_delete(self): """ Unit Test: test_contentstore_views_entrance_exam_delete """ resp = self.client.post(self.exam_url, {}, http_accept='application/json') self.assertEqual(resp.status_code, 201) resp = self.client.get(self.exam_url) self.assertEqual(resp.status_code, 200) resp = self.client.delete(self.exam_url) self.assertEqual(resp.status_code, 204) resp = self.client.get(self.exam_url) self.assertEqual(resp.status_code, 404) user = User.objects.create( username='******', email='*****@*****.**', is_active=True, ) user.set_password('test') user.save() milestones = milestones_helpers.get_course_milestones(unicode(self.course_key)) self.assertEqual(len(milestones), 1) milestone_key = '{}.{}'.format(milestones[0]['namespace'], milestones[0]['name']) paths = milestones_helpers.get_course_milestones_fulfillment_paths( unicode(self.course_key), milestones_helpers.serialize_user(user) ) # What we have now is a course milestone requirement and no valid fulfillment # paths for the specified user. The LMS is going to have to ignore this situation, # because we can't confidently prevent it from occuring at some point in the future. # milestone_key_1 = self.assertEqual(len(paths[milestone_key]), 0) # Re-adding an entrance exam to the course should fix the missing link # It wipes out any old entrance exam artifacts and inserts a new exam course chapter/module resp = self.client.post(self.exam_url, {}, http_accept='application/json') self.assertEqual(resp.status_code, 201) resp = self.client.get(self.exam_url) self.assertEqual(resp.status_code, 200) # Confirm that we have only one Entrance Exam grader after re-adding the exam (validates SOL-475) graders = CourseGradingModel.fetch(self.course_key).graders count = 0 for grader in graders: if grader['type'] == GRADER_TYPES['ENTRANCE_EXAM']: count += 1 self.assertEqual(count, 1) def test_contentstore_views_entrance_exam_delete_bogus_course(self): """ Unit Test: test_contentstore_views_entrance_exam_delete_bogus_course """ resp = self.client.delete('/course/bad/course/key/entrance_exam') self.assertEqual(resp.status_code, 400) def test_contentstore_views_entrance_exam_get_bogus_course(self): """ Unit Test: test_contentstore_views_entrance_exam_get_bogus_course """ resp = self.client.get('/course/bad/course/key/entrance_exam') self.assertEqual(resp.status_code, 400) def test_contentstore_views_entrance_exam_get_bogus_exam(self): """ Unit Test: test_contentstore_views_entrance_exam_get_bogus_exam """ resp = self.client.post( self.exam_url, {'entrance_exam_minimum_score_pct': '50'}, http_accept='application/json' ) self.assertEqual(resp.status_code, 201) resp = self.client.get(self.exam_url) self.assertEqual(resp.status_code, 200) self.course = modulestore().get_course(self.course.id) # Should raise an ItemNotFoundError and return a 404 updated_metadata = {'entrance_exam_id': 'i4x://org.4/course_4/chapter/ed7c4c6a4d68409998e2c8554c4629d1'} CourseMetadata.update_from_dict( updated_metadata, self.course, self.user, ) self.course = modulestore().get_course(self.course.id) resp = self.client.get(self.exam_url) self.assertEqual(resp.status_code, 404) # Should raise an InvalidKeyError and return a 404 updated_metadata = {'entrance_exam_id': '123afsdfsad90f87'} CourseMetadata.update_from_dict( updated_metadata, self.course, self.user, ) self.course = modulestore().get_course(self.course.id) resp = self.client.get(self.exam_url) self.assertEqual(resp.status_code, 404) def test_contentstore_views_entrance_exam_post_bogus_course(self): """ Unit Test: test_contentstore_views_entrance_exam_post_bogus_course """ resp = self.client.post( '/course/bad/course/key/entrance_exam', {}, http_accept='application/json' ) self.assertEqual(resp.status_code, 400) def test_contentstore_views_entrance_exam_post_invalid_http_accept(self): """ Unit Test: test_contentstore_views_entrance_exam_post_invalid_http_accept """ resp = self.client.post( '/course/bad/course/key/entrance_exam', {}, http_accept='text/html' ) self.assertEqual(resp.status_code, 400) def test_contentstore_views_entrance_exam_get_invalid_user(self): """ Unit Test: test_contentstore_views_entrance_exam_get_invalid_user """ user = User.objects.create( username='******', email='*****@*****.**', is_active=True, ) user.set_password('test') user.save() self.client = AjaxEnabledTestClient() self.client.login(username='******', password='******') resp = self.client.get(self.exam_url) self.assertEqual(resp.status_code, 403) def test_contentstore_views_entrance_exam_unsupported_method(self): """ Unit Test: test_contentstore_views_entrance_exam_unsupported_method """ resp = self.client.put(self.exam_url) self.assertEqual(resp.status_code, 405) def test_entrance_exam_view_direct_missing_score_setting(self): """ Unit Test: test_entrance_exam_view_direct_missing_score_setting """ user = UserFactory() user.is_staff = True request = RequestFactory() request.user = user resp = create_entrance_exam(request, self.course.id, None) self.assertEqual(resp.status_code, 201) @patch.dict('django.conf.settings.FEATURES', {'ENTRANCE_EXAMS': False}) def test_entrance_exam_feature_flag_gating(self): user = UserFactory() user.is_staff = True request = RequestFactory() request.user = user resp = self.client.get(self.exam_url) self.assertEqual(resp.status_code, 400) resp = create_entrance_exam(request, self.course.id, None) self.assertEqual(resp.status_code, 400) resp = delete_entrance_exam(request, self.course.id) self.assertEqual(resp.status_code, 400) # No return, so we'll just ensure no exception is thrown update_entrance_exam(request, self.course.id, {})