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