class TestInstructorEnrollmentStudentModule(TestCase):
    """ Test student module manipulations. """

    def setUp(self):
        super(TestInstructorEnrollmentStudentModule, self).setUp()
        self.course_key = SlashSeparatedCourseKey("fake", "course", "id")

    def test_reset_student_attempts(self):
        user = UserFactory()
        msk = self.course_key.make_usage_key("dummy", "module")
        original_state = json.dumps({"attempts": 32, "otherstuff": "alsorobots"})
        StudentModule.objects.create(
            student=user, course_id=self.course_key, module_state_key=msk, state=original_state
        )
        # lambda to reload the module state from the database
        module = lambda: StudentModule.objects.get(student=user, course_id=self.course_key, module_state_key=msk)
        self.assertEqual(json.loads(module().state)["attempts"], 32)
        reset_student_attempts(self.course_key, user, msk)
        self.assertEqual(json.loads(module().state)["attempts"], 0)

    def test_delete_student_attempts(self):
        user = UserFactory()
        msk = self.course_key.make_usage_key("dummy", "module")
        original_state = json.dumps({"attempts": 32, "otherstuff": "alsorobots"})
        StudentModule.objects.create(
            student=user, course_id=self.course_key, module_state_key=msk, state=original_state
        )
        self.assertEqual(
            StudentModule.objects.filter(student=user, course_id=self.course_key, module_state_key=msk).count(), 1
        )
        reset_student_attempts(self.course_key, user, msk, delete_module=True)
        self.assertEqual(
            StudentModule.objects.filter(student=user, course_id=self.course_key, module_state_key=msk).count(), 0
        )

    def test_delete_submission_scores(self):
        user = UserFactory()
        problem_location = self.course_key.make_usage_key("dummy", "module")

        # Create a student module for the user
        StudentModule.objects.create(
            student=user, course_id=self.course_key, module_state_key=problem_location, state=json.dumps({})
        )

        # Create a submission and score for the student using the submissions API
        student_item = {
            "student_id": anonymous_id_for_user(user, self.course_key),
            "course_id": self.course_key.to_deprecated_string(),
            "item_id": problem_location.to_deprecated_string(),
            "item_type": "openassessment",
        }
        submission = sub_api.create_submission(student_item, "test answer")
        sub_api.set_score(submission["uuid"], 1, 2)

        # Delete student state using the instructor dash
        reset_student_attempts(self.course_key, user, problem_location, delete_module=True)

        # Verify that the student's scores have been reset in the submissions API
        score = sub_api.get_score(student_item)
        self.assertIs(score, None)
Beispiel #2
0
    def lms_link_test(self):
        """ Tests get_lms_link_for_item. """
        course_key = SlashSeparatedCourseKey('mitX', '101', 'test')
        location = course_key.make_usage_key('vertical', 'contacting_us')
        link = utils.get_lms_link_for_item(location, False)
        self.assertEquals(
            link,
            "//localhost:8000/courses/mitX/101/test/jump_to/i4x://mitX/101/vertical/contacting_us"
        )

        # test preview
        # for edunext the second parameter is False, its the same test that before
        # link = utils.get_lms_link_for_item(location, True)

        # self.assertEquals(
        #     link,
        #     "//localhost:8000/courses/mitX/101/test/jump_to/i4x://mitX/101/vertical/contacting_us"
        # )

        # now test with the course' location
        location = course_key.make_usage_key('course', 'test')
        link = utils.get_lms_link_for_item(location)
        self.assertEquals(
            link,
            "//localhost:8000/courses/mitX/101/test/jump_to/i4x://mitX/101/course/test"
        )
Beispiel #3
0
 def test_rewrite_reference_list(self):
     module_store = modulestore()
     target_course_id = SlashSeparatedCourseKey('testX', 'conditional_copy', 'copy_run')
     import_from_xml(
         module_store,
         self.user.id,
         'common/test/data/',
         ['conditional'],
         target_course_id=target_course_id
     )
     conditional_module = module_store.get_item(
         target_course_id.make_usage_key('conditional', 'condone')
     )
     self.assertIsNotNone(conditional_module)
     different_course_id = SlashSeparatedCourseKey('edX', 'different_course', None)
     self.assertListEqual(
         [
             target_course_id.make_usage_key('problem', 'choiceprob'),
             different_course_id.make_usage_key('html', 'for_testing_import_rewrites')
         ],
         conditional_module.sources_list
     )
     self.assertListEqual(
         [
             target_course_id.make_usage_key('html', 'congrats'),
             target_course_id.make_usage_key('html', 'secret_page')
         ],
         conditional_module.show_tag_list
     )
 def test_rewrite_reference_list(self):
     module_store = modulestore()
     target_course_id = SlashSeparatedCourseKey('testX', 'conditional_copy', 'copy_run')
     import_from_xml(
         module_store,
         self.user.id,
         'common/test/data/',
         ['conditional'],
         target_course_id=target_course_id
     )
     conditional_module = module_store.get_item(
         target_course_id.make_usage_key('conditional', 'condone')
     )
     self.assertIsNotNone(conditional_module)
     different_course_id = SlashSeparatedCourseKey('edX', 'different_course', None)
     self.assertListEqual(
         [
             target_course_id.make_usage_key('problem', 'choiceprob'),
             different_course_id.make_usage_key('html', 'for_testing_import_rewrites')
         ],
         conditional_module.sources_list
     )
     self.assertListEqual(
         [
             target_course_id.make_usage_key('html', 'congrats'),
             target_course_id.make_usage_key('html', 'secret_page')
         ],
         conditional_module.show_tag_list
     )
class TestJumpTo(TestCase):
    """
    Check the jumpto link for a course.
    """

    def setUp(self):
        # Use toy course from XML
        self.course_key = SlashSeparatedCourseKey('edX', 'toy', '2012_Fall')

    def test_jumpto_invalid_location(self):
        location = self.course_key.make_usage_key(None, 'NoSuchPlace')
        # This is fragile, but unfortunately the problem is that within the LMS we
        # can't use the reverse calls from the CMS
        jumpto_url = '{0}/{1}/jump_to/{2}'.format('/courses', self.course_key.to_deprecated_string(), location.to_deprecated_string())
        response = self.client.get(jumpto_url)
        self.assertEqual(response.status_code, 404)

    def test_jumpto_from_chapter(self):
        location = self.course_key.make_usage_key('chapter', 'Overview')
        jumpto_url = '{0}/{1}/jump_to/{2}'.format('/courses', self.course_key.to_deprecated_string(), location.to_deprecated_string())
        expected = 'courses/edX/toy/2012_Fall/courseware/Overview/'
        response = self.client.get(jumpto_url)
        self.assertRedirects(response, expected, status_code=302, target_status_code=302)

    def test_jumpto_id(self):
        jumpto_url = '{0}/{1}/jump_to_id/{2}'.format('/courses', self.course_key.to_deprecated_string(), 'Overview')
        expected = 'courses/edX/toy/2012_Fall/courseware/Overview/'
        response = self.client.get(jumpto_url)
        self.assertRedirects(response, expected, status_code=302, target_status_code=302)

    def test_jumpto_id_invalid_location(self):
        location = Location('edX', 'toy', 'NoSuchPlace', None, None, None)
        jumpto_url = '{0}/{1}/jump_to_id/{2}'.format('/courses', self.course_key.to_deprecated_string(), location.to_deprecated_string())
        response = self.client.get(jumpto_url)
        self.assertEqual(response.status_code, 404)
Beispiel #6
0
class TestInstructorEnrollmentStudentModule(TestCase):
    """ Test student module manipulations. """
    def setUp(self):
        self.course_key = SlashSeparatedCourseKey('fake', 'course', 'id')

    def test_reset_student_attempts(self):
        user = UserFactory()
        msk = self.course_key.make_usage_key('dummy', 'module')
        original_state = json.dumps({'attempts': 32, 'otherstuff': 'alsorobots'})
        StudentModule.objects.create(student=user, course_id=self.course_key, module_state_key=msk, state=original_state)
        # lambda to reload the module state from the database
        module = lambda: StudentModule.objects.get(student=user, course_id=self.course_key, module_state_key=msk)
        self.assertEqual(json.loads(module().state)['attempts'], 32)
        reset_student_attempts(self.course_key, user, msk)
        self.assertEqual(json.loads(module().state)['attempts'], 0)

    def test_delete_student_attempts(self):
        user = UserFactory()
        msk = self.course_key.make_usage_key('dummy', 'module')
        original_state = json.dumps({'attempts': 32, 'otherstuff': 'alsorobots'})
        StudentModule.objects.create(student=user, course_id=self.course_key, module_state_key=msk, state=original_state)
        self.assertEqual(StudentModule.objects.filter(student=user, course_id=self.course_key, module_state_key=msk).count(), 1)
        reset_student_attempts(self.course_key, user, msk, delete_module=True)
        self.assertEqual(StudentModule.objects.filter(student=user, course_id=self.course_key, module_state_key=msk).count(), 0)

    def test_delete_submission_scores(self):
        user = UserFactory()
        problem_location = self.course_key.make_usage_key('dummy', 'module')

        # Create a student module for the user
        StudentModule.objects.create(
            student=user,
            course_id=self.course_key,
            module_state_key=problem_location,
            state=json.dumps({})
        )

        # Create a submission and score for the student using the submissions API
        student_item = {
            'student_id': anonymous_id_for_user(user, self.course_key),
            'course_id': self.course_key.to_deprecated_string(),
            'item_id': problem_location.to_deprecated_string(),
            'item_type': 'openassessment'
        }
        submission = sub_api.create_submission(student_item, 'test answer')
        sub_api.set_score(submission['uuid'], 1, 2)

        # Delete student state using the instructor dash
        reset_student_attempts(
            self.course_key, user, problem_location,
            delete_module=True
        )

        # Verify that the student's scores have been reset in the submissions API
        score = sub_api.get_score(student_item)
        self.assertIs(score, None)
Beispiel #7
0
    def import_and_populate_course(self):
        """
        Imports the test toy course and populates it with additional test data
        """
        content_store = contentstore()
        import_from_xml(self.store, self.user.id, 'common/test/data/', ['toy'], static_content_store=content_store)
        course_id = SlashSeparatedCourseKey('edX', 'toy', '2012_Fall')

        # create an Orphan
        # We had a bug where orphaned draft nodes caused export to fail. This is here to cover that case.
        vertical = self.store.get_item(course_id.make_usage_key('vertical', self.TEST_VERTICAL), depth=1)
        vertical.location = vertical.location.replace(name='no_references')
        self.store.update_item(vertical, self.user.id, allow_not_found=True)
        orphan_vertical = self.store.get_item(vertical.location)
        self.assertEqual(orphan_vertical.location.name, 'no_references')
        self.assertEqual(len(orphan_vertical.children), len(vertical.children))

        # create a Draft vertical
        vertical = self.store.get_item(course_id.make_usage_key('vertical', self.TEST_VERTICAL), depth=1)
        draft_vertical = self.store.convert_to_draft(vertical.location, self.user.id)
        self.assertEqual(self.store.compute_publish_state(draft_vertical), PublishState.draft)

        # create a Private (draft only) vertical
        private_vertical = self.store.create_item(self.user.id, course_id, 'vertical', self.PRIVATE_VERTICAL)
        self.assertEqual(self.store.compute_publish_state(private_vertical), PublishState.private)

        # create a Published (no draft) vertical
        public_vertical = self.store.create_item(self.user.id, course_id, 'vertical', self.PUBLISHED_VERTICAL)
        public_vertical = self.store.publish(public_vertical.location, self.user.id)
        self.assertEqual(self.store.compute_publish_state(public_vertical), PublishState.public)

        # add the new private and new public as children of the sequential
        sequential = self.store.get_item(course_id.make_usage_key('sequential', self.SEQUENTIAL))
        sequential.children.append(private_vertical.location)
        sequential.children.append(public_vertical.location)
        self.store.update_item(sequential, self.user.id)

        # lock an asset
        content_store.set_attr(self.LOCKED_ASSET_KEY, 'locked', True)

        # create a non-portable link - should be rewritten in new courses
        html_module = self.store.get_item(course_id.make_usage_key('html', 'nonportable'))
        new_data = html_module.data = html_module.data.replace(
            '/static/',
            '/c4x/{0}/{1}/asset/'.format(course_id.org, course_id.course)
        )
        self.store.update_item(html_module, self.user.id)

        html_module = self.store.get_item(html_module.location)
        self.assertEqual(new_data, html_module.data)

        return course_id
Beispiel #8
0
    def import_and_populate_course(self):
        """
        Imports the test toy course and populates it with additional test data
        """
        content_store = contentstore()
        import_from_xml(self.store, self.user.id, 'common/test/data/', ['toy'], static_content_store=content_store)
        course_id = SlashSeparatedCourseKey('edX', 'toy', '2012_Fall')

        # create an Orphan
        # We had a bug where orphaned draft nodes caused export to fail. This is here to cover that case.
        vertical = self.store.get_item(course_id.make_usage_key('vertical', self.TEST_VERTICAL), depth=1)
        vertical.location = vertical.location.replace(name='no_references')
        self.store.update_item(vertical, self.user.id, allow_not_found=True)
        orphan_vertical = self.store.get_item(vertical.location)
        self.assertEqual(orphan_vertical.location.name, 'no_references')
        self.assertEqual(len(orphan_vertical.children), len(vertical.children))

        # create a Draft vertical
        vertical = self.store.get_item(course_id.make_usage_key('vertical', self.TEST_VERTICAL), depth=1)
        draft_vertical = self.store.convert_to_draft(vertical.location, self.user.id)
        self.assertTrue(self.store.has_published_version(draft_vertical))

        # create a Private (draft only) vertical
        private_vertical = self.store.create_item(self.user.id, course_id, 'vertical', self.PRIVATE_VERTICAL)
        self.assertFalse(self.store.has_published_version(private_vertical))

        # create a Published (no draft) vertical
        public_vertical = self.store.create_item(self.user.id, course_id, 'vertical', self.PUBLISHED_VERTICAL)
        public_vertical = self.store.publish(public_vertical.location, self.user.id)
        self.assertTrue(self.store.has_published_version(public_vertical))

        # add the new private and new public as children of the sequential
        sequential = self.store.get_item(course_id.make_usage_key('sequential', self.SEQUENTIAL))
        sequential.children.append(private_vertical.location)
        sequential.children.append(public_vertical.location)
        self.store.update_item(sequential, self.user.id)

        # lock an asset
        content_store.set_attr(self.LOCKED_ASSET_KEY, 'locked', True)

        # create a non-portable link - should be rewritten in new courses
        html_module = self.store.get_item(course_id.make_usage_key('html', 'nonportable'))
        new_data = html_module.data = html_module.data.replace(
            '/static/',
            '/c4x/{0}/{1}/asset/'.format(course_id.org, course_id.course)
        )
        self.store.update_item(html_module, self.user.id)

        html_module = self.store.get_item(html_module.location)
        self.assertEqual(new_data, html_module.data)

        return course_id
Beispiel #9
0
    def lms_link_test(self):
        """ Tests get_lms_link_for_item. """
        course_key = SlashSeparatedCourseKey("mitX", "101", "test")
        location = course_key.make_usage_key("vertical", "contacting_us")
        link = utils.get_lms_link_for_item(location, False)
        self.assertEquals(link, "//localhost:8000/courses/mitX/101/test/jump_to/i4x://mitX/101/vertical/contacting_us")

        # test preview
        link = utils.get_lms_link_for_item(location, True)
        self.assertEquals(link, "//preview/courses/mitX/101/test/jump_to/i4x://mitX/101/vertical/contacting_us")

        # now test with the course' location
        location = course_key.make_usage_key("course", "test")
        link = utils.get_lms_link_for_item(location)
        self.assertEquals(link, "//localhost:8000/courses/mitX/101/test/jump_to/i4x://mitX/101/course/test")
Beispiel #10
0
 def test_rewrite_reference(self):
     module_store = modulestore('direct')
     target_course_id = SlashSeparatedCourseKey('testX', 'peergrading_copy',
                                                'copy_run')
     import_from_xml(module_store,
                     'common/test/data/', ['open_ended'],
                     target_course_id=target_course_id)
     peergrading_module = module_store.get_item(
         target_course_id.make_usage_key('peergrading',
                                         'PeerGradingLinked'))
     self.assertIsNotNone(peergrading_module)
     self.assertEqual(
         target_course_id.make_usage_key('combinedopenended',
                                         'SampleQuestion'),
         peergrading_module.link_to_location)
    def test_delete_course_map(self):
        """
        Test that course location is properly remove from loc_mapper and cache when course is deleted
        """
        org = u'foo_org'
        course = u'bar_course'
        run = u'baz_run'
        course_location = SlashSeparatedCourseKey(org, course, run)
        loc_mapper().create_map_entry(course_location)
        # pylint: disable=protected-access
        entry = loc_mapper().location_map.find_one({
            '_id': loc_mapper()._construct_course_son(course_location)
        })
        self.assertIsNotNone(entry, 'Entry not found in loc_mapper')
        self.assertEqual(entry['offering'], u'{1}.{2}'.format(org, course, run))

        # now delete course location from loc_mapper and cache and test that course location no longer
        # exists in loca_mapper and cache
        loc_mapper().delete_course_mapping(course_location)
        # pylint: disable=protected-access
        entry = loc_mapper().location_map.find_one({
            '_id': loc_mapper()._construct_course_son(course_location)
        })
        self.assertIsNone(entry, 'Entry found in loc_mapper')
        # pylint: disable=protected-access
        cached_value = loc_mapper()._get_location_from_cache(course_location.make_usage_key('course', run))
        self.assertIsNone(cached_value, 'course_locator found in cache')
        # pylint: disable=protected-access
        cached_value = loc_mapper()._get_course_location_from_cache(course_location)
        self.assertIsNone(cached_value, 'Entry found in cache')
class SetupTestErrorModules():
    def setUp(self):
        self.system = get_test_system()
        self.course_id = SlashSeparatedCourseKey('org', 'course', 'run')
        self.location = self.course_id.make_usage_key('foo', 'bar')
        self.valid_xml = u"<problem>ABC \N{SNOWMAN}</problem>"
        self.error_msg = "Error"
class SetupTestErrorModules():
    def setUp(self):
        self.system = get_test_system()
        self.course_id = SlashSeparatedCourseKey('org', 'course', 'run')
        self.location = self.course_id.make_usage_key('foo', 'bar')
        self.valid_xml = u"<problem>ABC \N{SNOWMAN}</problem>"
        self.error_msg = "Error"
Beispiel #14
0
    def test_delete_course_map(self):
        """
        Test that course location is properly remove from loc_mapper and cache when course is deleted
        """
        org = u"foo_org"
        course = u"bar_course"
        run = u"baz_run"
        course_location = SlashSeparatedCourseKey(org, course, run)
        loc_mapper().create_map_entry(course_location)
        # pylint: disable=protected-access
        entry = loc_mapper().location_map.find_one({"_id": loc_mapper()._construct_course_son(course_location)})
        self.assertIsNotNone(entry, "Entry not found in loc_mapper")
        self.assertEqual(entry["offering"], u"{1}.{2}".format(org, course, run))

        # now delete course location from loc_mapper and cache and test that course location no longer
        # exists in loca_mapper and cache
        loc_mapper().delete_course_mapping(course_location)
        # pylint: disable=protected-access
        entry = loc_mapper().location_map.find_one({"_id": loc_mapper()._construct_course_son(course_location)})
        self.assertIsNone(entry, "Entry found in loc_mapper")
        # pylint: disable=protected-access
        cached_value = loc_mapper()._get_location_from_cache(course_location.make_usage_key("course", run))
        self.assertIsNone(cached_value, "course_locator found in cache")
        # pylint: disable=protected-access
        cached_value = loc_mapper()._get_course_location_from_cache(course_location)
        self.assertIsNone(cached_value, "Entry found in cache")
    def test_round_trip(self, source_builder, dest_builder, source_content_builder, dest_content_builder, course_data_name):
        source_course_key = SlashSeparatedCourseKey('source', 'course', 'key')
        dest_course_key = SlashSeparatedCourseKey('dest', 'course', 'key')

        # Construct the contentstore for storing the first import
        with source_content_builder.build() as source_content:
            # Construct the modulestore for storing the first import (using the previously created contentstore)
            with source_builder.build(source_content) as source_store:
                # Construct the contentstore for storing the second import
                with dest_content_builder.build() as dest_content:
                    # Construct the modulestore for storing the second import (using the second contentstore)
                    with dest_builder.build(dest_content) as dest_store:
                        import_from_xml(
                            source_store,
                            'test_user',
                            'common/test/data',
                            course_dirs=[course_data_name],
                            static_content_store=source_content,
                            target_course_id=source_course_key,
                            create_new_course_if_not_present=True,
                        )

                        export_to_xml(
                            source_store,
                            source_content,
                            source_course_key,
                            self.export_dir,
                            'exported_course',
                        )

                        import_from_xml(
                            dest_store,
                            'test_user',
                            self.export_dir,
                            static_content_store=dest_content,
                            target_course_id=dest_course_key,
                            create_new_course_if_not_present=True,
                        )

                        self.exclude_field(source_course_key.make_usage_key('course', 'key'), 'wiki_slug')
                        self.exclude_field(None, 'xml_attributes')
                        self.ignore_asset_key('_id')
                        self.ignore_asset_key('uploadDate')
                        self.ignore_asset_key('content_son')
                        self.ignore_asset_key('thumbnail_location')

                        self.assertCoursesEqual(
                            source_store,
                            source_course_key,
                            dest_store,
                            dest_course_key,
                        )

                        self.assertAssetsEqual(
                            source_content,
                            source_course_key,
                            dest_content,
                            dest_course_key,
                        )
Beispiel #16
0
 def setUp(self):
     course_key = SlashSeparatedCourseKey('edX', 'toy', '2012_Fall')
     self.course = course_key.make_usage_key('course', course_key.run)
     self.anonymous_user = AnonymousUserFactory()
     self.student = UserFactory()
     self.global_staff = UserFactory(is_staff=True)
     self.course_staff = StaffFactory(course_key=self.course.course_key)
     self.course_instructor = InstructorFactory(course_key=self.course.course_key)
Beispiel #17
0
 def test_rewrite_reference(self):
     module_store = modulestore('direct')
     target_course_id = SlashSeparatedCourseKey('testX', 'peergrading_copy', 'copy_run')
     import_from_xml(
         module_store,
         'common/test/data/',
         ['open_ended'],
         target_course_id=target_course_id
     )
     peergrading_module = module_store.get_item(
         target_course_id.make_usage_key('peergrading', 'PeerGradingLinked')
     )
     self.assertIsNotNone(peergrading_module)
     self.assertEqual(
         target_course_id.make_usage_key('combinedopenended', 'SampleQuestion'),
         peergrading_module.link_to_location
     )
Beispiel #18
0
 def setUp(self):
     course_key = SlashSeparatedCourseKey('edX', 'toy', '2012_Fall')
     self.course = course_key.make_usage_key('course', course_key.run)
     self.anonymous_user = AnonymousUserFactory()
     self.student = UserFactory()
     self.global_staff = UserFactory(is_staff=True)
     self.course_staff = StaffFactory(course_key=self.course.course_key)
     self.course_instructor = InstructorFactory(course_key=self.course.course_key)
Beispiel #19
0
 def test_rewrite_reference_value_dict(self):
     module_store = modulestore('direct')
     target_course_id = SlashSeparatedCourseKey('testX', 'split_test_copy',
                                                'copy_run')
     import_from_xml(module_store,
                     'common/test/data/', ['split_test_module'],
                     target_course_id=target_course_id)
     split_test_module = module_store.get_item(
         target_course_id.make_usage_key('split_test', 'split1'))
     self.assertIsNotNone(split_test_module)
     self.assertEqual(
         {
             "0": target_course_id.make_usage_key('vertical', 'sample_0'),
             "2": target_course_id.make_usage_key('vertical', 'sample_2'),
         },
         split_test_module.group_id_to_child,
     )
Beispiel #20
0
 def setUp(self):
     system = get_test_descriptor_system()
     course_key = SlashSeparatedCourseKey("org", "course", "run")
     usage_key = course_key.make_usage_key("video", "name")
     self.descriptor = system.construct_xblock_from_class(
         VideoDescriptor, scope_ids=ScopeIds(None, None, usage_key, usage_key), field_data=DictFieldData({})
     )
     self.descriptor.runtime.handler_url = MagicMock()
class SetupTestErrorModules(unittest.TestCase):
    """Common setUp for use in ErrorModule tests."""
    def setUp(self):
        super(SetupTestErrorModules, self).setUp()
        self.system = get_test_system()
        self.course_id = SlashSeparatedCourseKey('org', 'course', 'run')
        self.location = self.course_id.make_usage_key('foo', 'bar')
        self.valid_xml = u"<problem>ABC \N{SNOWMAN}</problem>"
        self.error_msg = "Error"
Beispiel #22
0
def i_click_on_error_dialog(step):
    world.click_link_by_text('Correct failed component')
    assert_true(world.css_html("span.inline-error").startswith("Problem i4x://MITx/999/problem"))
    course_key = SlashSeparatedCourseKey("MITx", "999", "Robot_Super_Course")
    # we don't know the actual ID of the vertical. So just check that we did go to a
    # vertical page in the course (there should only be one).
    vertical_usage_key = course_key.make_usage_key("vertical", "")
    vertical_url = reverse_usage_url('unit_handler', vertical_usage_key)
    assert_equal(1, world.browser.url.count(vertical_url))
Beispiel #23
0
class SetupTestErrorModules(unittest.TestCase):
    """Common setUp for use in ErrorModule tests."""
    def setUp(self):
        super(SetupTestErrorModules, self).setUp()
        self.system = get_test_system()
        self.course_id = SlashSeparatedCourseKey('org', 'course', 'run')
        self.location = self.course_id.make_usage_key('foo', 'bar')
        self.valid_xml = u"<problem>ABC \N{SNOWMAN}</problem>"
        self.error_msg = "Error"
    def setup_xml_course(self):
        """
        Define the XML backed course to use.
        Toy courses are already loaded in XML and mixed modulestores.
        """
        course_key = SlashSeparatedCourseKey("edX", "toy", "2012_Fall")
        location = course_key.make_usage_key("chapter", "Overview")
        descriptor = modulestore().get_item(location)

        self.module = self._get_module(course_key, descriptor, location)
Beispiel #25
0
 def setUp(self):
     system = get_test_descriptor_system()
     course_key = SlashSeparatedCourseKey('org', 'course', 'run')
     usage_key = course_key.make_usage_key('video', 'name')
     self.descriptor = system.construct_xblock_from_class(
         VideoDescriptor,
         scope_ids=ScopeIds(None, None, usage_key, usage_key),
         field_data=DictFieldData({}),
     )
     self.descriptor.runtime.handler_url = MagicMock()
Beispiel #26
0
    def setup_xml_course(self):
        """
        Define the XML backed course to use.
        Toy courses are already loaded in XML and mixed modulestores.
        """
        course_key = SlashSeparatedCourseKey('edX', 'toy', '2012_Fall')
        location = course_key.make_usage_key('chapter', 'Overview')
        descriptor = modulestore().get_item(location)

        self.module = self._get_module(course_key, descriptor, location)
def instantiate_descriptor(**field_data):
    """
    Instantiate descriptor with most properties.
    """
    system = get_test_descriptor_system()
    course_key = SlashSeparatedCourseKey("org", "course", "run")
    usage_key = course_key.make_usage_key("html", "SampleHtml")
    return system.construct_xblock_from_class(
        HtmlDescriptor, scope_ids=ScopeIds(None, None, usage_key, usage_key), field_data=DictFieldData(field_data)
    )
Beispiel #28
0
    def create_course(self,
                      org,
                      offering,
                      user_id=None,
                      fields=None,
                      **kwargs):
        """
        Creates and returns the course.

        Args:
            org (str): the organization that owns the course
            offering (str): the name of the course offering
            user_id: id of the user creating the course
            fields (dict): Fields to set on the course at initialization
            kwargs: Any optional arguments understood by a subset of modulestores to customize instantiation

        Returns: a CourseDescriptor

        Raises:
            InvalidLocationError: If a course with the same org and offering already exists
        """

        course, _, run = offering.partition('/')
        course_id = SlashSeparatedCourseKey(org, course, run)

        # Check if a course with this org/course has been defined before (case-insensitive)
        course_search_location = SON([
            ('_id.tag', 'i4x'),
            ('_id.org', re.compile(u'^{}$'.format(course_id.org),
                                   re.IGNORECASE)),
            ('_id.course',
             re.compile(u'^{}$'.format(course_id.course), re.IGNORECASE)),
            ('_id.category', 'course'),
        ])
        courses = self.collection.find(course_search_location, fields=('_id'))
        if courses.count() > 0:
            raise InvalidLocationError(
                "There are already courses with the given org and course id: {}"
                .format([course['_id'] for course in courses]))

        location = course_id.make_usage_key('course', course_id.run)
        course = self.create_and_save_xmodule(location,
                                              fields=fields,
                                              **kwargs)

        # clone a default 'about' overview module as well
        about_location = location.replace(category='about', name='overview')
        overview_template = AboutDescriptor.get_template('overview.yaml')
        self.create_and_save_xmodule(
            about_location,
            system=course.system,
            definition_data=overview_template.get('data'))

        return course
Beispiel #29
0
    def _verify_split_test_import(self, target_course_name, source_course_name,
                                  split_test_name, groups_to_verticals):
        module_store = modulestore()
        target_course_id = SlashSeparatedCourseKey('testX', target_course_name,
                                                   'copy_run')
        import_from_xml(module_store,
                        self.user.id,
                        'common/test/data/', [source_course_name],
                        target_course_id=target_course_id)
        split_test_module = module_store.get_item(
            target_course_id.make_usage_key('split_test', split_test_name))
        self.assertIsNotNone(split_test_module)

        remapped_verticals = {
            key: target_course_id.make_usage_key('vertical', value)
            for key, value in groups_to_verticals.iteritems()
        }

        self.assertEqual(remapped_verticals,
                         split_test_module.group_id_to_child)
Beispiel #30
0
 def test_rewrite_reference_value_dict(self):
     module_store = modulestore('direct')
     target_course_id = SlashSeparatedCourseKey('testX', 'split_test_copy', 'copy_run')
     import_from_xml(
         module_store,
         'common/test/data/',
         ['split_test_module'],
         target_course_id=target_course_id
     )
     split_test_module = module_store.get_item(
         target_course_id.make_usage_key('split_test', 'split1')
     )
     self.assertIsNotNone(split_test_module)
     self.assertEqual(
         {
             "0": target_course_id.make_usage_key('vertical', 'sample_0'),
             "2": target_course_id.make_usage_key('vertical', 'sample_2'),
         },
         split_test_module.group_id_to_child,
     )
Beispiel #31
0
 def setUp(self):
     super(AccessTestCase, self).setUp()
     course_key = SlashSeparatedCourseKey("edX", "toy", "2012_Fall")
     self.course = course_key.make_usage_key("course", course_key.run)
     self.anonymous_user = AnonymousUserFactory()
     self.beta_user = BetaTesterFactory(course_key=self.course.course_key)
     self.student = UserFactory()
     self.global_staff = UserFactory(is_staff=True)
     self.course_staff = StaffFactory(course_key=self.course.course_key)
     self.course_instructor = InstructorFactory(course_key=self.course.course_key)
     self.staff = GlobalStaffFactory()
    def _verify_split_test_import(self, target_course_name, source_course_name, split_test_name, groups_to_verticals):
        module_store = modulestore()
        target_course_id = SlashSeparatedCourseKey('testX', target_course_name, 'copy_run')
        import_from_xml(
            module_store,
            self.user.id,
            'common/test/data/',
            [source_course_name],
            target_course_id=target_course_id
        )
        split_test_module = module_store.get_item(
            target_course_id.make_usage_key('split_test', split_test_name)
        )
        self.assertIsNotNone(split_test_module)

        remapped_verticals = {
            key: target_course_id.make_usage_key('vertical', value) for key, value in groups_to_verticals.iteritems()
        }

        self.assertEqual(remapped_verticals, split_test_module.group_id_to_child)
Beispiel #33
0
def i_click_on_error_dialog(step):
    world.click_link_by_text('Correct failed component')
    assert_true(
        world.css_html("span.inline-error").startswith(
            "Problem i4x://MITx/999/problem"))
    course_key = SlashSeparatedCourseKey("MITx", "999", "Robot_Super_Course")
    # we don't know the actual ID of the vertical. So just check that we did go to a
    # vertical page in the course (there should only be one).
    vertical_usage_key = course_key.make_usage_key("vertical", "")
    vertical_url = reverse_usage_url('unit_handler', vertical_usage_key)
    assert_equal(1, world.browser.url.count(vertical_url))
Beispiel #34
0
def instantiate_descriptor(**field_data):
    """
    Instantiate descriptor with most properties.
    """
    system = get_test_descriptor_system()
    course_key = SlashSeparatedCourseKey('org', 'course', 'run')
    usage_key = course_key.make_usage_key('video', 'SampleProblem')
    return system.construct_xblock_from_class(
        VideoDescriptor,
        scope_ids=ScopeIds(None, None, usage_key, usage_key),
        field_data=DictFieldData(field_data),
    )
Beispiel #35
0
 def setUp(self):
     super(AccessTestCase, self).setUp()
     course_key = SlashSeparatedCourseKey('edX', 'toy', '2012_Fall')
     self.course = course_key.make_usage_key('course', course_key.run)
     self.anonymous_user = AnonymousUserFactory()
     self.beta_user = BetaTesterFactory(course_key=self.course.course_key)
     self.student = UserFactory()
     self.global_staff = UserFactory(is_staff=True)
     self.course_staff = StaffFactory(course_key=self.course.course_key)
     self.course_instructor = InstructorFactory(
         course_key=self.course.course_key)
     self.staff = GlobalStaffFactory()
Beispiel #36
0
def instantiate_descriptor(**field_data):
    """
    Instantiate descriptor with most properties.
    """
    system = get_test_descriptor_system()
    course_key = SlashSeparatedCourseKey('org', 'course', 'run')
    usage_key = course_key.make_usage_key('video', 'SampleProblem')
    return system.construct_xblock_from_class(
        VideoDescriptor,
        scope_ids=ScopeIds(None, None, usage_key, usage_key),
        field_data=DictFieldData(field_data),
    )
def i_click_on_error_dialog(step):
    world.click_link_by_text('Correct failed component')
    assert_true(world.css_html("span.inline-error").startswith("Problem i4x://MITx/999/problem"))
    course_key = SlashSeparatedCourseKey("MITx", "999", "Robot_Super_Course")
    # we don't know the actual ID of the vertical. So just check that we did go to a
    # vertical page in the course (there should only be one).
    vertical_usage_key = course_key.make_usage_key("vertical", None)
    vertical_url = reverse_usage_url('container_handler', vertical_usage_key)
    # Remove the trailing "/None" from the URL - we don't know the course ID, so we just want to
    # check that we visited a vertical URL.
    if vertical_url.endswith("/None"):
        vertical_url = vertical_url[:-5]
    assert_equal(1, world.browser.url.count(vertical_url))
Beispiel #38
0
    def create_course(self, org, offering, user_id=None, fields=None, **kwargs):
        """
        Creates and returns the course.

        Args:
            org (str): the organization that owns the course
            offering (str): the name of the course offering
            user_id: id of the user creating the course
            fields (dict): Fields to set on the course at initialization
            kwargs: Any optional arguments understood by a subset of modulestores to customize instantiation

        Returns: a CourseDescriptor

        Raises:
            InvalidLocationError: If a course with the same org and offering already exists
        """

        course, _, run = offering.partition('/')
        course_id = SlashSeparatedCourseKey(org, course, run)

        # Check if a course with this org/course has been defined before (case-insensitive)
        course_search_location = SON([
            ('_id.tag', 'i4x'),
            ('_id.org', re.compile(u'^{}$'.format(course_id.org), re.IGNORECASE)),
            ('_id.course', re.compile(u'^{}$'.format(course_id.course), re.IGNORECASE)),
            ('_id.category', 'course'),
        ])
        courses = self.collection.find(course_search_location, fields=('_id'))
        if courses.count() > 0:
            raise InvalidLocationError(
                "There are already courses with the given org and course id: {}".format([
                    course['_id'] for course in courses
                ]))

        location = course_id.make_usage_key('course', course_id.run)
        course = self.create_and_save_xmodule(location, user_id, fields=fields, **kwargs)

        # clone a default 'about' overview module as well
        about_location = location.replace(
            category='about',
            name='overview'
        )
        overview_template = AboutDescriptor.get_template('overview.yaml')
        self.create_and_save_xmodule(
            about_location,
            user_id,
            definition_data=overview_template.get('data'),
            runtime=course.system
        )

        return course
def i_click_on_error_dialog(step):
    world.click_link_by_text('Correct failed component')
    assert_true(
        world.css_html("span.inline-error").startswith(
            "Problem i4x://MITx/999/problem"))
    course_key = SlashSeparatedCourseKey("MITx", "999", "Robot_Super_Course")
    # we don't know the actual ID of the vertical. So just check that we did go to a
    # vertical page in the course (there should only be one).
    vertical_usage_key = course_key.make_usage_key("vertical", None)
    vertical_url = reverse_usage_url('container_handler', vertical_usage_key)
    # Remove the trailing "/None" from the URL - we don't know the course ID, so we just want to
    # check that we visited a vertical URL.
    if vertical_url.endswith("/None"):
        vertical_url = vertical_url[:-5]
    assert_equal(1, world.browser.url.count(vertical_url))
def check_path_to_location(modulestore):
    """
    Make sure that path_to_location works: should be passed a modulestore
    with the toy and simple courses loaded.
    """
    course_id = SlashSeparatedCourseKey("edX", "toy", "2012_Fall")

    should_work = (
        (course_id.make_usage_key('video', 'Welcome'),
         (course_id, "Overview", "Welcome", None)),
        (course_id.make_usage_key('chapter', 'Overview'),
         (course_id, "Overview", None, None)),
    )

    for location, expected in should_work:
        assert_equals(path_to_location(modulestore, location), expected)

    not_found = (
        course_id.make_usage_key('video', 'WelcomeX'),
        course_id.make_usage_key('course', 'NotHome'),
    )
    for location in not_found:
        with assert_raises(ItemNotFoundError):
            path_to_location(modulestore, location)
Beispiel #41
0
def check_path_to_location(modulestore):
    """
    Make sure that path_to_location works: should be passed a modulestore
    with the toy and simple courses loaded.
    """
    course_id = SlashSeparatedCourseKey("edX", "toy", "2012_Fall")

    should_work = (
        (course_id.make_usage_key('video', 'Welcome'), (course_id, "Overview",
                                                        "Welcome", None)),
        (course_id.make_usage_key('chapter', 'Overview'),
         (course_id, "Overview", None, None)),
    )

    for location, expected in should_work:
        assert_equals(path_to_location(modulestore, location), expected)

    not_found = (
        course_id.make_usage_key('video', 'WelcomeX'),
        course_id.make_usage_key('course', 'NotHome'),
    )
    for location in not_found:
        with assert_raises(ItemNotFoundError):
            path_to_location(modulestore, location)
Beispiel #42
0
    def test_graphicslidertool_import(self):
        '''
        Check to see if definition_from_xml in gst_module.py
        works properly.  Pulls data from the graphic_slider_tool directory
        in the test data directory.
        '''
        modulestore = XMLModuleStore(DATA_DIR, course_dirs=['graphic_slider_tool'])

        sa_id = SlashSeparatedCourseKey("edX", "gst_test", "2012_Fall")
        location = sa_id.make_usage_key("graphical_slider_tool", "sample_gst")
        gst_sample = modulestore.get_item(location)
        render_string_from_sample_gst_xml = """
        <slider var="a" style="width:400px;float:left;"/>\
<plot style="margin-top:15px;margin-bottom:15px;"/>""".strip()
        self.assertIn(render_string_from_sample_gst_xml, gst_sample.data)
    def test_graphicslidertool_import(self):
        '''
        Check to see if definition_from_xml in gst_module.py
        works properly.  Pulls data from the graphic_slider_tool directory
        in the test data directory.
        '''
        modulestore = XMLModuleStore(DATA_DIR, course_dirs=['graphic_slider_tool'])

        sa_id = SlashSeparatedCourseKey("edX", "gst_test", "2012_Fall")
        location = sa_id.make_usage_key("graphical_slider_tool", "sample_gst")
        gst_sample = modulestore.get_item(location)
        render_string_from_sample_gst_xml = """
        <slider var="a" style="width:400px;float:left;"/>\
<plot style="margin-top:15px;margin-bottom:15px;"/>""".strip()
        self.assertIn(render_string_from_sample_gst_xml, gst_sample.data)
    def test_round_trip(self, source_builder, dest_builder,
                        source_content_builder, dest_content_builder,
                        course_data_name):
        source_course_key = SlashSeparatedCourseKey('source', 'course', 'key')
        dest_course_key = SlashSeparatedCourseKey('dest', 'course', 'key')

        # Construct the contentstore for storing the first import
        with source_content_builder.build() as source_content:
            # Construct the modulestore for storing the first import (using the previously created contentstore)
            with source_builder.build(source_content) as source_store:
                # Construct the contentstore for storing the second import
                with dest_content_builder.build() as dest_content:
                    # Construct the modulestore for storing the second import (using the second contentstore)
                    with dest_builder.build(dest_content) as dest_store:
                        import_from_xml(
                            source_store,
                            'test_user',
                            'common/test/data',
                            course_dirs=[course_data_name],
                            static_content_store=source_content,
                            target_course_id=source_course_key,
                            create_new_course_if_not_present=True,
                        )

                        export_to_xml(
                            source_store,
                            source_content,
                            source_course_key,
                            self.export_dir,
                            'exported_course',
                        )

                        import_from_xml(
                            dest_store,
                            'test_user',
                            self.export_dir,
                            static_content_store=dest_content,
                            target_course_id=dest_course_key,
                            create_new_course_if_not_present=True,
                        )

                        self.exclude_field(
                            source_course_key.make_usage_key('course', 'key'),
                            'wiki_slug')
                        self.exclude_field(None, 'xml_attributes')
                        self.ignore_asset_key('_id')
                        self.ignore_asset_key('uploadDate')
                        self.ignore_asset_key('content_son')
                        self.ignore_asset_key('thumbnail_location')

                        self.assertCoursesEqual(
                            source_store,
                            source_course_key,
                            dest_store,
                            dest_course_key,
                        )

                        self.assertAssetsEqual(
                            source_content,
                            source_course_key,
                            dest_content,
                            dest_course_key,
                        )
Beispiel #45
0
    def import_and_populate_course(self):
        """
        Imports the test toy course and populates it with additional test data
        """
        content_store = contentstore()
        import_course_from_xml(self.store, self.user.id, TEST_DATA_DIR, ['toy'], static_content_store=content_store)
        course_id = SlashSeparatedCourseKey('edX', 'toy', '2012_Fall')

        # create an Orphan
        # We had a bug where orphaned draft nodes caused export to fail. This is here to cover that case.
        vertical = self.store.get_item(course_id.make_usage_key('vertical', self.TEST_VERTICAL), depth=1)
        vertical.location = vertical.location.replace(name='no_references')
        self.store.update_item(vertical, self.user.id, allow_not_found=True)
        orphan_vertical = self.store.get_item(vertical.location)
        self.assertEqual(orphan_vertical.location.name, 'no_references')
        self.assertEqual(len(orphan_vertical.children), len(vertical.children))

        # create an orphan vertical and html; we already don't try to import
        # the orphaned vertical, but we should make sure we don't import
        # the orphaned vertical's child html, too
        orphan_draft_vertical = self.store.create_item(
            self.user.id, course_id, 'vertical', self.ORPHAN_DRAFT_VERTICAL
        )
        orphan_draft_html = self.store.create_item(
            self.user.id, course_id, 'html', self.ORPHAN_DRAFT_HTML
        )
        orphan_draft_vertical.children.append(orphan_draft_html.location)
        self.store.update_item(orphan_draft_vertical, self.user.id)

        # create a Draft vertical
        vertical = self.store.get_item(course_id.make_usage_key('vertical', self.TEST_VERTICAL), depth=1)
        draft_vertical = self.store.convert_to_draft(vertical.location, self.user.id)
        self.assertTrue(self.store.has_published_version(draft_vertical))

        # create a Private (draft only) vertical
        private_vertical = self.store.create_item(self.user.id, course_id, 'vertical', self.PRIVATE_VERTICAL)
        self.assertFalse(self.store.has_published_version(private_vertical))

        # create a Published (no draft) vertical
        public_vertical = self.store.create_item(self.user.id, course_id, 'vertical', self.PUBLISHED_VERTICAL)
        public_vertical = self.store.publish(public_vertical.location, self.user.id)
        self.assertTrue(self.store.has_published_version(public_vertical))

        # add the new private and new public as children of the sequential
        sequential = self.store.get_item(course_id.make_usage_key('sequential', self.SEQUENTIAL))
        sequential.children.append(private_vertical.location)
        sequential.children.append(public_vertical.location)
        self.store.update_item(sequential, self.user.id)

        # create an html and video component to make drafts:
        draft_html = self.store.create_item(self.user.id, course_id, 'html', self.DRAFT_HTML)
        draft_video = self.store.create_item(self.user.id, course_id, 'video', self.DRAFT_VIDEO)

        # add them as children to the public_vertical
        public_vertical.children.append(draft_html.location)
        public_vertical.children.append(draft_video.location)
        self.store.update_item(public_vertical, self.user.id)
        # publish changes to vertical
        self.store.publish(public_vertical.location, self.user.id)
        # convert html/video to draft
        self.store.convert_to_draft(draft_html.location, self.user.id)
        self.store.convert_to_draft(draft_video.location, self.user.id)

        # lock an asset
        content_store.set_attr(self.LOCKED_ASSET_KEY, 'locked', True)

        # create a non-portable link - should be rewritten in new courses
        html_module = self.store.get_item(course_id.make_usage_key('html', 'nonportable'))
        new_data = html_module.data = html_module.data.replace(
            '/static/',
            '/c4x/{0}/{1}/asset/'.format(course_id.org, course_id.course)
        )
        self.store.update_item(html_module, self.user.id)

        html_module = self.store.get_item(html_module.location)
        self.assertEqual(new_data, html_module.data)

        return course_id
Beispiel #46
0
class RolesTestCase(TestCase):
    """
    Tests of student.roles
    """
    def setUp(self):
        self.course_key = SlashSeparatedCourseKey('edX', 'toy', '2012_Fall')
        self.course_loc = self.course_key.make_usage_key('course', '2012_Fall')
        self.anonymous_user = AnonymousUserFactory()
        self.student = UserFactory()
        self.global_staff = UserFactory(is_staff=True)
        self.course_staff = StaffFactory(course_key=self.course_key)
        self.course_instructor = InstructorFactory(course_key=self.course_key)

    def test_global_staff(self):
        self.assertFalse(GlobalStaff().has_user(self.student))
        self.assertFalse(GlobalStaff().has_user(self.course_staff))
        self.assertFalse(GlobalStaff().has_user(self.course_instructor))
        self.assertTrue(GlobalStaff().has_user(self.global_staff))

    def test_group_name_case_sensitive(self):
        uppercase_course_id = "ORG/COURSE/NAME"
        lowercase_course_id = uppercase_course_id.lower()
        uppercase_course_key = SlashSeparatedCourseKey.from_deprecated_string(
            uppercase_course_id)
        lowercase_course_key = SlashSeparatedCourseKey.from_deprecated_string(
            lowercase_course_id)

        role = "role"

        lowercase_user = UserFactory()
        CourseRole(role, lowercase_course_key).add_users(lowercase_user)
        uppercase_user = UserFactory()
        CourseRole(role, uppercase_course_key).add_users(uppercase_user)

        self.assertTrue(
            CourseRole(role, lowercase_course_key).has_user(lowercase_user))
        self.assertFalse(
            CourseRole(role, uppercase_course_key).has_user(lowercase_user))
        self.assertFalse(
            CourseRole(role, lowercase_course_key).has_user(uppercase_user))
        self.assertTrue(
            CourseRole(role, uppercase_course_key).has_user(uppercase_user))

    def test_course_role(self):
        """
        Test that giving a user a course role enables access appropriately
        """
        self.assertFalse(
            CourseStaffRole(self.course_key).has_user(self.student),
            "Student has premature access to {}".format(self.course_key))
        CourseStaffRole(self.course_key).add_users(self.student)
        self.assertTrue(
            CourseStaffRole(self.course_key).has_user(self.student),
            "Student doesn't have access to {}".format(unicode(
                self.course_key)))

        # remove access and confirm
        CourseStaffRole(self.course_key).remove_users(self.student)
        self.assertFalse(
            CourseStaffRole(self.course_key).has_user(self.student),
            "Student still has access to {}".format(self.course_key))

    def test_org_role(self):
        """
        Test that giving a user an org role enables access appropriately
        """
        self.assertFalse(
            OrgStaffRole(self.course_key.org).has_user(self.student),
            "Student has premature access to {}".format(self.course_key.org))
        OrgStaffRole(self.course_key.org).add_users(self.student)
        self.assertTrue(
            OrgStaffRole(self.course_key.org).has_user(self.student),
            "Student doesn't have access to {}".format(
                unicode(self.course_key.org)))

        # remove access and confirm
        OrgStaffRole(self.course_key.org).remove_users(self.student)
        if hasattr(self.student, '_roles'):
            del self.student._roles
        self.assertFalse(
            OrgStaffRole(self.course_key.org).has_user(self.student),
            "Student still has access to {}".format(self.course_key.org))

    def test_org_and_course_roles(self):
        """
        Test that Org and course roles don't interfere with course roles or vice versa
        """
        OrgInstructorRole(self.course_key.org).add_users(self.student)
        CourseInstructorRole(self.course_key).add_users(self.student)
        self.assertTrue(
            OrgInstructorRole(self.course_key.org).has_user(self.student),
            "Student doesn't have access to {}".format(
                unicode(self.course_key.org)))
        self.assertTrue(
            CourseInstructorRole(self.course_key).has_user(self.student),
            "Student doesn't have access to {}".format(unicode(
                self.course_key)))

        # remove access and confirm
        OrgInstructorRole(self.course_key.org).remove_users(self.student)
        self.assertFalse(
            OrgInstructorRole(self.course_key.org).has_user(self.student),
            "Student still has access to {}".format(self.course_key.org))
        self.assertTrue(
            CourseInstructorRole(self.course_key).has_user(self.student),
            "Student doesn't have access to {}".format(unicode(
                self.course_key)))

        # ok now keep org role and get rid of course one
        OrgInstructorRole(self.course_key.org).add_users(self.student)
        CourseInstructorRole(self.course_key).remove_users(self.student)
        self.assertTrue(
            OrgInstructorRole(self.course_key.org).has_user(self.student),
            "Student lost has access to {}".format(self.course_key.org))
        self.assertFalse(
            CourseInstructorRole(self.course_key).has_user(self.student),
            "Student doesn't have access to {}".format(unicode(
                self.course_key)))

    def test_get_user_for_role(self):
        """
        test users_for_role
        """
        role = CourseStaffRole(self.course_key)
        role.add_users(self.student)
        self.assertGreater(len(role.users_with_role()), 0)

    def test_add_users_doesnt_add_duplicate_entry(self):
        """
        Tests that calling add_users multiple times before a single call
        to remove_users does not result in the user remaining in the group.
        """
        role = CourseStaffRole(self.course_key)
        role.add_users(self.student)
        self.assertTrue(role.has_user(self.student))
        # Call add_users a second time, then remove just once.
        role.add_users(self.student)
        role.remove_users(self.student)
        self.assertFalse(role.has_user(self.student))
Beispiel #47
0
class TestMongoKeyValueStore(object):
    """
    Tests for MongoKeyValueStore.
    """
    def setUp(self):
        self.data = {'foo': 'foo_value'}
        self.course_id = SlashSeparatedCourseKey('org', 'course', 'run')
        self.children = [
            self.course_id.make_usage_key('child', 'a'),
            self.course_id.make_usage_key('child', 'b')
        ]
        self.metadata = {'meta': 'meta_val'}
        self.kvs = MongoKeyValueStore(self.data, self.children, self.metadata)

    def test_read(self):
        assert_equals(
            self.data['foo'],
            self.kvs.get(KeyValueStore.Key(Scope.content, None, None, 'foo')))
        assert_equals(
            self.children,
            self.kvs.get(
                KeyValueStore.Key(Scope.children, None, None, 'children')))
        assert_equals(
            self.metadata['meta'],
            self.kvs.get(KeyValueStore.Key(Scope.settings, None, None,
                                           'meta')))
        assert_equals(
            None,
            self.kvs.get(KeyValueStore.Key(Scope.parent, None, None,
                                           'parent')))

    def test_read_invalid_scope(self):
        for scope in (Scope.preferences, Scope.user_info, Scope.user_state):
            key = KeyValueStore.Key(scope, None, None, 'foo')
            with assert_raises(InvalidScopeError):
                self.kvs.get(key)
            assert_false(self.kvs.has(key))

    def test_read_non_dict_data(self):
        self.kvs = MongoKeyValueStore('xml_data', self.children, self.metadata)
        assert_equals(
            'xml_data',
            self.kvs.get(KeyValueStore.Key(Scope.content, None, None, 'data')))

    def _check_write(self, key, value):
        self.kvs.set(key, value)
        assert_equals(value, self.kvs.get(key))

    def test_write(self):
        yield (self._check_write,
               KeyValueStore.Key(Scope.content, None, None, 'foo'), 'new_data')
        yield (self._check_write,
               KeyValueStore.Key(Scope.children, None, None, 'children'), [])
        yield (self._check_write,
               KeyValueStore.Key(Scope.settings, None, None,
                                 'meta'), 'new_settings')

    def test_write_non_dict_data(self):
        self.kvs = MongoKeyValueStore('xml_data', self.children, self.metadata)
        self._check_write(KeyValueStore.Key(Scope.content, None, None, 'data'),
                          'new_data')

    def test_write_invalid_scope(self):
        for scope in (Scope.preferences, Scope.user_info, Scope.user_state,
                      Scope.parent):
            with assert_raises(InvalidScopeError):
                self.kvs.set(KeyValueStore.Key(scope, None, None, 'foo'),
                             'new_value')

    def _check_delete_default(self, key, default_value):
        self.kvs.delete(key)
        assert_equals(default_value, self.kvs.get(key))
        assert self.kvs.has(key)

    def _check_delete_key_error(self, key):
        self.kvs.delete(key)
        with assert_raises(KeyError):
            self.kvs.get(key)
        assert_false(self.kvs.has(key))

    def test_delete(self):
        yield (self._check_delete_key_error,
               KeyValueStore.Key(Scope.content, None, None, 'foo'))
        yield (self._check_delete_default,
               KeyValueStore.Key(Scope.children, None, None, 'children'), [])
        yield (self._check_delete_key_error,
               KeyValueStore.Key(Scope.settings, None, None, 'meta'))

    def test_delete_invalid_scope(self):
        for scope in (Scope.preferences, Scope.user_info, Scope.user_state,
                      Scope.parent):
            with assert_raises(InvalidScopeError):
                self.kvs.delete(KeyValueStore.Key(scope, None, None, 'foo'))
Beispiel #48
0
class TestHandleXBlockCallback(ModuleStoreTestCase, LoginEnrollmentTestCase):
    """
    Test the handle_xblock_callback function
    """
    def setUp(self):
        self.course_key = SlashSeparatedCourseKey('edX', 'toy', '2012_Fall')
        self.location = self.course_key.make_usage_key('chapter', 'Overview')
        self.toy_course = modulestore().get_course(self.course_key)
        self.mock_user = UserFactory()
        self.mock_user.id = 1
        self.request_factory = RequestFactory()

        # Construct a mock module for the modulestore to return
        self.mock_module = MagicMock()
        self.mock_module.id = 1
        self.dispatch = 'score_update'

        # Construct a 'standard' xqueue_callback url
        self.callback_url = reverse('xqueue_callback',
                                    kwargs={
                                        'course_id':
                                        self.course_key.to_deprecated_string(),
                                        'userid':
                                        str(self.mock_user.id),
                                        'mod_id':
                                        self.mock_module.id,
                                        'dispatch':
                                        self.dispatch
                                    })

    def _mock_file(self, name='file', size=10):
        """Create a mock file object for testing uploads"""
        mock_file = MagicMock(size=size, read=lambda: 'x' * size)
        # We can't use `name` as a kwarg to Mock to set the name attribute
        # because mock uses `name` to name the mock itself
        mock_file.name = name
        return mock_file

    def test_invalid_location(self):
        request = self.request_factory.post('dummy_url', data={'position': 1})
        request.user = self.mock_user
        with self.assertRaises(Http404):
            render.handle_xblock_callback(
                request, self.course_key.to_deprecated_string(),
                'invalid Location', 'dummy_handler'
                'dummy_dispatch')

    def test_too_many_files(self):
        request = self.request_factory.post(
            'dummy_url',
            data={
                'file_id': (self._mock_file(), ) *
                (settings.MAX_FILEUPLOADS_PER_INPUT + 1)
            })
        request.user = self.mock_user
        self.assertEquals(
            render.handle_xblock_callback(
                request, self.course_key.to_deprecated_string(),
                quote_slashes(self.location.to_deprecated_string()),
                'dummy_handler').content,
            json.dumps({
                'success':
                'Submission aborted! Maximum %d files may be submitted at once'
                % settings.MAX_FILEUPLOADS_PER_INPUT
            }))

    def test_too_large_file(self):
        inputfile = self._mock_file(size=1 +
                                    settings.STUDENT_FILEUPLOAD_MAX_SIZE)
        request = self.request_factory.post('dummy_url',
                                            data={'file_id': inputfile})
        request.user = self.mock_user
        self.assertEquals(
            render.handle_xblock_callback(
                request, self.course_key.to_deprecated_string(),
                quote_slashes(self.location.to_deprecated_string()),
                'dummy_handler').content,
            json.dumps({
                'success':
                'Submission aborted! Your file "%s" is too large (max size: %d MB)'
                % (inputfile.name, settings.STUDENT_FILEUPLOAD_MAX_SIZE /
                   (1000**2))
            }))

    def test_xmodule_dispatch(self):
        request = self.request_factory.post('dummy_url', data={'position': 1})
        request.user = self.mock_user
        response = render.handle_xblock_callback(
            request,
            self.course_key.to_deprecated_string(),
            quote_slashes(self.location.to_deprecated_string()),
            'xmodule_handler',
            'goto_position',
        )
        self.assertIsInstance(response, HttpResponse)

    def test_bad_course_id(self):
        request = self.request_factory.post('dummy_url')
        request.user = self.mock_user
        with self.assertRaises(Http404):
            render.handle_xblock_callback(
                request,
                'bad_course_id',
                quote_slashes(self.location.to_deprecated_string()),
                'xmodule_handler',
                'goto_position',
            )

    def test_bad_location(self):
        request = self.request_factory.post('dummy_url')
        request.user = self.mock_user
        with self.assertRaises(Http404):
            render.handle_xblock_callback(
                request,
                self.course_key.to_deprecated_string(),
                quote_slashes(
                    self.course_key.make_usage_key(
                        'chapter', 'bad_location').to_deprecated_string()),
                'xmodule_handler',
                'goto_position',
            )

    def test_bad_xmodule_dispatch(self):
        request = self.request_factory.post('dummy_url')
        request.user = self.mock_user
        with self.assertRaises(Http404):
            render.handle_xblock_callback(
                request,
                self.course_key.to_deprecated_string(),
                quote_slashes(self.location.to_deprecated_string()),
                'xmodule_handler',
                'bad_dispatch',
            )

    def test_missing_handler(self):
        request = self.request_factory.post('dummy_url')
        request.user = self.mock_user
        with self.assertRaises(Http404):
            render.handle_xblock_callback(
                request,
                self.course_key.to_deprecated_string(),
                quote_slashes(self.location.to_deprecated_string()),
                'bad_handler',
                'bad_dispatch',
            )
Beispiel #49
0
class TestPeerGradingService(ModuleStoreTestCase, LoginEnrollmentTestCase):
    '''
    Check that staff grading service proxy works.  Basically just checking the
    access control and error handling logic -- all the actual work is on the
    backend.
    '''

    def setUp(self):
        self.student = '*****@*****.**'
        self.instructor = '*****@*****.**'
        self.password = '******'
        self.create_account('u1', self.student, self.password)
        self.create_account('u2', self.instructor, self.password)
        self.activate_user(self.student)
        self.activate_user(self.instructor)

        self.course_id = SlashSeparatedCourseKey("edX", "toy", "2012_Fall")
        self.location_string = self.course_id.make_usage_key('html', 'TestLocation').to_deprecated_string()
        self.toy = modulestore().get_course(self.course_id)
        location = "i4x://edX/toy/peergrading/init"
        field_data = DictFieldData({'data': "<peergrading/>", 'location': location, 'category': 'peergrading'})
        self.mock_service = peer_grading_service.MockPeerGradingService()
        self.system = LmsModuleSystem(
            static_url=settings.STATIC_URL,
            track_function=None,
            get_module=None,
            render_template=render_to_string,
            replace_urls=None,
            s3_interface=test_util_open_ended.S3_INTERFACE,
            open_ended_grading_interface=test_util_open_ended.OPEN_ENDED_GRADING_INTERFACE,
            mixins=settings.XBLOCK_MIXINS,
            error_descriptor_class=ErrorDescriptor,
            descriptor_runtime=None,
        )
        self.descriptor = peer_grading_module.PeerGradingDescriptor(self.system, field_data, ScopeIds(None, None, None, None))
        self.descriptor.xmodule_runtime = self.system
        self.peer_module = self.descriptor
        self.peer_module.peer_gs = self.mock_service
        self.logout()

    def test_get_next_submission_success(self):
        data = {'location': self.location_string}

        response = self.peer_module.get_next_submission(data)
        content = response

        self.assertTrue(content['success'])
        self.assertIsNotNone(content['submission_id'])
        self.assertIsNotNone(content['prompt'])
        self.assertIsNotNone(content['submission_key'])
        self.assertIsNotNone(content['max_score'])

    def test_get_next_submission_missing_location(self):
        data = {}
        d = self.peer_module.get_next_submission(data)
        self.assertFalse(d['success'])
        self.assertEqual(d['error'], "Missing required keys: location")

    def test_save_grade_success(self):
        data = {
            'rubric_scores[]': [0, 0],
            'location': self.location_string,
            'submission_id': 1,
            'submission_key': 'fake key',
            'score': 2,
            'feedback': 'feedback',
            'submission_flagged': 'false',
            'answer_unknown': 'false',
            'rubric_scores_complete': 'true'
        }

        qdict = MagicMock()

        def fake_get_item(key):
            return data[key]

        qdict.__getitem__.side_effect = fake_get_item
        qdict.getlist = fake_get_item
        qdict.keys = data.keys

        response = self.peer_module.save_grade(qdict)

        self.assertTrue(response['success'])

    def test_save_grade_missing_keys(self):
        data = {}
        d = self.peer_module.save_grade(data)
        self.assertFalse(d['success'])
        self.assertTrue(d['error'].find('Missing required keys:') > -1)

    def test_is_calibrated_success(self):
        data = {'location': self.location_string}
        response = self.peer_module.is_student_calibrated(data)

        self.assertTrue(response['success'])
        self.assertTrue('calibrated' in response)

    def test_is_calibrated_failure(self):
        data = {}
        response = self.peer_module.is_student_calibrated(data)
        self.assertFalse(response['success'])
        self.assertFalse('calibrated' in response)

    def test_show_calibration_essay_success(self):
        data = {'location': self.location_string}

        response = self.peer_module.show_calibration_essay(data)

        self.assertTrue(response['success'])
        self.assertIsNotNone(response['submission_id'])
        self.assertIsNotNone(response['prompt'])
        self.assertIsNotNone(response['submission_key'])
        self.assertIsNotNone(response['max_score'])

    def test_show_calibration_essay_missing_key(self):
        data = {}

        response = self.peer_module.show_calibration_essay(data)

        self.assertFalse(response['success'])
        self.assertEqual(response['error'], "Missing required keys: location")

    def test_save_calibration_essay_success(self):
        data = {
            'rubric_scores[]': [0, 0],
            'location': self.location_string,
            'submission_id': 1,
            'submission_key': 'fake key',
            'score': 2,
            'feedback': 'feedback',
            'submission_flagged': 'false'
        }

        qdict = MagicMock()

        def fake_get_item(key):
            return data[key]

        qdict.__getitem__.side_effect = fake_get_item
        qdict.getlist = fake_get_item
        qdict.keys = data.keys

        response = self.peer_module.save_calibration_essay(qdict)
        self.assertTrue(response['success'])
        self.assertTrue('actual_score' in response)

    def test_save_calibration_essay_missing_keys(self):
        data = {}
        response = self.peer_module.save_calibration_essay(data)
        self.assertFalse(response['success'])
        self.assertTrue(response['error'].find('Missing required keys:') > -1)
        self.assertFalse('actual_score' in response)

    def test_save_grade_with_long_feedback(self):
        """
        Test if feedback is too long save_grade() should return error message.
        """
        data = {
            'rubric_scores[]': [0, 0],
            'location': self.location_string,
            'submission_id': 1,
            'submission_key': 'fake key',
            'score': 2,
            'feedback': '',
            'submission_flagged': 'false',
            'answer_unknown': 'false',
            'rubric_scores_complete': 'true'
        }

        feedback_fragment = "This is very long feedback."
        data["feedback"] = feedback_fragment * (
            (staff_grading_service.MAX_ALLOWED_FEEDBACK_LENGTH / len(feedback_fragment) + 1)
        )

        response_dict = self.peer_module.save_grade(data)

        # Should not succeed.
        self.assertEquals(response_dict['success'], False)
        self.assertEquals(
            response_dict['error'],
            "Feedback is too long, Max length is {0} characters.".format(
                staff_grading_service.MAX_ALLOWED_FEEDBACK_LENGTH
            )
        )
Beispiel #50
0
class TestStaffGradingService(ModuleStoreTestCase, LoginEnrollmentTestCase):
    '''
    Check that staff grading service proxy works.  Basically just checking the
    access control and error handling logic -- all the actual work is on the
    backend.
    '''
    MODULESTORE = TEST_DATA_MIXED_TOY_MODULESTORE

    def setUp(self):
        super(TestStaffGradingService, self).setUp()
        self.student = '*****@*****.**'
        self.instructor = '*****@*****.**'
        self.password = '******'
        self.create_account('u1', self.student, self.password)
        self.create_account('u2', self.instructor, self.password)
        self.activate_user(self.student)
        self.activate_user(self.instructor)

        self.course_id = SlashSeparatedCourseKey("edX", "toy", "2012_Fall")
        self.location_string = self.course_id.make_usage_key(
            'html', 'TestLocation').to_deprecated_string()
        self.toy = modulestore().get_course(self.course_id)

        make_instructor(self.toy, self.instructor)

        self.mock_service = staff_grading_service.staff_grading_service()

        self.logout()

    def test_access(self):
        """
        Make sure only staff have access.
        """
        self.login(self.student, self.password)

        # both get and post should return 404
        for view_name in ('staff_grading_get_next',
                          'staff_grading_save_grade'):
            url = reverse(
                view_name,
                kwargs={'course_id': self.course_id.to_deprecated_string()})
            self.assert_request_status_code(404, url, method="GET")
            self.assert_request_status_code(404, url, method="POST")

    def test_get_next(self):
        self.login(self.instructor, self.password)

        url = reverse(
            'staff_grading_get_next',
            kwargs={'course_id': self.course_id.to_deprecated_string()})
        data = {'location': self.location_string}

        response = self.assert_request_status_code(200,
                                                   url,
                                                   method="POST",
                                                   data=data)

        content = json.loads(response.content)

        self.assertTrue(content['success'])
        self.assertEquals(content['submission_id'], self.mock_service.cnt)
        self.assertIsNotNone(content['submission'])
        self.assertIsNotNone(content['num_graded'])
        self.assertIsNotNone(content['min_for_ml'])
        self.assertIsNotNone(content['num_pending'])
        self.assertIsNotNone(content['prompt'])
        self.assertIsNotNone(content['ml_error_info'])
        self.assertIsNotNone(content['max_score'])
        self.assertIsNotNone(content['rubric'])

    def save_grade_base(self, skip=False):
        self.login(self.instructor, self.password)

        url = reverse(
            'staff_grading_save_grade',
            kwargs={'course_id': self.course_id.to_deprecated_string()})

        data = {
            'score': '12',
            'feedback': 'great!',
            'submission_id': '123',
            'location': self.location_string,
            'submission_flagged': "true",
            'rubric_scores[]': ['1', '2']
        }
        if skip:
            data.update({'skipped': True})

        response = self.assert_request_status_code(200,
                                                   url,
                                                   method="POST",
                                                   data=data)
        content = json.loads(response.content)
        self.assertTrue(content['success'], str(content))
        self.assertEquals(content['submission_id'], self.mock_service.cnt)

    def test_save_grade(self):
        self.save_grade_base(skip=False)

    def test_save_grade_skip(self):
        self.save_grade_base(skip=True)

    def test_get_problem_list(self):
        self.login(self.instructor, self.password)

        url = reverse(
            'staff_grading_get_problem_list',
            kwargs={'course_id': self.course_id.to_deprecated_string()})
        data = {}

        response = self.assert_request_status_code(200,
                                                   url,
                                                   method="POST",
                                                   data=data)
        content = json.loads(response.content)

        self.assertTrue(content['success'])
        self.assertEqual(content['problem_list'], [])

    @patch('open_ended_grading.staff_grading_service._service',
           EmptyStaffGradingService())
    def test_get_problem_list_missing(self):
        """
        Test to see if a staff grading response missing a problem list is given the appropriate error.
        Mock the staff grading service to enable the key to be missing.
        """

        # Get a valid user object.
        instructor = User.objects.get(email=self.instructor)
        # Mock a request object.
        request = Mock(user=instructor, )
        # Get the response and load its content.
        response = json.loads(
            staff_grading_service.get_problem_list(
                request, self.course_id.to_deprecated_string()).content)

        # A valid response will have an "error" key.
        self.assertTrue('error' in response)
        # Check that the error text is correct.
        self.assertIn("Cannot find", response['error'])

    def test_save_grade_with_long_feedback(self):
        """
        Test if feedback is too long save_grade() should return error message.
        """
        self.login(self.instructor, self.password)

        url = reverse(
            'staff_grading_save_grade',
            kwargs={'course_id': self.course_id.to_deprecated_string()})

        data = {
            'score': '12',
            'feedback': '',
            'submission_id': '123',
            'location': self.location_string,
            'submission_flagged': "false",
            'rubric_scores[]': ['1', '2']
        }

        feedback_fragment = "This is very long feedback."
        data["feedback"] = feedback_fragment * (
            (staff_grading_service.MAX_ALLOWED_FEEDBACK_LENGTH /
             len(feedback_fragment) + 1))

        response = self.assert_request_status_code(200,
                                                   url,
                                                   method="POST",
                                                   data=data)
        content = json.loads(response.content)

        # Should not succeed.
        self.assertEquals(content['success'], False)
        self.assertEquals(
            content['error'],
            "Feedback is too long, Max length is {0} characters.".format(
                staff_grading_service.MAX_ALLOWED_FEEDBACK_LENGTH))
Beispiel #51
0
class TestPeerGradingService(ModuleStoreTestCase, LoginEnrollmentTestCase):
    '''
    Check that staff grading service proxy works.  Basically just checking the
    access control and error handling logic -- all the actual work is on the
    backend.
    '''
    def setUp(self):
        super(TestPeerGradingService, self).setUp()
        self.student = '*****@*****.**'
        self.instructor = '*****@*****.**'
        self.password = '******'
        self.create_account('u1', self.student, self.password)
        self.create_account('u2', self.instructor, self.password)
        self.activate_user(self.student)
        self.activate_user(self.instructor)

        self.course_id = SlashSeparatedCourseKey("edX", "toy", "2012_Fall")
        self.location_string = self.course_id.make_usage_key(
            'html', 'TestLocation').to_deprecated_string()
        self.toy = modulestore().get_course(self.course_id)
        location = "i4x://edX/toy/peergrading/init"
        field_data = DictFieldData({
            'data': "<peergrading/>",
            'location': location,
            'category': 'peergrading'
        })
        self.mock_service = peer_grading_service.MockPeerGradingService()
        self.system = LmsModuleSystem(
            static_url=settings.STATIC_URL,
            track_function=None,
            get_module=None,
            render_template=render_to_string,
            replace_urls=None,
            s3_interface=test_util_open_ended.S3_INTERFACE,
            open_ended_grading_interface=test_util_open_ended.
            OPEN_ENDED_GRADING_INTERFACE,
            mixins=settings.XBLOCK_MIXINS,
            error_descriptor_class=ErrorDescriptor,
            descriptor_runtime=Mock(spec=DescriptorSystem,
                                    name="descriptor_runtime"),
        )
        self.descriptor = peer_grading_module.PeerGradingDescriptor(
            self.system, field_data, ScopeIds(None, None, None, None))
        self.descriptor.xmodule_runtime = self.system
        self.peer_module = self.descriptor
        self.peer_module.peer_gs = self.mock_service
        self.logout()

    def test_get_next_submission_success(self):
        data = {'location': self.location_string}

        response = self.peer_module.get_next_submission(data)
        content = response

        self.assertTrue(content['success'])
        self.assertIsNotNone(content['submission_id'])
        self.assertIsNotNone(content['prompt'])
        self.assertIsNotNone(content['submission_key'])
        self.assertIsNotNone(content['max_score'])

    def test_get_next_submission_missing_location(self):
        data = {}
        d = self.peer_module.get_next_submission(data)
        self.assertFalse(d['success'])
        self.assertEqual(d['error'], "Missing required keys: location")

    def test_save_grade_success(self):
        data = {
            'rubric_scores[]': [0, 0],
            'location': self.location_string,
            'submission_id': 1,
            'submission_key': 'fake key',
            'score': 2,
            'feedback': 'feedback',
            'submission_flagged': 'false',
            'answer_unknown': 'false',
            'rubric_scores_complete': 'true'
        }

        qdict = MagicMock()

        def fake_get_item(key):
            return data[key]

        qdict.__getitem__.side_effect = fake_get_item
        qdict.getlist = fake_get_item
        qdict.keys = data.keys

        response = self.peer_module.save_grade(qdict)

        self.assertTrue(response['success'])

    def test_save_grade_missing_keys(self):
        data = {}
        d = self.peer_module.save_grade(data)
        self.assertFalse(d['success'])
        self.assertTrue(d['error'].find('Missing required keys:') > -1)

    def test_is_calibrated_success(self):
        data = {'location': self.location_string}
        response = self.peer_module.is_student_calibrated(data)

        self.assertTrue(response['success'])
        self.assertTrue('calibrated' in response)

    def test_is_calibrated_failure(self):
        data = {}
        response = self.peer_module.is_student_calibrated(data)
        self.assertFalse(response['success'])
        self.assertFalse('calibrated' in response)

    def test_show_calibration_essay_success(self):
        data = {'location': self.location_string}

        response = self.peer_module.show_calibration_essay(data)

        self.assertTrue(response['success'])
        self.assertIsNotNone(response['submission_id'])
        self.assertIsNotNone(response['prompt'])
        self.assertIsNotNone(response['submission_key'])
        self.assertIsNotNone(response['max_score'])

    def test_show_calibration_essay_missing_key(self):
        data = {}

        response = self.peer_module.show_calibration_essay(data)

        self.assertFalse(response['success'])
        self.assertEqual(response['error'], "Missing required keys: location")

    def test_save_calibration_essay_success(self):
        data = {
            'rubric_scores[]': [0, 0],
            'location': self.location_string,
            'submission_id': 1,
            'submission_key': 'fake key',
            'score': 2,
            'feedback': 'feedback',
            'submission_flagged': 'false'
        }

        qdict = MagicMock()

        def fake_get_item(key):
            return data[key]

        qdict.__getitem__.side_effect = fake_get_item
        qdict.getlist = fake_get_item
        qdict.keys = data.keys

        response = self.peer_module.save_calibration_essay(qdict)
        self.assertTrue(response['success'])
        self.assertTrue('actual_score' in response)

    def test_save_calibration_essay_missing_keys(self):
        data = {}
        response = self.peer_module.save_calibration_essay(data)
        self.assertFalse(response['success'])
        self.assertTrue(response['error'].find('Missing required keys:') > -1)
        self.assertFalse('actual_score' in response)

    def test_save_grade_with_long_feedback(self):
        """
        Test if feedback is too long save_grade() should return error message.
        """
        data = {
            'rubric_scores[]': [0, 0],
            'location': self.location_string,
            'submission_id': 1,
            'submission_key': 'fake key',
            'score': 2,
            'feedback': '',
            'submission_flagged': 'false',
            'answer_unknown': 'false',
            'rubric_scores_complete': 'true'
        }

        feedback_fragment = "This is very long feedback."
        data["feedback"] = feedback_fragment * (
            (staff_grading_service.MAX_ALLOWED_FEEDBACK_LENGTH /
             len(feedback_fragment) + 1))

        response_dict = self.peer_module.save_grade(data)

        # Should not succeed.
        self.assertEquals(response_dict['success'], False)
        self.assertEquals(
            response_dict['error'],
            "Feedback is too long, Max length is {0} characters.".format(
                staff_grading_service.MAX_ALLOWED_FEEDBACK_LENGTH))
Beispiel #52
0
class TestMongoKeyValueStore(object):
    """
    Tests for MongoKeyValueStore.
    """

    def setUp(self):
        self.data = {'foo': 'foo_value'}
        self.course_id = SlashSeparatedCourseKey('org', 'course', 'run')
        self.children = [self.course_id.make_usage_key('child', 'a'), self.course_id.make_usage_key('child', 'b')]
        self.metadata = {'meta': 'meta_val'}
        self.kvs = MongoKeyValueStore(self.data, self.children, self.metadata)

    def test_read(self):
        assert_equals(self.data['foo'], self.kvs.get(KeyValueStore.Key(Scope.content, None, None, 'foo')))
        assert_equals(self.children, self.kvs.get(KeyValueStore.Key(Scope.children, None, None, 'children')))
        assert_equals(self.metadata['meta'], self.kvs.get(KeyValueStore.Key(Scope.settings, None, None, 'meta')))
        assert_equals(None, self.kvs.get(KeyValueStore.Key(Scope.parent, None, None, 'parent')))

    def test_read_invalid_scope(self):
        for scope in (Scope.preferences, Scope.user_info, Scope.user_state):
            key = KeyValueStore.Key(scope, None, None, 'foo')
            with assert_raises(InvalidScopeError):
                self.kvs.get(key)
            assert_false(self.kvs.has(key))

    def test_read_non_dict_data(self):
        self.kvs = MongoKeyValueStore('xml_data', self.children, self.metadata)
        assert_equals('xml_data', self.kvs.get(KeyValueStore.Key(Scope.content, None, None, 'data')))

    def _check_write(self, key, value):
        self.kvs.set(key, value)
        assert_equals(value, self.kvs.get(key))

    def test_write(self):
        yield (self._check_write, KeyValueStore.Key(Scope.content, None, None, 'foo'), 'new_data')
        yield (self._check_write, KeyValueStore.Key(Scope.children, None, None, 'children'), [])
        yield (self._check_write, KeyValueStore.Key(Scope.settings, None, None, 'meta'), 'new_settings')

    def test_write_non_dict_data(self):
        self.kvs = MongoKeyValueStore('xml_data', self.children, self.metadata)
        self._check_write(KeyValueStore.Key(Scope.content, None, None, 'data'), 'new_data')

    def test_write_invalid_scope(self):
        for scope in (Scope.preferences, Scope.user_info, Scope.user_state, Scope.parent):
            with assert_raises(InvalidScopeError):
                self.kvs.set(KeyValueStore.Key(scope, None, None, 'foo'), 'new_value')

    def _check_delete_default(self, key, default_value):
        self.kvs.delete(key)
        assert_equals(default_value, self.kvs.get(key))
        assert self.kvs.has(key)

    def _check_delete_key_error(self, key):
        self.kvs.delete(key)
        with assert_raises(KeyError):
            self.kvs.get(key)
        assert_false(self.kvs.has(key))

    def test_delete(self):
        yield (self._check_delete_key_error, KeyValueStore.Key(Scope.content, None, None, 'foo'))
        yield (self._check_delete_default, KeyValueStore.Key(Scope.children, None, None, 'children'), [])
        yield (self._check_delete_key_error, KeyValueStore.Key(Scope.settings, None, None, 'meta'))

    def test_delete_invalid_scope(self):
        for scope in (Scope.preferences, Scope.user_info, Scope.user_state, Scope.parent):
            with assert_raises(InvalidScopeError):
                self.kvs.delete(KeyValueStore.Key(scope, None, None, 'foo'))
class TestHandleXBlockCallback(ModuleStoreTestCase, LoginEnrollmentTestCase):
    """
    Test the handle_xblock_callback function
    """

    def setUp(self):
        self.course_key = SlashSeparatedCourseKey("edX", "toy", "2012_Fall")
        self.location = self.course_key.make_usage_key("chapter", "Overview")
        self.toy_course = modulestore().get_course(self.course_key)
        self.mock_user = UserFactory()
        self.mock_user.id = 1
        self.request_factory = RequestFactory()

        # Construct a mock module for the modulestore to return
        self.mock_module = MagicMock()
        self.mock_module.id = 1
        self.dispatch = "score_update"

        # Construct a 'standard' xqueue_callback url
        self.callback_url = reverse(
            "xqueue_callback",
            kwargs={
                "course_id": self.course_key.to_deprecated_string(),
                "userid": str(self.mock_user.id),
                "mod_id": self.mock_module.id,
                "dispatch": self.dispatch,
            },
        )

    def _mock_file(self, name="file", size=10):
        """Create a mock file object for testing uploads"""
        mock_file = MagicMock(size=size, read=lambda: "x" * size)
        # We can't use `name` as a kwarg to Mock to set the name attribute
        # because mock uses `name` to name the mock itself
        mock_file.name = name
        return mock_file

    def test_invalid_location(self):
        request = self.request_factory.post("dummy_url", data={"position": 1})
        request.user = self.mock_user
        with self.assertRaises(Http404):
            render.handle_xblock_callback(
                request, self.course_key.to_deprecated_string(), "invalid Location", "dummy_handler" "dummy_dispatch"
            )

    def test_too_many_files(self):
        request = self.request_factory.post(
            "dummy_url", data={"file_id": (self._mock_file(),) * (settings.MAX_FILEUPLOADS_PER_INPUT + 1)}
        )
        request.user = self.mock_user
        self.assertEquals(
            render.handle_xblock_callback(
                request,
                self.course_key.to_deprecated_string(),
                quote_slashes(self.location.to_deprecated_string()),
                "dummy_handler",
            ).content,
            json.dumps(
                {
                    "success": "Submission aborted! Maximum %d files may be submitted at once"
                    % settings.MAX_FILEUPLOADS_PER_INPUT
                }
            ),
        )

    def test_too_large_file(self):
        inputfile = self._mock_file(size=1 + settings.STUDENT_FILEUPLOAD_MAX_SIZE)
        request = self.request_factory.post("dummy_url", data={"file_id": inputfile})
        request.user = self.mock_user
        self.assertEquals(
            render.handle_xblock_callback(
                request,
                self.course_key.to_deprecated_string(),
                quote_slashes(self.location.to_deprecated_string()),
                "dummy_handler",
            ).content,
            json.dumps(
                {
                    "success": 'Submission aborted! Your file "%s" is too large (max size: %d MB)'
                    % (inputfile.name, settings.STUDENT_FILEUPLOAD_MAX_SIZE / (1000 ** 2))
                }
            ),
        )

    def test_xmodule_dispatch(self):
        request = self.request_factory.post("dummy_url", data={"position": 1})
        request.user = self.mock_user
        response = render.handle_xblock_callback(
            request,
            self.course_key.to_deprecated_string(),
            quote_slashes(self.location.to_deprecated_string()),
            "xmodule_handler",
            "goto_position",
        )
        self.assertIsInstance(response, HttpResponse)

    def test_bad_course_id(self):
        request = self.request_factory.post("dummy_url")
        request.user = self.mock_user
        with self.assertRaises(Http404):
            render.handle_xblock_callback(
                request,
                "bad_course_id",
                quote_slashes(self.location.to_deprecated_string()),
                "xmodule_handler",
                "goto_position",
            )

    def test_bad_location(self):
        request = self.request_factory.post("dummy_url")
        request.user = self.mock_user
        with self.assertRaises(Http404):
            render.handle_xblock_callback(
                request,
                self.course_key.to_deprecated_string(),
                quote_slashes(self.course_key.make_usage_key("chapter", "bad_location").to_deprecated_string()),
                "xmodule_handler",
                "goto_position",
            )

    def test_bad_xmodule_dispatch(self):
        request = self.request_factory.post("dummy_url")
        request.user = self.mock_user
        with self.assertRaises(Http404):
            render.handle_xblock_callback(
                request,
                self.course_key.to_deprecated_string(),
                quote_slashes(self.location.to_deprecated_string()),
                "xmodule_handler",
                "bad_dispatch",
            )

    def test_missing_handler(self):
        request = self.request_factory.post("dummy_url")
        request.user = self.mock_user
        with self.assertRaises(Http404):
            render.handle_xblock_callback(
                request,
                self.course_key.to_deprecated_string(),
                quote_slashes(self.location.to_deprecated_string()),
                "bad_handler",
                "bad_dispatch",
            )
Beispiel #54
0
class TestStaffGradingService(ModuleStoreTestCase, LoginEnrollmentTestCase):
    '''
    Check that staff grading service proxy works.  Basically just checking the
    access control and error handling logic -- all the actual work is on the
    backend.
    '''

    def setUp(self):
        self.student = '*****@*****.**'
        self.instructor = '*****@*****.**'
        self.password = '******'
        self.create_account('u1', self.student, self.password)
        self.create_account('u2', self.instructor, self.password)
        self.activate_user(self.student)
        self.activate_user(self.instructor)

        self.course_id = SlashSeparatedCourseKey("edX", "toy", "2012_Fall")
        self.location_string = self.course_id.make_usage_key('html', 'TestLocation').to_deprecated_string()
        self.toy = modulestore().get_course(self.course_id)

        make_instructor(self.toy, self.instructor)

        self.mock_service = staff_grading_service.staff_grading_service()

        self.logout()

    def test_access(self):
        """
        Make sure only staff have access.
        """
        self.login(self.student, self.password)

        # both get and post should return 404
        for view_name in ('staff_grading_get_next', 'staff_grading_save_grade'):
            url = reverse(view_name, kwargs={'course_id': self.course_id.to_deprecated_string()})
            self.assert_request_status_code(404, url, method="GET")
            self.assert_request_status_code(404, url, method="POST")

    def test_get_next(self):
        self.login(self.instructor, self.password)

        url = reverse('staff_grading_get_next', kwargs={'course_id': self.course_id.to_deprecated_string()})
        data = {'location': self.location_string}

        response = self.assert_request_status_code(200, url, method="POST", data=data)

        content = json.loads(response.content)

        self.assertTrue(content['success'])
        self.assertEquals(content['submission_id'], self.mock_service.cnt)
        self.assertIsNotNone(content['submission'])
        self.assertIsNotNone(content['num_graded'])
        self.assertIsNotNone(content['min_for_ml'])
        self.assertIsNotNone(content['num_pending'])
        self.assertIsNotNone(content['prompt'])
        self.assertIsNotNone(content['ml_error_info'])
        self.assertIsNotNone(content['max_score'])
        self.assertIsNotNone(content['rubric'])

    def save_grade_base(self, skip=False):
        self.login(self.instructor, self.password)

        url = reverse('staff_grading_save_grade', kwargs={'course_id': self.course_id.to_deprecated_string()})

        data = {'score': '12',
                'feedback': 'great!',
                'submission_id': '123',
                'location': self.location_string,
                'submission_flagged': "true",
                'rubric_scores[]': ['1', '2']}
        if skip:
            data.update({'skipped': True})

        response = self.assert_request_status_code(200, url, method="POST", data=data)
        content = json.loads(response.content)
        self.assertTrue(content['success'], str(content))
        self.assertEquals(content['submission_id'], self.mock_service.cnt)

    def test_save_grade(self):
        self.save_grade_base(skip=False)

    def test_save_grade_skip(self):
        self.save_grade_base(skip=True)

    def test_get_problem_list(self):
        self.login(self.instructor, self.password)

        url = reverse('staff_grading_get_problem_list', kwargs={'course_id': self.course_id.to_deprecated_string()})
        data = {}

        response = self.assert_request_status_code(200, url, method="POST", data=data)
        content = json.loads(response.content)

        self.assertTrue(content['success'])
        self.assertEqual(content['problem_list'], [])

    @patch('open_ended_grading.staff_grading_service._service', EmptyStaffGradingService())
    def test_get_problem_list_missing(self):
        """
        Test to see if a staff grading response missing a problem list is given the appropriate error.
        Mock the staff grading service to enable the key to be missing.
        """

        # Get a valid user object.
        instructor = User.objects.get(email=self.instructor)
        # Mock a request object.
        request = Mock(
            user=instructor,
        )
        # Get the response and load its content.
        response = json.loads(staff_grading_service.get_problem_list(request, self.course_id.to_deprecated_string()).content)

        # A valid response will have an "error" key.
        self.assertTrue('error' in response)
        # Check that the error text is correct.
        self.assertIn("Cannot find", response['error'])

    def test_save_grade_with_long_feedback(self):
        """
        Test if feedback is too long save_grade() should return error message.
        """
        self.login(self.instructor, self.password)

        url = reverse('staff_grading_save_grade', kwargs={'course_id': self.course_id.to_deprecated_string()})

        data = {
            'score': '12',
            'feedback': '',
            'submission_id': '123',
            'location': self.location_string,
            'submission_flagged': "false",
            'rubric_scores[]': ['1', '2']
        }

        feedback_fragment = "This is very long feedback."
        data["feedback"] = feedback_fragment * (
            (staff_grading_service.MAX_ALLOWED_FEEDBACK_LENGTH / len(feedback_fragment) + 1)
        )

        response = self.assert_request_status_code(200, url, method="POST", data=data)
        content = json.loads(response.content)

        # Should not succeed.
        self.assertEquals(content['success'], False)
        self.assertEquals(
            content['error'],
            "Feedback is too long, Max length is {0} characters.".format(
                staff_grading_service.MAX_ALLOWED_FEEDBACK_LENGTH
            )
        )
class ModuleRenderTestCase(ModuleStoreTestCase, LoginEnrollmentTestCase):
    """
    Tests of courseware.module_render
    """

    def setUp(self):
        self.course_key = SlashSeparatedCourseKey("edX", "toy", "2012_Fall")
        self.location = self.course_key.make_usage_key("chapter", "Overview")
        self.toy_course = modulestore().get_course(self.course_key)
        self.mock_user = UserFactory()
        self.mock_user.id = 1
        self.request_factory = RequestFactory()

        # Construct a mock module for the modulestore to return
        self.mock_module = MagicMock()
        self.mock_module.id = 1
        self.dispatch = "score_update"

        # Construct a 'standard' xqueue_callback url
        self.callback_url = reverse(
            "xqueue_callback",
            kwargs=dict(
                course_id=self.course_key.to_deprecated_string(),
                userid=str(self.mock_user.id),
                mod_id=self.mock_module.id,
                dispatch=self.dispatch,
            ),
        )

    def test_get_module(self):
        self.assertEqual(None, render.get_module("dummyuser", None, "invalid location", None))

    def test_module_render_with_jump_to_id(self):
        """
        This test validates that the /jump_to_id/<id> shorthand for intracourse linking works assertIn
        expected. Note there's a HTML element in the 'toy' course with the url_name 'toyjumpto' which
        defines this linkage
        """
        mock_request = MagicMock()
        mock_request.user = self.mock_user

        course = get_course_with_access(self.mock_user, "load", self.course_key)

        field_data_cache = FieldDataCache.cache_for_descriptor_descendents(
            self.course_key, self.mock_user, course, depth=2
        )

        module = render.get_module(
            self.mock_user, mock_request, self.course_key.make_usage_key("html", "toyjumpto"), field_data_cache
        )

        # get the rendered HTML output which should have the rewritten link
        html = module.render(STUDENT_VIEW).content

        # See if the url got rewritten to the target link
        # note if the URL mapping changes then this assertion will break
        self.assertIn("/courses/" + self.course_key.to_deprecated_string() + "/jump_to_id/vertical_test", html)

    def test_xqueue_callback_success(self):
        """
        Test for happy-path xqueue_callback
        """
        fake_key = "fake key"
        xqueue_header = json.dumps({"lms_key": fake_key})
        data = {"xqueue_header": xqueue_header, "xqueue_body": "hello world"}

        # Patch getmodule to return our mock module
        with patch("courseware.module_render.find_target_student_module") as get_fake_module:
            get_fake_module.return_value = self.mock_module
            # call xqueue_callback with our mocked information
            request = self.request_factory.post(self.callback_url, data)
            render.xqueue_callback(request, self.course_key, self.mock_user.id, self.mock_module.id, self.dispatch)

        # Verify that handle ajax is called with the correct data
        request.POST["queuekey"] = fake_key
        self.mock_module.handle_ajax.assert_called_once_with(self.dispatch, request.POST)

    def test_xqueue_callback_missing_header_info(self):
        data = {"xqueue_header": "{}", "xqueue_body": "hello world"}

        with patch("courseware.module_render.find_target_student_module") as get_fake_module:
            get_fake_module.return_value = self.mock_module
            # Test with missing xqueue data
            with self.assertRaises(Http404):
                request = self.request_factory.post(self.callback_url, {})
                render.xqueue_callback(request, self.course_key, self.mock_user.id, self.mock_module.id, self.dispatch)

            # Test with missing xqueue_header
            with self.assertRaises(Http404):
                request = self.request_factory.post(self.callback_url, data)
                render.xqueue_callback(request, self.course_key, self.mock_user.id, self.mock_module.id, self.dispatch)

    def test_get_score_bucket(self):
        self.assertEquals(render.get_score_bucket(0, 10), "incorrect")
        self.assertEquals(render.get_score_bucket(1, 10), "partial")
        self.assertEquals(render.get_score_bucket(10, 10), "correct")
        # get_score_bucket calls error cases 'incorrect'
        self.assertEquals(render.get_score_bucket(11, 10), "incorrect")
        self.assertEquals(render.get_score_bucket(-1, 10), "incorrect")

    def test_anonymous_handle_xblock_callback(self):
        dispatch_url = reverse(
            "xblock_handler",
            args=[
                self.course_key.to_deprecated_string(),
                quote_slashes(self.course_key.make_usage_key("videosequence", "Toy_Videos").to_deprecated_string()),
                "xmodule_handler",
                "goto_position",
            ],
        )
        response = self.client.post(dispatch_url, {"position": 2})
        self.assertEquals(403, response.status_code)
        self.assertEquals("Unauthenticated", response.content)

    @ddt.data("pure", "vertical")
    @XBlock.register_temp_plugin(PureXBlock, identifier="pure")
    def test_rebinding_same_user(self, block_type):
        request = self.request_factory.get("")
        request.user = self.mock_user
        course = CourseFactory()
        descriptor = ItemFactory(category=block_type, parent=course)
        field_data_cache = FieldDataCache([self.toy_course, descriptor], self.toy_course.id, self.mock_user)
        render.get_module_for_descriptor(self.mock_user, request, descriptor, field_data_cache, self.toy_course.id)
        render.get_module_for_descriptor(self.mock_user, request, descriptor, field_data_cache, self.toy_course.id)
Beispiel #56
0
class TestJumpTo(ModuleStoreTestCase):
    """
    Check the jumpto link for a course.
    """
    MODULESTORE = TEST_DATA_MIXED_TOY_MODULESTORE

    def setUp(self):
        super(TestJumpTo, self).setUp()
        # Use toy course from XML
        self.course_key = SlashSeparatedCourseKey('edX', 'toy', '2012_Fall')

    def test_jumpto_invalid_location(self):
        location = self.course_key.make_usage_key(None, 'NoSuchPlace')
        # This is fragile, but unfortunately the problem is that within the LMS we
        # can't use the reverse calls from the CMS
        jumpto_url = '{0}/{1}/jump_to/{2}'.format('/courses', self.course_key.to_deprecated_string(), location.to_deprecated_string())
        response = self.client.get(jumpto_url)
        self.assertEqual(response.status_code, 404)

    @unittest.skip
    def test_jumpto_from_chapter(self):
        location = self.course_key.make_usage_key('chapter', 'Overview')
        jumpto_url = '{0}/{1}/jump_to/{2}'.format('/courses', self.course_key.to_deprecated_string(), location.to_deprecated_string())
        expected = 'courses/edX/toy/2012_Fall/courseware/Overview/'
        response = self.client.get(jumpto_url)
        self.assertRedirects(response, expected, status_code=302, target_status_code=302)

    @unittest.skip
    def test_jumpto_id(self):
        jumpto_url = '{0}/{1}/jump_to_id/{2}'.format('/courses', self.course_key.to_deprecated_string(), 'Overview')
        expected = 'courses/edX/toy/2012_Fall/courseware/Overview/'
        response = self.client.get(jumpto_url)
        self.assertRedirects(response, expected, status_code=302, target_status_code=302)

    def test_jumpto_from_section(self):
        course = CourseFactory.create()
        chapter = ItemFactory.create(category='chapter', parent_location=course.location)
        section = ItemFactory.create(category='sequential', parent_location=chapter.location)
        expected = 'courses/{course_id}/courseware/{chapter_id}/{section_id}/'.format(
            course_id=unicode(course.id),
            chapter_id=chapter.url_name,
            section_id=section.url_name,
        )
        jumpto_url = '{0}/{1}/jump_to/{2}'.format(
            '/courses',
            unicode(course.id),
            unicode(section.location),
        )
        response = self.client.get(jumpto_url)
        self.assertRedirects(response, expected, status_code=302, target_status_code=302)

    def test_jumpto_from_module(self):
        course = CourseFactory.create()
        chapter = ItemFactory.create(category='chapter', parent_location=course.location)
        section = ItemFactory.create(category='sequential', parent_location=chapter.location)
        vertical1 = ItemFactory.create(category='vertical', parent_location=section.location)
        vertical2 = ItemFactory.create(category='vertical', parent_location=section.location)
        module1 = ItemFactory.create(category='html', parent_location=vertical1.location)
        module2 = ItemFactory.create(category='html', parent_location=vertical2.location)

        expected = 'courses/{course_id}/courseware/{chapter_id}/{section_id}/1'.format(
            course_id=unicode(course.id),
            chapter_id=chapter.url_name,
            section_id=section.url_name,
        )
        jumpto_url = '{0}/{1}/jump_to/{2}'.format(
            '/courses',
            unicode(course.id),
            unicode(module1.location),
        )
        response = self.client.get(jumpto_url)
        self.assertRedirects(response, expected, status_code=302, target_status_code=302)

        expected = 'courses/{course_id}/courseware/{chapter_id}/{section_id}/2'.format(
            course_id=unicode(course.id),
            chapter_id=chapter.url_name,
            section_id=section.url_name,
        )
        jumpto_url = '{0}/{1}/jump_to/{2}'.format(
            '/courses',
            unicode(course.id),
            unicode(module2.location),
        )
        response = self.client.get(jumpto_url)
        self.assertRedirects(response, expected, status_code=302, target_status_code=302)

    def test_jumpto_from_nested_module(self):
        course = CourseFactory.create()
        chapter = ItemFactory.create(category='chapter', parent_location=course.location)
        section = ItemFactory.create(category='sequential', parent_location=chapter.location)
        vertical = ItemFactory.create(category='vertical', parent_location=section.location)
        nested_section = ItemFactory.create(category='sequential', parent_location=vertical.location)
        nested_vertical1 = ItemFactory.create(category='vertical', parent_location=nested_section.location)
        # put a module into nested_vertical1 for completeness
        ItemFactory.create(category='html', parent_location=nested_vertical1.location)
        nested_vertical2 = ItemFactory.create(category='vertical', parent_location=nested_section.location)
        module2 = ItemFactory.create(category='html', parent_location=nested_vertical2.location)

        # internal position of module2 will be 1_2 (2nd item withing 1st item)

        expected = 'courses/{course_id}/courseware/{chapter_id}/{section_id}/1'.format(
            course_id=unicode(course.id),
            chapter_id=chapter.url_name,
            section_id=section.url_name,
        )
        jumpto_url = '{0}/{1}/jump_to/{2}'.format(
            '/courses',
            unicode(course.id),
            unicode(module2.location),
        )
        response = self.client.get(jumpto_url)
        self.assertRedirects(response, expected, status_code=302, target_status_code=302)

    def test_jumpto_id_invalid_location(self):
        location = Location('edX', 'toy', 'NoSuchPlace', None, None, None)
        jumpto_url = '{0}/{1}/jump_to_id/{2}'.format('/courses', self.course_key.to_deprecated_string(), location.to_deprecated_string())
        response = self.client.get(jumpto_url)
        self.assertEqual(response.status_code, 404)
Beispiel #57
0
class ModuleRenderTestCase(ModuleStoreTestCase, LoginEnrollmentTestCase):
    """
    Tests of courseware.module_render
    """
    def setUp(self):
        self.course_key = SlashSeparatedCourseKey('edX', 'toy', '2012_Fall')
        self.location = self.course_key.make_usage_key('chapter', 'Overview')
        self.toy_course = modulestore().get_course(self.course_key)
        self.mock_user = UserFactory()
        self.mock_user.id = 1
        self.request_factory = RequestFactory()

        # Construct a mock module for the modulestore to return
        self.mock_module = MagicMock()
        self.mock_module.id = 1
        self.dispatch = 'score_update'

        # Construct a 'standard' xqueue_callback url
        self.callback_url = reverse(
            'xqueue_callback',
            kwargs=dict(course_id=self.course_key.to_deprecated_string(),
                        userid=str(self.mock_user.id),
                        mod_id=self.mock_module.id,
                        dispatch=self.dispatch))

    def test_get_module(self):
        self.assertEqual(
            None,
            render.get_module('dummyuser', None, 'invalid location', None,
                              None))

    def test_module_render_with_jump_to_id(self):
        """
        This test validates that the /jump_to_id/<id> shorthand for intracourse linking works assertIn
        expected. Note there's a HTML element in the 'toy' course with the url_name 'toyjumpto' which
        defines this linkage
        """
        mock_request = MagicMock()
        mock_request.user = self.mock_user

        course = get_course_with_access(self.mock_user, 'load',
                                        self.course_key)

        field_data_cache = FieldDataCache.cache_for_descriptor_descendents(
            self.course_key, self.mock_user, course, depth=2)

        module = render.get_module(
            self.mock_user, mock_request,
            self.course_key.make_usage_key('html', 'toyjumpto'),
            field_data_cache, self.course_key)

        # get the rendered HTML output which should have the rewritten link
        html = module.render('student_view').content

        # See if the url got rewritten to the target link
        # note if the URL mapping changes then this assertion will break
        self.assertIn(
            '/courses/' + self.course_key.to_deprecated_string() +
            '/jump_to_id/vertical_test', html)

    def test_xqueue_callback_success(self):
        """
        Test for happy-path xqueue_callback
        """
        fake_key = 'fake key'
        xqueue_header = json.dumps({'lms_key': fake_key})
        data = {
            'xqueue_header': xqueue_header,
            'xqueue_body': 'hello world',
        }

        # Patch getmodule to return our mock module
        with patch('courseware.module_render.find_target_student_module'
                   ) as get_fake_module:
            get_fake_module.return_value = self.mock_module
            # call xqueue_callback with our mocked information
            request = self.request_factory.post(self.callback_url, data)
            render.xqueue_callback(request, self.course_key, self.mock_user.id,
                                   self.mock_module.id, self.dispatch)

        # Verify that handle ajax is called with the correct data
        request.POST['queuekey'] = fake_key
        self.mock_module.handle_ajax.assert_called_once_with(
            self.dispatch, request.POST)

    def test_xqueue_callback_missing_header_info(self):
        data = {
            'xqueue_header': '{}',
            'xqueue_body': 'hello world',
        }

        with patch('courseware.module_render.find_target_student_module'
                   ) as get_fake_module:
            get_fake_module.return_value = self.mock_module
            # Test with missing xqueue data
            with self.assertRaises(Http404):
                request = self.request_factory.post(self.callback_url, {})
                render.xqueue_callback(request, self.course_key,
                                       self.mock_user.id, self.mock_module.id,
                                       self.dispatch)

            # Test with missing xqueue_header
            with self.assertRaises(Http404):
                request = self.request_factory.post(self.callback_url, data)
                render.xqueue_callback(request, self.course_key,
                                       self.mock_user.id, self.mock_module.id,
                                       self.dispatch)

    def test_get_score_bucket(self):
        self.assertEquals(render.get_score_bucket(0, 10), 'incorrect')
        self.assertEquals(render.get_score_bucket(1, 10), 'partial')
        self.assertEquals(render.get_score_bucket(10, 10), 'correct')
        # get_score_bucket calls error cases 'incorrect'
        self.assertEquals(render.get_score_bucket(11, 10), 'incorrect')
        self.assertEquals(render.get_score_bucket(-1, 10), 'incorrect')

    def test_anonymous_handle_xblock_callback(self):
        dispatch_url = reverse(
            'xblock_handler',
            args=[
                self.course_key.to_deprecated_string(),
                quote_slashes(
                    self.course_key.make_usage_key(
                        'videosequence', 'Toy_Videos').to_deprecated_string()),
                'xmodule_handler', 'goto_position'
            ])
        response = self.client.post(dispatch_url, {'position': 2})
        self.assertEquals(403, response.status_code)
Beispiel #58
0
class TestJumpTo(ModuleStoreTestCase):
    """
    Check the jumpto link for a course.
    """
    MODULESTORE = TEST_DATA_MIXED_TOY_MODULESTORE

    def setUp(self):
        super(TestJumpTo, self).setUp()
        # Use toy course from XML
        self.course_key = SlashSeparatedCourseKey('edX', 'toy', '2012_Fall')

    def test_jumpto_invalid_location(self):
        location = self.course_key.make_usage_key(None, 'NoSuchPlace')
        # This is fragile, but unfortunately the problem is that within the LMS we
        # can't use the reverse calls from the CMS
        jumpto_url = '{0}/{1}/jump_to/{2}'.format('/courses', self.course_key.to_deprecated_string(), location.to_deprecated_string())
        response = self.client.get(jumpto_url)
        self.assertEqual(response.status_code, 404)

    @unittest.skip
    def test_jumpto_from_chapter(self):
        location = self.course_key.make_usage_key('chapter', 'Overview')
        jumpto_url = '{0}/{1}/jump_to/{2}'.format('/courses', self.course_key.to_deprecated_string(), location.to_deprecated_string())
        expected = 'courses/edX/toy/2012_Fall/courseware/Overview/'
        response = self.client.get(jumpto_url)
        self.assertRedirects(response, expected, status_code=302, target_status_code=302)

    @unittest.skip
    def test_jumpto_id(self):
        jumpto_url = '{0}/{1}/jump_to_id/{2}'.format('/courses', self.course_key.to_deprecated_string(), 'Overview')
        expected = 'courses/edX/toy/2012_Fall/courseware/Overview/'
        response = self.client.get(jumpto_url)
        self.assertRedirects(response, expected, status_code=302, target_status_code=302)

    def test_jumpto_from_section(self):
        course = CourseFactory.create()
        chapter = ItemFactory.create(category='chapter', parent_location=course.location)
        section = ItemFactory.create(category='sequential', parent_location=chapter.location)
        expected = 'courses/{course_id}/courseware/{chapter_id}/{section_id}/?{activate_block_id}'.format(
            course_id=unicode(course.id),
            chapter_id=chapter.url_name,
            section_id=section.url_name,
            activate_block_id=urlencode({'activate_block_id': unicode(section.location)})
        )
        jumpto_url = '{0}/{1}/jump_to/{2}'.format(
            '/courses',
            unicode(course.id),
            unicode(section.location),
        )
        response = self.client.get(jumpto_url)
        self.assertRedirects(response, expected, status_code=302, target_status_code=302)

    def test_jumpto_from_module(self):
        course = CourseFactory.create()
        chapter = ItemFactory.create(category='chapter', parent_location=course.location)
        section = ItemFactory.create(category='sequential', parent_location=chapter.location)
        vertical1 = ItemFactory.create(category='vertical', parent_location=section.location)
        vertical2 = ItemFactory.create(category='vertical', parent_location=section.location)
        module1 = ItemFactory.create(category='html', parent_location=vertical1.location)
        module2 = ItemFactory.create(category='html', parent_location=vertical2.location)

        expected = 'courses/{course_id}/courseware/{chapter_id}/{section_id}/1?{activate_block_id}'.format(
            course_id=unicode(course.id),
            chapter_id=chapter.url_name,
            section_id=section.url_name,
            activate_block_id=urlencode({'activate_block_id': unicode(module1.location)})
        )
        jumpto_url = '{0}/{1}/jump_to/{2}'.format(
            '/courses',
            unicode(course.id),
            unicode(module1.location),
        )
        response = self.client.get(jumpto_url)
        self.assertRedirects(response, expected, status_code=302, target_status_code=302)

        expected = 'courses/{course_id}/courseware/{chapter_id}/{section_id}/2?{activate_block_id}'.format(
            course_id=unicode(course.id),
            chapter_id=chapter.url_name,
            section_id=section.url_name,
            activate_block_id=urlencode({'activate_block_id': unicode(module2.location)})
        )
        jumpto_url = '{0}/{1}/jump_to/{2}'.format(
            '/courses',
            unicode(course.id),
            unicode(module2.location),
        )
        response = self.client.get(jumpto_url)
        self.assertRedirects(response, expected, status_code=302, target_status_code=302)

    def test_jumpto_from_nested_module(self):
        course = CourseFactory.create()
        chapter = ItemFactory.create(category='chapter', parent_location=course.location)
        section = ItemFactory.create(category='sequential', parent_location=chapter.location)
        vertical = ItemFactory.create(category='vertical', parent_location=section.location)
        nested_section = ItemFactory.create(category='sequential', parent_location=vertical.location)
        nested_vertical1 = ItemFactory.create(category='vertical', parent_location=nested_section.location)
        # put a module into nested_vertical1 for completeness
        ItemFactory.create(category='html', parent_location=nested_vertical1.location)
        nested_vertical2 = ItemFactory.create(category='vertical', parent_location=nested_section.location)
        module2 = ItemFactory.create(category='html', parent_location=nested_vertical2.location)

        # internal position of module2 will be 1_2 (2nd item withing 1st item)

        expected = 'courses/{course_id}/courseware/{chapter_id}/{section_id}/1?{activate_block_id}'.format(
            course_id=unicode(course.id),
            chapter_id=chapter.url_name,
            section_id=section.url_name,
            activate_block_id=urlencode({'activate_block_id': unicode(module2.location)})
        )
        jumpto_url = '{0}/{1}/jump_to/{2}'.format(
            '/courses',
            unicode(course.id),
            unicode(module2.location),
        )
        response = self.client.get(jumpto_url)
        self.assertRedirects(response, expected, status_code=302, target_status_code=302)

    def test_jumpto_id_invalid_location(self):
        location = Location('edX', 'toy', 'NoSuchPlace', None, None, None)
        jumpto_url = '{0}/{1}/jump_to_id/{2}'.format('/courses', self.course_key.to_deprecated_string(), location.to_deprecated_string())
        response = self.client.get(jumpto_url)
        self.assertEqual(response.status_code, 404)
    def test_translate_location_read_only(self):
        """
        Test the variants of translate_location which don't create entries, just decode
        """
        # lookup before there are any maps
        org = 'foo_org'
        course = 'bar_course'
        run = 'baz_run'
        slash_course_key = SlashSeparatedCourseKey(org, course, run)
        with self.assertRaises(ItemNotFoundError):
            _ = loc_mapper().translate_location(
                Location(org, course, run, 'problem', 'abc123'),
                add_entry_if_missing=False
            )

        new_style_org = '{}.geek_dept'.format(org)
        new_style_offering = '.{}.{}'.format(course, run)
        block_map = {
            'abc123': {'problem': 'problem2', 'vertical': 'vertical2'},
            'def456': {'problem': 'problem4'},
            'ghi789': {'problem': 'problem7'},
        }
        loc_mapper().create_map_entry(
            slash_course_key,
            new_style_org, new_style_offering,
            block_map=block_map
        )
        test_problem_locn = Location(org, course, run, 'problem', 'abc123')

        self.translate_n_check(test_problem_locn, new_style_org, new_style_offering, 'problem2', 'published')
        # look for non-existent problem
        with self.assertRaises(ItemNotFoundError):
            loc_mapper().translate_location(
                Location(org, course, run, 'problem', '1def23'),
                add_entry_if_missing=False
            )
        test_no_cat_locn = test_problem_locn.replace(category=None)
        with self.assertRaises(InvalidLocationError):
            loc_mapper().translate_location(
                slash_course_key.make_usage_key(None, 'abc123'), test_no_cat_locn, False, False
            )
        test_no_cat_locn = test_no_cat_locn.replace(name='def456')

        self.translate_n_check(
            test_no_cat_locn, new_style_org, new_style_offering, 'problem4', 'published'
        )

        # add a distractor course (note that abc123 has a different translation in this one)
        distractor_block_map = {
            'abc123': {'problem': 'problem3'},
            'def456': {'problem': 'problem4'},
            'ghi789': {'problem': 'problem7'},
        }
        run = 'delta_run'
        test_delta_new_org = '{}.geek_dept'.format(org)
        test_delta_new_offering = '{}.{}'.format(course, run)
        loc_mapper().create_map_entry(
            SlashSeparatedCourseKey(org, course, run),
            test_delta_new_org, test_delta_new_offering,
            block_map=distractor_block_map
        )
        # test that old translation still works
        self.translate_n_check(
            test_problem_locn, new_style_org, new_style_offering, 'problem2', 'published'
        )
        # and new returns new id
        self.translate_n_check(
            test_problem_locn.replace(run=run), test_delta_new_org, test_delta_new_offering,
            'problem3', 'published'
        )