import json from django.test.client import Client, RequestFactory from django.test.utils import override_settings from mock import patch, MagicMock from courseware.models import XModuleUserStateSummaryField from courseware.tests.factories import UserStateSummaryFactory from courseware.tests.modulestore_config import TEST_DATA_MIXED_MODULESTORE import instructor.hint_manager as view from student.tests.factories import UserFactory from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory @override_settings(MODULESTORE=TEST_DATA_MIXED_MODULESTORE) class HintManagerTest(ModuleStoreTestCase): def setUp(self): """ Makes a course, which will be the same for all tests. Set up mako middleware, which is necessary for template rendering to happen. """ self.course = CourseFactory.create(org='Me', number='19.002', display_name='test_course') self.url = '/courses/Me/19.002/test_course/hint_manager' self.user = UserFactory.create(username='robot', email='robot@edx.org', password='test', is_staff=True) self.c = Client() self.c.login(username='robot', password='test') self.problem_id = 'i4x://Me/19.002/crowdsource_hinter/crowdsource_hinter_001' self.course_id = 'Me/19.002/test_course' UserStateSummaryFactory.create(field_name='hints', usage_id=self.problem_id, value=json.dumps({'1.0': {'1': ['Hint 1', 2], '3': ['Hint 3', 12]}, '2.0': {'4': ['Hint 4', 3]} })) UserStateSummaryFactory.create(field_name='mod_queue', usage_id=self.problem_id, value=json.dumps({'2.0': {'2': ['Hint 2', 1]}})) UserStateSummaryFactory.create(field_name='hint_pk', usage_id=self.problem_id, value=5) # Mock out location_to_problem_name, which ordinarily accesses the modulestore. # (I can't figure out how to get fake structures into the modulestore.) view.location_to_problem_name = lambda course_id, loc: "Test problem" def test_student_block(self): """ Makes sure that students cannot see the hint management view. """ c = Client() UserFactory.create(username='student', email='student@edx.org', password='test') c.login(username='student', password='test') out = c.get(self.url) print out self.assertTrue('Sorry, but students are not allowed to access the hint manager!' in out.content) def test_staff_access(self): """ Makes sure that staff can access the hint management view. """ out = self.c.get('/courses/Me/19.002/test_course/hint_manager') print out self.assertTrue('Hints Awaiting Moderation' in out.content) def test_invalid_field_access(self): """ Makes sure that field names other than 'mod_queue' and 'hints' are rejected. """ out = self.c.post(self.url, {'op': 'delete hints', 'field': 'all your private data'}) print out self.assertTrue('an invalid field was accessed' in out.content) def test_switchfields(self): """ Checks that the op: 'switch fields' POST request works. """ out = self.c.post(self.url, {'op': 'switch fields', 'field': 'mod_queue'}) print out self.assertTrue('Hint 2' in out.content) def test_gethints(self): """ Checks that gethints returns the right data. """ request = RequestFactory() post = request.post(self.url, {'field': 'mod_queue'}) out = view.get_hints(post, self.course_id, 'mod_queue') print out self.assertTrue(out['other_field'] == 'hints') expected = {self.problem_id: [(u'2.0', {u'2': [u'Hint 2', 1]})]} self.assertTrue(out['all_hints'] == expected) def test_gethints_other(self): """ Same as above, with hints instead of mod_queue """ request = RequestFactory() post = request.post(self.url, {'field': 'hints'}) out = view.get_hints(post, self.course_id, 'hints') print out self.assertTrue(out['other_field'] == 'mod_queue') expected = {self.problem_id: [('1.0', {'1': ['Hint 1', 2], '3': ['Hint 3', 12]}), ('2.0', {'4': ['Hint 4', 3]}) ]} self.assertTrue(out['all_hints'] == expected) def test_deletehints(self): """ Checks that delete_hints deletes the right stuff. """ request = RequestFactory() post = request.post(self.url, {'field': 'hints', 'op': 'delete hints', 1: [self.problem_id, '1.0', '1']}) view.delete_hints(post, self.course_id, 'hints') problem_hints = XModuleUserStateSummaryField.objects.get(field_name='hints', usage_id=self.problem_id).value self.assertTrue('1' not in json.loads(problem_hints)['1.0']) def test_changevotes(self): """ Checks that vote changing works. """ request = RequestFactory() post = request.post(self.url, {'field': 'hints', 'op': 'change votes', 1: [self.problem_id, '1.0', '1', 5]}) view.change_votes(post, self.course_id, 'hints') problem_hints = XModuleUserStateSummaryField.objects.get(field_name='hints', usage_id=self.problem_id).value # hints[answer][hint_pk (string)] = [hint text, vote count] print json.loads(problem_hints)['1.0']['1'] self.assertTrue(json.loads(problem_hints)['1.0']['1'][1] == 5) def test_addhint(self): """ Check that instructors can add new hints. """ # Because add_hint accesses the xmodule, this test requires a bunch # of monkey patching. hinter = MagicMock() hinter.validate_answer = lambda string: True request = RequestFactory() post = request.post(self.url, {'field': 'mod_queue', 'op': 'add hint', 'problem': self.problem_id, 'answer': '3.14', 'hint': 'This is a new hint.'}) post.user = 'fake user' with patch('courseware.module_render.get_module', MagicMock(return_value=hinter)): with patch('courseware.model_data.FieldDataCache', MagicMock(return_value=None)): view.add_hint(post, self.course_id, 'mod_queue') problem_hints = XModuleUserStateSummaryField.objects.get(field_name='mod_queue', usage_id=self.problem_id).value self.assertTrue('3.14' in json.loads(problem_hints)) def test_addbadhint(self): """ Check that instructors cannot add hints with unparsable answers. """ # Patching. hinter = MagicMock() hinter.validate_answer = lambda string: False request = RequestFactory() post = request.post(self.url, {'field': 'mod_queue', 'op': 'add hint', 'problem': self.problem_id, 'answer': 'fish', 'hint': 'This is a new hint.'}) post.user = 'fake user' with patch('courseware.module_render.get_module', MagicMock(return_value=hinter)): with patch('courseware.model_data.FieldDataCache', MagicMock(return_value=None)): view.add_hint(post, self.course_id, 'mod_queue') problem_hints = XModuleUserStateSummaryField.objects.get(field_name='mod_queue', usage_id=self.problem_id).value self.assertTrue('fish' not in json.loads(problem_hints)) def test_approve(self): """ Check that instructors can approve hints. (Move them from the mod_queue to the hints.) """ request = RequestFactory() post = request.post(self.url, {'field': 'mod_queue', 'op': 'approve', 1: [self.problem_id, '2.0', '2']}) view.approve(post, self.course_id, 'mod_queue') problem_hints = XModuleUserStateSummaryField.objects.get(field_name='mod_queue', usage_id=self.problem_id).value self.assertTrue('2.0' not in json.loads(problem_hints) or len(json.loads(problem_hints)['2.0']) == 0) problem_hints = XModuleUserStateSummaryField.objects.get(field_name='hints', usage_id=self.problem_id).value self.assertTrue(json.loads(problem_hints)['2.0']['2'] == ['Hint 2', 1]) self.assertTrue(len(json.loads(problem_hints)['2.0']) == 2)