Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
259 changes: 69 additions & 190 deletions cms/djangoapps/contentstore/tests/test_contentstore.py

Large diffs are not rendered by default.

8 changes: 6 additions & 2 deletions cms/djangoapps/contentstore/tests/test_users_default_role.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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):
"""
Expand Down Expand Up @@ -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
Expand Down
156 changes: 1 addition & 155 deletions cms/djangoapps/contentstore/tests/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just as a sanity check: It's okay to remove this because there's already an equivalent testing split mongo specifically, right?

@UvgenGen UvgenGen Jan 17, 2023

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes we have similar test for split mongo, so I think we can remove this test.
(xmodule/modulestore/tests/test_cross_modulestore_import_export.py::CrossStoreXMLRoundtrip)

"""
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
Expand Down
28 changes: 18 additions & 10 deletions cms/djangoapps/contentstore/views/tests/test_assets.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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)
Expand Down Expand Up @@ -96,15 +100,16 @@ def test_pdf_asset(self):
TEST_DATA_DIR,
['toy'],
static_content_store=contentstore(),
create_if_not_present=True,
verbose=True
)
course = course_items[0]
url = reverse_course_url('assets_handler', course.id)

# 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')

Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -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)

Expand All @@ -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})
Expand All @@ -558,16 +565,17 @@ 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)

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)
14 changes: 7 additions & 7 deletions cms/lib/xblock/test/test_authoring_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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
Expand All @@ -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
Expand Down
Loading