Example #1
0
 def test_mongo_modulestore_type(self):
     store = MongoModuleStore(
         None,
         {'host': HOST, 'db': DB, 'collection': COLLECTION},
         FS_ROOT, RENDER_TEMPLATE, default_class=DEFAULT_CLASS
     )
     assert_equals(store.get_modulestore_type(''), ModuleStoreEnum.Type.mongo)
Example #2
0
    def setUp(self):
        self.db_config['collection'] = 'modulestore{0}'.format(uuid.uuid4().hex[:5])

        self.old_mongo = MongoModuleStore(self.db_config, **self.modulestore_options)
        self.draft_mongo = DraftMongoModuleStore(self.db_config, **self.modulestore_options)
        self.addCleanup(self.tear_down_mongo)
        self.course_location = None
 def test_mongo_modulestore_type(self):
     store = MongoModuleStore(HOST,
                              DB,
                              COLLECTION,
                              FS_ROOT,
                              RENDER_TEMPLATE,
                              default_class=DEFAULT_CLASS)
     assert_equals(store.get_modulestore_type('foo/bar/baz'), 'mongo')
Example #4
0
 def test_mongo_modulestore_type(self):
     store = MongoModuleStore(
         {
             'host': HOST,
             'db': DB,
             'collection': COLLECTION
         },
         FS_ROOT,
         RENDER_TEMPLATE,
         default_class=DEFAULT_CLASS)
     assert_equals(store.get_modulestore_type('foo/bar/baz'), 'mongo')
Example #5
0
    def setUp(self):
        self.db_config['collection'] = 'modulestore{0}'.format(
            uuid.uuid4().hex[:5])

        self.userid = random.getrandbits(32)
        super(TestOrphan, self).setUp()
        self.split_mongo = SplitMongoModuleStore(self.db_config,
                                                 **self.modulestore_options)
        self.addCleanup(self.tear_down_split)
        self.old_mongo = MongoModuleStore(self.db_config,
                                          **self.modulestore_options)
        self.addCleanup(self.tear_down_mongo)
        self.course_location = None
        self._create_course()
Example #6
0
 def initdb():
     # connect to the db
     store = MongoModuleStore(HOST, DB, COLLECTION, FS_ROOT, RENDER_TEMPLATE, default_class=DEFAULT_CLASS)
     # Explicitly list the courses to load (don't want the big one)
     courses = ['toy', 'simple']
     import_from_xml(store, DATA_DIR, courses)
     return store
Example #7
0
    def initdb():
        # connect to the db
        doc_store_config = {
            'host': HOST,
            'db': DB,
            'collection': COLLECTION,
        }
        store = MongoModuleStore(doc_store_config, FS_ROOT, RENDER_TEMPLATE, default_class=DEFAULT_CLASS)
        # since MongoModuleStore and MongoContentStore are basically assumed to be together, create this class
        # as well
        content_store = MongoContentStore(HOST, DB)
        #
        # Also test draft store imports
        #
        draft_store = DraftModuleStore(doc_store_config, FS_ROOT, RENDER_TEMPLATE, default_class=DEFAULT_CLASS)
        import_from_xml(store, DATA_DIR, TestMongoModuleStore.courses, draft_store=draft_store, static_content_store=content_store)

        # also test a course with no importing of static content
        import_from_xml(
            store,
            DATA_DIR,
            ['test_import_course'],
            static_content_store=content_store,
            do_import_static=False,
            verbose=True
        )

        return store, content_store, draft_store
Example #8
0
    def initdb():
        # connect to the db
        store = MongoModuleStore(HOST, DB, COLLECTION, FS_ROOT, RENDER_TEMPLATE, default_class=DEFAULT_CLASS)
        # since MongoModuleStore and MongoContentStore are basically assumed to be together, create this class
        # as well
        content_store = MongoContentStore(HOST, DB)
        #
        # Also test draft store imports
        #
        draft_store = DraftModuleStore(HOST, DB, COLLECTION, FS_ROOT, RENDER_TEMPLATE, default_class=DEFAULT_CLASS)
        # Explicitly list the courses to load (don't want the big one)
        courses = ['toy', 'simple', 'simple_with_draft', 'test_unicode']
        import_from_xml(store, DATA_DIR, courses, draft_store=draft_store, static_content_store=content_store)

        # also test a course with no importing of static content
        import_from_xml(
            store,
            DATA_DIR,
            ['test_import_course'],
            static_content_store=content_store,
            do_import_static=False,
            verbose=True
        )

        return store, content_store, draft_store
    def setUp(self):
        self.db_config['collection'] = 'modulestore{0}'.format(uuid.uuid4().hex[:5])

        self.userid = random.getrandbits(32)
        super(SplitWMongoCourseBoostrapper, self).setUp()
        self.split_mongo = SplitMongoModuleStore(
            self.db_config,
            **self.modulestore_options
        )
        self.addCleanup(self.split_mongo.db.connection.close)
        self.addCleanup(self.tear_down_split)
        self.old_mongo = MongoModuleStore(self.db_config, **self.modulestore_options)
        self.draft_mongo = DraftMongoModuleStore(self.db_config, **self.modulestore_options)
        self.addCleanup(self.tear_down_mongo)
        self.old_course_key = None
        self.runtime = None
        self._create_course()
    def setUp(self):
        self.db_config['collection'] = 'modulestore{0}'.format(uuid.uuid4().hex[:5])

        self.userid = random.getrandbits(32)
        super(TestOrphan, self).setUp()
        self.split_mongo = SplitMongoModuleStore(
            self.db_config,
            **self.modulestore_options
        )
        self.addCleanup(self.tear_down_split)
        self.old_mongo = MongoModuleStore(self.db_config, **self.modulestore_options)
        self.addCleanup(self.tear_down_mongo)
        self.course_location = None
        self._create_course()
Example #11
0
 def test_mongo_modulestore_type(self):
     store = MongoModuleStore(HOST, DB, COLLECTION, FS_ROOT, RENDER_TEMPLATE, default_class=DEFAULT_CLASS)
     assert_equals(store.get_modulestore_type('foo/bar/baz'), 'mongo')
Example #12
0
class TestPublish(unittest.TestCase):
    """
    Test the publish code (primary causing orphans)
    """

    # Snippet of what would be in the django settings envs file
    db_config = {
        'host': 'localhost',
        'db': 'test_xmodule',
    }

    modulestore_options = {
        'default_class': 'xmodule.raw_module.RawDescriptor',
        'fs_root': '',
        'render_template': mock.Mock(return_value=""),
        'xblock_mixins': (InheritanceMixin,)
    }

    def setUp(self):
        self.db_config['collection'] = 'modulestore{0}'.format(uuid.uuid4().hex[:5])

        self.old_mongo = MongoModuleStore(self.db_config, **self.modulestore_options)
        self.draft_mongo = DraftMongoModuleStore(self.db_config, **self.modulestore_options)
        self.addCleanup(self.tear_down_mongo)
        self.course_location = None

    def tear_down_mongo(self):
        # old_mongo doesn't give a db attr, but all of the dbs are the same and draft and pub use same collection
        dbref = self.old_mongo.collection.database
        dbref.drop_collection(self.old_mongo.collection)
        dbref.connection.close()

    def _create_item(self, category, name, data, metadata, parent_category, parent_name, runtime):
        """
        Create the item in either draft or direct based on category and attach to its parent.
        """
        location = self.course_location.replace(category=category, name=name)
        if category in DIRECT_ONLY_CATEGORIES:
            mongo = self.old_mongo
        else:
            mongo = self.draft_mongo
        mongo.create_and_save_xmodule(location, data, metadata, runtime)
        if isinstance(data, basestring):
            fields = {'data': data}
        else:
            fields = data.copy()
        fields.update(metadata)
        if parent_name:
            # add child to parent in mongo
            parent_location = self.course_location.replace(category=parent_category, name=parent_name)
            parent = self.draft_mongo.get_item(parent_location)
            parent.children.append(location.url())
            if parent_category in DIRECT_ONLY_CATEGORIES:
                mongo = self.old_mongo
            else:
                mongo = self.draft_mongo
            mongo.update_children(parent_location, parent.children)

    def _create_course(self):
        """
        Create the course, publish all verticals
        * some detached items
        """
        date_proxy = Date()
        metadata = {
            'start': date_proxy.to_json(datetime.datetime(2000, 3, 13, 4)),
            'display_name': 'Migration test course',
        }
        data = {
            'wiki_slug': 'test_course_slug'
        }
        fields = metadata.copy()
        fields.update(data)

        self.course_location = Location('i4x', 'test_org', 'test_course', 'course', 'runid')
        self.old_mongo.create_and_save_xmodule(self.course_location, data, metadata)
        runtime = self.draft_mongo.get_item(self.course_location).runtime

        self._create_item('chapter', 'Chapter1', {}, {'display_name': 'Chapter 1'}, 'course', 'runid', runtime)
        self._create_item('chapter', 'Chapter2', {}, {'display_name': 'Chapter 2'}, 'course', 'runid', runtime)
        self._create_item('vertical', 'Vert1', {}, {'display_name': 'Vertical 1'}, 'chapter', 'Chapter1', runtime)
        self._create_item('vertical', 'Vert2', {}, {'display_name': 'Vertical 2'}, 'chapter', 'Chapter1', runtime)
        self._create_item('html', 'Html1', "<p>Goodbye</p>", {'display_name': 'Parented Html'}, 'vertical', 'Vert1', runtime)
        self._create_item(
            'discussion', 'Discussion1',
            "discussion discussion_category=\"Lecture 1\" discussion_id=\"a08bfd89b2aa40fa81f2c650a9332846\" discussion_target=\"Lecture 1\"/>\n",
            {
                "discussion_category": "Lecture 1",
                "discussion_target": "Lecture 1",
                "display_name": "Lecture 1 Discussion",
                "discussion_id": "a08bfd89b2aa40fa81f2c650a9332846"
            },
            'vertical', 'Vert1', runtime
        )
        self._create_item('html', 'Html2', "<p>Hellow</p>", {'display_name': 'Hollow Html'}, 'vertical', 'Vert1', runtime)
        self._create_item(
            'discussion', 'Discussion2',
            "discussion discussion_category=\"Lecture 2\" discussion_id=\"b08bfd89b2aa40fa81f2c650a9332846\" discussion_target=\"Lecture 2\"/>\n",
            {
                "discussion_category": "Lecture 2",
                "discussion_target": "Lecture 2",
                "display_name": "Lecture 2 Discussion",
                "discussion_id": "b08bfd89b2aa40fa81f2c650a9332846"
            },
            'vertical', 'Vert2', runtime
        )
        self._create_item('static_tab', 'staticuno', "<p>tab</p>", {'display_name': 'Tab uno'}, None, None, runtime)
        self._create_item('about', 'overview', "<p>overview</p>", {}, None, None, runtime)
        self._create_item('course_info', 'updates', "<ol><li><h2>Sep 22</h2><p>test</p></li></ol>", {}, None, None, runtime)

    def _xmodule_recurse(self, item, action):
        """
        Applies action depth-first down tree and to item last.

        A copy of  cms.djangoapps.contentstore.views.helpers._xmodule_recurse to reproduce its use and behavior
        outside of django.
        """
        for child in item.get_children():
            self._xmodule_recurse(child, action)

        action(item)

    def test_publish_draft_delete(self):
        """
        To reproduce a bug (STUD-811) publish a vertical, convert to draft, delete a child, move a child, publish.
        See if deleted and moved children still is connected or exists in db (bug was disconnected but existed)
        """
        self._create_course()
        userid = random.getrandbits(32)
        location = self.course_location.replace(category='vertical', name='Vert1')
        item = self.draft_mongo.get_item(location, 2)
        self._xmodule_recurse(
            item,
            lambda i: self.draft_mongo.publish(i.location, userid)
        )
        # verify status
        item = self.draft_mongo.get_item(location, 0)
        self.assertFalse(getattr(item, 'is_draft', False), "Item was published. Draft should not exist")
        # however, children are still draft, but I'm not sure that's by design

        # convert back to draft
        self.draft_mongo.convert_to_draft(location)
        # both draft and published should exist
        draft_vert = self.draft_mongo.get_item(location, 0)
        self.assertTrue(getattr(draft_vert, 'is_draft', False), "Item was converted to draft but doesn't say so")
        item = self.old_mongo.get_item(location, 0)
        self.assertFalse(getattr(item, 'is_draft', False), "Published item doesn't say so")

        # delete the discussion (which oddly is not in draft mode)
        location = self.course_location.replace(category='discussion', name='Discussion1')
        self.draft_mongo.delete_item(location)
        # remove pointer from draft vertical (verify presence first to ensure process is valid)
        self.assertIn(location.url(), draft_vert.children)
        draft_vert.children.remove(location.url())
        # move the other child
        other_child_loc = self.course_location.replace(category='html', name='Html2')
        draft_vert.children.remove(other_child_loc.url())
        other_vert = self.draft_mongo.get_item(self.course_location.replace(category='vertical', name='Vert2'), 0)
        other_vert.children.append(other_child_loc.url())
        self.draft_mongo.update_children(draft_vert.location, draft_vert.children)
        self.draft_mongo.update_children(other_vert.location, other_vert.children)
        # publish
        self._xmodule_recurse(
            draft_vert,
            lambda i: self.draft_mongo.publish(i.location, userid)
        )
        item = self.old_mongo.get_item(draft_vert.location, 0)
        self.assertNotIn(location.url(), item.children)
        with self.assertRaises(ItemNotFoundError):
            self.draft_mongo.get_item(location)
        self.assertNotIn(other_child_loc.url(), item.children)
        self.assertTrue(self.draft_mongo.has_item(None, other_child_loc), "Oops, lost moved item")
class SplitWMongoCourseBoostrapper(unittest.TestCase):
    """
    Helper for tests which need to construct split mongo & old mongo based courses to get interesting internal structure.
    Override _create_course and after invoking the super() _create_course, have it call _create_item for
    each xblock you want in the course.
    This class ensures the db gets created, opened, and cleaned up in addition to creating the course

    Defines the following attrs on self:
    * userid: a random non-registered mock user id
    * split_mongo: a pointer to the split mongo instance
    * old_mongo: a pointer to the old_mongo instance
    * draft_mongo: a pointer to the old draft instance
    * split_course_key (CourseLocator): of the new course
    * old_course_key: the SlashSpecifiedCourseKey for the course
    """
        # Snippet of what would be in the django settings envs file
    db_config = {
        'host': 'localhost',
        'db': 'test_xmodule',
    }

    modulestore_options = {
        'default_class': 'xmodule.raw_module.RawDescriptor',
        'fs_root': '',
        'render_template': mock.Mock(return_value=""),
        'xblock_mixins': (InheritanceMixin,)
    }

    split_course_key = CourseLocator('test_org', 'test_course.runid', branch=ModuleStoreEnum.BranchName.draft)

    def setUp(self):
        self.db_config['collection'] = 'modulestore{0}'.format(uuid.uuid4().hex[:5])

        self.userid = random.getrandbits(32)
        super(SplitWMongoCourseBoostrapper, self).setUp()
        self.split_mongo = SplitMongoModuleStore(
            self.db_config,
            **self.modulestore_options
        )
        self.addCleanup(self.split_mongo.db.connection.close)
        self.addCleanup(self.tear_down_split)
        self.old_mongo = MongoModuleStore(self.db_config, **self.modulestore_options)
        self.draft_mongo = DraftMongoModuleStore(
            self.db_config, branch_setting_func=lambda: ModuleStoreEnum.Branch.draft_preferred, **self.modulestore_options
        )
        self.addCleanup(self.tear_down_mongo)
        self.old_course_key = None
        self.runtime = None
        self._create_course()

    def tear_down_split(self):
        """
        Remove the test collections, close the db connection
        """
        split_db = self.split_mongo.db
        split_db.drop_collection(split_db.course_index)
        split_db.drop_collection(split_db.structures)
        split_db.drop_collection(split_db.definitions)

    def tear_down_mongo(self):
        """
        Remove the test collections, close the db connection
        """
        split_db = self.split_mongo.db
        # old_mongo doesn't give a db attr, but all of the dbs are the same
        split_db.drop_collection(self.old_mongo.collection)

    def _create_item(self, category, name, data, metadata, parent_category, parent_name, draft=True, split=True):
        """
        Create the item of the given category and block id in split and old mongo, add it to the optional
        parent. The parent category is only needed because old mongo requires it for the id.
        """
        location = self.old_course_key.make_usage_key(category, name)
        if not draft or category in DIRECT_ONLY_CATEGORIES:
            mongo = self.old_mongo
        else:
            mongo = self.draft_mongo
        mongo.create_and_save_xmodule(location, self.userid, definition_data=data, metadata=metadata, runtime=self.runtime)
        if isinstance(data, basestring):
            fields = {'data': data}
        else:
            fields = data.copy()
        fields.update(metadata)
        if parent_name:
            # add child to parent in mongo
            parent_location = self.old_course_key.make_usage_key(parent_category, parent_name)
            if not draft or parent_category in DIRECT_ONLY_CATEGORIES:
                mongo = self.old_mongo
            else:
                mongo = self.draft_mongo
            parent = mongo.get_item(parent_location)
            parent.children.append(location)
            mongo.update_item(parent, self.userid)
            # create pointer for split
            course_or_parent_locator = BlockUsageLocator(
                course_key=self.split_course_key,
                block_type=parent_category,
                block_id=parent_name
            )
        else:
            course_or_parent_locator = self.split_course_key
        if split:
            self.split_mongo.create_item(course_or_parent_locator, category, self.userid, block_id=name, fields=fields)

    def _create_course(self, split=True):
        """
        * some detached items
        * some attached children
        * some orphans
        """
        metadata = {
            'start': datetime.datetime(2000, 3, 13, 4),
            'display_name': 'Migration test course',
        }
        data = {
            'wiki_slug': 'test_course_slug'
        }
        fields = metadata.copy()
        fields.update(data)
        if split:
            # split requires the course to be created separately from creating items
            self.split_mongo.create_course(
                self.split_course_key.org, self.split_course_key.offering, self.userid, fields=fields, root_block_id='runid'
            )
        old_course = self.old_mongo.create_course(self.split_course_key.org, 'test_course/runid', fields=fields)
        self.old_course_key = old_course.id
        self.runtime = old_course.runtime
Example #14
0
 def test_mongo_modulestore_type(self):
     store = MongoModuleStore(
         {"host": HOST, "db": DB, "collection": COLLECTION}, FS_ROOT, RENDER_TEMPLATE, default_class=DEFAULT_CLASS
     )
     assert_equals(store.get_modulestore_type("foo/bar/baz"), MONGO_MODULESTORE_TYPE)
Example #15
0
class SplitWMongoCourseBoostrapper(unittest.TestCase):
    """
    Helper for tests which need to construct split mongo & old mongo based courses to get interesting internal structure.
    Override _create_course and after invoking the super() _create_course, have it call _create_item for
    each xblock you want in the course.
    This class ensures the db gets created, opened, and cleaned up in addition to creating the course

    Defines the following attrs on self:
    * userid: a random non-registered mock user id
    * split_mongo: a pointer to the split mongo instance
    * old_mongo: a pointer to the old_mongo instance
    * draft_mongo: a pointer to the old draft instance
    * split_course_key (CourseLocator): of the new course
    * old_course_key: the SlashSpecifiedCourseKey for the course
    """
    # Snippet of what would be in the django settings envs file
    db_config = {
        'host': 'localhost',
        'db': 'test_xmodule',
    }

    modulestore_options = {
        'default_class': 'xmodule.raw_module.RawDescriptor',
        'fs_root': '',
        'render_template': mock.Mock(return_value=""),
        'xblock_mixins': (InheritanceMixin, )
    }

    split_course_key = CourseLocator('test_org',
                                     'test_course.runid',
                                     branch='draft')

    def setUp(self):
        self.db_config['collection'] = 'modulestore{0}'.format(
            uuid.uuid4().hex[:5])

        self.userid = random.getrandbits(32)
        super(SplitWMongoCourseBoostrapper, self).setUp()
        self.split_mongo = SplitMongoModuleStore(self.db_config,
                                                 **self.modulestore_options)
        self.addCleanup(self.split_mongo.db.connection.close)
        self.addCleanup(self.tear_down_split)
        self.old_mongo = MongoModuleStore(self.db_config,
                                          **self.modulestore_options)
        self.draft_mongo = DraftMongoModuleStore(self.db_config,
                                                 **self.modulestore_options)
        self.addCleanup(self.tear_down_mongo)
        self.old_course_key = None
        self.runtime = None
        self._create_course()

    def tear_down_split(self):
        """
        Remove the test collections, close the db connection
        """
        split_db = self.split_mongo.db
        split_db.drop_collection(split_db.course_index)
        split_db.drop_collection(split_db.structures)
        split_db.drop_collection(split_db.definitions)

    def tear_down_mongo(self):
        """
        Remove the test collections, close the db connection
        """
        split_db = self.split_mongo.db
        # old_mongo doesn't give a db attr, but all of the dbs are the same
        split_db.drop_collection(self.old_mongo.collection)

    def _create_item(self,
                     category,
                     name,
                     data,
                     metadata,
                     parent_category,
                     parent_name,
                     draft=True,
                     split=True):
        """
        Create the item of the given category and block id in split and old mongo, add it to the optional
        parent. The parent category is only needed because old mongo requires it for the id.
        """
        location = self.old_course_key.make_usage_key(category, name)
        if not draft or category in DIRECT_ONLY_CATEGORIES:
            mongo = self.old_mongo
        else:
            mongo = self.draft_mongo
        mongo.create_and_save_xmodule(location, data, metadata, self.runtime)
        if isinstance(data, basestring):
            fields = {'data': data}
        else:
            fields = data.copy()
        fields.update(metadata)
        if parent_name:
            # add child to parent in mongo
            parent_location = self.old_course_key.make_usage_key(
                parent_category, parent_name)
            if not draft or parent_category in DIRECT_ONLY_CATEGORIES:
                mongo = self.old_mongo
            else:
                mongo = self.draft_mongo
            parent = mongo.get_item(parent_location)
            parent.children.append(location)
            mongo.update_item(parent, self.userid)
            # create pointer for split
            course_or_parent_locator = BlockUsageLocator(
                course_key=self.split_course_key,
                block_type=parent_category,
                block_id=parent_name)
        else:
            course_or_parent_locator = self.split_course_key
        if split:
            self.split_mongo.create_item(course_or_parent_locator,
                                         category,
                                         self.userid,
                                         block_id=name,
                                         fields=fields)

    def _create_course(self, split=True):
        """
        * some detached items
        * some attached children
        * some orphans
        """
        metadata = {
            'start': datetime.datetime(2000, 3, 13, 4),
            'display_name': 'Migration test course',
        }
        data = {'wiki_slug': 'test_course_slug'}
        fields = metadata.copy()
        fields.update(data)
        if split:
            # split requires the course to be created separately from creating items
            self.split_mongo.create_course(self.split_course_key.org,
                                           self.split_course_key.offering,
                                           self.userid,
                                           fields=fields,
                                           root_block_id='runid')
        old_course = self.old_mongo.create_course(self.split_course_key.org,
                                                  'test_course/runid',
                                                  fields=fields)
        self.old_course_key = old_course.id
        self.runtime = old_course.runtime
Example #16
0
class TestOrphan(unittest.TestCase):
    """
    Test the orphan finding code
    """

    # Snippet of what would be in the django settings envs file
    db_config = {
        'host': 'localhost',
        'db': 'test_xmodule',
    }

    modulestore_options = {
        'default_class': 'xmodule.raw_module.RawDescriptor',
        'fs_root': '',
        'render_template': mock.Mock(return_value=""),
        'xblock_mixins': (InheritanceMixin, )
    }

    split_package_id = 'test_org.test_course.runid'

    def setUp(self):
        self.db_config['collection'] = 'modulestore{0}'.format(
            uuid.uuid4().hex[:5])

        self.userid = random.getrandbits(32)
        super(TestOrphan, self).setUp()
        self.split_mongo = SplitMongoModuleStore(self.db_config,
                                                 **self.modulestore_options)
        self.addCleanup(self.tear_down_split)
        self.old_mongo = MongoModuleStore(self.db_config,
                                          **self.modulestore_options)
        self.addCleanup(self.tear_down_mongo)
        self.course_location = None
        self._create_course()

    def tear_down_split(self):
        """
        Remove the test collections, close the db connection
        """
        split_db = self.split_mongo.db
        split_db.drop_collection(split_db.course_index)
        split_db.drop_collection(split_db.structures)
        split_db.drop_collection(split_db.definitions)
        split_db.connection.close()

    def tear_down_mongo(self):
        """
        Remove the test collections, close the db connection
        """
        split_db = self.split_mongo.db
        # old_mongo doesn't give a db attr, but all of the dbs are the same
        split_db.drop_collection(self.old_mongo.collection)

    def _create_item(self, category, name, data, metadata, parent_category,
                     parent_name, runtime):
        """
        Create the item of the given category and block id in split and old mongo, add it to the optional
        parent. The parent category is only needed because old mongo requires it for the id.
        """
        location = Location('i4x', 'test_org', 'test_course', category, name)
        self.old_mongo.create_and_save_xmodule(location, data, metadata,
                                               runtime)
        if isinstance(data, basestring):
            fields = {'data': data}
        else:
            fields = data.copy()
        fields.update(metadata)
        if parent_name:
            # add child to parent in mongo
            parent_location = Location('i4x', 'test_org', 'test_course',
                                       parent_category, parent_name)
            parent = self.old_mongo.get_item(parent_location)
            parent.children.append(location.url())
            self.old_mongo.update_item(parent, self.userid)
            # create pointer for split
            course_or_parent_locator = BlockUsageLocator(
                package_id=self.split_package_id,
                branch='draft',
                block_id=parent_name)
        else:
            course_or_parent_locator = CourseLocator(
                package_id='test_org.test_course.runid',
                branch='draft',
            )
        self.split_mongo.create_item(course_or_parent_locator,
                                     category,
                                     self.userid,
                                     block_id=name,
                                     fields=fields)

    def _create_course(self):
        """
        * some detached items
        * some attached children
        * some orphans
        """
        date_proxy = Date()
        metadata = {
            'start': date_proxy.to_json(datetime.datetime(2000, 3, 13, 4)),
            'display_name': 'Migration test course',
        }
        data = {'wiki_slug': 'test_course_slug'}
        fields = metadata.copy()
        fields.update(data)
        # split requires the course to be created separately from creating items
        self.split_mongo.create_course(self.split_package_id,
                                       'test_org',
                                       self.userid,
                                       fields=fields,
                                       root_block_id='runid')
        self.course_location = Location('i4x', 'test_org', 'test_course',
                                        'course', 'runid')
        self.old_mongo.create_and_save_xmodule(self.course_location, data,
                                               metadata)
        runtime = self.old_mongo.get_item(self.course_location).runtime

        self._create_item('chapter', 'Chapter1', {},
                          {'display_name': 'Chapter 1'}, 'course', 'runid',
                          runtime)
        self._create_item('chapter', 'Chapter2', {},
                          {'display_name': 'Chapter 2'}, 'course', 'runid',
                          runtime)
        self._create_item('chapter', 'OrphanChapter', {},
                          {'display_name': 'Orphan Chapter'}, None, None,
                          runtime)
        self._create_item('vertical', 'Vert1', {},
                          {'display_name': 'Vertical 1'}, 'chapter',
                          'Chapter1', runtime)
        self._create_item('vertical', 'OrphanVert', {},
                          {'display_name': 'Orphan Vertical'}, None, None,
                          runtime)
        self._create_item('html', 'Html1', "<p>Goodbye</p>",
                          {'display_name': 'Parented Html'}, 'vertical',
                          'Vert1', runtime)
        self._create_item('html', 'OrphanHtml', "<p>Hello</p>",
                          {'display_name': 'Orphan html'}, None, None, runtime)
        self._create_item('static_tab', 'staticuno', "<p>tab</p>",
                          {'display_name': 'Tab uno'}, None, None, runtime)
        self._create_item('about', 'overview', "<p>overview</p>", {}, None,
                          None, runtime)
        self._create_item('course_info', 'updates',
                          "<ol><li><h2>Sep 22</h2><p>test</p></li></ol>", {},
                          None, None, runtime)

    def test_mongo_orphan(self):
        """
        Test that old mongo finds the orphans
        """
        orphans = self.old_mongo.get_orphans(self.course_location, None)
        self.assertEqual(len(orphans), 3, "Wrong # {}".format(orphans))
        location = self.course_location.replace(category='chapter',
                                                name='OrphanChapter')
        self.assertIn(location.url(), orphans)
        location = self.course_location.replace(category='vertical',
                                                name='OrphanVert')
        self.assertIn(location.url(), orphans)
        location = self.course_location.replace(category='html',
                                                name='OrphanHtml')
        self.assertIn(location.url(), orphans)

    def test_split_orphan(self):
        """
        Test that old mongo finds the orphans
        """
        orphans = self.split_mongo.get_orphans(self.split_package_id, 'draft')
        self.assertEqual(len(orphans), 3, "Wrong # {}".format(orphans))
        location = BlockUsageLocator(package_id=self.split_package_id,
                                     branch='draft',
                                     block_id='OrphanChapter')
        self.assertIn(location, orphans)
        location = BlockUsageLocator(package_id=self.split_package_id,
                                     branch='draft',
                                     block_id='OrphanVert')
        self.assertIn(location, orphans)
        location = BlockUsageLocator(package_id=self.split_package_id,
                                     branch='draft',
                                     block_id='OrphanHtml')
        self.assertIn(location, orphans)
Example #17
0
 def test_mongo_modulestore_type(self):
     store = MongoModuleStore(
         {'host': HOST, 'db': DB, 'collection': COLLECTION},
         FS_ROOT, RENDER_TEMPLATE, default_class=DEFAULT_CLASS
     )
     assert_equals(store.get_modulestore_type('foo/bar/baz'), MONGO_MODULESTORE_TYPE)
class TestOrphan(unittest.TestCase):
    """
    Test the orphan finding code
    """

    # Snippet of what would be in the django settings envs file
    db_config = {
        'host': 'localhost',
        'db': 'test_xmodule',
    }

    modulestore_options = {
        'default_class': 'xmodule.raw_module.RawDescriptor',
        'fs_root': '',
        'render_template': mock.Mock(return_value=""),
        'xblock_mixins': (InheritanceMixin,)
    }

    split_package_id = 'test_org.test_course.runid'

    def setUp(self):
        self.db_config['collection'] = 'modulestore{0}'.format(uuid.uuid4().hex[:5])

        self.userid = random.getrandbits(32)
        super(TestOrphan, self).setUp()
        self.split_mongo = SplitMongoModuleStore(
            self.db_config,
            **self.modulestore_options
        )
        self.addCleanup(self.tear_down_split)
        self.old_mongo = MongoModuleStore(self.db_config, **self.modulestore_options)
        self.addCleanup(self.tear_down_mongo)
        self.course_location = None
        self._create_course()

    def tear_down_split(self):
        """
        Remove the test collections, close the db connection
        """
        split_db = self.split_mongo.db
        split_db.drop_collection(split_db.course_index)
        split_db.drop_collection(split_db.structures)
        split_db.drop_collection(split_db.definitions)
        split_db.connection.close()

    def tear_down_mongo(self):
        """
        Remove the test collections, close the db connection
        """
        split_db = self.split_mongo.db
        # old_mongo doesn't give a db attr, but all of the dbs are the same
        split_db.drop_collection(self.old_mongo.collection)

    def _create_item(self, category, name, data, metadata, parent_category, parent_name, runtime):
        """
        Create the item of the given category and block id in split and old mongo, add it to the optional
        parent. The parent category is only needed because old mongo requires it for the id.
        """
        location = Location('i4x', 'test_org', 'test_course', category, name)
        self.old_mongo.create_and_save_xmodule(location, data, metadata, runtime)
        if isinstance(data, basestring):
            fields = {'data': data}
        else:
            fields = data.copy()
        fields.update(metadata)
        if parent_name:
            # add child to parent in mongo
            parent_location = Location('i4x', 'test_org', 'test_course', parent_category, parent_name)
            parent = self.old_mongo.get_item(parent_location)
            parent.children.append(location.url())
            self.old_mongo.update_item(parent, self.userid)
            # create pointer for split
            course_or_parent_locator = BlockUsageLocator(
                package_id=self.split_package_id,
                branch='draft',
                block_id=parent_name
            )
        else:
            course_or_parent_locator = CourseLocator(
                package_id='test_org.test_course.runid',
                branch='draft',
            )
        self.split_mongo.create_item(course_or_parent_locator, category, self.userid, block_id=name, fields=fields)

    def _create_course(self):
        """
        * some detached items
        * some attached children
        * some orphans
        """
        date_proxy = Date()
        metadata = {
            'start': date_proxy.to_json(datetime.datetime(2000, 3, 13, 4)),
            'display_name': 'Migration test course',
        }
        data = {
            'wiki_slug': 'test_course_slug'
        }
        fields = metadata.copy()
        fields.update(data)
        # split requires the course to be created separately from creating items
        self.split_mongo.create_course(
            self.split_package_id, 'test_org', self.userid, fields=fields, root_block_id='runid'
        )
        self.course_location = Location('i4x', 'test_org', 'test_course', 'course', 'runid')
        self.old_mongo.create_and_save_xmodule(self.course_location, data, metadata)
        runtime = self.old_mongo.get_item(self.course_location).runtime

        self._create_item('chapter', 'Chapter1', {}, {'display_name': 'Chapter 1'}, 'course', 'runid', runtime)
        self._create_item('chapter', 'Chapter2', {}, {'display_name': 'Chapter 2'}, 'course', 'runid', runtime)
        self._create_item('chapter', 'OrphanChapter', {}, {'display_name': 'Orphan Chapter'}, None, None, runtime)
        self._create_item('vertical', 'Vert1', {}, {'display_name': 'Vertical 1'}, 'chapter', 'Chapter1', runtime)
        self._create_item('vertical', 'OrphanVert', {}, {'display_name': 'Orphan Vertical'}, None, None, runtime)
        self._create_item('html', 'Html1', "<p>Goodbye</p>", {'display_name': 'Parented Html'}, 'vertical', 'Vert1', runtime)
        self._create_item('html', 'OrphanHtml', "<p>Hello</p>", {'display_name': 'Orphan html'}, None, None, runtime)
        self._create_item('static_tab', 'staticuno', "<p>tab</p>", {'display_name': 'Tab uno'}, None, None, runtime)
        self._create_item('about', 'overview', "<p>overview</p>", {}, None, None, runtime)
        self._create_item('course_info', 'updates', "<ol><li><h2>Sep 22</h2><p>test</p></li></ol>", {}, None, None, runtime)

    def test_mongo_orphan(self):
        """
        Test that old mongo finds the orphans
        """
        orphans = self.old_mongo.get_orphans(self.course_location, None)
        self.assertEqual(len(orphans), 3, "Wrong # {}".format(orphans))
        location = self.course_location.replace(category='chapter', name='OrphanChapter')
        self.assertIn(location.url(), orphans)
        location = self.course_location.replace(category='vertical', name='OrphanVert')
        self.assertIn(location.url(), orphans)
        location = self.course_location.replace(category='html', name='OrphanHtml')
        self.assertIn(location.url(), orphans)

    def test_split_orphan(self):
        """
        Test that old mongo finds the orphans
        """
        orphans = self.split_mongo.get_orphans(self.split_package_id, 'draft')
        self.assertEqual(len(orphans), 3, "Wrong # {}".format(orphans))
        location = BlockUsageLocator(package_id=self.split_package_id, branch='draft', block_id='OrphanChapter')
        self.assertIn(location, orphans)
        location = BlockUsageLocator(package_id=self.split_package_id, branch='draft', block_id='OrphanVert')
        self.assertIn(location, orphans)
        location = BlockUsageLocator(package_id=self.split_package_id, branch='draft', block_id='OrphanHtml')
        self.assertIn(location, orphans)