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')
def setUp(self): super(CourseModeModelTest, self).setUp() self.course_key = CourseLocator('Test', 'TestCourse', 'TestCourseRun') CourseMode.objects.all().delete()
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)
def setUp(self): super().setUp() self.course_key = CourseLocator('org', 'course', str(uuid4()))
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)
def setUp(self) -> None: super().setUp() self.course_key = CourseLocator(org="org", course="course", run="run") self.default_app_id = "test-app"
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")
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), })
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))
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)
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)
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)
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))
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)
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'))
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")
""" 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))
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')
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])
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))
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)
def __init__(self, *args, **kwargs): super(CSMLoadModel, self).__init__(*args, **kwargs) self.course_key = CourseLocator('org', 'course', 'run') self.usages_with_data = set()
def setUp(self): super(TestLTIConsumerHideFieldsFlag, self).setUp() self.course_id = CourseLocator(org="edx", course="course", run="run")
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)
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
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)
def location(self): return BlockUsageLocator(CourseLocator('org', 'course', 'run'), 'category', self.url_name)