test_crud.py 12.5 KB
Newer Older
1
import unittest
2

3
from xmodule import templates
4
from xmodule.modulestore import ModuleStoreEnum
5 6
from xmodule.modulestore.tests import persistent_factories
from xmodule.course_module import CourseDescriptor
7 8
from xmodule.modulestore.django import modulestore, clear_existing_modulestores, _MIXED_MODULESTORE, \
    loc_mapper, _loc_singleton
9 10
from xmodule.seq_module import SequenceDescriptor
from xmodule.capa_module import CapaDescriptor
11
from opaque_keys.edx.locator import BlockUsageLocator, LocalId
12
from xmodule.modulestore.exceptions import ItemNotFoundError, DuplicateCourseError
13
from xmodule.html_module import HtmlDescriptor
14
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
15 16 17 18 19 20 21


class TemplateTests(unittest.TestCase):
    """
    Test finding and using the templates (boilerplates) for xblocks.
    """

22
    def setUp(self):
23
        clear_existing_modulestores()  # redundant w/ cleanup but someone was getting errors
24
        self.addCleanup(ModuleStoreTestCase.drop_mongo_collections, ModuleStoreEnum.Type.split)
25
        self.addCleanup(clear_existing_modulestores)
26
        self.split_store = modulestore()._get_modulestore_by_type(ModuleStoreEnum.Type.split)
27

28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
    def test_get_templates(self):
        found = templates.all_templates()
        self.assertIsNotNone(found.get('course'))
        self.assertIsNotNone(found.get('about'))
        self.assertIsNotNone(found.get('html'))
        self.assertIsNotNone(found.get('problem'))
        self.assertEqual(len(found.get('course')), 0)
        self.assertEqual(len(found.get('about')), 1)
        self.assertGreaterEqual(len(found.get('html')), 2)
        self.assertGreaterEqual(len(found.get('problem')), 10)
        dropdown = None
        for template in found['problem']:
            self.assertIn('metadata', template)
            self.assertIn('display_name', template['metadata'])
            if template['metadata']['display_name'] == 'Dropdown':
                dropdown = template
                break
        self.assertIsNotNone(dropdown)
        self.assertIn('markdown', dropdown['metadata'])
        self.assertIn('data', dropdown)
        self.assertRegexpMatches(dropdown['metadata']['markdown'], r'^Dropdown.*')
        self.assertRegexpMatches(dropdown['data'], r'<problem>\s*<p>Dropdown.*')

    def test_get_some_templates(self):
        self.assertEqual(len(SequenceDescriptor.templates()), 0)
        self.assertGreater(len(HtmlDescriptor.templates()), 0)
        self.assertIsNone(SequenceDescriptor.get_template('doesntexist.yaml'))
        self.assertIsNone(HtmlDescriptor.get_template('doesntexist.yaml'))
        self.assertIsNotNone(HtmlDescriptor.get_template('announcement.yaml'))

    def test_factories(self):
59
        test_course = persistent_factories.PersistentCourseFactory.create(
60
            offering='tempcourse', org='testx',
61 62
            display_name='fun test course', user_id='testbot'
        )
63 64
        self.assertIsInstance(test_course, CourseDescriptor)
        self.assertEqual(test_course.display_name, 'fun test course')
65
        index_info = self.split_store.get_course_index_info(test_course.id)
66
        self.assertEqual(index_info['org'], 'testx')
67
        self.assertEqual(index_info['offering'], 'tempcourse')
68 69 70 71 72

        test_chapter = persistent_factories.ItemFactory.create(display_name='chapter 1',
            parent_location=test_course.location)
        self.assertIsInstance(test_chapter, SequenceDescriptor)
        # refetch parent which should now point to child
73
        test_course = self.split_store.get_course(test_course.id.version_agnostic())
74
        self.assertIn(test_chapter.location, test_course.children)
75

76 77
        with self.assertRaises(DuplicateCourseError):
            persistent_factories.PersistentCourseFactory.create(
78
                offering='tempcourse', org='testx',
79 80 81
                display_name='fun test course', user_id='testbot'
            )

82 83
    def test_temporary_xblocks(self):
        """
84
        Test create_xblock to create non persisted xblocks
85
        """
86
        test_course = persistent_factories.PersistentCourseFactory.create(
87
            offering='tempcourse', org='testx',
88 89
            display_name='fun test course', user_id='testbot'
        )
90

91
        test_chapter = self.split_store.create_xblock(
92 93
            test_course.system, 'chapter', {'display_name': 'chapter n'}, parent_xblock=test_course
        )
94 95 96 97 98 99
        self.assertIsInstance(test_chapter, SequenceDescriptor)
        self.assertEqual(test_chapter.display_name, 'chapter n')
        self.assertIn(test_chapter, test_course.get_children())

        # test w/ a definition (e.g., a problem)
        test_def_content = '<problem>boo</problem>'
100
        test_problem = self.split_store.create_xblock(
101 102
            test_course.system, 'problem', {'data': test_def_content}, parent_xblock=test_chapter
        )
103 104 105 106 107 108 109 110 111 112
        self.assertIsInstance(test_problem, CapaDescriptor)
        self.assertEqual(test_problem.data, test_def_content)
        self.assertIn(test_problem, test_chapter.get_children())
        test_problem.display_name = 'test problem'
        self.assertEqual(test_problem.display_name, 'test problem')

    def test_persist_dag(self):
        """
        try saving temporary xblocks
        """
Don Mitchell committed
113
        test_course = persistent_factories.PersistentCourseFactory.create(
114
            offering='tempcourse', org='testx',
115 116
            display_name='fun test course', user_id='testbot'
        )
117
        test_chapter = self.split_store.create_xblock(
118 119 120
            test_course.system, 'chapter', {'display_name': 'chapter n'}, parent_xblock=test_course
        )
        self.assertEqual(test_chapter.display_name, 'chapter n')
121
        test_def_content = '<problem>boo</problem>'
122
        # create child
123
        new_block = self.split_store.create_xblock(
124 125 126
            test_course.system,
            'problem',
            fields={
Don Mitchell committed
127 128
                'data': test_def_content,
                'display_name': 'problem'
129
            },
130 131 132 133
            parent_xblock=test_chapter
        )
        self.assertIsNotNone(new_block.definition_locator)
        self.assertTrue(isinstance(new_block.definition_locator.definition_id, LocalId))
134 135 136
        # better to pass in persisted parent over the subdag so
        # subdag gets the parent pointer (otherwise 2 ops, persist dag, update parent children,
        # persist parent
137
        persisted_course = self.split_store.persist_xblock_dag(test_course, 'testbot')
138 139 140 141 142 143 144 145
        self.assertEqual(len(persisted_course.children), 1)
        persisted_chapter = persisted_course.get_children()[0]
        self.assertEqual(persisted_chapter.category, 'chapter')
        self.assertEqual(persisted_chapter.display_name, 'chapter n')
        self.assertEqual(len(persisted_chapter.children), 1)
        persisted_problem = persisted_chapter.get_children()[0]
        self.assertEqual(persisted_problem.category, 'problem')
        self.assertEqual(persisted_problem.data, test_def_content)
Don Mitchell committed
146 147
        # update it
        persisted_problem.display_name = 'altered problem'
148
        persisted_problem = self.split_store.persist_xblock_dag(persisted_problem, 'testbot')
Don Mitchell committed
149
        self.assertEqual(persisted_problem.display_name, 'altered problem')
150 151 152

    def test_delete_course(self):
        test_course = persistent_factories.PersistentCourseFactory.create(
153
            offering='history.doomed', org='edu.harvard',
154 155 156 157 158
            display_name='doomed test course',
            user_id='testbot')
        persistent_factories.ItemFactory.create(display_name='chapter 1',
            parent_location=test_course.location)

159
        id_locator = test_course.id.for_branch(ModuleStoreEnum.BranchName.draft)
160 161
        guid_locator = test_course.location.course_agnostic()
        # verify it can be retrieved by id
162
        self.assertIsInstance(self.split_store.get_course(id_locator), CourseDescriptor)
163
        # and by guid
164 165
        self.assertIsInstance(self.split_store.get_item(guid_locator), CourseDescriptor)
        self.split_store.delete_course(id_locator)
166
        # test can no longer retrieve by id
167
        self.assertRaises(ItemNotFoundError, self.split_store.get_course, id_locator)
168
        # but can by guid
169
        self.assertIsInstance(self.split_store.get_item(guid_locator), CourseDescriptor)
170 171 172 173 174 175

    def test_block_generations(self):
        """
        Test get_block_generations
        """
        test_course = persistent_factories.PersistentCourseFactory.create(
176
            offering='history.hist101', org='edu.harvard',
177
            display_name='history test course',
178 179
            user_id='testbot'
        )
180 181 182 183
        chapter = persistent_factories.ItemFactory.create(display_name='chapter 1',
            parent_location=test_course.location, user_id='testbot')
        sub = persistent_factories.ItemFactory.create(display_name='subsection 1',
            parent_location=chapter.location, user_id='testbot', category='vertical')
184 185
        first_problem = persistent_factories.ItemFactory.create(
            display_name='problem 1', parent_location=sub.location, user_id='testbot', category='problem',
186
            data="<problem></problem>"
187
        )
188
        first_problem.max_attempts = 3
189
        first_problem.save()  # decache the above into the kvs
190
        updated_problem = self.split_store.update_item(first_problem, '**replace_user**')
191 192 193
        self.assertIsNotNone(updated_problem.previous_version)
        self.assertEqual(updated_problem.previous_version, first_problem.update_version)
        self.assertNotEqual(updated_problem.update_version, first_problem.update_version)
194
        updated_loc = self.split_store.delete_item(updated_problem.location, '**replace_user**', 'testbot')
195

196 197
        second_problem = persistent_factories.ItemFactory.create(
            display_name='problem 2',
198 199 200
            parent_location=BlockUsageLocator.make_relative(
                updated_loc, block_type='problem', block_id=sub.location.block_id
            ),
201
            user_id='testbot', category='problem',
202
            data="<problem></problem>"
203
        )
204 205

        # course root only updated 2x
206
        version_history = self.split_store.get_block_generations(test_course.location)
207 208 209 210 211 212
        self.assertEqual(version_history.locator.version_guid, test_course.location.version_guid)
        self.assertEqual(len(version_history.children), 1)
        self.assertEqual(version_history.children[0].children, [])
        self.assertEqual(version_history.children[0].locator.version_guid, chapter.location.version_guid)

        # sub changed on add, add problem, delete problem, add problem in strict linear seq
213
        version_history = self.split_store.get_block_generations(sub.location)
214 215 216 217 218 219
        self.assertEqual(len(version_history.children), 1)
        self.assertEqual(len(version_history.children[0].children), 1)
        self.assertEqual(len(version_history.children[0].children[0].children), 1)
        self.assertEqual(len(version_history.children[0].children[0].children[0].children), 0)

        # first and second problem may show as same usage_id; so, need to ensure their histories are right
220
        version_history = self.split_store.get_block_generations(updated_problem.location)
221 222 223 224
        self.assertEqual(version_history.locator.version_guid, first_problem.location.version_guid)
        self.assertEqual(len(version_history.children), 1)  # updated max_attempts
        self.assertEqual(len(version_history.children[0].children), 0)

225
        version_history = self.split_store.get_block_generations(second_problem.location)
226
        self.assertNotEqual(version_history.locator.version_guid, first_problem.location.version_guid)
227

228 229 230 231 232

class SplitAndLocMapperTests(unittest.TestCase):
    """
    Test injection of loc_mapper into Split
    """
233 234
    def test_split_inject_loc_mapper(self):
        """
235
        Test loc_mapper created before split
236
        """
237 238 239
        # ensure modulestore is not instantiated
        self.assertIsNone(_MIXED_MODULESTORE)

240 241
        # instantiate location mapper before split
        mapper = loc_mapper()
242 243

        # instantiate mixed modulestore and thus split
244
        split_store = modulestore()._get_modulestore_by_type(ModuleStoreEnum.Type.split)
245 246 247

        # split must inject the same location mapper object since the mapper existed before it did
        self.assertEqual(split_store.loc_mapper, mapper)
248 249 250

    def test_loc_inject_into_split(self):
        """
251
        Test split created before loc_mapper
252
        """
253 254
        # ensure loc_mapper is not instantiated
        self.assertIsNone(_loc_singleton)
255

256
        # instantiate split before location mapper
257
        split_store = modulestore()._get_modulestore_by_type(ModuleStoreEnum.Type.split)
258 259 260 261

        # split must have instantiated loc_mapper
        mapper = loc_mapper()
        self.assertEqual(split_store.loc_mapper, mapper)