Exemplo n.º 1
0
    def test_static_url_generation(self):

        course_key = CourseLocator('org', 'class', 'run')
        location = course_key.make_asset_key('asset', 'my_file_name.jpg')
        path = StaticContent.get_static_path_from_location(location)
        self.assertEquals(path, '/static/my_file_name.jpg')
Exemplo n.º 2
0
 def setUp(self):
     super(CourseModeModelTest, self).setUp()
     self.course_key = CourseLocator('Test', 'TestCourse', 'TestCourseRun')
     CourseMode.objects.all().delete()
Exemplo n.º 3
0
class CertificateGenerationEnabledTest(EventTestMixin, TestCase):
    """Test enabling/disabling self-generated certificates for a course. """

    COURSE_KEY = CourseLocator(org='test', course='test', run='test')

    def setUp(self):  # pylint: disable=arguments-differ
        super(CertificateGenerationEnabledTest,
              self).setUp('lms.djangoapps.certificates.api.tracker')

        # Since model-based configuration is cached, we need
        # to clear the cache before each test.
        cache.clear()

    @ddt.data((None, None, False), (False, None, False), (False, True, False),
              (True, None, False), (True, False, False), (True, True, True))
    @ddt.unpack
    def test_cert_generation_enabled(self, is_feature_enabled,
                                     is_course_enabled, expect_enabled):
        if is_feature_enabled is not None:
            CertificateGenerationConfiguration.objects.create(
                enabled=is_feature_enabled)

        if is_course_enabled is not None:
            certs_api.set_cert_generation_enabled(self.COURSE_KEY,
                                                  is_course_enabled)
            cert_event_type = 'enabled' if is_course_enabled else 'disabled'
            event_name = '.'.join(
                ['edx', 'certificate', 'generation', cert_event_type])
            self.assert_event_emitted(
                event_name,
                course_id=six.text_type(self.COURSE_KEY),
            )

        self._assert_enabled_for_course(self.COURSE_KEY, expect_enabled)

    def test_latest_setting_used(self):
        # Enable the feature
        CertificateGenerationConfiguration.objects.create(enabled=True)

        # Enable for the course
        certs_api.set_cert_generation_enabled(self.COURSE_KEY, True)
        self._assert_enabled_for_course(self.COURSE_KEY, True)

        # Disable for the course
        certs_api.set_cert_generation_enabled(self.COURSE_KEY, False)
        self._assert_enabled_for_course(self.COURSE_KEY, False)

    def test_setting_is_course_specific(self):
        # Enable the feature
        CertificateGenerationConfiguration.objects.create(enabled=True)

        # Enable for one course
        certs_api.set_cert_generation_enabled(self.COURSE_KEY, True)
        self._assert_enabled_for_course(self.COURSE_KEY, True)

        # Should be disabled for another course
        other_course = CourseLocator(org='other', course='other', run='other')
        self._assert_enabled_for_course(other_course, False)

    def _assert_enabled_for_course(self, course_key, expect_enabled):
        """Check that self-generated certificates are enabled or disabled for the course. """
        actual_enabled = certs_api.cert_generation_enabled(course_key)
        self.assertEqual(expect_enabled, actual_enabled)
Exemplo n.º 4
0
 def setUp(self):
     super().setUp()
     self.course_key = CourseLocator('org', 'course', str(uuid4()))
Exemplo n.º 5
0
 def test_non_existent_course(self):
     usage_key = self.store.make_course_usage_key(
         CourseLocator('non', 'existent', 'course'))
     url = reverse('blocks_in_block_tree',
                   kwargs={'usage_key_string': str(usage_key)})
     self.verify_response(403, url=url)
Exemplo n.º 6
0
 def setUp(self) -> None:
     super().setUp()
     self.course_key = CourseLocator(org="org", course="course", run="run")
     self.default_app_id = "test-app"
Exemplo n.º 7
0
 def test_get_filename_with_colon(self):
     course_id = unicode(CourseLocator(org='org', course='course:id', run='course:run'))
     self.assertEquals(opaque_key_util.get_filename_safe_course_id(VALID_COURSE_ID), "org_course_id_course_run")
     self.assertEquals(opaque_key_util.get_filename_safe_course_id(course_id, '-'), "org-course-id-course-run")
Exemplo n.º 8
0
    def test_all_current_course_configs(self):
        # Set up test objects
        for global_setting in (True, False, None):
            CourseDurationLimitConfig.objects.create(enabled=global_setting,
                                                     enabled_as_of=datetime(
                                                         2018, 1, 1))
            for site_setting in (True, False, None):
                test_site_cfg = SiteConfigurationFactory.create(
                    site_values={'course_org_filter': []})
                CourseDurationLimitConfig.objects.create(
                    site=test_site_cfg.site,
                    enabled=site_setting,
                    enabled_as_of=datetime(2018, 1, 1))

                for org_setting in (True, False, None):
                    test_org = "{}-{}".format(test_site_cfg.id, org_setting)
                    test_site_cfg.site_values['course_org_filter'].append(
                        test_org)
                    test_site_cfg.save()

                    CourseDurationLimitConfig.objects.create(
                        org=test_org,
                        enabled=org_setting,
                        enabled_as_of=datetime(2018, 1, 1))

                    for course_setting in (True, False, None):
                        test_course = CourseOverviewFactory.create(
                            org=test_org,
                            id=CourseLocator(test_org, 'test_course',
                                             'run-{}'.format(course_setting)))
                        CourseDurationLimitConfig.objects.create(
                            course=test_course,
                            enabled=course_setting,
                            enabled_as_of=datetime(2018, 1, 1))

            with self.assertNumQueries(4):
                all_configs = CourseDurationLimitConfig.all_current_course_configs(
                )

        # Deliberatly using the last all_configs that was checked after the 3rd pass through the global_settings loop
        # We should be creating 3^4 courses (3 global values * 3 site values * 3 org values * 3 course values)
        # Plus 1 for the edX/toy/2012_Fall course
        self.assertEqual(len(all_configs), 3**4 + 1)

        # Point-test some of the final configurations
        self.assertEqual(
            all_configs[CourseLocator('7-True', 'test_course', 'run-None')], {
                'enabled': (True, Provenance.org),
                'enabled_as_of':
                (datetime(2018, 1, 1, 0, tzinfo=pytz.UTC), Provenance.run),
            })
        self.assertEqual(
            all_configs[CourseLocator('7-True', 'test_course', 'run-False')], {
                'enabled': (False, Provenance.run),
                'enabled_as_of':
                (datetime(2018, 1, 1, 0, tzinfo=pytz.UTC), Provenance.run),
            })
        self.assertEqual(
            all_configs[CourseLocator('7-None', 'test_course', 'run-None')], {
                'enabled': (True, Provenance.site),
                'enabled_as_of':
                (datetime(2018, 1, 1, 0, tzinfo=pytz.UTC), Provenance.run),
            })
Exemplo n.º 9
0
 def test_unicode_values(self):
     course_id = CourseLocator('abc', '123', 'doremi')
     restricted_course = RestrictedCourse.objects.create(
         course_key=course_id)
     self.assertEqual(six.text_type(restricted_course),
                      six.text_type(course_id))
Exemplo n.º 10
0
class CourseBlocksSignalTest(ModuleStoreTestCase):
    """
    Tests for the Course Blocks signal
    """
    ENABLED_SIGNALS = ['course_deleted', 'course_published']

    def setUp(self):
        super(CourseBlocksSignalTest, self).setUp()
        self.course = CourseFactory.create()
        self.course_usage_key = self.store.make_course_usage_key(
            self.course.id)

    def test_course_update(self):
        test_display_name = "Lightsabers 101"

        # Course exists in cache initially
        bs_manager = get_block_structure_manager(self.course.id)
        orig_block_structure = bs_manager.get_collected()
        self.assertTrue(
            is_course_in_block_structure_cache(self.course.id, self.store))
        self.assertNotEqual(
            test_display_name,
            orig_block_structure.get_xblock_field(self.course_usage_key,
                                                  'display_name'))

        self.course.display_name = test_display_name
        self.store.update_item(self.course, self.user.id)

        # Cached version of course has been updated
        updated_block_structure = bs_manager.get_collected()
        self.assertEqual(
            test_display_name,
            updated_block_structure.get_xblock_field(self.course_usage_key,
                                                     'display_name'))

    @ddt.data(True, False)
    @patch(
        'openedx.core.djangoapps.content.block_structure.manager.BlockStructureManager.clear'
    )
    def test_cache_invalidation(self, invalidate_cache_enabled,
                                mock_bs_manager_clear):
        test_display_name = "Jedi 101"

        with waffle().override(INVALIDATE_CACHE_ON_PUBLISH,
                               active=invalidate_cache_enabled):
            self.course.display_name = test_display_name
            self.store.update_item(self.course, self.user.id)

        self.assertEquals(mock_bs_manager_clear.called,
                          invalidate_cache_enabled)

    def test_course_delete(self):
        bs_manager = get_block_structure_manager(self.course.id)
        self.assertIsNotNone(bs_manager.get_collected())
        self.assertTrue(
            is_course_in_block_structure_cache(self.course.id, self.store))

        self.store.delete_course(self.course.id, self.user.id)
        with self.assertRaises(ItemNotFoundError):
            bs_manager.get_collected()

        self.assertFalse(
            is_course_in_block_structure_cache(self.course.id, self.store))

    @ddt.data(
        (CourseLocator(org='org', course='course', run='run'), True),
        (LibraryLocator(org='org', course='course'), False),
    )
    @ddt.unpack
    @patch(
        'openedx.core.djangoapps.content.block_structure.tasks.update_course_in_cache_v2.apply_async'
    )
    def test_update_only_for_courses(self, key, expect_update_called,
                                     mock_update):
        update_block_structure_on_course_publish(sender=None, course_key=key)
        self.assertEqual(mock_update.called, expect_update_called)
Exemplo n.º 11
0
 def test_make_course_usage_key(self):
     """Test that we get back the appropriate usage key for the root of a course key."""
     course_key = CourseLocator(org="edX", course="101", run="2015")
     root_block_key = self.draft_store.make_course_usage_key(course_key)
     self.assertEqual(root_block_key.block_type, "course")
     self.assertEqual(root_block_key.name, "2015")
 def get_key(self):
     return CourseLocator(self.courselike_key.org,
                          self.courselike_key.course,
                          self.courselike_key.run,
                          deprecated=True)
Exemplo n.º 13
0
def export_to_xml(modulestore, contentstore, course_key, root_dir, course_dir):
    """
    Export all modules from `modulestore` and content from `contentstore` as xml to `root_dir`.

    `modulestore`: A `ModuleStore` object that is the source of the modules to export
    `contentstore`: A `ContentStore` object that is the source of the content to export, can be None
    `course_key`: The `CourseKey` of the `CourseModuleDescriptor` to export
    `root_dir`: The directory to write the exported xml to
    `course_dir`: The name of the directory inside `root_dir` to write the course content to
    """

    course = modulestore.get_course(course_key,
                                    depth=None)  # None means infinite
    fsm = OSFS(root_dir)
    export_fs = course.runtime.export_fs = fsm.makeopendir(course_dir)

    root = lxml.etree.Element('unknown')

    # export only the published content
    with modulestore.branch_setting(ModuleStoreEnum.Branch.published_only,
                                    course_key):
        # change all of the references inside the course to use the xml expected key type w/o version & branch
        xml_centric_course_key = CourseLocator(course_key.org,
                                               course_key.course,
                                               course_key.run,
                                               deprecated=True)
        adapt_references(course, xml_centric_course_key, export_fs)

        course.add_xml_to_node(root)

    with export_fs.open('course.xml', 'w') as course_xml:
        lxml.etree.ElementTree(root).write(course_xml)

    # export the static assets
    policies_dir = export_fs.makeopendir('policies')
    if contentstore:
        contentstore.export_all_for_course(
            course_key,
            root_dir + '/' + course_dir + '/static/',
            root_dir + '/' + course_dir + '/policies/assets.json',
        )

        # If we are using the default course image, export it to the
        # legacy location to support backwards compatibility.
        if course.course_image == course.fields['course_image'].default:
            try:
                course_image = contentstore.find(
                    StaticContent.compute_location(course.id,
                                                   course.course_image), )
            except NotFoundError:
                pass
            else:
                output_dir = root_dir + '/' + course_dir + '/static/images/'
                if not os.path.isdir(output_dir):
                    os.makedirs(output_dir)
                with OSFS(output_dir).open('course_image.jpg',
                                           'wb') as course_image_file:
                    course_image_file.write(course_image.data)

    # export the static tabs
    export_extra_content(export_fs, modulestore, xml_centric_course_key,
                         'static_tab', 'tabs', '.html')

    # export the custom tags
    export_extra_content(export_fs, modulestore, xml_centric_course_key,
                         'custom_tag_template', 'custom_tags')

    # export the course updates
    export_extra_content(export_fs, modulestore, xml_centric_course_key,
                         'course_info', 'info', '.html')

    # export the 'about' data (e.g. overview, etc.)
    export_extra_content(export_fs, modulestore, xml_centric_course_key,
                         'about', 'about', '.html')

    # export the grading policy
    course_run_policy_dir = policies_dir.makeopendir(course.location.name)
    with course_run_policy_dir.open('grading_policy.json',
                                    'w') as grading_policy:
        grading_policy.write(dumps(course.grading_policy, cls=EdxJSONEncoder))

    # export all of the course metadata in policy.json
    with course_run_policy_dir.open('policy.json', 'w') as course_policy:
        policy = {'course/' + course.location.name: own_metadata(course)}
        course_policy.write(dumps(policy, cls=EdxJSONEncoder))

    #### DRAFTS ####
    # xml backed courses don't support drafts!
    if course.runtime.modulestore.get_modulestore_type(
    ) != ModuleStoreEnum.Type.xml:
        # NOTE: this code assumes that verticals are the top most draftable container
        # should we change the application, then this assumption will no longer be valid
        # NOTE: we need to explicitly implement the logic for setting the vertical's parent
        # and index here since the XML modulestore cannot load draft modules
        with modulestore.branch_setting(ModuleStoreEnum.Branch.draft_preferred,
                                        course_key):
            draft_verticals = modulestore.get_items(
                course_key,
                qualifiers={'category': 'vertical'},
                revision=ModuleStoreEnum.RevisionOption.draft_only)

            if len(draft_verticals) > 0:
                draft_course_dir = export_fs.makeopendir(DRAFT_DIR)
                for draft_vertical in draft_verticals:
                    parent_loc = modulestore.get_parent_location(
                        draft_vertical.location,
                        revision=ModuleStoreEnum.RevisionOption.draft_preferred
                    )
                    # Don't try to export orphaned items.
                    if parent_loc is not None:
                        logging.debug('parent_loc = {0}'.format(parent_loc))
                        if parent_loc.category in DIRECT_ONLY_CATEGORIES:
                            draft_vertical.xml_attributes[
                                'parent_sequential_url'] = parent_loc.to_deprecated_string(
                                )
                            sequential = modulestore.get_item(parent_loc)
                            index = sequential.children.index(
                                draft_vertical.location)
                            draft_vertical.xml_attributes[
                                'index_in_children_list'] = str(index)
                        draft_vertical.runtime.export_fs = draft_course_dir
                        adapt_references(draft_vertical,
                                         xml_centric_course_key,
                                         draft_course_dir)
                        node = lxml.etree.Element('unknown')
                        draft_vertical.add_xml_to_node(node)
Exemplo n.º 14
0
class FeatureFlagTestMixin(object):
    """
    Adds util methods to test the behavior of the flags for video feature.
    """
    course_id_1 = CourseLocator(org="edx", course="course", run="run")
    course_id_2 = CourseLocator(org="edx", course="course2", run="run")

    def verify_feature_flags(self, all_courses_model_class, course_specific_model_class,
                             global_flag, enabled_for_all_courses, enabled_for_course_1):
        """
        Verifies that the feature flags works correctly on tweaking global flags in combination
        with course-specific flags.
        """
        with video_feature_flags(
            all_courses_model_class=all_courses_model_class,
            course_specific_model_class=course_specific_model_class,
            global_flag=global_flag,
            enabled_for_all_courses=enabled_for_all_courses,
            course_id=self.course_id_1,
            enabled_for_course=enabled_for_course_1
        ):
            self.assertEqual(
                all_courses_model_class.feature_enabled(self.course_id_1),
                global_flag and (enabled_for_all_courses or enabled_for_course_1)
            )
            self.assertEqual(
                all_courses_model_class.feature_enabled(self.course_id_2),
                global_flag and enabled_for_all_courses
            )

    def verify_enable_disable_course_flag(self, all_courses_model_class, course_specific_model_class):
        """
        Verifies that the course specific flag, once enabled for a course, can also be disabled.
        """
        with video_feature_flags(
            all_courses_model_class=all_courses_model_class,
            course_specific_model_class=course_specific_model_class,
            global_flag=True,
            enabled_for_all_courses=False,
            course_id=self.course_id_1,
            enabled_for_course=True
        ):
            self.assertTrue(all_courses_model_class.feature_enabled(self.course_id_1))
            with video_feature_flags(
                all_courses_model_class=all_courses_model_class,
                course_specific_model_class=course_specific_model_class,
                global_flag=True,
                enabled_for_all_courses=False,
                course_id=self.course_id_1,
                enabled_for_course=False
            ):
                self.assertFalse(all_courses_model_class.feature_enabled(self.course_id_1))

    def verify_enable_disable_globally(self, all_courses_model_class, course_specific_model_class):
        """
        Verifies that global flag, once enabled globally, can also be disabled.
        """
        with video_feature_flags(
            all_courses_model_class=all_courses_model_class,
            course_specific_model_class=course_specific_model_class,
            global_flag=True,
            enabled_for_all_courses=True,
        ):
            self.assertTrue(all_courses_model_class.feature_enabled(self.course_id_1))
            self.assertTrue(all_courses_model_class.feature_enabled(self.course_id_2))
            with video_feature_flags(
                all_courses_model_class=all_courses_model_class,
                course_specific_model_class=course_specific_model_class,
                global_flag=True,
                enabled_for_all_courses=False,
            ):
                self.assertFalse(all_courses_model_class.feature_enabled(self.course_id_1))
                self.assertFalse(all_courses_model_class.feature_enabled(self.course_id_2))
                with video_feature_flags(
                    all_courses_model_class=all_courses_model_class,
                    course_specific_model_class=course_specific_model_class,
                    global_flag=False,
                ):
                    self.assertFalse(all_courses_model_class.feature_enabled(self.course_id_1))
                    self.assertFalse(all_courses_model_class.feature_enabled(self.course_id_2))
Exemplo n.º 15
0
class TestAsideKeys(TestCase):
    """Test of Aside keys."""
    @ddt.data(*itertools.product([
        AsideUsageKeyV1,
        AsideUsageKeyV2,
    ], [
        Location.from_string('i4x://org/course/cat/name'),
        BlockUsageLocator(CourseLocator('org', 'course', 'run'), 'block_type', 'block_id'),
    ], [
        'aside',
        'aside_b'
    ]))
    @ddt.unpack
    def test_usage_round_trip_deserialized(self, key_class, usage_key, aside_type):
        key = key_class(usage_key, aside_type)
        serialized = str(key)
        deserialized = AsideUsageKey.from_string(serialized)
        self.assertEqual(key, deserialized)
        self.assertEqual(usage_key, key.usage_key, usage_key)
        self.assertEqual(usage_key, deserialized.usage_key)
        self.assertEqual(aside_type, key.aside_type)
        self.assertEqual(aside_type, deserialized.aside_type)

    @ddt.data(
        'aside-usage-v1:i4x://org/course/cat/name::aside',
        'aside-usage-v1:block-v1:org+course+cat+type@block_type+block@name::aside',
        'aside-usage-v2:lib-block-v1$:$:+-+branch@-+version@000000000000000000000000+type@-+block@-::0',
        'aside-usage-v2:i4x$://-/-/-/$:$:-::0',
        'aside-usage-v2:i4x$://-/-/-/$:$:$:-::0',
        'aside-usage-v2:i4x$://-/-/$:$:$:$:$:/-::0',
    )
    def test_usage_round_trip_serialized(self, aside_key):
        deserialized = AsideUsageKey.from_string(aside_key)
        serialized = str(deserialized)
        self.assertEqual(aside_key, serialized)

    @ddt.data(*itertools.product([
        AsideDefinitionKeyV1,
        AsideDefinitionKeyV2,
    ], [
        DefinitionLocator('block_type', 'abcd1234abcd1234abcd1234'),
    ], [
        'aside',
        'aside_b'
    ]))
    @ddt.unpack
    def test_definition_round_trip_deserialized(self, key_class, definition_key, aside_type):
        key = key_class(definition_key, aside_type)
        serialized = str(key)
        deserialized = AsideDefinitionKey.from_string(serialized)
        self.assertEqual(key, deserialized)
        self.assertEqual(definition_key, key.definition_key, definition_key)
        self.assertEqual(definition_key, deserialized.definition_key)
        self.assertEqual(aside_type, key.aside_type)
        self.assertEqual(aside_type, deserialized.aside_type)

    @ddt.data(
        'aside-def-v1:def-v1:abcd1234abcd1234abcd1234+type@block_type::aside',
        'aside-def-v2:def-v1$:abcd1234abcd1234abcd1234+type@block_type::aside'
    )
    def test_definition_round_trip_serialized(self, aside_key):
        deserialized = AsideDefinitionKey.from_string(aside_key)
        serialized = str(deserialized)
        self.assertEqual(aside_key, serialized)

    @ddt.data(*itertools.product([
        AsideUsageKeyV1,
        AsideUsageKeyV2,
    ], [
        ('aside_type', 'bside'),
        ('usage_key', BlockUsageLocator(CourseLocator('borg', 'horse', 'gun'), 'lock_type', 'lock_id')),
        ('block_id', 'lock_id'),
        ('block_type', 'lock_type'),
        # BlockUsageLocator can't `replace` a definition_key, so skip for now
        # ('definition_key', DefinitionLocator('block_type', 'abcd1234abcd1234abcd1234')),
        ('course_key', CourseLocator('borg', 'horse', 'gun')),
    ]))
    @ddt.unpack
    def test_usage_key_replace(self, key_class, attr_value):
        attr, value = attr_value
        key = key_class(
            BlockUsageLocator(CourseLocator('org', 'course', 'run'), 'block_type', 'block_id'),
            'aside'
        )
        new_key = key.replace(**{attr: value})
        self.assertEqual(getattr(new_key, attr), value)

    @ddt.data(*itertools.product([
        AsideDefinitionKeyV1,
        AsideDefinitionKeyV2,
    ], [
        ('aside_type', 'bside'),
        ('definition_key', DefinitionLocator('block_type', 'abcd1234abcd1234abcd1234')),
        ('block_type', 'lock_type'),
    ]))
    @ddt.unpack
    def test_definition_key_replace(self, key_class, attr_value):
        attr, value = attr_value
        key = key_class(DefinitionLocator('block_type', 'abcd1234abcd1234abcd1234'), 'aside')
        new_key = key.replace(**{attr: value})
        self.assertEqual(getattr(new_key, attr), value)
 def test_output_path(self):
     course_id = str(CourseLocator(org='Sample', course='Course', run='ID'))
     filename = self.task.output_path_for_key(course_id)
     expected = 'test://output/Sample-Course-ID-courseware_studentmodule-test-analytics.sql'
     self.assertEqual(filename, expected)
Exemplo n.º 17
0
def generate_course_key(org, number, run):
    """
    Makes a CourseLocator from org, number and run
    """
    default_store = os.environ.get('DEFAULT_STORE', 'draft')
    return CourseLocator(org, number, run, deprecated=(default_store == 'draft'))
Exemplo n.º 18
0
 def setUp(self):
     super(PersistentGradesFeatureFlagTests, self).setUp()
     self.course_id_1 = CourseLocator(org="edx", course="course", run="run")
     self.course_id_2 = CourseLocator(org="edx", course="course2", run="run")
Exemplo n.º 19
0
"""
Tests for utilities that parse event logs.
"""

from opaque_keys.edx.locator import CourseLocator

import edx.analytics.tasks.util.opaque_key_util as opaque_key_util
from edx.analytics.tasks.tests import unittest


VALID_COURSE_ID = unicode(CourseLocator(org='org', course='course_id', run='course_run'))
VALID_LEGACY_COURSE_ID = "org/course_id/course_run"
INVALID_LEGACY_COURSE_ID = "org:course_id:course_run"
INVALID_NONASCII_LEGACY_COURSE_ID = u"org/course\ufffd_id/course_run"
VALID_NONASCII_LEGACY_COURSE_ID = u"org/cours\u00e9_id/course_run"


class CourseIdTest(unittest.TestCase):
    """
    Verify that course_id filtering works correctly.
    """

    def test_normal_opaque_course_id(self):
        self.assertTrue(opaque_key_util.is_valid_course_id(VALID_COURSE_ID))

    def test_normal_legacy_course_id(self):
        self.assertTrue(opaque_key_util.is_valid_course_id(VALID_LEGACY_COURSE_ID))

    def test_legacy_course_id_without_components(self):
        self.assertFalse(opaque_key_util.is_valid_course_id(INVALID_LEGACY_COURSE_ID))
Exemplo n.º 20
0
 def setUp(self):
     super(BlockRecordListTestCase, self).setUp()
     self.course_key = CourseLocator(org='some_org',
                                     course='some_course',
                                     run='some_run')
 def setUp(self):
     super().setUp()
     self.course_key = CourseLocator('Robot', 'fAKE', 'C--se--ID')
Exemplo n.º 22
0
class XBlockCacheModelTest(ModuleStoreTestCase):
    """
    Test the XBlockCache model.
    """
    COURSE_KEY = CourseLocator(org='test', course='test', run='test')
    CHAPTER1_USAGE_KEY = BlockUsageLocator(COURSE_KEY,
                                           block_type='chapter',
                                           block_id='chapter1')
    SECTION1_USAGE_KEY = BlockUsageLocator(COURSE_KEY,
                                           block_type='section',
                                           block_id='section1')
    SECTION2_USAGE_KEY = BlockUsageLocator(COURSE_KEY,
                                           block_type='section',
                                           block_id='section1')
    VERTICAL1_USAGE_KEY = BlockUsageLocator(COURSE_KEY,
                                            block_type='vertical',
                                            block_id='sequential1')
    PATH1 = [
        [text_type(CHAPTER1_USAGE_KEY), 'Chapter 1'],
        [text_type(SECTION1_USAGE_KEY), 'Section 1'],
    ]
    PATH2 = [
        [text_type(CHAPTER1_USAGE_KEY), 'Chapter 1'],
        [text_type(SECTION2_USAGE_KEY), 'Section 2'],
    ]

    def assert_xblock_cache_data(self, xblock_cache, data):
        """
        Assert that the XBlockCache object values match.
        """
        self.assertEqual(xblock_cache.usage_key, data['usage_key'])
        self.assertEqual(xblock_cache.course_key, data['usage_key'].course_key)
        self.assertEqual(xblock_cache.display_name, data['display_name'])
        self.assertEqual(xblock_cache._paths, data['_paths'])  # pylint: disable=protected-access
        self.assertEqual(xblock_cache.paths,
                         [parse_path_data(path) for path in data['_paths']])

    @ddt.data(
        (
            [
                {
                    'usage_key': VERTICAL1_USAGE_KEY,
                },
                {
                    'display_name': '',
                    '_paths': [],
                },
            ],
            [
                {
                    'usage_key': VERTICAL1_USAGE_KEY,
                    'display_name': 'Vertical 5',
                    '_paths': [PATH2]
                },
                {
                    '_paths': []
                },
            ],
        ),
        (
            [
                {
                    'usage_key': VERTICAL1_USAGE_KEY,
                    'display_name': 'Vertical 4',
                    '_paths': [PATH1]
                },
                {},
            ],
            [
                {
                    'usage_key': VERTICAL1_USAGE_KEY,
                    'display_name': 'Vertical 5',
                    '_paths': [PATH2]
                },
                {
                    '_paths': [PATH1]
                },
            ],
        ),
    )
    def test_create(self, data):
        """
        Test XBlockCache.create() constructs and updates objects correctly.
        """
        for create_data, additional_data_to_expect in data:
            xblock_cache = XBlockCache.create(create_data)
            create_data.update(additional_data_to_expect)
            self.assert_xblock_cache_data(xblock_cache, create_data)

    @ddt.data(
        ([], [PATH1]),
        ([PATH1, PATH2], [PATH1]),
        ([PATH1], []),
    )
    @ddt.unpack
    def test_paths(self, original_paths, updated_paths):
        xblock_cache = XBlockCache.create({
            'usage_key': self.VERTICAL1_USAGE_KEY,
            'display_name': 'The end.',
            '_paths': original_paths,
        })
        self.assertEqual(xblock_cache.paths,
                         [parse_path_data(path) for path in original_paths])

        xblock_cache.paths = [parse_path_data(path) for path in updated_paths]
        xblock_cache.save()

        xblock_cache = XBlockCache.objects.get(id=xblock_cache.id)
        self.assertEqual(xblock_cache._paths, updated_paths)  # pylint: disable=protected-access
        self.assertEqual(xblock_cache.paths,
                         [parse_path_data(path) for path in updated_paths])
Exemplo n.º 23
0
 def test_possibly_scored(self):
     course_key = CourseLocator(u'org', u'course', u'run')
     for block_type in self.possibly_scored_block_types:
         usage_key = BlockUsageLocator(course_key, block_type, 'mock_block_id')
         self.assertTrue(scores.possibly_scored(usage_key))
Exemplo n.º 24
0
class UpdateExampleCertificateViewTest(CacheIsolationTestCase):
    """Tests for the XQueue callback that updates example certificates. """

    COURSE_KEY = CourseLocator(org='test', course='test', run='test')

    DESCRIPTION = 'test'
    TEMPLATE = 'test.pdf'
    DOWNLOAD_URL = 'http://www.example.com'
    ERROR_REASON = 'Kaboom!'

    ENABLED_CACHES = ['default']

    def setUp(self):
        super(UpdateExampleCertificateViewTest, self).setUp()
        self.cert_set = ExampleCertificateSet.objects.create(
            course_key=self.COURSE_KEY)
        self.cert = ExampleCertificate.objects.create(
            example_cert_set=self.cert_set,
            description=self.DESCRIPTION,
            template=self.TEMPLATE,
        )
        self.url = reverse('update_example_certificate')

        # Since rate limit counts are cached, we need to clear
        # this before each test.
        cache.clear()

    def test_update_example_certificate_success(self):
        response = self._post_to_view(self.cert,
                                      download_url=self.DOWNLOAD_URL)
        self._assert_response(response)

        self.cert = ExampleCertificate.objects.get()
        self.assertEqual(self.cert.status, ExampleCertificate.STATUS_SUCCESS)
        self.assertEqual(self.cert.download_url, self.DOWNLOAD_URL)

    def test_update_example_certificate_invalid_key(self):
        payload = {
            'xqueue_header':
            json.dumps({'lms_key': 'invalid'}),
            'xqueue_body':
            json.dumps({
                'username': self.cert.uuid,
                'url': self.DOWNLOAD_URL
            })
        }
        response = self.client.post(self.url, data=payload)
        self.assertEqual(response.status_code, 404)

    def test_update_example_certificate_error(self):
        response = self._post_to_view(self.cert,
                                      error_reason=self.ERROR_REASON)
        self._assert_response(response)

        self.cert = ExampleCertificate.objects.get()
        self.assertEqual(self.cert.status, ExampleCertificate.STATUS_ERROR)
        self.assertEqual(self.cert.error_reason, self.ERROR_REASON)

    @ddt.data('xqueue_header', 'xqueue_body')
    def test_update_example_certificate_invalid_params(self, missing_param):
        payload = {
            'xqueue_header':
            json.dumps({'lms_key': self.cert.access_key}),
            'xqueue_body':
            json.dumps({
                'username': self.cert.uuid,
                'url': self.DOWNLOAD_URL
            })
        }
        del payload[missing_param]

        response = self.client.post(self.url, data=payload)
        self.assertEqual(response.status_code, 400)

    def test_update_example_certificate_missing_download_url(self):
        payload = {
            'xqueue_header': json.dumps({'lms_key': self.cert.access_key}),
            'xqueue_body': json.dumps({'username': self.cert.uuid})
        }
        response = self.client.post(self.url, data=payload)
        self.assertEqual(response.status_code, 400)

    def test_update_example_cetificate_non_json_param(self):
        payload = {'xqueue_header': '{/invalid', 'xqueue_body': '{/invalid'}
        response = self.client.post(self.url, data=payload)
        self.assertEqual(response.status_code, 400)

    def test_unsupported_http_method(self):
        response = self.client.get(self.url)
        self.assertEqual(response.status_code, 405)

    def test_bad_request_rate_limiting(self):
        payload = {
            'xqueue_header':
            json.dumps({'lms_key': 'invalid'}),
            'xqueue_body':
            json.dumps({
                'username': self.cert.uuid,
                'url': self.DOWNLOAD_URL
            })
        }

        # Exceed the rate limit for invalid requests
        # (simulate a DDOS with invalid keys)
        for _ in range(100):
            response = self.client.post(self.url, data=payload)
            if response.status_code == 403:
                break

        # The final status code should indicate that the rate
        # limit was exceeded.
        self.assertEqual(response.status_code, 403)

    def _post_to_view(self, cert, download_url=None, error_reason=None):
        """Simulate a callback from the XQueue to the example certificate end-point. """
        header = {'lms_key': cert.access_key}
        body = {'username': cert.uuid}

        if download_url is not None:
            body['url'] = download_url

        if error_reason is not None:
            body['error'] = 'error'
            body['error_reason'] = self.ERROR_REASON

        payload = {
            'xqueue_header': json.dumps(header),
            'xqueue_body': json.dumps(body)
        }
        return self.client.post(self.url, data=payload)

    def _assert_response(self, response):
        """Check the response from the callback end-point. """
        content = json.loads(response.content)
        self.assertEqual(response.status_code, 200)
        self.assertEqual(content['return_code'], 0)
Exemplo n.º 25
0
 def __init__(self, *args, **kwargs):
     super(CSMLoadModel, self).__init__(*args, **kwargs)
     self.course_key = CourseLocator('org', 'course', 'run')
     self.usages_with_data = set()
Exemplo n.º 26
0
 def setUp(self):
     super(TestLTIConsumerHideFieldsFlag, self).setUp()
     self.course_id = CourseLocator(org="edx", course="course", run="run")
Exemplo n.º 27
0
class XQueueCertInterfaceExampleCertificateTest(TestCase):
    """Tests for the XQueue interface for certificate generation. """

    COURSE_KEY = CourseLocator(org='test', course='test', run='test')

    TEMPLATE = 'test.pdf'
    DESCRIPTION = 'test'
    ERROR_MSG = 'Kaboom!'

    def setUp(self):
        super(XQueueCertInterfaceExampleCertificateTest, self).setUp()
        self.xqueue = XQueueCertInterface()

    def test_add_example_cert(self):
        cert = self._create_example_cert()
        with self._mock_xqueue() as mock_send:
            self.xqueue.add_example_cert(cert)

        # Verify that the correct payload was sent to the XQueue
        self._assert_queue_task(mock_send, cert)

        # Verify the certificate status
        self.assertEqual(cert.status, ExampleCertificate.STATUS_STARTED)

    def test_add_example_cert_error(self):
        cert = self._create_example_cert()
        with self._mock_xqueue(success=False):
            self.xqueue.add_example_cert(cert)

        # Verify the error status of the certificate
        self.assertEqual(cert.status, ExampleCertificate.STATUS_ERROR)
        self.assertIn(self.ERROR_MSG, cert.error_reason)

    def _create_example_cert(self):
        """Create an example certificate. """
        cert_set = ExampleCertificateSet.objects.create(
            course_key=self.COURSE_KEY)
        return ExampleCertificate.objects.create(example_cert_set=cert_set,
                                                 description=self.DESCRIPTION,
                                                 template=self.TEMPLATE)

    @contextmanager
    def _mock_xqueue(self, success=True):
        """Mock the XQueue method for sending a task to the queue. """
        with patch.object(XQueueInterface, 'send_to_queue') as mock_send:
            mock_send.return_value = (0, None) if success else (1,
                                                                self.ERROR_MSG)
            yield mock_send

    def _assert_queue_task(self, mock_send, cert):
        """Check that the task was added to the queue. """
        expected_header = {
            'lms_key':
            cert.access_key,
            'lms_callback_url':
            'https://edx.org/update_example_certificate?key={key}'.format(
                key=cert.uuid),
            'queue_name':
            'certificates'
        }

        expected_body = {
            'action': 'create',
            'username': cert.uuid,
            'name': u'John Doë',
            'course_id': unicode(self.COURSE_KEY),
            'template_pdf': 'test.pdf',
            'example_certificate': True
        }

        self.assertTrue(mock_send.called)

        __, kwargs = mock_send.call_args_list[0]
        actual_header = json.loads(kwargs['header'])
        actual_body = json.loads(kwargs['body'])

        self.assertEqual(expected_header, actual_header)
        self.assertEqual(expected_body, actual_body)
Exemplo n.º 28
0
    def xblock_from_json(self,
                         class_,
                         course_key,
                         block_key,
                         json_data,
                         inherited_settings,
                         course_entry_override=None,
                         **kwargs):
        if course_entry_override is None:
            course_entry_override = self.course_entry
        else:
            # most recent retrieval is most likely the right one for next caller (see comment above fn)
            self.course_entry['branch'] = course_entry_override['branch']
            self.course_entry['org'] = course_entry_override['org']
            self.course_entry['course'] = course_entry_override['course']
            self.course_entry['run'] = course_entry_override['run']

        definition_id = json_data.get('definition')

        # If no usage id is provided, generate an in-memory id
        if block_key is None:
            block_key = BlockKey(json_data['block_type'], LocalId())
        else:
            if inherited_settings is None:
                # see if there's a value in course_entry
                if block_key in self.course_entry['inherited_settings']:
                    inherited_settings = self.course_entry[
                        'inherited_settings'][block_key]
            elif block_key not in self.course_entry['inherited_settings']:
                self.course_entry['inherited_settings'][
                    block_key] = inherited_settings

        if definition_id is not None and not json_data.get(
                'definition_loaded', False):
            definition_loader = DefinitionLazyLoader(
                self.modulestore, block_key.type, definition_id,
                lambda fields: self.modulestore.convert_references_to_keys(
                    course_key,
                    self.load_block_type(block_key.type),
                    fields,
                    self.course_entry['structure']['blocks'],
                ))
        else:
            definition_loader = None

        # If no definition id is provide, generate an in-memory id
        if definition_id is None:
            definition_id = LocalId()

        block_locator = BlockUsageLocator(
            course_key,
            block_type=block_key.type,
            block_id=block_key.id,
        )

        converted_fields = self.modulestore.convert_references_to_keys(
            block_locator.course_key,
            class_,
            json_data.get('fields', {}),
            self.course_entry['structure']['blocks'],
        )
        kvs = SplitMongoKVS(definition_loader, converted_fields,
                            inherited_settings, **kwargs)
        field_data = KvsFieldData(kvs)

        try:
            module = self.construct_xblock_from_class(
                class_,
                ScopeIds(None, block_key.type, definition_id, block_locator),
                field_data,
            )
        except Exception:
            log.warning("Failed to load descriptor", exc_info=True)
            return ErrorDescriptor.from_json(
                json_data,
                self,
                BlockUsageLocator(CourseLocator(
                    version_guid=course_entry_override['structure']['_id']),
                                  block_type='error',
                                  block_id=block_key.id),
                error_msg=exc_info_to_str(sys.exc_info()))

        edit_info = json_data.get('edit_info', {})
        module._edited_by = edit_info.get('edited_by')
        module._edited_on = edit_info.get('edited_on')
        module.previous_version = edit_info.get('previous_version')
        module.update_version = edit_info.get('update_version')
        module.source_version = edit_info.get('source_version', None)
        module.definition_locator = DefinitionLocator(block_key.type,
                                                      definition_id)
        # decache any pending field settings
        module.save()

        # If this is an in-memory block, store it in this system
        if isinstance(block_locator.block_id, LocalId):
            self.local_modules[block_locator] = module

        return module
Exemplo n.º 29
0
class GenerateExampleCertificatesTest(ModuleStoreTestCase):
    """Test generation of example certificates. """

    COURSE_KEY = CourseLocator(org='test', course='test', run='test')

    def test_generate_example_certs(self):
        # Generate certificates for the course
        CourseModeFactory.create(course_id=self.COURSE_KEY,
                                 mode_slug=CourseMode.HONOR)
        with self._mock_xqueue() as mock_queue:
            certs_api.generate_example_certificates(self.COURSE_KEY)

        # Verify that the appropriate certs were added to the queue
        self._assert_certs_in_queue(mock_queue, 1)

        # Verify that the certificate status is "started"
        self._assert_cert_status({'description': 'honor', 'status': 'started'})

    def test_generate_example_certs_with_verified_mode(self):
        # Create verified and honor modes for the course
        CourseModeFactory.create(course_id=self.COURSE_KEY, mode_slug='honor')
        CourseModeFactory.create(course_id=self.COURSE_KEY,
                                 mode_slug='verified')

        # Generate certificates for the course
        with self._mock_xqueue() as mock_queue:
            certs_api.generate_example_certificates(self.COURSE_KEY)

        # Verify that the appropriate certs were added to the queue
        self._assert_certs_in_queue(mock_queue, 2)

        # Verify that the certificate status is "started"
        self._assert_cert_status(
            {
                'description': 'verified',
                'status': 'started'
            }, {
                'description': 'honor',
                'status': 'started'
            })

    @contextmanager
    def _mock_xqueue(self):
        """Mock the XQueue method for adding a task to the queue. """
        with patch.object(XQueueCertInterface,
                          'add_example_cert') as mock_queue:
            yield mock_queue

    def _assert_certs_in_queue(self, mock_queue, expected_num):
        """Check that the certificate generation task was added to the queue. """
        certs_in_queue = [
            call_args[0] for (call_args, __) in mock_queue.call_args_list
        ]
        self.assertEqual(len(certs_in_queue), expected_num)
        for cert in certs_in_queue:
            self.assertTrue(isinstance(cert, ExampleCertificate))

    def _assert_cert_status(self, *expected_statuses):
        """Check the example certificate status. """
        actual_status = certs_api.example_certificates_status(self.COURSE_KEY)
        self.assertEqual(list(expected_statuses), actual_status)
Exemplo n.º 30
0
 def location(self):
     return BlockUsageLocator(CourseLocator('org', 'course', 'run'),
                              'category', self.url_name)