Commit 4a5679b1 by Christina Roberts

Merge pull request #6762 from edx/christina/split-default

Enable split for new courses.
parents b22b0a35 d86c7bec
......@@ -5,7 +5,6 @@
from lettuce import world, step
from component_settings_editor_helpers import enter_xml_in_advanced_problem
from nose.tools import assert_true, assert_equal
from opaque_keys.edx.locations import SlashSeparatedCourseKey
from contentstore.utils import reverse_usage_url
......
......@@ -5,7 +5,6 @@ from lettuce import world, step
from common import *
from terrain.steps import reload_the_page
from selenium.common.exceptions import InvalidElementStateException
from opaque_keys.edx.locations import SlashSeparatedCourseKey
from contentstore.utils import reverse_course_url
from nose.tools import assert_in, assert_not_in, assert_equal, assert_not_equal # pylint: disable=no-name-in-module
......
......@@ -8,6 +8,7 @@ from opaque_keys.edx.locations import SlashSeparatedCourseKey
from xmodule.contentstore.content import XASSET_LOCATION_TAG
from xmodule.contentstore.django import contentstore
from xmodule.modulestore.django import modulestore
from xmodule.modulestore import ModuleStoreEnum
from xmodule.modulestore.mongo.base import location_to_query
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.xml_importer import import_from_xml
......@@ -25,7 +26,8 @@ class ExportAllCourses(ModuleStoreTestCase):
super(ExportAllCourses, self).setUp()
self.content_store = contentstore()
self.module_store = modulestore()
# pylint: disable=protected-access
self.module_store = modulestore()._get_modulestore_by_type(ModuleStoreEnum.Type.mongo)
def test_export_all_courses(self):
"""
......
......@@ -21,8 +21,12 @@ class ExportAllCourses(ModuleStoreTestCase):
super(ExportAllCourses, self).setUp()
self.store = modulestore()._get_modulestore_by_type(ModuleStoreEnum.Type.mongo)
self.temp_dir = mkdtemp()
self.first_course = CourseFactory.create(org="test", course="course1", display_name="run1")
self.second_course = CourseFactory.create(org="test", course="course2", display_name="run2")
self.first_course = CourseFactory.create(
org="test", course="course1", display_name="run1", default_store=ModuleStoreEnum.Type.mongo
)
self.second_course = CourseFactory.create(
org="test", course="course2", display_name="run2", default_store=ModuleStoreEnum.Type.mongo
)
def test_export_all_courses(self):
"""
......
......@@ -11,8 +11,8 @@ from django.core.management import call_command
from django_comment_common.utils import are_permissions_roles_seeded
from xmodule.modulestore.django import modulestore
from xmodule.modulestore import ModuleStoreEnum
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from opaque_keys.edx.locations import SlashSeparatedCourseKey
class TestImport(ModuleStoreTestCase):
......@@ -20,10 +20,6 @@ class TestImport(ModuleStoreTestCase):
Unit tests for importing a course from command line
"""
BASE_COURSE_KEY = SlashSeparatedCourseKey(u'edX', u'test_import_course', u'2013_Spring')
DIFF_KEY = SlashSeparatedCourseKey(u'edX', u'test_import_course', u'2014_Spring')
TRUNCATED_KEY = SlashSeparatedCourseKey(u'edX', u'test_import', u'2014_Spring')
def create_course_xml(self, content_dir, course_id):
directory = tempfile.mkdtemp(dir=content_dir)
os.makedirs(os.path.join(directory, "course"))
......@@ -32,7 +28,7 @@ class TestImport(ModuleStoreTestCase):
'course="{0.course}"/>'.format(course_id))
with open(os.path.join(directory, "course", "{0.run}.xml".format(course_id)), "w+") as f:
f.write('<course></course>')
f.write('<course><chapter name="Test Chapter"></chapter></course>')
return directory
......@@ -44,38 +40,23 @@ class TestImport(ModuleStoreTestCase):
self.content_dir = path(tempfile.mkdtemp())
self.addCleanup(shutil.rmtree, self.content_dir)
# Create good course xml
self.good_dir = self.create_course_xml(self.content_dir, self.BASE_COURSE_KEY)
self.base_course_key = self.store.make_course_key(u'edX', u'test_import_course', u'2013_Spring')
self.truncated_key = self.store.make_course_key(u'edX', u'test_import', u'2014_Spring')
# Create run changed course xml
self.dupe_dir = self.create_course_xml(self.content_dir, self.DIFF_KEY)
# Create good course xml
self.good_dir = self.create_course_xml(self.content_dir, self.base_course_key)
# Create course XML where TRUNCATED_COURSE.org == BASE_COURSE_ID.org
# and BASE_COURSE_ID.startswith(TRUNCATED_COURSE.course)
self.course_dir = self.create_course_xml(self.content_dir, self.TRUNCATED_KEY)
self.course_dir = self.create_course_xml(self.content_dir, self.truncated_key)
def test_forum_seed(self):
"""
Tests that forum roles were created with import.
"""
self.assertFalse(are_permissions_roles_seeded(self.BASE_COURSE_KEY))
self.assertFalse(are_permissions_roles_seeded(self.base_course_key))
call_command('import', self.content_dir, self.good_dir)
self.assertTrue(are_permissions_roles_seeded(self.BASE_COURSE_KEY))
def test_duplicate_with_url(self):
"""
Check to make sure an import doesn't import courses that have the
same org and course, but they have different runs in order to
prevent modulestore "findone" exceptions on deletion
"""
# Load up base course and verify it is available
call_command('import', self.content_dir, self.good_dir)
store = modulestore()
self.assertIsNotNone(store.get_course(self.BASE_COURSE_KEY))
# Now load up duped course and verify it doesn't load
call_command('import', self.content_dir, self.dupe_dir)
self.assertIsNone(store.get_course(self.DIFF_KEY))
self.assertTrue(are_permissions_roles_seeded(self.base_course_key))
def test_truncated_course_with_url(self):
"""
......@@ -87,8 +68,28 @@ class TestImport(ModuleStoreTestCase):
# Load up base course and verify it is available
call_command('import', self.content_dir, self.good_dir)
store = modulestore()
self.assertIsNotNone(store.get_course(self.BASE_COURSE_KEY))
self.assertIsNotNone(store.get_course(self.base_course_key))
# Now load up the course with a similar course_id and verify it loads
call_command('import', self.content_dir, self.course_dir)
self.assertIsNotNone(store.get_course(self.TRUNCATED_KEY))
self.assertIsNotNone(store.get_course(self.truncated_key))
def test_existing_course_with_different_modulestore(self):
"""
Checks that a course that originally existed in old mongo can be re-imported when
split is the default modulestore.
"""
with modulestore().default_store(ModuleStoreEnum.Type.mongo):
call_command('import', self.content_dir, self.good_dir)
# Clear out the modulestore mappings, else when the next import command goes to create a destination
# course_key, it will find the existing course and return the mongo course_key. To reproduce TNL-1362,
# the destination course_key needs to be the one for split modulestore.
modulestore().mappings = {}
with modulestore().default_store(ModuleStoreEnum.Type.split):
call_command('import', self.content_dir, self.good_dir)
course = modulestore().get_course(self.base_course_key)
# With the bug, this fails because the chapter's course_key is the split mongo form,
# while the course's course_key is the old mongo form.
self.assertEqual(unicode(course.location.course_key), unicode(course.children[0].course_key))
......@@ -60,7 +60,7 @@ class TestMigrateToSplit(ModuleStoreTestCase):
def setUp(self):
super(TestMigrateToSplit, self).setUp(create_user=True)
self.course = CourseFactory()
self.course = CourseFactory(default_store=ModuleStoreEnum.Type.mongo)
def test_user_email(self):
"""
......
......@@ -18,7 +18,7 @@ from student.roles import CourseInstructorRole, CourseStaffRole, GlobalStaff, Or
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory, check_mongo_calls
from xmodule.modulestore import ModuleStoreEnum
from opaque_keys.edx.locations import SlashSeparatedCourseKey, CourseLocator
from opaque_keys.edx.locations import CourseLocator
from xmodule.modulestore.django import modulestore
from xmodule.error_module import ErrorDescriptor
from course_action_state.models import CourseRerunState
......@@ -53,7 +53,8 @@ class TestCourseListing(ModuleStoreTestCase):
course = CourseFactory.create(
org=course_location.org,
number=course_location.course,
run=course_location.run
run=course_location.run,
default_store=ModuleStoreEnum.Type.mongo
)
if user is not None:
......@@ -73,7 +74,7 @@ class TestCourseListing(ModuleStoreTestCase):
"""
Test getting courses with new access group format e.g. 'instructor_edx.course.run'
"""
course_location = SlashSeparatedCourseKey('Org1', 'Course1', 'Run1')
course_location = self.store.make_course_key('Org1', 'Course1', 'Run1')
self._create_course_with_access_groups(course_location, self.user)
# get courses through iterating all courses
......@@ -92,7 +93,7 @@ class TestCourseListing(ModuleStoreTestCase):
"""
GlobalStaff().add_users(self.user)
course_key = SlashSeparatedCourseKey('Org1', 'Course1', 'Run1')
course_key = self.store.make_course_key('Org1', 'Course1', 'Run1')
self._create_course_with_access_groups(course_key, self.user)
with patch('xmodule.modulestore.mongo.base.MongoKeyValueStore', Mock(side_effect=Exception)):
......@@ -111,9 +112,9 @@ class TestCourseListing(ModuleStoreTestCase):
Test the course list for regular staff when get_course returns an ErrorDescriptor
"""
GlobalStaff().remove_users(self.user)
CourseStaffRole(SlashSeparatedCourseKey('Non', 'Existent', 'Course')).add_users(self.user)
CourseStaffRole(self.store.make_course_key('Non', 'Existent', 'Course')).add_users(self.user)
course_key = SlashSeparatedCourseKey('Org1', 'Course1', 'Run1')
course_key = self.store.make_course_key('Org1', 'Course1', 'Run1')
self._create_course_with_access_groups(course_key, self.user)
with patch('xmodule.modulestore.mongo.base.MongoKeyValueStore', Mock(side_effect=Exception)):
......@@ -132,7 +133,7 @@ class TestCourseListing(ModuleStoreTestCase):
"""
Test getting courses with invalid course location (course deleted from modulestore).
"""
course_key = SlashSeparatedCourseKey('Org', 'Course', 'Run')
course_key = self.store.make_course_key('Org', 'Course', 'Run')
self._create_course_with_access_groups(course_key, self.user)
# get courses through iterating all courses
......@@ -168,7 +169,7 @@ class TestCourseListing(ModuleStoreTestCase):
org = 'Org{0}'.format(number)
course = 'Course{0}'.format(number)
run = 'Run{0}'.format(number)
course_location = SlashSeparatedCourseKey(org, course, run)
course_location = self.store.make_course_key(org, course, run)
if number in user_course_ids:
self._create_course_with_access_groups(course_location, self.user)
else:
......@@ -217,14 +218,14 @@ class TestCourseListing(ModuleStoreTestCase):
"""
store = modulestore()._get_modulestore_by_type(ModuleStoreEnum.Type.mongo)
course_location = SlashSeparatedCourseKey('testOrg', 'testCourse', 'RunBabyRun')
course_location = self.store.make_course_key('testOrg', 'testCourse', 'RunBabyRun')
self._create_course_with_access_groups(course_location, self.user)
course_location = SlashSeparatedCourseKey('testOrg', 'doomedCourse', 'RunBabyRun')
course_location = self.store.make_course_key('testOrg', 'doomedCourse', 'RunBabyRun')
self._create_course_with_access_groups(course_location, self.user)
store.delete_course(course_location, self.user.id)
course_location = SlashSeparatedCourseKey('testOrg', 'erroredCourse', 'RunBabyRun')
course_location = self.store.make_course_key('testOrg', 'erroredCourse', 'RunBabyRun')
course = self._create_course_with_access_groups(course_location, self.user)
course_db_record = store._find_one(course.location)
course_db_record.setdefault('metadata', {}).get('tabs', []).append({"type": "wiko", "name": "Wiki"})
......@@ -244,14 +245,14 @@ class TestCourseListing(ModuleStoreTestCase):
Create multiple courses within the same org. Verify that someone with org-wide permissions can access
all of them.
"""
org_course_one = SlashSeparatedCourseKey('AwesomeOrg', 'Course1', 'RunBabyRun')
org_course_one = self.store.make_course_key('AwesomeOrg', 'Course1', 'RunBabyRun')
CourseFactory.create(
org=org_course_one.org,
number=org_course_one.course,
run=org_course_one.run
)
org_course_two = SlashSeparatedCourseKey('AwesomeOrg', 'Course2', 'RunRunRun')
org_course_two = self.store.make_course_key('AwesomeOrg', 'Course2', 'RunRunRun')
CourseFactory.create(
org=org_course_two.org,
number=org_course_two.course,
......
......@@ -16,7 +16,6 @@ from xmodule.modulestore import ModuleStoreEnum
from xmodule.modulestore.django import modulestore
from xmodule.contentstore.django import contentstore
from xmodule.modulestore.tests.factories import check_exact_number_of_calls, check_number_of_calls
from opaque_keys.edx.locations import SlashSeparatedCourseKey, AssetLocation
from xmodule.modulestore.xml_importer import import_from_xml
from xmodule.exceptions import NotFoundError
from uuid import uuid4
......@@ -40,13 +39,14 @@ class ContentStoreImportTest(ModuleStoreTestCase):
self.client = Client()
self.client.login(username=self.user.username, password=password)
def load_test_import_course(self, target_course_id=None, create_new_course_if_not_present=False):
def load_test_import_course(self, target_course_id=None, create_course_if_not_present=True, module_store=None):
'''
Load the standard course used to test imports
(for do_import_static=False behavior).
'''
content_store = contentstore()
module_store = modulestore()
if module_store is None:
module_store = modulestore()
import_from_xml(
module_store,
self.user.id,
......@@ -56,7 +56,7 @@ class ContentStoreImportTest(ModuleStoreTestCase):
do_import_static=False,
verbose=True,
target_course_id=target_course_id,
create_course_if_not_present=create_new_course_if_not_present,
create_course_if_not_present=create_course_if_not_present,
)
course_id = module_store.make_course_key('edX', 'test_import_course', '2012_Fall')
course = module_store.get_course(course_id)
......@@ -83,21 +83,24 @@ class ContentStoreImportTest(ModuleStoreTestCase):
"""
# Test that importing course with unicode 'id' and 'display name' doesn't give UnicodeEncodeError
"""
module_store = modulestore()
course_id = SlashSeparatedCourseKey(u'Юникода', u'unicode_course', u'échantillon')
import_from_xml(
module_store,
self.user.id,
TEST_DATA_DIR,
['2014_Uni'],
target_course_id=course_id
)
# Test with the split modulestore because store.has_course fails in old mongo with unicode characters.
with modulestore().default_store(ModuleStoreEnum.Type.split):
module_store = modulestore()
course_id = module_store.make_course_key(u'Юникода', u'unicode_course', u'échantillon')
import_from_xml(
module_store,
self.user.id,
TEST_DATA_DIR,
['2014_Uni'],
target_course_id=course_id,
create_course_if_not_present=True
)
course = module_store.get_course(course_id)
self.assertIsNotNone(course)
course = module_store.get_course(course_id)
self.assertIsNotNone(course)
# test that course 'display_name' same as imported course 'display_name'
self.assertEqual(course.display_name, u"Φυσικά το όνομα Unicode")
# test that course 'display_name' same as imported course 'display_name'
self.assertEqual(course.display_name, u"Φυσικά το όνομα Unicode")
def test_static_import(self):
'''
......@@ -113,9 +116,7 @@ class ContentStoreImportTest(ModuleStoreTestCase):
content = None
try:
location = AssetLocation.from_deprecated_string(
'/c4x/edX/test_import_course/asset/should_be_imported.html'
)
location = course.id.make_asset_key('asset', 'should_be_imported.html')
content = content_store.find(location)
except NotFoundError:
pass
......@@ -133,9 +134,13 @@ class ContentStoreImportTest(ModuleStoreTestCase):
content_store = contentstore()
module_store = modulestore()
import_from_xml(module_store, self.user.id, TEST_DATA_DIR, ['toy'], static_content_store=content_store, do_import_static=False, verbose=True)
import_from_xml(
module_store, self.user.id, TEST_DATA_DIR, ['toy'],
static_content_store=content_store, do_import_static=False,
create_course_if_not_present=True, verbose=True
)
course = module_store.get_course(SlashSeparatedCourseKey('edX', 'toy', '2012_Fall'))
course = module_store.get_course(module_store.make_course_key('edX', 'toy', '2012_Fall'))
# make sure we have NO assets in our contentstore
all_assets, count = content_store.get_all_content_for_course(course.id)
......@@ -144,7 +149,10 @@ class ContentStoreImportTest(ModuleStoreTestCase):
def test_no_static_link_rewrites_on_import(self):
module_store = modulestore()
courses = import_from_xml(module_store, self.user.id, TEST_DATA_DIR, ['toy'], do_import_static=False, verbose=True)
courses = import_from_xml(
module_store, self.user.id, TEST_DATA_DIR, ['toy'], do_import_static=False, verbose=True,
create_course_if_not_present=True
)
course_key = courses[0].id
handouts = module_store.get_item(course_key.make_usage_key('course_info', 'handouts'))
......@@ -171,17 +179,19 @@ class ContentStoreImportTest(ModuleStoreTestCase):
# NOTE: On Jenkins, with memcache enabled, the number of calls here is only 1.
# Locally, without memcache, the number of calls is actually 2 (once more during the publish step)
with check_number_of_calls(store, '_compute_metadata_inheritance_tree', 2):
self.load_test_import_course()
self.load_test_import_course(create_course_if_not_present=False, module_store=store)
@ddt.data(ModuleStoreEnum.Type.mongo, ModuleStoreEnum.Type.split)
def test_reimport(self, default_ms_type):
with modulestore().default_store(default_ms_type):
__, __, course = self.load_test_import_course(create_new_course_if_not_present=True)
__, __, course = self.load_test_import_course(create_course_if_not_present=True)
self.load_test_import_course(target_course_id=course.id)
def test_rewrite_reference_list(self):
module_store = modulestore()
target_course_id = SlashSeparatedCourseKey('testX', 'conditional_copy', 'copy_run')
# This test fails with split modulestore (the HTML component is not in "different_course_id" namespace).
# More investigation needs to be done.
module_store = modulestore()._get_modulestore_by_type(ModuleStoreEnum.Type.mongo)
target_course_id = module_store.make_course_key('testX', 'conditional_copy', 'copy_run')
import_from_xml(
module_store,
self.user.id,
......@@ -193,7 +203,7 @@ class ContentStoreImportTest(ModuleStoreTestCase):
target_course_id.make_usage_key('conditional', 'condone')
)
self.assertIsNotNone(conditional_module)
different_course_id = SlashSeparatedCourseKey('edX', 'different_course', None)
different_course_id = module_store.make_course_key('edX', 'different_course', None)
self.assertListEqual(
[
target_course_id.make_usage_key('problem', 'choiceprob'),
......@@ -211,13 +221,14 @@ class ContentStoreImportTest(ModuleStoreTestCase):
def test_rewrite_reference(self):
module_store = modulestore()
target_course_id = SlashSeparatedCourseKey('testX', 'peergrading_copy', 'copy_run')
target_course_id = module_store.make_course_key('testX', 'peergrading_copy', 'copy_run')
import_from_xml(
module_store,
self.user.id,
TEST_DATA_DIR,
['open_ended'],
target_course_id=target_course_id
target_course_id=target_course_id,
create_course_if_not_present=True
)
peergrading_module = module_store.get_item(
target_course_id.make_usage_key('peergrading', 'PeerGradingLinked')
......@@ -252,13 +263,14 @@ class ContentStoreImportTest(ModuleStoreTestCase):
def _verify_split_test_import(self, target_course_name, source_course_name, split_test_name, groups_to_verticals):
module_store = modulestore()
target_course_id = SlashSeparatedCourseKey('testX', target_course_name, 'copy_run')
target_course_id = module_store.make_course_key('testX', target_course_name, 'copy_run')
import_from_xml(
module_store,
self.user.id,
TEST_DATA_DIR,
[source_course_name],
target_course_id=target_course_id
target_course_id=target_course_id,
create_course_if_not_present=True
)
split_test_module = module_store.get_item(
target_course_id.make_usage_key('split_test', split_test_name)
......
......@@ -13,7 +13,9 @@ class DraftReorderTestCase(ModuleStoreTestCase):
def test_order(self):
store = modulestore()
course_items = import_from_xml(store, self.user.id, TEST_DATA_DIR, ['import_draft_order'])
course_items = import_from_xml(
store, self.user.id, TEST_DATA_DIR, ['import_draft_order'], create_course_if_not_present=True
)
course_key = course_items[0].id
sequential = store.get_item(course_key.make_usage_key('sequential', '0f4f7649b10141b0bdc9922dcf94515a'))
verticals = sequential.children
......
......@@ -5,6 +5,8 @@ Integration tests for importing courses containing pure XBlocks.
from xblock.core import XBlock
from xblock.fields import String
from xmodule.modulestore.django import modulestore
from xmodule.modulestore import ModuleStoreEnum
from xmodule.modulestore.xml_importer import import_from_xml
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.mongo.draft import as_draft
......@@ -60,8 +62,11 @@ class XBlockImportTest(ModuleStoreTestCase):
the expected field value set.
"""
# It is necessary to use the "old mongo" modulestore because split doesn't work
# with the "has_draft" logic below.
store = modulestore()._get_modulestore_by_type(ModuleStoreEnum.Type.mongo) # pylint: disable=protected-access
courses = import_from_xml(
self.store, self.user.id, TEST_DATA_DIR, [course_dir]
store, self.user.id, TEST_DATA_DIR, [course_dir], create_course_if_not_present=True
)
xblock_location = courses[0].id.make_usage_key('stubxblock', 'xblock_test')
......@@ -69,12 +74,12 @@ class XBlockImportTest(ModuleStoreTestCase):
if has_draft:
xblock_location = as_draft(xblock_location)
xblock = self.store.get_item(xblock_location)
xblock = store.get_item(xblock_location)
self.assertTrue(isinstance(xblock, StubXBlock))
self.assertEqual(xblock.test_field, expected_field_val)
if has_draft:
draft_xblock = self.store.get_item(xblock_location)
draft_xblock = store.get_item(xblock_location)
self.assertTrue(getattr(draft_xblock, 'is_draft', False))
self.assertTrue(isinstance(draft_xblock, StubXBlock))
self.assertEqual(draft_xblock.test_field, expected_field_val)
......@@ -7,7 +7,6 @@ from django.contrib.auth.models import User
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from contentstore.tests.utils import AjaxEnabledTestClient
from opaque_keys.edx.locations import SlashSeparatedCourseKey
from contentstore.utils import reverse_url, reverse_course_url
from student.roles import CourseInstructorRole, CourseStaffRole, OrgStaffRole, OrgInstructorRole
from student import auth
......@@ -29,7 +28,7 @@ class TestCourseAccess(ModuleStoreTestCase):
self.client.login(username=self.user.username, password=user_password)
# create a course via the view handler which has a different strategy for permissions than the factory
self.course_key = SlashSeparatedCourseKey('myu', 'mydept.mycourse', 'myrun')
self.course_key = self.store.make_course_key('myu', 'mydept.mycourse', 'myrun')
course_url = reverse_url('course_handler')
self.client.ajax_post(
course_url,
......@@ -101,7 +100,7 @@ class TestCourseAccess(ModuleStoreTestCase):
self.assertContains(response, user.email)
# test copying course permissions
copy_course_key = SlashSeparatedCourseKey('copyu', 'copydept.mycourse', 'myrun')
copy_course_key = self.store.make_course_key('copyu', 'copydept.mycourse', 'myrun')
for role in [CourseInstructorRole, CourseStaffRole, OrgStaffRole, OrgInstructorRole]:
if (role is OrgStaffRole) or (role is OrgInstructorRole):
auth.add_users(
......
......@@ -6,7 +6,6 @@ from contentstore.tests.utils import AjaxEnabledTestClient
from contentstore.utils import delete_course_and_groups, reverse_url
from courseware.tests.factories import UserFactory
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from opaque_keys.edx.locations import SlashSeparatedCourseKey
from student.models import CourseEnrollment
......@@ -26,7 +25,7 @@ class TestUsersDefaultRole(ModuleStoreTestCase):
self.client.login(username=self.user.username, password='test')
# create a course via the view handler to create course
self.course_key = SlashSeparatedCourseKey('Org_1', 'Course_1', 'Run_1')
self.course_key = self.store.make_course_key('Org_1', 'Course_1', 'Run_1')
self._create_course_with_given_location(self.course_key)
def _create_course_with_given_location(self, course_key):
......
......@@ -168,32 +168,42 @@ class ExtraPanelTabTestCase(TestCase):
class CourseImageTestCase(ModuleStoreTestCase):
"""Tests for course image URLs."""
def verify_url(self, expected_url, actual_url):
"""
Helper method for verifying the URL is as expected.
"""
if not expected_url.startswith("/"):
expected_url = "/" + expected_url
self.assertEquals(expected_url, actual_url)
def test_get_image_url(self):
"""Test image URL formatting."""
course = CourseFactory.create()
url = utils.course_image_url(course)
self.assertEquals(url, unicode(course.id.make_asset_key('asset', course.course_image)))
self.verify_url(
unicode(course.id.make_asset_key('asset', course.course_image)),
utils.course_image_url(course)
)
def test_non_ascii_image_name(self):
""" Verify that non-ascii image names are cleaned """
course_image = u'before_\N{SNOWMAN}_after.jpg'
course = CourseFactory.create(course_image=course_image)
self.assertEquals(
utils.course_image_url(course),
unicode(course.id.make_asset_key('asset', course_image.replace(u'\N{SNOWMAN}', '_')))
self.verify_url(
unicode(course.id.make_asset_key('asset', course_image.replace(u'\N{SNOWMAN}', '_'))),
utils.course_image_url(course)
)
def test_spaces_in_image_name(self):
""" Verify that image names with spaces in them are cleaned """
course_image = u'before after.jpg'
course = CourseFactory.create(course_image=u'before after.jpg')
self.assertEquals(
utils.course_image_url(course),
unicode(course.id.make_asset_key('asset', course_image.replace(" ", "_")))
self.verify_url(
unicode(course.id.make_asset_key('asset', course_image.replace(" ", "_"))),
utils.course_image_url(course)
)
class XBlockVisibilityTestCase(TestCase):
class XBlockVisibilityTestCase(ModuleStoreTestCase):
"""Tests for xblock visibility for students."""
def setUp(self):
......@@ -202,6 +212,7 @@ class XBlockVisibilityTestCase(TestCase):
self.dummy_user = ModuleStoreEnum.UserID.test
self.past = datetime(1970, 1, 1)
self.future = datetime.now(UTC) + timedelta(days=1)
self.course = CourseFactory.create()
def test_private_unreleased_xblock(self):
"""Verifies that a private unreleased xblock is not visible"""
......@@ -253,10 +264,9 @@ class XBlockVisibilityTestCase(TestCase):
def _create_xblock_with_start_date(self, name, start_date, publish=False, visible_to_staff_only=False):
"""Helper to create an xblock with a start date, optionally publishing it"""
course_key = CourseLocator('edX', 'visibility', '2012_Fall')
vertical = modulestore().create_item(
self.dummy_user, course_key, 'vertical', name,
self.dummy_user, self.course.location.course_key, 'vertical', name,
fields={'start': start_date, 'visible_to_staff_only': visible_to_staff_only}
)
......
......@@ -664,10 +664,7 @@ def _create_new_course(request, org, number, run, fields):
Returns the URL for the course overview page.
Raises DuplicateCourseError if the course already exists
"""
store_for_new_course = (
settings.FEATURES.get('DEFAULT_STORE_FOR_NEW_COURSE') or
modulestore().default_modulestore.get_modulestore_type()
)
store_for_new_course = modulestore().default_modulestore.get_modulestore_type()
new_course = create_new_course_in_store(store_for_new_course, request.user, org, number, run, fields)
return JsonResponse({
'url': reverse_course_url('course_handler', new_course.id),
......
......@@ -5,7 +5,6 @@ import json
from contentstore.tests.test_course_settings import CourseTestCase
from contentstore.utils import reverse_course_url, reverse_usage_url
from opaque_keys.edx.locations import SlashSeparatedCourseKey
from opaque_keys.edx.keys import UsageKey
from xmodule.modulestore.django import modulestore
......
......@@ -35,7 +35,7 @@ import sys
import lms.envs.common
# Although this module itself may not use these imported variables, other dependent modules may.
from lms.envs.common import (
USE_TZ, TECH_SUPPORT_EMAIL, PLATFORM_NAME, BUGS_EMAIL, DOC_STORE_CONFIG, ALL_LANGUAGES, WIKI_ENABLED, MODULESTORE,
USE_TZ, TECH_SUPPORT_EMAIL, PLATFORM_NAME, BUGS_EMAIL, DOC_STORE_CONFIG, DATA_DIR, ALL_LANGUAGES, WIKI_ENABLED,
update_module_store_settings, ASSET_IGNORE_REGEX, COPYRIGHT_YEAR
)
from path import path
......@@ -112,9 +112,6 @@ FEATURES = {
# Turn off Advanced Security by default
'ADVANCED_SECURITY': False,
# Modulestore to use for new courses
'DEFAULT_STORE_FOR_NEW_COURSE': None,
# Turn off Video Upload Pipeline through Studio, by default
'ENABLE_VIDEO_UPLOAD_PIPELINE': False,
......@@ -130,9 +127,7 @@ FEATURES = {
'ENABLE_EDXNOTES': False,
# Enable support for content libraries. Note that content libraries are
# only supported in courses using split mongo. Change the setting
# DEFAULT_STORE_FOR_NEW_COURSE to be 'split' to have future courses
# and libraries created with split.
# only supported in courses using split mongo.
'ENABLE_CONTENT_LIBRARIES': False,
# Milestones application flag
......@@ -314,6 +309,37 @@ XBLOCK_SELECT_FUNCTION = prefer_xmodules
############################ Modulestore Configuration ################################
MODULESTORE_BRANCH = 'draft-preferred'
MODULESTORE = {
'default': {
'ENGINE': 'xmodule.modulestore.mixed.MixedModuleStore',
'OPTIONS': {
'mappings': {},
'stores': [
{
'NAME': 'split',
'ENGINE': 'xmodule.modulestore.split_mongo.split_draft.DraftVersioningModuleStore',
'DOC_STORE_CONFIG': DOC_STORE_CONFIG,
'OPTIONS': {
'default_class': 'xmodule.hidden_module.HiddenDescriptor',
'fs_root': DATA_DIR,
'render_template': 'edxmako.shortcuts.render_to_string',
}
},
{
'NAME': 'draft',
'ENGINE': 'xmodule.modulestore.mongo.DraftMongoModuleStore',
'DOC_STORE_CONFIG': DOC_STORE_CONFIG,
'OPTIONS': {
'default_class': 'xmodule.hidden_module.HiddenDescriptor',
'fs_root': DATA_DIR,
'render_template': 'edxmako.shortcuts.render_to_string',
}
}
]
}
}
}
############################ DJANGO_BUILTINS ################################
# Change DEBUG/TEMPLATE_DEBUG in your environment settings files, not here
DEBUG = False
......
......@@ -27,16 +27,18 @@ class AuthoringMixinTestCase(ModuleStoreTestCase):
parent_location=chapter.location,
display_name='Test Sequential'
)
self.vertical = ItemFactory.create(
vertical = ItemFactory.create(
category='vertical',
parent_location=sequential.location,
display_name='Test Vertical'
)
self.video = ItemFactory.create(
video = ItemFactory.create(
category='video',
parent_location=self.vertical.location,
parent_location=vertical.location,
display_name='Test Vertical'
)
self.vertical_location = vertical.location
self.video_location = video.location
self.pet_groups = [Group(1, 'Cat Lovers'), Group(2, 'Dog Lovers')]
def create_content_groups(self, content_groups):
......@@ -54,68 +56,72 @@ class AuthoringMixinTestCase(ModuleStoreTestCase):
self.course.user_partitions = [self.content_partition]
self.store.update_item(self.course, self.user.id)
def set_staff_only(self, item):
def set_staff_only(self, item_location):
"""Make an item visible to staff only."""
item = self.store.get_item(item_location)
item.visible_to_staff_only = True
self.store.update_item(item, self.user.id)
def set_group_access(self, item, group_ids):
def set_group_access(self, item_location, group_ids):
"""
Set group_access for the specified item to the specified group
ids within the content partition.
"""
item = self.store.get_item(item_location)
item.group_access[self.content_partition.id] = group_ids # pylint: disable=no-member
self.store.update_item(item, self.user.id)
def verify_visibility_view_contains(self, item, substrings):
def verify_visibility_view_contains(self, item_location, substrings):
"""
Verify that an item's visibility view returns an html string
containing all the expected substrings.
"""
item = self.store.get_item(item_location)
html = item.visibility_view().body_html()
for string in substrings:
self.assertIn(string, html)
def test_html_no_partition(self):
self.verify_visibility_view_contains(self.video, 'No content groups exist')
self.verify_visibility_view_contains(self.video_location, 'No content groups exist')
def test_html_empty_partition(self):
self.create_content_groups([])
self.verify_visibility_view_contains(self.video, 'No content groups exist')
self.verify_visibility_view_contains(self.video_location, 'No content groups exist')
def test_html_populated_partition(self):
self.create_content_groups(self.pet_groups)
self.verify_visibility_view_contains(self.video, ['Cat Lovers', 'Dog Lovers'])
self.verify_visibility_view_contains(self.video_location, ['Cat Lovers', 'Dog Lovers'])
def test_html_no_partition_staff_locked(self):
self.set_staff_only(self.vertical)
self.verify_visibility_view_contains(self.video, ['No content groups exist'])
self.set_staff_only(self.vertical_location)
self.verify_visibility_view_contains(self.video_location, ['No content groups exist'])
def test_html_empty_partition_staff_locked(self):
self.create_content_groups([])
self.set_staff_only(self.vertical)
self.verify_visibility_view_contains(self.video, 'No content groups exist')
self.set_staff_only(self.vertical_location)
self.verify_visibility_view_contains(self.video_location, 'No content groups exist')
def test_html_populated_partition_staff_locked(self):
self.create_content_groups(self.pet_groups)
self.set_staff_only(self.vertical)
self.set_staff_only(self.vertical_location)
self.verify_visibility_view_contains(
self.video, ['The Unit this component is contained in is hidden from students.', 'Cat Lovers', 'Dog Lovers']
self.video_location,
['The Unit this component is contained in is hidden from students.', 'Cat Lovers', 'Dog Lovers']
)
def test_html_false_content_group(self):
self.create_content_groups(self.pet_groups)
self.set_group_access(self.video, ['false_group_id'])
self.set_group_access(self.video_location, ['false_group_id'])
self.verify_visibility_view_contains(
self.video, ['Cat Lovers', 'Dog Lovers', 'Content group no longer exists.']
self.video_location, ['Cat Lovers', 'Dog Lovers', 'Content group no longer exists.']
)
def test_html_false_content_group_staff_locked(self):
self.create_content_groups(self.pet_groups)
self.set_staff_only(self.vertical)
self.set_group_access(self.video, ['false_group_id'])
self.set_staff_only(self.vertical_location)
self.set_group_access(self.video_location, ['false_group_id'])
self.verify_visibility_view_contains(
self.video,
self.video_location,
[
'Cat Lovers',
'Dog Lovers',
......
......@@ -13,8 +13,8 @@ from django.test.utils import override_settings
from xmodule.contentstore.django import contentstore
from xmodule.modulestore.django import modulestore
from opaque_keys.edx.locations import SlashSeparatedCourseKey
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore import ModuleStoreEnum
from xmodule.modulestore.xml_importer import import_from_xml
from contentserver.middleware import parse_range_header
......@@ -45,22 +45,23 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
self.client = Client()
self.contentstore = contentstore()
store = modulestore()._get_modulestore_by_type(ModuleStoreEnum.Type.mongo) # pylint: disable=protected-access
self.course_key = SlashSeparatedCourseKey('edX', 'toy', '2012_Fall')
self.course_key = store.make_course_key('edX', 'toy', '2012_Fall')
import_from_xml(
modulestore(), self.user.id, TEST_DATA_DIR, ['toy'],
store, self.user.id, TEST_DATA_DIR, ['toy'],
static_content_store=self.contentstore, verbose=True
)
# A locked asset
self.locked_asset = self.course_key.make_asset_key('asset', 'sample_static.txt')
self.url_locked = self.locked_asset.to_deprecated_string()
self.url_locked = unicode(self.locked_asset)
self.contentstore.set_attr(self.locked_asset, 'locked', True)
# An unlocked asset
self.unlocked_asset = self.course_key.make_asset_key('asset', 'another_static.txt')
self.url_unlocked = self.unlocked_asset.to_deprecated_string()
self.url_unlocked = unicode(self.unlocked_asset)
self.length_unlocked = self.contentstore.get_attr(self.unlocked_asset, 'length')
def test_unlocked_asset(self):
......
......@@ -10,13 +10,11 @@ from student.roles import GlobalStaff
from xmodule.modulestore import ModuleStoreEnum
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
from opaque_keys.edx.locations import SlashSeparatedCourseKey
from xmodule.modulestore.django import modulestore
from xmodule.error_module import ErrorDescriptor
from django.test.client import Client
from student.models import CourseEnrollment
from student.views import get_course_enrollment_pairs
from opaque_keys.edx.keys import CourseKey
from util.milestones_helpers import (
get_pre_requisite_courses_not_completed,
set_prerequisite_courses,
......@@ -42,7 +40,7 @@ class TestCourseListing(ModuleStoreTestCase):
self.client = Client()
self.client.login(username=self.teacher.username, password='test')
def _create_course_with_access_groups(self, course_location, metadata=None):
def _create_course_with_access_groups(self, course_location, metadata=None, default_store=None):
"""
Create dummy course with 'CourseFactory' and enroll the student
"""
......@@ -51,7 +49,8 @@ class TestCourseListing(ModuleStoreTestCase):
org=course_location.org,
number=course_location.course,
run=course_location.run,
metadata=metadata
metadata=metadata,
default_store=default_store
)
CourseEnrollment.enroll(self.student, course.id)
......@@ -70,7 +69,7 @@ class TestCourseListing(ModuleStoreTestCase):
"""
Test getting courses
"""
course_location = SlashSeparatedCourseKey('Org1', 'Course1', 'Run1')
course_location = self.store.make_course_key('Org1', 'Course1', 'Run1')
self._create_course_with_access_groups(course_location)
# get dashboard
......@@ -87,8 +86,10 @@ class TestCourseListing(ModuleStoreTestCase):
"""
Test the course list for regular staff when get_course returns an ErrorDescriptor
"""
course_key = SlashSeparatedCourseKey('Org1', 'Course1', 'Run1')
self._create_course_with_access_groups(course_key)
# pylint: disable=protected-access
mongo_store = modulestore()._get_modulestore_by_type(ModuleStoreEnum.Type.mongo)
course_key = mongo_store.make_course_key('Org1', 'Course1', 'Run1')
self._create_course_with_access_groups(course_key, default_store=ModuleStoreEnum.Type.mongo)
with patch('xmodule.modulestore.mongo.base.MongoKeyValueStore', Mock(side_effect=Exception)):
self.assertIsInstance(modulestore().get_course(course_key), ErrorDescriptor)
......@@ -104,15 +105,15 @@ class TestCourseListing(ModuleStoreTestCase):
"""
mongo_store = modulestore()._get_modulestore_by_type(ModuleStoreEnum.Type.mongo)
good_location = SlashSeparatedCourseKey('testOrg', 'testCourse', 'RunBabyRun')
self._create_course_with_access_groups(good_location)
good_location = mongo_store.make_course_key('testOrg', 'testCourse', 'RunBabyRun')
self._create_course_with_access_groups(good_location, default_store=ModuleStoreEnum.Type.mongo)
course_location = SlashSeparatedCourseKey('testOrg', 'doomedCourse', 'RunBabyRun')
self._create_course_with_access_groups(course_location)
course_location = mongo_store.make_course_key('testOrg', 'doomedCourse', 'RunBabyRun')
self._create_course_with_access_groups(course_location, default_store=ModuleStoreEnum.Type.mongo)
mongo_store.delete_course(course_location, ModuleStoreEnum.UserID.test)
course_location = SlashSeparatedCourseKey('testOrg', 'erroredCourse', 'RunBabyRun')
course = self._create_course_with_access_groups(course_location)
course_location = mongo_store.make_course_key('testOrg', 'erroredCourse', 'RunBabyRun')
course = self._create_course_with_access_groups(course_location, default_store=ModuleStoreEnum.Type.mongo)
course_db_record = mongo_store._find_one(course.location)
course_db_record.setdefault('metadata', {}).get('tabs', []).append({
"type": "wiko",
......@@ -137,18 +138,18 @@ class TestCourseListing(ModuleStoreTestCase):
Checks course where pre-requisite course is set has appropriate info.
"""
seed_milestone_relationship_types()
course_location2 = CourseKey.from_string('Org1/Course2/Run2')
course_location2 = self.store.make_course_key('Org1', 'Course2', 'Run2')
self._create_course_with_access_groups(course_location2)
pre_requisite_course_location = CourseKey.from_string('Org1/Course3/Run3')
pre_requisite_course_location = self.store.make_course_key('Org1', 'Course3', 'Run3')
self._create_course_with_access_groups(pre_requisite_course_location)
pre_requisite_course_location2 = CourseKey.from_string('Org1/Course4/Run4')
pre_requisite_course_location2 = self.store.make_course_key('Org1', 'Course4', 'Run4')
self._create_course_with_access_groups(pre_requisite_course_location2)
# create a course with pre_requisite_courses
pre_requisite_courses = [
unicode(pre_requisite_course_location),
unicode(pre_requisite_course_location2),
]
course_location = CourseKey.from_string('Org1/Course1/Run1')
course_location = self.store.make_course_key('Org1', 'Course1', 'Run1')
self._create_course_with_access_groups(course_location, {
'pre_requisite_courses': pre_requisite_courses
})
......
......@@ -102,7 +102,7 @@ class CourseFactory(XModuleFactory):
number = kwargs.pop('course', kwargs.pop('number', None))
store = kwargs.pop('modulestore')
name = kwargs.get('name', kwargs.get('run', Location.clean(kwargs.get('display_name'))))
run = kwargs.get('run', name)
run = kwargs.pop('run', name)
user_id = kwargs.pop('user_id', ModuleStoreEnum.UserID.test)
# Pass the metadata just as field=value pairs
......
......@@ -113,7 +113,7 @@ def export_to_xml(modulestore, contentstore, course_key, root_dir, course_dir):
export_extra_content(export_fs, modulestore, course_key, xml_centric_course_key, 'about', 'about', '.html')
# export the grading policy
course_run_policy_dir = policies_dir.makeopendir(course.location.name)
course_run_policy_dir = policies_dir.makeopendir(course.location.run)
with course_run_policy_dir.open('grading_policy.json', 'w') as grading_policy:
grading_policy.write(dumps(course.grading_policy, cls=EdxJSONEncoder, sort_keys=True, indent=4))
......
......@@ -195,11 +195,18 @@ def import_from_xml(
if target_course_id is not None:
dest_course_id = target_course_id
else:
# Note that dest_course_id will be in the format for the default modulestore.
dest_course_id = store.make_course_key(course_key.org, course_key.course, course_key.run)
existing_course_id = store.has_course(dest_course_id, ignore_case=True)
# store.has_course will return the course_key in the format for the modulestore in which it was found.
# This may be different from dest_course_id, so correct to the format found.
if existing_course_id:
dest_course_id = existing_course_id
runtime = None
# Creates a new course if it doesn't already exist
if create_course_if_not_present and not store.has_course(dest_course_id, ignore_case=True):
if create_course_if_not_present and not existing_course_id:
try:
new_course = store.create_course(dest_course_id.org, dest_course_id.course, dest_course_id.run, user_id)
runtime = new_course.runtime
......
......@@ -8,22 +8,22 @@ import shutil
from StringIO import StringIO
import tarfile
from tempfile import mkdtemp
import factory
from django.conf import settings
from django.core.management import call_command
from django.test.utils import override_settings
from django.test.testcases import TestCase
from opaque_keys.edx.locations import SlashSeparatedCourseKey
from xmodule.modulestore import ModuleStoreEnum
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase, mixed_store_config
from xmodule.modulestore.tests.django_utils import TEST_DATA_MONGO_MODULESTORE
from xmodule.modulestore.tests.django_utils import (
TEST_DATA_MONGO_MODULESTORE, TEST_DATA_SPLIT_MODULESTORE
)
from xmodule.modulestore.tests.factories import CourseFactory
from xmodule.modulestore.xml_importer import import_from_xml
DATA_DIR = settings.COMMON_TEST_DATA_ROOT
TEST_COURSE_ID = 'edX/simple/2012_Fall'
XML_COURSE_DIRS = ['toy', 'simple', 'open_ended']
MAPPINGS = {
'edX/toy/2012_Fall': 'xml',
......@@ -36,7 +36,7 @@ TEST_DATA_MIXED_XML_MODULESTORE = mixed_store_config(
)
class CommandsTestBase(TestCase):
class CommandsTestBase(ModuleStoreTestCase):
"""
Base class for testing different django commands.
......@@ -44,27 +44,32 @@ class CommandsTestBase(TestCase):
to be tested.
"""
__test__ = False
def setUp(self):
super(CommandsTestBase, self).setUp()
self.test_course_key = modulestore().make_course_key("edX", "simple", "2012_Fall")
self.loaded_courses = self.load_courses()
def load_courses(self):
"""Load test courses and return list of ids"""
store = modulestore()
# Add a course with a unicode name, if the modulestore
# supports adding modules.
if hasattr(store, 'create_xmodule'):
CourseFactory.create(org=u'ëḋẌ',
course=u'śíḿṕĺé',
display_name=u'2012_Fáĺĺ',
modulestore=store)
# Add a course with a unicode name.
unique_org = factory.Sequence(lambda n: u'ëḋẌ.%d' % n)
CourseFactory.create(
org=unique_org,
course=u'śíḿṕĺé',
display_name=u'2012_Fáĺĺ',
modulestore=store
)
courses = store.get_courses()
# NOTE: if xml store owns these, it won't import them into mongo
if SlashSeparatedCourseKey.from_deprecated_string(TEST_COURSE_ID) not in [c.id for c in courses]:
import_from_xml(store, ModuleStoreEnum.UserID.mgmt_command, DATA_DIR, XML_COURSE_DIRS)
if self.test_course_key not in [c.id for c in courses]:
import_from_xml(
store, ModuleStoreEnum.UserID.mgmt_command, DATA_DIR, XML_COURSE_DIRS, create_course_if_not_present=True
)
return [course.id for course in store.get_courses()]
......@@ -80,12 +85,12 @@ class CommandsTestBase(TestCase):
output = self.call_command('dump_course_ids', **kwargs)
dumped_courses = output.decode('utf-8').strip().split('\n')
course_ids = {course_id.to_deprecated_string() for course_id in self.loaded_courses}
course_ids = {unicode(course_id) for course_id in self.loaded_courses}
dumped_ids = set(dumped_courses)
self.assertEqual(course_ids, dumped_ids)
def test_correct_course_structure_metadata(self):
course_id = 'edX/open_ended/2012_Fall'
course_id = unicode(modulestore().make_course_key('edX', 'open_ended', '2012_Fall'))
args = [course_id]
kwargs = {'modulestore': 'default'}
......@@ -98,7 +103,7 @@ class CommandsTestBase(TestCase):
self.assertGreater(len(dump.values()), 0)
def test_dump_course_structure(self):
args = [TEST_COURSE_ID]
args = [unicode(self.test_course_key)]
kwargs = {'modulestore': 'default'}
output = self.call_command('dump_course_structure', *args, **kwargs)
......@@ -113,8 +118,8 @@ class CommandsTestBase(TestCase):
self.assertNotIn('inherited_metadata', element)
# Check a few elements in the course dump
test_course_key = SlashSeparatedCourseKey.from_deprecated_string(TEST_COURSE_ID)
parent_id = test_course_key.make_usage_key('chapter', 'Overview').to_deprecated_string()
test_course_key = self.test_course_key
parent_id = unicode(test_course_key.make_usage_key('chapter', 'Overview'))
self.assertEqual(dump[parent_id]['category'], 'chapter')
self.assertEqual(len(dump[parent_id]['children']), 3)
......@@ -132,7 +137,7 @@ class CommandsTestBase(TestCase):
self.assertEqual(len(dump), 16)
def test_dump_inherited_course_structure(self):
args = [TEST_COURSE_ID]
args = [unicode(self.test_course_key)]
kwargs = {'modulestore': 'default', 'inherited': True}
output = self.call_command('dump_course_structure', *args, **kwargs)
dump = json.loads(output)
......@@ -148,7 +153,7 @@ class CommandsTestBase(TestCase):
self.assertNotIn('due', element['inherited_metadata'])
def test_dump_inherited_course_structure_with_defaults(self):
args = [TEST_COURSE_ID]
args = [unicode(self.test_course_key)]
kwargs = {'modulestore': 'default', 'inherited': True, 'inherited_defaults': True}
output = self.call_command('dump_course_structure', *args, **kwargs)
dump = json.loads(output)
......@@ -180,7 +185,7 @@ class CommandsTestBase(TestCase):
self.check_export_file(tar_file)
def run_export_course(self, filename): # pylint: disable=missing-docstring
args = [TEST_COURSE_ID, filename]
args = [unicode(self.test_course_key), filename]
kwargs = {'modulestore': 'default'}
return self.call_command('export_course', *args, **kwargs)
......@@ -200,17 +205,28 @@ class CommandsTestBase(TestCase):
assert_in('edX-simple-2012_Fall/sequential/Lecture_2.xml', names)
class CommandsXMLTestCase(CommandsTestBase, ModuleStoreTestCase):
class CommandsXMLTestCase(CommandsTestBase):
"""
Test case for management commands using the xml modulestore.
Test case for management commands with the xml modulestore present.
"""
MODULESTORE = TEST_DATA_MIXED_XML_MODULESTORE
__test__ = True
class CommandsMongoTestCase(CommandsTestBase, ModuleStoreTestCase):
class CommandsMongoTestCase(CommandsTestBase):
"""
Test case for management commands using the mixed mongo modulestore.
Test case for management commands using the mixed mongo modulestore with old mongo as the default.
"""
MODULESTORE = TEST_DATA_MONGO_MODULESTORE
__test__ = True
class CommandSplitMongoTestCase(CommandsTestBase):
"""
Test case for management commands using the mixed mongo modulestore with split as the default.
"""
MODULESTORE = TEST_DATA_SPLIT_MODULESTORE
__test__ = True
......@@ -43,9 +43,9 @@ class CoursesTest(ModuleStoreTestCase):
org='org', number='num', display_name='name'
)
cms_url = u"//{}/course/org/num/name".format(CMS_BASE_TEST)
cms_url = u"//{}/course/{}".format(CMS_BASE_TEST, unicode(self.course.id))
self.assertEqual(cms_url, get_cms_course_link(self.course))
cms_url = u"//{}/course/i4x://org/num/course/name".format(CMS_BASE_TEST)
cms_url = u"//{}/course/{}".format(CMS_BASE_TEST, unicode(self.course.location))
self.assertEqual(cms_url, get_cms_block_link(self.course, 'course'))
......
......@@ -64,13 +64,22 @@ class GroupAccessTestCase(ModuleStoreTestCase):
"""
partition.scheme.set_group_for_user(user, partition, group)
def set_group_access(self, block, access_dict):
def set_group_access(self, block_location, access_dict):
"""
DRY helper.
Set group_access on block specified by location.
"""
block = modulestore().get_item(block_location)
block.group_access = access_dict
modulestore().update_item(block, 1)
def set_user_partitions(self, block_location, partitions):
"""
Sets the user_partitions on block specified by location.
"""
block = modulestore().get_item(block_location)
block.user_partitions = partitions
modulestore().update_item(block, 1)
def setUp(self):
super(GroupAccessTestCase, self).setUp()
......@@ -117,10 +126,15 @@ class GroupAccessTestCase(ModuleStoreTestCase):
self.course = CourseFactory.create(
user_partitions=[self.animal_partition, self.color_partition],
)
self.chapter = ItemFactory.create(category='chapter', parent=self.course)
self.section = ItemFactory.create(category='sequential', parent=self.chapter)
self.vertical = ItemFactory.create(category='vertical', parent=self.section)
self.component = ItemFactory.create(category='problem', parent=self.vertical)
chapter = ItemFactory.create(category='chapter', parent=self.course)
section = ItemFactory.create(category='sequential', parent=chapter)
vertical = ItemFactory.create(category='vertical', parent=section)
component = ItemFactory.create(category='problem', parent=vertical)
self.chapter_location = chapter.location
self.section_location = section.location
self.vertical_location = vertical.location
self.component_location = component.location
self.red_cat = UserFactory() # student in red and cat groups
self.set_user_group(self.red_cat, self.animal_partition, self.cat_group)
......@@ -145,15 +159,15 @@ class GroupAccessTestCase(ModuleStoreTestCase):
# avoid repeatedly declaring the same sequence for ddt in all the test cases.
PARENT_CHILD_PAIRS = (
('chapter', 'chapter'),
('chapter', 'section'),
('chapter', 'vertical'),
('chapter', 'component'),
('section', 'section'),
('section', 'vertical'),
('section', 'component'),
('vertical', 'vertical'),
('vertical', 'component'),
('chapter_location', 'chapter_location'),
('chapter_location', 'section_location'),
('chapter_location', 'vertical_location'),
('chapter_location', 'component_location'),
('section_location', 'section_location'),
('section_location', 'vertical_location'),
('section_location', 'component_location'),
('vertical_location', 'vertical_location'),
('vertical_location', 'component_location'),
)
def tearDown(self):
......@@ -163,19 +177,20 @@ class GroupAccessTestCase(ModuleStoreTestCase):
"""
UserPartition.scheme_extensions = None
def check_access(self, user, block, is_accessible):
def check_access(self, user, block_location, is_accessible):
"""
DRY helper.
"""
self.assertIs(
access.has_access(user, 'load', block, self.course.id),
access.has_access(user, 'load', modulestore().get_item(block_location), self.course.id),
is_accessible
)
def ensure_staff_access(self, block):
def ensure_staff_access(self, block_location):
"""
Another DRY helper.
"""
block = modulestore().get_item(block_location)
self.assertTrue(access.has_access(self.staff, 'load', block, self.course.id))
# NOTE: in all the tests that follow, `block_specified` and
......@@ -396,12 +411,12 @@ class GroupAccessTestCase(ModuleStoreTestCase):
except user_partitions in use by the split_test module.
"""
# Initially, "red_cat" user can't view the vertical.
self.set_group_access(self.chapter, {self.animal_partition.id: [self.dog_group.id]})
self.check_access(self.red_cat, self.vertical, False)
self.set_group_access(self.chapter_location, {self.animal_partition.id: [self.dog_group.id]})
self.check_access(self.red_cat, self.vertical_location, False)
# Change the vertical's user_partitions value to the empty list. Now red_cat can view the vertical.
self.vertical.user_partitions = []
self.check_access(self.red_cat, self.vertical, True)
self.set_user_partitions(self.vertical_location, [])
self.check_access(self.red_cat, self.vertical_location, True)
# Change the vertical's user_partitions value to include only "split_test" partitions.
split_test_partition = UserPartition(
......@@ -411,9 +426,9 @@ class GroupAccessTestCase(ModuleStoreTestCase):
[Group(2, 'random group')],
scheme=UserPartition.get_scheme("random"),
)
self.vertical.user_partitions = [split_test_partition]
self.check_access(self.red_cat, self.vertical, True)
self.set_user_partitions(self.vertical_location, [split_test_partition])
self.check_access(self.red_cat, self.vertical_location, True)
# Finally, add back in a cohort user_partition
self.vertical.user_partitions = [split_test_partition, self.animal_partition]
self.check_access(self.red_cat, self.vertical, False)
self.set_user_partitions(self.vertical_location, [split_test_partition, self.animal_partition])
self.check_access(self.red_cat, self.vertical_location, False)
......@@ -19,6 +19,7 @@ from provider.oauth2.models import Client
from xmodule.tabs import EdxNotesTab
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore import ModuleStoreEnum
from xmodule.modulestore.django import modulestore
from courseware.model_data import FieldDataCache
from courseware.module_render import get_module_for_descriptor
......@@ -65,7 +66,9 @@ class EdxNotesDecoratorTest(ModuleStoreTestCase):
super(EdxNotesDecoratorTest, self).setUp()
ClientFactory(name="edx-notes")
self.course = CourseFactory.create(edxnotes=True)
# Using old mongo because of locator comparison issues (see longer
# note below in EdxNotesHelpersTest setUp.
self.course = CourseFactory.create(edxnotes=True, default_store=ModuleStoreEnum.Type.mongo)
self.user = UserFactory.create(username="Bob", email="bob@example.com", password="edx")
self.client.login(username=self.user.username, password="edx")
self.problem = TestProblem(self.course)
......@@ -144,34 +147,42 @@ class EdxNotesHelpersTest(ModuleStoreTestCase):
Setup a dummy course content.
"""
super(EdxNotesHelpersTest, self).setUp()
ClientFactory(name="edx-notes")
self.course = CourseFactory.create()
self.chapter = ItemFactory.create(category="chapter", parent_location=self.course.location)
self.chapter_2 = ItemFactory.create(category="chapter", parent_location=self.course.location)
self.sequential = ItemFactory.create(category="sequential", parent_location=self.chapter.location)
self.vertical = ItemFactory.create(category="vertical", parent_location=self.sequential.location)
self.html_module_1 = ItemFactory.create(category="html", parent_location=self.vertical.location)
self.html_module_2 = ItemFactory.create(category="html", parent_location=self.vertical.location)
self.vertical_with_container = ItemFactory.create(category='vertical', parent_location=self.sequential.location)
self.child_container = ItemFactory.create(
category='split_test', parent_location=self.vertical_with_container.location)
self.child_vertical = ItemFactory.create(category='vertical', parent_location=self.child_container.location)
self.child_html_module = ItemFactory.create(category="html", parent_location=self.child_vertical.location)
# Read again so that children lists are accurate
self.course = self.store.get_item(self.course.location)
self.chapter = self.store.get_item(self.chapter.location)
self.chapter_2 = self.store.get_item(self.chapter_2.location)
self.sequential = self.store.get_item(self.sequential.location)
self.vertical = self.store.get_item(self.vertical.location)
self.vertical_with_container = self.store.get_item(self.vertical_with_container.location)
self.child_container = self.store.get_item(self.child_container.location)
self.child_vertical = self.store.get_item(self.child_vertical.location)
self.child_html_module = self.store.get_item(self.child_html_module.location)
self.user = UserFactory.create(username="Joe", email="joe@example.com", password="edx")
self.client.login(username=self.user.username, password="edx")
# There are many tests that are comparing locators as returned from helper methods. When using
# the split modulestore, some of those locators have version and branch information, but the
# comparison values do not. This needs further investigation in order to enable these tests
# with the split modulestore.
with self.store.default_store(ModuleStoreEnum.Type.mongo):
ClientFactory(name="edx-notes")
self.course = CourseFactory.create()
self.chapter = ItemFactory.create(category="chapter", parent_location=self.course.location)
self.chapter_2 = ItemFactory.create(category="chapter", parent_location=self.course.location)
self.sequential = ItemFactory.create(category="sequential", parent_location=self.chapter.location)
self.vertical = ItemFactory.create(category="vertical", parent_location=self.sequential.location)
self.html_module_1 = ItemFactory.create(category="html", parent_location=self.vertical.location)
self.html_module_2 = ItemFactory.create(category="html", parent_location=self.vertical.location)
self.vertical_with_container = ItemFactory.create(
category='vertical', parent_location=self.sequential.location
)
self.child_container = ItemFactory.create(
category='split_test', parent_location=self.vertical_with_container.location)
self.child_vertical = ItemFactory.create(category='vertical', parent_location=self.child_container.location)
self.child_html_module = ItemFactory.create(category="html", parent_location=self.child_vertical.location)
# Read again so that children lists are accurate
self.course = self.store.get_item(self.course.location)
self.chapter = self.store.get_item(self.chapter.location)
self.chapter_2 = self.store.get_item(self.chapter_2.location)
self.sequential = self.store.get_item(self.sequential.location)
self.vertical = self.store.get_item(self.vertical.location)
self.vertical_with_container = self.store.get_item(self.vertical_with_container.location)
self.child_container = self.store.get_item(self.child_container.location)
self.child_vertical = self.store.get_item(self.child_vertical.location)
self.child_html_module = self.store.get_item(self.child_html_module.location)
self.user = UserFactory.create(username="Joe", email="joe@example.com", password="edx")
self.client.login(username=self.user.username, password="edx")
def _get_unit_url(self, course, chapter, section, position=1):
"""
......
......@@ -8,7 +8,6 @@ from django.core.urlresolvers import reverse
from mock import patch
from student.roles import CourseSalesAdminRole
from student.tests.factories import UserFactory, CourseModeFactory
from opaque_keys.edx.locations import SlashSeparatedCourseKey
from shoppingcart.models import (
CourseRegistrationCode, RegistrationCodeRedemption, Order,
Invoice, Coupon, CourseRegCodeItem, CouponRedemption, CourseRegistrationCodeInvoiceItem
......@@ -33,7 +32,7 @@ class TestAnalyticsBasic(ModuleStoreTestCase):
def setUp(self):
super(TestAnalyticsBasic, self).setUp()
self.course_key = SlashSeparatedCourseKey('robot', 'course', 'id')
self.course_key = self.store.make_course_key('robot', 'course', 'id')
self.users = tuple(UserFactory() for _ in xrange(30))
self.ces = tuple(CourseEnrollment.enroll(user, self.course_key)
for user in self.users)
......@@ -80,7 +79,7 @@ class TestAnalyticsBasic(ModuleStoreTestCase):
self.assertIn(userreport['meta.company'], ["Open edX Inc {}".format(user.id) for user in self.users])
def test_enrolled_students_features_keys_cohorted(self):
course = CourseFactory.create(course_key=self.course_key)
course = CourseFactory.create(org="test", course="course1", display_name="run1")
course.cohort_config = {'cohorted': True, 'auto_cohort': True, 'auto_cohort_groups': ['cohort']}
self.store.update_item(course, self.instructor.id)
cohort = CohortFactory.create(name='cohort', course_id=course.id)
......
......@@ -120,6 +120,11 @@ class TestHandouts(MobileAPITestCase, MobileAuthTestMixin, MobileEnrolledCourseA
def setUp(self):
super(TestHandouts, self).setUp()
# Deleting handouts fails with split modulestore because the handout has no parent.
# This needs further investigation to determine if it is a bug in the split modulestore.
# pylint: disable=protected-access
self.store = modulestore()._get_modulestore_by_type(ModuleStoreEnum.Type.mongo)
# use toy course with handouts, and make it mobile_available
course_items = import_from_xml(self.store, self.user.id, settings.COMMON_TEST_DATA_ROOT, ['toy'])
self.course = course_items[0]
......
......@@ -35,7 +35,7 @@ class MobileAPITestCase(ModuleStoreTestCase, APITestCase):
"""
def setUp(self):
super(MobileAPITestCase, self).setUp()
self.course = CourseFactory.create(mobile_available=True)
self.course = CourseFactory.create(mobile_available=True, static_asset_path="needed_for_split")
self.user = UserFactory.create()
self.password = 'test'
self.username = self.user.username
......
......@@ -627,8 +627,8 @@ MODULESTORE = {
'mappings': {},
'stores': [
{
'NAME': 'draft',
'ENGINE': 'xmodule.modulestore.mongo.DraftMongoModuleStore',
'NAME': 'split',
'ENGINE': 'xmodule.modulestore.split_mongo.split_draft.DraftVersioningModuleStore',
'DOC_STORE_CONFIG': DOC_STORE_CONFIG,
'OPTIONS': {
'default_class': 'xmodule.hidden_module.HiddenDescriptor',
......@@ -637,23 +637,23 @@ MODULESTORE = {
}
},
{
'NAME': 'xml',
'ENGINE': 'xmodule.modulestore.xml.XMLModuleStore',
'NAME': 'draft',
'ENGINE': 'xmodule.modulestore.mongo.DraftMongoModuleStore',
'DOC_STORE_CONFIG': DOC_STORE_CONFIG,
'OPTIONS': {
'data_dir': DATA_DIR,
'default_class': 'xmodule.hidden_module.HiddenDescriptor',
'fs_root': DATA_DIR,
'render_template': 'edxmako.shortcuts.render_to_string',
}
},
{
'NAME': 'split',
'ENGINE': 'xmodule.modulestore.split_mongo.split_draft.DraftVersioningModuleStore',
'DOC_STORE_CONFIG': DOC_STORE_CONFIG,
'NAME': 'xml',
'ENGINE': 'xmodule.modulestore.xml.XMLModuleStore',
'OPTIONS': {
'data_dir': DATA_DIR,
'default_class': 'xmodule.hidden_module.HiddenDescriptor',
'fs_root': DATA_DIR,
'render_template': 'edxmako.shortcuts.render_to_string',
}
},
}
]
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment