diff --git a/cms/djangoapps/contentstore/tests/test_contentstore.py b/cms/djangoapps/contentstore/tests/test_contentstore.py index ca5ea3e95424..94194502be38 100644 --- a/cms/djangoapps/contentstore/tests/test_contentstore.py +++ b/cms/djangoapps/contentstore/tests/test_contentstore.py @@ -2,6 +2,7 @@ import copy +import re import shutil from datetime import timedelta from functools import wraps @@ -34,6 +35,8 @@ from xmodule.modulestore.django import modulestore from xmodule.modulestore.exceptions import ItemNotFoundError from xmodule.modulestore.inheritance import own_metadata +from xmodule.modulestore.split_mongo import BlockKey +from xmodule.modulestore.tests.django_utils import TEST_DATA_SPLIT_MODULESTORE from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory, check_mongo_calls from xmodule.modulestore.xml_exporter import export_course_to_xml from xmodule.modulestore.xml_importer import import_course_from_xml, perform_xlint @@ -83,6 +86,7 @@ class ContentStoreTestCase(CourseTestCase): """ Base class for Content Store Test Cases """ + MODULESTORE = TEST_DATA_SPLIT_MODULESTORE class ImportRequiredTestCases(ContentStoreTestCase): @@ -212,7 +216,7 @@ def test_asset_import(self): all_thumbnails = content_store.get_all_content_thumbnails_for_course(course.id) self.assertGreater(len(all_thumbnails), 0) - location = AssetKey.from_string('/c4x/edX/toy/asset/just_a_test.jpg') + location = AssetKey.from_string('asset-v1:edX+toy+2012_Fall+type@asset+block@just_a_test.jpg') content = content_store.find(location) self.assertIsNotNone(content) @@ -282,7 +286,7 @@ def test_rewrite_nonportable_links_on_import(self): ) # first check a static asset link - course_key = self.store.make_course_key('edX', 'toy', 'run') + course_key = self.store.make_course_key('edX', 'toy', '2012_Fall') html_block_location = course_key.make_usage_key('html', 'nonportable') html_block = self.store.get_item(html_block_location) self.assertIn('/static/foo.jpg', html_block.data) @@ -302,101 +306,6 @@ def verify_content_existence(self, store, root_dir, course_id, dirname, category filesystem = OSFS(root_dir / ('test_export/' + dirname)) self.assertTrue(filesystem.exists(item.location.block_id + filename_suffix)) - @mock.patch('xmodule.course_block.requests.get') - def test_export_course_roundtrip(self, mock_get): - mock_get.return_value.text = dedent(""" - - - - """).strip() - - content_store = contentstore() - course_id = self.import_and_populate_course() - - root_dir = path(mkdtemp_clean()) - print(f'Exporting to tempdir = {root_dir}') - - # export out to a tempdir - export_course_to_xml(self.store, content_store, course_id, root_dir, 'test_export') - - # check for static tabs - self.verify_content_existence(self.store, root_dir, course_id, 'tabs', 'static_tab', '.html') - - # check for about content - self.verify_content_existence(self.store, root_dir, course_id, 'about', 'about', '.html') - - # assert that there is an html and video directory in drafts: - draft_dir = OSFS(root_dir / 'test_export/drafts') - self.assertTrue(draft_dir.exists('html')) - self.assertTrue(draft_dir.exists('video')) - # and assert that they contain the created modules - self.assertIn(self.DRAFT_HTML + ".xml", draft_dir.listdir('html')) - self.assertIn(self.DRAFT_VIDEO + ".xml", draft_dir.listdir('video')) - # and assert the child of the orphaned draft wasn't exported - self.assertNotIn(self.ORPHAN_DRAFT_HTML + ".xml", draft_dir.listdir('html')) - - # check for grading_policy.json - filesystem = OSFS(root_dir / 'test_export/policies/2012_Fall') - self.assertTrue(filesystem.exists('grading_policy.json')) - - course = self.store.get_course(course_id) - # compare what's on disk compared to what we have in our course - with filesystem.open('grading_policy.json', 'r') as grading_policy: - on_disk = loads(grading_policy.read()) - self.assertEqual(on_disk, course.grading_policy) - - # check for policy.json - self.assertTrue(filesystem.exists('policy.json')) - - # compare what's on disk to what we have in the course block - with filesystem.open('policy.json', 'r') as course_policy: - on_disk = loads(course_policy.read()) - self.assertIn('course/2012_Fall', on_disk) - self.assertEqual(on_disk['course/2012_Fall'], own_metadata(course)) - - # remove old course - self.store.delete_course(course_id, self.user.id) - - # reimport over old course - self.check_import(root_dir, content_store, course_id) - - # import to different course id - new_course_id = self.store.make_course_key('anotherX', 'anotherToy', 'Someday') - self.check_import(root_dir, content_store, new_course_id) - self.assertCoursesEqual(course_id, new_course_id) - - shutil.rmtree(root_dir) - - def check_import(self, root_dir, content_store, course_id): - """Imports the course in root_dir into the given course_id and verifies its content""" - # reimport - import_course_from_xml( - self.store, - self.user.id, - root_dir, - ['test_export'], - static_content_store=content_store, - target_id=course_id, - ) - - # verify content of the course - self.check_populated_course(course_id) - - # verify additional export attributes - def verify_export_attrs_removed(attributes): - """Verifies all temporary attributes added during export are removed""" - self.assertNotIn('index_in_children_list', attributes) - self.assertNotIn('parent_sequential_url', attributes) - self.assertNotIn('parent_url', attributes) - - vertical = self.store.get_item(course_id.make_usage_key('vertical', self.TEST_VERTICAL)) - verify_export_attrs_removed(vertical.xml_attributes) - - for child in vertical.get_children(): - verify_export_attrs_removed(child.xml_attributes) - if hasattr(child, 'data'): - verify_export_attrs_removed(child.data) - def test_export_course_with_metadata_only_video(self): content_store = contentstore() @@ -552,7 +461,8 @@ def test_export_course_without_content_store(self): import_course_from_xml( self.store, self.user.id, root_dir, ['test_export_no_content_store'], static_content_store=None, - target_id=course_id + target_id=course_id, + create_if_not_present=True ) # Verify reimported course @@ -560,7 +470,6 @@ def test_export_course_without_content_store(self): items = self.store.get_items( course_id, qualifiers={ - 'category': 'sequential', 'name': 'vertical_sequential', } ) @@ -734,7 +643,7 @@ def test_export_with_orphan_vertical(self): information but the draft child xblock has parent information. """ # Make an existing unit a draft - self.store.convert_to_draft(self.problem.location, self.user.id) + self.problem = self.store.unpublish(self.problem.location, self.user.id) root_dir = path(mkdtemp_clean()) export_course_to_xml(self.store, None, self.course.id, root_dir, 'test_export') @@ -786,19 +695,12 @@ def test_assets_overwrite(self): def test_advanced_components_require_two_clicks(self): self.check_components_on_page(['word_cloud'], ['Word cloud']) - def test_malformed_edit_unit_request(self): - # just pick one vertical - usage_key = self.course.id.make_usage_key('vertical', None) - - resp = self.client.get_html(get_url('container_handler', usage_key)) - self.assertEqual(resp.status_code, 400) - def test_edit_unit(self): """Verifies rendering the editor in all the verticals in the given test course""" self._check_verticals([self.vert_loc]) def _get_draft_counts(self, item): # lint-amnesty, pylint: disable=missing-function-docstring - cnt = 1 if getattr(item, 'is_draft', False) else 0 + cnt = 1 if not self.store.has_published_version(item) else 0 for child in item.get_children(): cnt = cnt + self._get_draft_counts(child) @@ -810,15 +712,14 @@ def test_get_items(self): Unfortunately, None = published for the revision field, so get_items() would return both draft and non-draft copies. """ - self.store.convert_to_draft(self.problem.location, self.user.id) + self.problem = self.store.unpublish(self.problem.location, self.user.id) # Query get_items() and find the html item. This should just return back a single item (not 2). direct_store_items = self.store.get_items( self.course.id, revision=ModuleStoreEnum.RevisionOption.published_only ) items_from_direct_store = [item for item in direct_store_items if item.location == self.problem.location] - self.assertEqual(len(items_from_direct_store), 1) - self.assertFalse(getattr(items_from_direct_store[0], 'is_draft', False)) + self.assertEqual(len(items_from_direct_store), 0) # Fetch from the draft store. draft_store_items = self.store.get_items( @@ -826,8 +727,6 @@ def test_get_items(self): ) items_from_draft_store = [item for item in draft_store_items if item.location == self.problem.location] self.assertEqual(len(items_from_draft_store), 1) - # TODO the below won't work for split mongo - self.assertTrue(getattr(items_from_draft_store[0], 'is_draft', False)) def test_draft_metadata(self): """ @@ -899,11 +798,11 @@ def test_get_depth_with_drafts(self): self.assertEqual(num_drafts, 0) # put into draft - self.store.convert_to_draft(self.problem.location, self.user.id) + self.problem = self.store.unpublish(self.problem.location, self.user.id) # make sure we can query that item and verify that it is a draft draft_problem = self.store.get_item(self.problem.location) - self.assertTrue(getattr(draft_problem, 'is_draft', False)) + self.assertEqual(self.store.has_published_version(draft_problem), False) # now requery with depth course = self.store.get_course(self.course.id, depth=None) @@ -1025,15 +924,9 @@ def test_empty_trashcan(self): self.assertEqual(count, 0) def test_illegal_draft_crud_ops(self): - # this test presumes old mongo and split_draft not full split - with self.assertRaises(InvalidVersionError): - self.store.convert_to_draft(self.chapter_loc, self.user.id) - chapter = self.store.get_item(self.chapter_loc) chapter.data = 'chapter data' self.store.update_item(chapter, self.user.id) - newobject = self.store.get_item(self.chapter_loc) - self.assertFalse(getattr(newobject, 'is_draft', False)) with self.assertRaises(InvalidVersionError): self.store.unpublish(self.chapter_loc, self.user.id) @@ -1043,17 +936,11 @@ def test_bad_contentstore_request(self): Test that user get proper responses for urls with invalid url or asset/course key """ - resp = self.client.get_html('/c4x/CDX/123123/asset/&invalid.png') - self.assertEqual(resp.status_code, 400) - - resp = self.client.get_html('/c4x/CDX/123123/asset/invalid.png') + resp = self.client.get_html('asset-v1:CDX+123123+2012_Fall+type@asset+block@&invalid.png') self.assertEqual(resp.status_code, 404) - # Now test that 404 response is returned when user tries to access - # asset of some invalid course from split ModuleStore - with self.store.default_store(ModuleStoreEnum.Type.split): - resp = self.client.get_html('/c4x/InvalidOrg/InvalidCourse/asset/invalid.png') - self.assertEqual(resp.status_code, 404) + resp = self.client.get_html('asset-v1:CDX+123123+2012_Fall+type@asset+block@invalid.png') + self.assertEqual(resp.status_code, 404) @override_waffle_switch(waffle.ENABLE_ACCESSIBILITY_POLICY_PAGE, active=False) def test_disabled_accessibility_page(self): @@ -1078,20 +965,21 @@ def test_delete_course(self): self.assertGreater(len(assets), 0) self.assertGreater(count, 0) - self.store.convert_to_draft(self.vert_loc, self.user.id) + self.store.unpublish(self.vert_loc, self.user.id) # delete the course self.store.delete_course(self.course.id, self.user.id) # assert that there's absolutely no non-draft modules in the course # this should also include all draft items - items = self.store.get_items(self.course.id) - self.assertEqual(len(items), 0) + with self.assertRaises(ItemNotFoundError): + self.store.get_items(self.course.id) - # assert that all content in the asset library is also deleted + # assert that all content in the asset library is keeped + # in case the course is later restored. assets, count = contentstore().get_all_content_for_course(self.course.id) - self.assertEqual(len(assets), 0) - self.assertEqual(count, 0) + self.assertGreater(len(assets), 0) + self.assertGreater(count, 0) def test_course_handouts_rewrites(self): """ @@ -1111,7 +999,8 @@ def test_course_handouts_rewrites(self): # check that /static/ has been converted to the full path # note, we know the link it should be because that's what in the 'toy' course in the test data asset_key = self.course.id.make_asset_key('asset', 'handouts_sample_handout.txt') - self.assertContains(resp, str(asset_key)) + asset_url = '/'.join(str(asset_key).rsplit('@', 1)) + self.assertContains(resp, asset_url) def test_prefetch_children(self): # make sure we haven't done too many round trips to DB: @@ -1121,19 +1010,19 @@ def test_prefetch_children(self): # so we don't need to make an extra query to compute it. # set the branch to 'publish' in order to prevent extra lookups of draft versions with self.store.branch_setting(ModuleStoreEnum.Branch.published_only, self.course.id): - with check_mongo_calls(4): - course = self.store.get_course(self.course.id, depth=2) + with check_mongo_calls(2): + course = self.store.get_course(self.course.id, depth=2, lazy=False) # make sure we pre-fetched a known sequential which should be at depth=2 - self.assertIn(self.seq_loc, course.system.module_data) + self.assertIn(BlockKey.from_usage_key(self.seq_loc), course.system.module_data) # make sure we don't have a specific vertical which should be at depth=3 - self.assertNotIn(self.vert_loc, course.system.module_data) + self.assertNotIn(BlockKey.from_usage_key(self.vert_loc), course.system.module_data) # Now, test with the branch set to draft. No extra round trips b/c it doesn't go deep enough to get # beyond direct only categories with self.store.branch_setting(ModuleStoreEnum.Branch.draft_preferred, self.course.id): - with check_mongo_calls(4): + with check_mongo_calls(2): self.store.get_course(self.course.id, depth=2) def _check_verticals(self, locations): @@ -1205,21 +1094,19 @@ def test_create_course_with_dots(self): self.course_data['run'] = 'run.name' self.assert_created_course() - @ddt.data(ModuleStoreEnum.Type.split, ModuleStoreEnum.Type.mongo) - def test_course_with_different_cases(self, default_store): + def test_course_with_different_cases(self): """ Tests that course can not be created with different case using an AJAX request to course handler. """ course_number = '99x' - with self.store.default_store(default_store): - # Verify create a course passes with lower case. - self.course_data['number'] = course_number.lower() - self.assert_created_course() + # Verify create a course passes with lower case. + self.course_data['number'] = course_number.lower() + self.assert_created_course() - # Verify create a course fail when same course number is provided with different case. - self.course_data['number'] = course_number.upper() - self.assert_course_creation_failed(self.duplicate_course_error) + # Verify create a course fail when same course number is provided with different case. + self.course_data['number'] = course_number.upper() + self.assert_course_creation_failed(self.duplicate_course_error) def test_create_course_check_forum_seeding(self): """Test new course creation and verify forum seeding """ @@ -1356,38 +1243,35 @@ def test_create_course_duplicate_number(self): self.course_data['display_name'] = 'Robot Super Course Two' self.course_data['run'] = '2013_Summer' - self.assert_course_creation_failed(self.duplicate_course_error) + self.assert_created_course() - @ddt.data(ModuleStoreEnum.Type.split, ModuleStoreEnum.Type.mongo) - def test_create_course_case_change(self, default_store): + def test_create_course_case_change(self): """Test new course creation - error path due to case insensitive name equality""" self.course_data['number'] = '99x' - with self.store.default_store(default_store): - - # Verify that the course was created properly. - self.assert_created_course() + # Verify that the course was created properly. + self.assert_created_course() - # Keep the copy of original org - cache_current = self.course_data['org'] + # Keep the copy of original org + cache_current = self.course_data['org'] - # Change `org` to lower case and verify that course did not get created - self.course_data['org'] = self.course_data['org'].lower() - self.assert_course_creation_failed(self.duplicate_course_error) + # Change `org` to lower case and verify that course did not get created + self.course_data['org'] = self.course_data['org'].lower() + self.assert_course_creation_failed(self.duplicate_course_error) - # Replace the org with its actual value, and keep the copy of course number. - self.course_data['org'] = cache_current - cache_current = self.course_data['number'] + # Replace the org with its actual value, and keep the copy of course number. + self.course_data['org'] = cache_current + cache_current = self.course_data['number'] - self.course_data['number'] = self.course_data['number'].upper() - self.assert_course_creation_failed(self.duplicate_course_error) + self.course_data['number'] = self.course_data['number'].upper() + self.assert_course_creation_failed(self.duplicate_course_error) - # Replace the org with its actual value, and keep the copy of course number. - self.course_data['number'] = cache_current - __ = self.course_data['run'] + # Replace the org with its actual value, and keep the copy of course number. + self.course_data['number'] = cache_current + __ = self.course_data['run'] - self.course_data['run'] = self.course_data['run'].upper() - self.assert_course_creation_failed(self.duplicate_course_error) + self.course_data['run'] = self.course_data['run'].upper() + self.assert_course_creation_failed(self.duplicate_course_error) def test_course_substring(self): """ @@ -1515,7 +1399,9 @@ def test_create_item(self): self.assertEqual(resp.status_code, 200) data = parse_json(resp) - retarget = str(course.id.make_usage_key('chapter', 'REPLACE')).replace('REPLACE', r'([0-9]|[a-f]){3,}') + retarget = re.escape( + str(course.id.make_usage_key('chapter', 'REPLACE')) + ).replace('REPLACE', r'([0-9]|[a-f]){3,}') self.assertRegex(data['locator'], retarget) def test_capa_block(self): @@ -1625,7 +1511,7 @@ def test_import_into_new_course_id(self): self.assertEqual(course_block.pdf_textbooks[0]["chapters"][1]["url"], '/static/Chapter2.pdf') def test_import_into_new_course_id_wiki_slug_renamespacing(self): - # If reimporting into the same course do not change the wiki_slug. + # If reimporting into the same course change the wiki_slug. target_id = self.store.make_course_key('edX', 'toy', '2012_Fall') course_data = { 'org': target_id.org, @@ -1641,7 +1527,7 @@ def test_import_into_new_course_id_wiki_slug_renamespacing(self): # Import a course with wiki_slug == location.course import_course_from_xml(self.store, self.user.id, TEST_DATA_DIR, ['toy'], target_id=target_id) course_block = self.store.get_course(target_id) - self.assertEqual(course_block.wiki_slug, 'toy') + self.assertEqual(course_block.wiki_slug, 'edX.toy.2012_Fall') # But change the wiki_slug if it is a different course. target_id = self.store.make_course_key('MITx', '111', '2013_Spring') @@ -1667,7 +1553,7 @@ def test_import_metadata_with_attempts_empty_string(self): import_course_from_xml(self.store, self.user.id, TEST_DATA_DIR, ['simple'], create_if_not_present=True) did_load_item = False try: - course_key = self.store.make_course_key('edX', 'simple', 'problem') + course_key = self.store.make_course_key('edX', 'simple', '2012_Fall') usage_key = course_key.make_usage_key('problem', 'ps01-simple') self.store.get_item(usage_key) did_load_item = True @@ -1677,13 +1563,12 @@ def test_import_metadata_with_attempts_empty_string(self): # make sure we found the item (e.g. it didn't error while loading) self.assertTrue(did_load_item) - @ddt.data(ModuleStoreEnum.Type.split, ModuleStoreEnum.Type.mongo) - def test_forum_id_generation(self, default_store): + def test_forum_id_generation(self): """ Test that a discussion item, even if it doesn't set its discussion_id, consistently generates the same one """ - course = CourseFactory.create(default_store=default_store) + course = CourseFactory.create() # create a discussion item discussion_item = self.store.create_item(self.user.id, course.id, 'discussion', 'new_component') @@ -1825,9 +1710,11 @@ def setUp(self): """ + video_data = VideoBlock.parse_video_xml(video_sample_xml) + video_data.pop('source') self.video_descriptor = ItemFactory.create( parent_location=course.location, category='video', - **VideoBlock.parse_video_xml(video_sample_xml) + **video_data ) def test_metadata_not_persistence(self): @@ -1891,10 +1778,6 @@ def post_rerun_request( rerun_course_data.update(destination_course_data) destination_course_key = _get_course_id(self.store, destination_course_data) - # course_handler raise 404 for old mongo course - if destination_course_key.deprecated: - raise SkipTest('OldMongo Deprecation') - # post the request course_url = get_url('course_handler', destination_course_key, 'course_key_string') response = self.client.ajax_post(course_url, rerun_course_data) @@ -2154,7 +2037,7 @@ def test_course_license_export(self): self.course.license = "creative-commons: BY SA" self.store.update_item(self.course, None) export_course_to_xml(self.store, content_store, self.course.id, root_dir, 'test_license') - fname = f"{self.course.scope_ids.usage_id.block_id}.xml" + fname = f"{self.course.id.run}.xml" run_file_path = root_dir / "test_license" / "course" / fname with run_file_path.open() as f: run_xml = etree.parse(f) @@ -2221,10 +2104,6 @@ def _create_course(test, course_key, course_data): """ Creates a course via an AJAX request and verifies the URL returned in the response. """ - # course_handler raise 404 for old mongo course - if course_key.deprecated: - raise SkipTest('OldMongo Deprecation') - course_url = get_url('course_handler', course_key, 'course_key_string') response = test.client.ajax_post(course_url, course_data) test.assertEqual(response.status_code, 200) diff --git a/cms/djangoapps/contentstore/tests/test_users_default_role.py b/cms/djangoapps/contentstore/tests/test_users_default_role.py index 82bd4e960b9e..590efc0bff67 100644 --- a/cms/djangoapps/contentstore/tests/test_users_default_role.py +++ b/cms/djangoapps/contentstore/tests/test_users_default_role.py @@ -3,7 +3,9 @@ after deleting it creates same course again """ -from xmodule.modulestore.tests.django_utils import TEST_DATA_MONGO_AMNESTY_MODULESTORE, ModuleStoreTestCase +from unittest import skip + +from xmodule.modulestore.tests.django_utils import TEST_DATA_SPLIT_MODULESTORE, ModuleStoreTestCase from cms.djangoapps.contentstore.tests.utils import AjaxEnabledTestClient from cms.djangoapps.contentstore.utils import delete_course, reverse_url @@ -15,7 +17,7 @@ class TestUsersDefaultRole(ModuleStoreTestCase): """ Unit tests for checking enrollment and default forum role "Student" of a logged in user """ - MODULESTORE = TEST_DATA_MONGO_AMNESTY_MODULESTORE + MODULESTORE = TEST_DATA_SPLIT_MODULESTORE def setUp(self): """ @@ -92,6 +94,8 @@ def test_user_role_on_course_recreate(self): # check that user has his default "Student" forum role for this course self.assertTrue(self.user.roles.filter(name="Student", course_id=self.course_key)) + @skip("OldMongo Deprecation") + # Issue with case-insensitive course keys def test_user_role_on_course_recreate_with_change_name_case(self): """ Test that creating same course again with different name case after deleting it gives user diff --git a/cms/djangoapps/contentstore/tests/utils.py b/cms/djangoapps/contentstore/tests/utils.py index cae265f5abc2..b58ec17502b8 100644 --- a/cms/djangoapps/contentstore/tests/utils.py +++ b/cms/djangoapps/contentstore/tests/utils.py @@ -8,15 +8,13 @@ from django.conf import settings from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user from django.test.client import Client -from opaque_keys.edx.keys import AssetKey, CourseKey +from opaque_keys.edx.keys import AssetKey from xmodule.contentstore.django import contentstore -from xmodule.modulestore import ModuleStoreEnum from xmodule.modulestore.inheritance import own_metadata from xmodule.modulestore.split_mongo.split import SplitMongoModuleStore from xmodule.modulestore.tests.django_utils import TEST_DATA_MONGO_MODULESTORE, ModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory from xmodule.modulestore.tests.utils import ProceduralCourseTestMixin -from xmodule.modulestore.xml_importer import import_course_from_xml from xmodule.tests.test_transcripts_utils import YoutubeVideoHTMLResponse from cms.djangoapps.contentstore.utils import reverse_url @@ -125,158 +123,6 @@ def save_course(self): DRAFT_VIDEO = 'draft_video' LOCKED_ASSET_KEY = AssetKey.from_string('/c4x/edX/toy/asset/sample_static.html') - 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 = CourseKey.from_string('/'.join(['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.block_id, '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_block = self.store.get_item(course_id.make_usage_key('html', 'nonportable')) - new_data = html_block.data = html_block.data.replace( - '/static/', - f'/c4x/{course_id.org}/{course_id.course}/asset/' - ) - self.store.update_item(html_block, self.user.id) - - html_block = self.store.get_item(html_block.location) - self.assertEqual(new_data, html_block.data) - - return course_id - - def check_populated_course(self, course_id): - """ - Verifies the content of the given course, per data that was populated in import_and_populate_course - """ - items = self.store.get_items( - course_id, - qualifiers={'category': 'vertical'}, - revision=ModuleStoreEnum.RevisionOption.published_only - ) - self.check_verticals(items) - - def verify_item_publish_state(item, publish_state): - """Verifies the publish state of the item is as expected.""" - self.assertEqual(self.store.has_published_version(item), publish_state) - - def get_and_verify_publish_state(item_type, item_name, publish_state): - """ - Gets the given item from the store and verifies the publish state - of the item is as expected. - """ - item = self.store.get_item(course_id.make_usage_key(item_type, item_name)) - verify_item_publish_state(item, publish_state) - return item - - # verify draft vertical has a published version with published children - vertical = get_and_verify_publish_state('vertical', self.TEST_VERTICAL, True) - for child in vertical.get_children(): - verify_item_publish_state(child, True) - - # verify that it has a draft too - self.assertTrue(getattr(vertical, "is_draft", False)) - - # make sure that we don't have a sequential that is in draft mode - sequential = get_and_verify_publish_state('sequential', self.SEQUENTIAL, True) - self.assertFalse(getattr(sequential, "is_draft", False)) - - # verify that we have the private vertical - private_vertical = get_and_verify_publish_state('vertical', self.PRIVATE_VERTICAL, False) - - # verify that we have the public vertical - public_vertical = get_and_verify_publish_state('vertical', self.PUBLISHED_VERTICAL, True) - - # verify that we have the draft html - draft_html = self.store.get_item(course_id.make_usage_key('html', self.DRAFT_HTML)) - self.assertTrue(getattr(draft_html, 'is_draft', False)) - - # verify that we have the draft video - draft_video = self.store.get_item(course_id.make_usage_key('video', self.DRAFT_VIDEO)) - self.assertTrue(getattr(draft_video, 'is_draft', False)) - - # verify verticals are children of sequential - for vert in [vertical, private_vertical, public_vertical]: - self.assertIn(vert.location, sequential.children) - - # verify draft html is the child of the public vertical - self.assertIn(draft_html.location, public_vertical.children) - - # verify draft video is the child of the public vertical - self.assertIn(draft_video.location, public_vertical.children) - - # verify textbook exists - course = self.store.get_course(course_id) - self.assertGreater(len(course.textbooks), 0) - - # verify asset attributes of locked asset key - self.assertAssetsEqual(self.LOCKED_ASSET_KEY, self.LOCKED_ASSET_KEY.course_key, course_id) - - # verify non-portable links are rewritten - html_block = self.store.get_item(course_id.make_usage_key('html', 'nonportable')) - self.assertIn('/static/foo.jpg', html_block.data) - - return course - def assertCoursesEqual(self, course1_id, course2_id): """ Verifies the content of the two given courses are equal diff --git a/cms/djangoapps/contentstore/views/tests/test_assets.py b/cms/djangoapps/contentstore/views/tests/test_assets.py index 6ee861888809..3f468e3f39cf 100644 --- a/cms/djangoapps/contentstore/views/tests/test_assets.py +++ b/cms/djangoapps/contentstore/views/tests/test_assets.py @@ -27,6 +27,7 @@ from xmodule.modulestore import ModuleStoreEnum # lint-amnesty, pylint: disable=wrong-import-order from xmodule.modulestore.django import modulestore # lint-amnesty, pylint: disable=wrong-import-order from xmodule.modulestore.xml_importer import import_course_from_xml # lint-amnesty, pylint: disable=wrong-import-order +from xmodule.modulestore.tests.django_utils import TEST_DATA_SPLIT_MODULESTORE TEST_DATA_DIR = settings.COMMON_TEST_DATA_ROOT @@ -41,6 +42,9 @@ class AssetsTestCase(CourseTestCase): """ Parent class for all asset tests. """ + + MODULESTORE = TEST_DATA_SPLIT_MODULESTORE + def setUp(self): super().setUp() self.url = reverse_course_url('assets_handler', self.course.id) @@ -96,6 +100,7 @@ def test_pdf_asset(self): TEST_DATA_DIR, ['toy'], static_content_store=contentstore(), + create_if_not_present=True, verbose=True ) course = course_items[0] @@ -103,8 +108,8 @@ def test_pdf_asset(self): # Test valid contentType for pdf asset (textbook.pdf) resp = self.client.get(url, HTTP_ACCEPT='application/json') - self.assertContains(resp, "/c4x/edX/toy/asset/textbook.pdf") - asset_location = AssetKey.from_string('/c4x/edX/toy/asset/textbook.pdf') + self.assertContains(resp, "/asset-v1:edX+toy+2012_Fall+type@asset+block@textbook.pdf") + asset_location = AssetKey.from_string('asset-v1:edX+toy+2012_Fall+type@asset+block@textbook.pdf') content = contentstore().find(asset_location) # Check after import textbook.pdf has valid contentType ('application/pdf') @@ -450,7 +455,8 @@ def test_locking(self): """ def verify_asset_locked_state(locked): """ Helper method to verify lock state in the contentstore """ - asset_location = StaticContent.get_location_from_path('/c4x/edX/toy/asset/sample_static.html') + asset_location = StaticContent.get_location_from_path( + 'asset-v1:edX+toy+2012_Fall+type@asset+block@sample_static.html') content = contentstore().find(asset_location) self.assertEqual(content.locked, locked) @@ -483,6 +489,7 @@ def post_asset_update(lock, course): TEST_DATA_DIR, ['toy'], static_content_store=contentstore(), + create_if_not_present=True, verbose=True ) course = course_items[0] @@ -513,15 +520,15 @@ def setUp(self): response = self.client.post(self.url, {"name": self.asset_name, "file": self.asset}) self.assertEqual(response.status_code, 200) - self.uploaded_url = json.loads(response.content.decode('utf-8'))['asset']['url'] + self.uploaded_id = json.loads(response.content.decode('utf-8'))['asset']['id'] - self.asset_location = AssetKey.from_string(self.uploaded_url) + self.asset_location = AssetKey.from_string(self.uploaded_id) self.content = contentstore().find(self.asset_location) def test_delete_asset(self): """ Tests the happy path :) """ test_url = reverse_course_url( - 'assets_handler', self.course.id, kwargs={'asset_key_string': str(self.uploaded_url)}) + 'assets_handler', self.course.id, kwargs={'asset_key_string': self.uploaded_id}) resp = self.client.delete(test_url, HTTP_ACCEPT="application/json") self.assertEqual(resp.status_code, 204) @@ -533,7 +540,7 @@ def test_delete_image_type_asset(self): # upload image response = self.client.post(self.url, {"name": "delete_image_test", "file": image_asset}) self.assertEqual(response.status_code, 200) - uploaded_image_url = json.loads(response.content.decode('utf-8'))['asset']['url'] + uploaded_image_url = json.loads(response.content.decode('utf-8'))['asset']['id'] # upload image thumbnail response = self.client.post(self.url, {"name": "delete_image_thumb_test", "file": thumbnail_image_asset}) @@ -558,7 +565,7 @@ def test_delete_asset_with_invalid_asset(self): """ Tests the sad path :( """ test_url = reverse_course_url( 'assets_handler', - self.course.id, kwargs={'asset_key_string': "/c4x/edX/toy/asset/invalid.pdf"} + self.course.id, kwargs={'asset_key_string': "asset-v1:edX+toy+2012_Fall+type@asset+block@invalid.pdf"} ) resp = self.client.delete(test_url, HTTP_ACCEPT="application/json") self.assertEqual(resp.status_code, 404) @@ -566,8 +573,9 @@ def test_delete_asset_with_invalid_asset(self): def test_delete_asset_with_invalid_thumbnail(self): """ Tests the sad path :( """ test_url = reverse_course_url( - 'assets_handler', self.course.id, kwargs={'asset_key_string': str(self.uploaded_url)}) - self.content.thumbnail_location = StaticContent.get_location_from_path('/c4x/edX/toy/asset/invalid') + 'assets_handler', self.course.id, kwargs={'asset_key_string': self.uploaded_id}) + self.content.thumbnail_location = StaticContent.get_location_from_path( + '/asset-v1:edX+toy+2012_Fall+type@asset+block@invalid.pdf') contentstore().save(self.content) resp = self.client.delete(test_url, HTTP_ACCEPT="application/json") self.assertEqual(resp.status_code, 204) diff --git a/cms/lib/xblock/test/test_authoring_mixin.py b/cms/lib/xblock/test/test_authoring_mixin.py index 6028f39614a2..5bbcdae3c298 100644 --- a/cms/lib/xblock/test/test_authoring_mixin.py +++ b/cms/lib/xblock/test/test_authoring_mixin.py @@ -6,7 +6,7 @@ from django.conf import settings from django.test.utils import override_settings from xblock.core import XBlock -from xmodule.modulestore.tests.django_utils import TEST_DATA_MONGO_AMNESTY_MODULESTORE, ModuleStoreTestCase +from xmodule.modulestore.tests.django_utils import TEST_DATA_SPLIT_MODULESTORE, ModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory from xmodule.partitions.partitions import ( ENROLLMENT_TRACK_PARTITION_ID, @@ -23,7 +23,7 @@ class AuthoringMixinTestCase(ModuleStoreTestCase): """ Tests the studio authoring XBlock mixin. """ - MODULESTORE = TEST_DATA_MONGO_AMNESTY_MODULESTORE + MODULESTORE = TEST_DATA_SPLIT_MODULESTORE GROUP_NO_LONGER_EXISTS = "This group no longer exists" NO_CONTENT_OR_ENROLLMENT_GROUPS = "Access to this component is not restricted" NO_CONTENT_ENROLLMENT_TRACK_ENABLED = "You can restrict access to this component to learners in specific enrollment tracks or content groups" # lint-amnesty, pylint: disable=line-too-long @@ -44,27 +44,27 @@ def setUp(self): self.course = CourseFactory.create() chapter = ItemFactory.create( category='chapter', - parent_location=self.course.location, + parent=self.course, display_name='Test Chapter' ) sequential = ItemFactory.create( category='sequential', - parent_location=chapter.location, + parent=chapter, display_name='Test Sequential' ) vertical = ItemFactory.create( category='vertical', - parent_location=sequential.location, + parent=sequential, display_name='Test Vertical' ) video = ItemFactory.create( category='video', - parent_location=vertical.location, + parent=vertical, display_name='Test Vertical' ) pure = ItemFactory.create( category='pure', - parent_location=vertical.location, + parent=vertical, display_name='Test Pure' ) self.vertical_location = vertical.location diff --git a/lms/djangoapps/support/tests/test_views.py b/lms/djangoapps/support/tests/test_views.py index bf31b23398e9..51d847d21802 100644 --- a/lms/djangoapps/support/tests/test_views.py +++ b/lms/djangoapps/support/tests/test_views.py @@ -31,7 +31,7 @@ from rest_framework import status from social_django.models import UserSocialAuth from xmodule.modulestore.tests.django_utils import ( - TEST_DATA_MONGO_AMNESTY_MODULESTORE, ModuleStoreTestCase, SharedModuleStoreTestCase, + TEST_DATA_SPLIT_MODULESTORE, ModuleStoreTestCase, SharedModuleStoreTestCase, ) from xmodule.modulestore.tests.factories import CourseFactory @@ -267,7 +267,7 @@ class SupportViewCertificatesTests(SupportViewTestCase): """ Tests for the certificates support view. """ - MODULESTORE = TEST_DATA_MONGO_AMNESTY_MODULESTORE + MODULESTORE = TEST_DATA_SPLIT_MODULESTORE def setUp(self): """Make the user support staff. """ @@ -290,7 +290,9 @@ def test_certificates_along_with_course_filter(self): url = reverse("support:certificates") + "?user=student@example.com&course_id=" + quote(str(self.course.id)) response = self.client.get(url) self.assertContains(response, "userFilter: 'student@example.com'") - self.assertContains(response, "courseFilter: '" + str(self.course.id) + "'") + # use replase due to escaping course id: + # https://docs.djangoproject.com/en/dev/ref/templates/builtins/#escapejs + self.assertContains(response, "courseFilter: '" + str(self.course.id).replace('-', '\\u002D') + "'") @ddt.ddt @@ -1859,12 +1861,15 @@ class TestOnboardingView(SupportViewTestCase, ProctoredExamTestCase): """ Tests for OnboardingView """ - MODULESTORE = TEST_DATA_MONGO_AMNESTY_MODULESTORE + MODULESTORE = TEST_DATA_SPLIT_MODULESTORE def setUp(self): super().setUp() SupportStaffRole().add_users(self.user) + # update default course key + self.course_id = 'course-v1:a+b+c' + self.proctored_exam_id = self._create_proctored_exam() self.onboarding_exam_id = self._create_onboarding_exam() @@ -1904,7 +1909,7 @@ def _url(self, username): def _create_enrollment(self): """ Create enrollment in default course """ - # default course key = 'a/b/c' + # updated course key = 'course-v1:a+b+c' self.course = CourseFactory.create( org='a', course='b', @@ -2024,7 +2029,7 @@ def test_verified_in_another_course(self): update_attempt_status(attempt_id, ProctoredExamStudentAttemptStatus.submitted) # Create an attempt in the other course that has been verified - other_course_id = 'x/y/z' + other_course_id = 'course-v1:x+y+z' other_course_onboarding_exam = ProctoredExam.objects.create( course_id=other_course_id, content_id=self.other_course_content, @@ -2057,8 +2062,8 @@ def test_verified_in_another_course(self): # assert that originally verified enrollment is reflected correctly self.assertEqual(response_data['verified_in']['onboarding_status'], 'verified') - self.assertEqual(response_data['verified_in']['course_id'], 'x/y/z') + self.assertEqual(response_data['verified_in']['course_id'], other_course_id) # assert that most recent enrollment (current status) has other_course_approved status self.assertEqual(response_data['current_status']['onboarding_status'], 'other_course_approved') - self.assertEqual(response_data['current_status']['course_id'], 'a/b/c') + self.assertEqual(response_data['current_status']['course_id'], self.course_id) diff --git a/openedx/core/djangoapps/content/course_overviews/tests/test_signals.py b/openedx/core/djangoapps/content/course_overviews/tests/test_signals.py index 0f98eb26b7cc..8c4fb25c8a73 100644 --- a/openedx/core/djangoapps/content/course_overviews/tests/test_signals.py +++ b/openedx/core/djangoapps/content/course_overviews/tests/test_signals.py @@ -6,10 +6,11 @@ import pytest import ddt +from pytz import UTC from xmodule.data import CertificatesDisplayBehaviors from xmodule.modulestore import ModuleStoreEnum -from xmodule.modulestore.tests.django_utils import TEST_DATA_MONGO_AMNESTY_MODULESTORE, ModuleStoreTestCase +from xmodule.modulestore.tests.django_utils import TEST_DATA_ONLY_SPLIT_MODULESTORE_DRAFT_PREFERRED, ModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory, check_mongo_calls from ..models import CourseOverview @@ -23,13 +24,12 @@ class CourseOverviewSignalsTestCase(ModuleStoreTestCase): """ Tests for CourseOverview signals. """ - MODULESTORE = TEST_DATA_MONGO_AMNESTY_MODULESTORE + MODULESTORE = TEST_DATA_ONLY_SPLIT_MODULESTORE_DRAFT_PREFERRED ENABLED_SIGNALS = ['course_deleted', 'course_published'] - TODAY = datetime.datetime.utcnow() + TODAY = datetime.datetime.utcnow().replace(tzinfo=UTC) NEXT_WEEK = TODAY + datetime.timedelta(days=7) - @ddt.data(ModuleStoreEnum.Type.split) - def test_caching(self, modulestore_type): + def test_caching(self): """ Tests that CourseOverview structures are actually getting cached. @@ -38,14 +38,13 @@ def test_caching(self, modulestore_type): course in. """ # Creating a new course will trigger a publish event and the course will be cached - course = CourseFactory.create(default_store=modulestore_type, emit_signals=True) + course = CourseFactory.create(emit_signals=True) # The cache will be hit and mongo will not be queried with check_mongo_calls(0): CourseOverview.get_from_id(course.id) - @ddt.data(ModuleStoreEnum.Type.mongo, ModuleStoreEnum.Type.split) - def test_cache_invalidation(self, modulestore_type): + def test_cache_invalidation(self): """ Tests that when a course is published or deleted, the corresponding course_overview is removed from the cache. @@ -54,28 +53,26 @@ def test_cache_invalidation(self, modulestore_type): modulestore_type (ModuleStoreEnum.Type): type of store to create the course in. """ - with self.store.default_store(modulestore_type): - - # Create a course where mobile_available is True. - course = CourseFactory.create(mobile_available=True, default_store=modulestore_type) - course_overview_1 = CourseOverview.get_from_id(course.id) - assert course_overview_1.mobile_available - - # Set mobile_available to False and update the course. - # This fires a course_published signal, which should be caught in signals.py, which should in turn - # delete the corresponding CourseOverview from the cache. - course.mobile_available = False - with self.store.branch_setting(ModuleStoreEnum.Branch.draft_preferred): - self.store.update_item(course, ModuleStoreEnum.UserID.test) + # Create a course where mobile_available is True. + course = CourseFactory.create(mobile_available=True) + course_overview_1 = CourseOverview.get_from_id(course.id) + assert course_overview_1.mobile_available + + # Set mobile_available to False and update the course. + # This fires a course_published signal, which should be caught in signals.py, which should in turn + # delete the corresponding CourseOverview from the cache. + course.mobile_available = False + with self.store.branch_setting(ModuleStoreEnum.Branch.draft_preferred): + self.store.update_item(course, ModuleStoreEnum.UserID.test) - # Make sure that when we load the CourseOverview again, mobile_available is updated. - course_overview_2 = CourseOverview.get_from_id(course.id) - assert not course_overview_2.mobile_available + # Make sure that when we load the CourseOverview again, mobile_available is updated. + course_overview_2 = CourseOverview.get_from_id(course.id) + assert not course_overview_2.mobile_available - # Verify that when the course is deleted, the corresponding CourseOverview is deleted as well. - with pytest.raises(CourseOverview.DoesNotExist): - self.store.delete_course(course.id, ModuleStoreEnum.UserID.test) - CourseOverview.get_from_id(course.id) + # Verify that when the course is deleted, the corresponding CourseOverview is deleted as well. + with pytest.raises(CourseOverview.DoesNotExist): + self.store.delete_course(course.id, ModuleStoreEnum.UserID.test) + CourseOverview.get_from_id(course.id) def assert_changed_signal_sent(self, changes, mock_signal): # lint-amnesty, pylint: disable=missing-function-docstring course = CourseFactory.create( @@ -86,7 +83,7 @@ def assert_changed_signal_sent(self, changes, mock_signal): # lint-amnesty, pyl # changing display name doesn't fire the signal with self.captureOnCommitCallbacks(execute=True) as callbacks: course.display_name = course.display_name + 'changed' - self.store.update_item(course, ModuleStoreEnum.UserID.test) + course = self.store.update_item(course, ModuleStoreEnum.UserID.test) assert not mock_signal.called # changing the given field fires the signal diff --git a/openedx/core/lib/tests/test_xblock_utils.py b/openedx/core/lib/tests/test_xblock_utils.py index 6671267133c6..20dcaa1f02b7 100644 --- a/openedx/core/lib/tests/test_xblock_utils.py +++ b/openedx/core/lib/tests/test_xblock_utils.py @@ -13,7 +13,7 @@ from web_fragments.fragment import Fragment from xblock.core import XBlockAside from xmodule.modulestore import ModuleStoreEnum -from xmodule.modulestore.tests.django_utils import TEST_DATA_MONGO_AMNESTY_MODULESTORE, SharedModuleStoreTestCase +from xmodule.modulestore.tests.django_utils import TEST_DATA_SPLIT_MODULESTORE, SharedModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory from xmodule.modulestore.tests.test_asides import AsideTestType @@ -38,13 +38,7 @@ class TestXblockUtils(SharedModuleStoreTestCase): @classmethod def setUpClass(cls): super().setUpClass() - cls.course_mongo = CourseFactory.create( - default_store=ModuleStoreEnum.Type.mongo, - org='TestX', - number='TS01', - run='2015' - ) - cls.course_split = CourseFactory.create( + cls.course = CourseFactory.create( default_store=ModuleStoreEnum.Type.split, org='TestX', number='TS02', @@ -86,21 +80,15 @@ def test_request_token(self): test_uuid = uuid.UUID(token, version=1) assert token == test_uuid.hex - @ddt.data( - ('course_mongo', 'data-usage-id="i4x:;_;_TestX;_TS01;_course;_2015"'), - ('course_split', 'data-usage-id="block-v1:TestX+TS02+2015+type@course+block@course"') - ) - @ddt.unpack - def test_wrap_xblock(self, course_id, data_usage_id): + def test_wrap_xblock(self): """ Verify that new content is added and the resources are the same. """ fragment = self.create_fragment("

Test!

") fragment.initialize_js('BlockMain') # wrap_block() sets some attributes only if there is JS. - course = getattr(self, course_id) test_wrap_output = wrap_xblock( runtime_class='TestRuntime', - block=course, + block=self.course, view='baseview', frag=fragment, context={"wrap_xblock_data": {"custom-attribute": "custom-value"}}, @@ -110,7 +98,7 @@ def test_wrap_xblock(self, course_id, data_usage_id): assert isinstance(test_wrap_output, Fragment) assert 'xblock-baseview' in test_wrap_output.content assert 'data-runtime-class="TestRuntime"' in test_wrap_output.content - assert data_usage_id in test_wrap_output.content + assert 'data-usage-id="block-v1:TestX+TS02+2015+type@course+block@course"' in test_wrap_output.content assert '

Test!

' in test_wrap_output.content assert 'data-custom-attribute="custom-value"' in test_wrap_output.content assert test_wrap_output.resources[0].data == 'body {background-color:red;}' @@ -170,13 +158,13 @@ def test_get_js_dependencies(self, pipeline_enabled, expected_js_dependencies): class TestXBlockAside(SharedModuleStoreTestCase): """Test the xblock aside function.""" - MODULESTORE = TEST_DATA_MONGO_AMNESTY_MODULESTORE + MODULESTORE = TEST_DATA_SPLIT_MODULESTORE @classmethod def setUpClass(cls): super().setUpClass() cls.course = CourseFactory.create() - cls.block = ItemFactory.create(category='aside', parent=cls.course) + cls.block = ItemFactory.create(parent=cls.course) cls.aside_v2 = AsideUsageKeyV2(cls.block.scope_ids.usage_id, "aside") cls.aside_v1 = AsideUsageKeyV1(cls.block.scope_ids.usage_id, "aside")