Files
edx-platform/lms/djangoapps/courseware/tests/test_model_data.py

299 lines
12 KiB
Python

import factory
import json
from mock import Mock
from django.contrib.auth.models import User
from functools import partial
from courseware.model_data import LmsKeyValueStore, InvalidWriteError, InvalidScopeError, ModelDataCache
from courseware.models import StudentModule, XModuleContentField, XModuleSettingsField, XModuleStudentInfoField, XModuleStudentPrefsField
from xblock.core import Scope, BlockScope
from xmodule.modulestore import Location
from django.test import TestCase
def mock_field(scope, name):
field = Mock()
field.scope = scope
field.name = name
return field
def mock_descriptor(fields=[], lms_fields=[]):
descriptor = Mock()
descriptor.stores_state = True
descriptor.location = location('def_id')
descriptor.module_class.fields = fields
descriptor.module_class.lms.fields = lms_fields
return descriptor
location = partial(Location, 'i4x', 'edX', 'test_course', 'problem')
course_id = 'edX/test_course/test'
content_key = partial(LmsKeyValueStore.Key, Scope.content, None, location('def_id'))
settings_key = partial(LmsKeyValueStore.Key, Scope.settings, None, location('def_id'))
student_state_key = partial(LmsKeyValueStore.Key, Scope.student_state, 'user', location('def_id'))
student_prefs_key = partial(LmsKeyValueStore.Key, Scope.student_preferences, 'user', 'problem')
student_info_key = partial(LmsKeyValueStore.Key, Scope.student_info, 'user', None)
class UserFactory(factory.Factory):
FACTORY_FOR = User
username = 'user'
class StudentModuleFactory(factory.Factory):
FACTORY_FOR = StudentModule
module_type = 'problem'
module_state_key = location('def_id').url()
student = factory.SubFactory(UserFactory)
course_id = course_id
state = None
class ContentFactory(factory.Factory):
FACTORY_FOR = XModuleContentField
field_name = 'existing_field'
value = json.dumps('old_value')
definition_id = location('def_id').url()
class SettingsFactory(factory.Factory):
FACTORY_FOR = XModuleSettingsField
field_name = 'existing_field'
value = json.dumps('old_value')
usage_id = '%s-%s' % (course_id, location('def_id').url())
class StudentPrefsFactory(factory.Factory):
FACTORY_FOR = XModuleStudentPrefsField
field_name = 'existing_field'
value = json.dumps('old_value')
student = factory.SubFactory(UserFactory)
module_type = 'problem'
class StudentInfoFactory(factory.Factory):
FACTORY_FOR = XModuleStudentInfoField
field_name = 'existing_field'
value = json.dumps('old_value')
student = factory.SubFactory(UserFactory)
class TestDescriptorFallback(TestCase):
def setUp(self):
self.desc_md = {
'field_a': 'content',
'field_b': 'settings',
}
self.kvs = LmsKeyValueStore(self.desc_md, None)
def test_get_from_descriptor(self):
self.assertEquals('content', self.kvs.get(content_key('field_a')))
self.assertEquals('settings', self.kvs.get(settings_key('field_b')))
def test_write_to_descriptor(self):
self.assertRaises(InvalidWriteError, self.kvs.set, content_key('field_a'), 'foo')
self.assertEquals('content', self.desc_md['field_a'])
self.assertRaises(InvalidWriteError, self.kvs.set, settings_key('field_b'), 'foo')
self.assertEquals('settings', self.desc_md['field_b'])
self.assertRaises(InvalidWriteError, self.kvs.delete, content_key('field_a'))
self.assertEquals('content', self.desc_md['field_a'])
self.assertRaises(InvalidWriteError, self.kvs.delete, settings_key('field_b'))
self.assertEquals('settings', self.desc_md['field_b'])
class TestInvalidScopes(TestCase):
def setUp(self):
self.desc_md = {}
self.user = UserFactory.create()
self.mdc = ModelDataCache([mock_descriptor([mock_field(Scope.student_state, 'a_field')])], course_id, self.user)
self.kvs = LmsKeyValueStore(self.desc_md, self.mdc)
def test_invalid_scopes(self):
for scope in (Scope(student=True, block=BlockScope.DEFINITION),
Scope(student=False, block=BlockScope.TYPE),
Scope(student=False, block=BlockScope.ALL)):
self.assertRaises(InvalidScopeError, self.kvs.get, LmsKeyValueStore.Key(scope, None, None, 'field'))
self.assertRaises(InvalidScopeError, self.kvs.set, LmsKeyValueStore.Key(scope, None, None, 'field'), 'value')
self.assertRaises(InvalidScopeError, self.kvs.delete, LmsKeyValueStore.Key(scope, None, None, 'field'))
self.assertRaises(InvalidScopeError, self.kvs.has, LmsKeyValueStore.Key(scope, None, None, 'field'))
class TestStudentModuleStorage(TestCase):
def setUp(self):
self.desc_md = {}
student_module = StudentModuleFactory(state=json.dumps({'a_field': 'a_value'}))
self.user = student_module.student
self.mdc = ModelDataCache([mock_descriptor([mock_field(Scope.student_state, 'a_field')])], course_id, self.user)
self.kvs = LmsKeyValueStore(self.desc_md, self.mdc)
def test_get_existing_field(self):
"Test that getting an existing field in an existing StudentModule works"
self.assertEquals('a_value', self.kvs.get(student_state_key('a_field')))
def test_get_missing_field(self):
"Test that getting a missing field from an existing StudentModule raises a KeyError"
self.assertRaises(KeyError, self.kvs.get, student_state_key('not_a_field'))
def test_set_existing_field(self):
"Test that setting an existing student_state field changes the value"
self.kvs.set(student_state_key('a_field'), 'new_value')
self.assertEquals(1, StudentModule.objects.all().count())
self.assertEquals({'a_field': 'new_value'}, json.loads(StudentModule.objects.all()[0].state))
def test_set_missing_field(self):
"Test that setting a new student_state field changes the value"
self.kvs.set(student_state_key('not_a_field'), 'new_value')
self.assertEquals(1, StudentModule.objects.all().count())
self.assertEquals({'a_field': 'a_value', 'not_a_field': 'new_value'}, json.loads(StudentModule.objects.all()[0].state))
def test_delete_existing_field(self):
"Test that deleting an existing field removes it from the StudentModule"
self.kvs.delete(student_state_key('a_field'))
self.assertEquals(1, StudentModule.objects.all().count())
self.assertRaises(KeyError, self.kvs.get, student_state_key('not_a_field'))
def test_delete_missing_field(self):
"Test that deleting a missing field from an existing StudentModule raises a KeyError"
self.assertRaises(KeyError, self.kvs.delete, student_state_key('not_a_field'))
self.assertEquals(1, StudentModule.objects.all().count())
self.assertEquals({'a_field': 'a_value'}, json.loads(StudentModule.objects.all()[0].state))
def test_has_existing_field(self):
"Test that `has` returns True for existing fields in StudentModules"
self.assertTrue(self.kvs.has(student_state_key('a_field')))
def test_has_missing_field(self):
"Test that `has` returns False for missing fields in StudentModule"
self.assertFalse(self.kvs.has(student_state_key('not_a_field')))
class TestMissingStudentModule(TestCase):
def setUp(self):
self.user = UserFactory.create()
self.desc_md = {}
self.mdc = ModelDataCache([mock_descriptor()], course_id, self.user)
self.kvs = LmsKeyValueStore(self.desc_md, self.mdc)
def test_get_field_from_missing_student_module(self):
"Test that getting a field from a missing StudentModule raises a KeyError"
self.assertRaises(KeyError, self.kvs.get, student_state_key('a_field'))
def test_set_field_in_missing_student_module(self):
"Test that setting a field in a missing StudentModule creates the student module"
self.assertEquals(0, len(self.mdc.cache))
self.assertEquals(0, StudentModule.objects.all().count())
self.kvs.set(student_state_key('a_field'), 'a_value')
self.assertEquals(1, len(self.mdc.cache))
self.assertEquals(1, StudentModule.objects.all().count())
student_module = StudentModule.objects.all()[0]
self.assertEquals({'a_field': 'a_value'}, json.loads(student_module.state))
self.assertEquals(self.user, student_module.student)
self.assertEquals(location('def_id').url(), student_module.module_state_key)
self.assertEquals(course_id, student_module.course_id)
def test_delete_field_from_missing_student_module(self):
"Test that deleting a field from a missing StudentModule raises a KeyError"
self.assertRaises(KeyError, self.kvs.delete, student_state_key('a_field'))
def test_has_field_for_missing_student_module(self):
"Test that `has` returns False for missing StudentModules"
self.assertFalse(self.kvs.has(student_state_key('a_field')))
class StorageTestBase(object):
factory = None
scope = None
key_factory = None
storage_class = None
def setUp(self):
field_storage = self.factory.create()
if hasattr(field_storage, 'student'):
self.user = field_storage.student
else:
self.user = UserFactory.create()
self.desc_md = {}
self.mdc = ModelDataCache([mock_descriptor([mock_field(self.scope, 'existing_field')])], course_id, self.user)
self.kvs = LmsKeyValueStore(self.desc_md, self.mdc)
def test_get_existing_field(self):
"Test that getting an existing field in an existing Storage Field works"
self.assertEquals('old_value', self.kvs.get(self.key_factory('existing_field')))
def test_get_missing_field(self):
"Test that getting a missing field from an existing Storage Field raises a KeyError"
self.assertRaises(KeyError, self.kvs.get, self.key_factory('missing_field'))
def test_set_existing_field(self):
"Test that setting an existing field changes the value"
self.kvs.set(self.key_factory('existing_field'), 'new_value')
self.assertEquals(1, self.storage_class.objects.all().count())
self.assertEquals('new_value', json.loads(self.storage_class.objects.all()[0].value))
def test_set_missing_field(self):
"Test that setting a new field changes the value"
self.kvs.set(self.key_factory('missing_field'), 'new_value')
self.assertEquals(2, self.storage_class.objects.all().count())
self.assertEquals('old_value', json.loads(self.storage_class.objects.get(field_name='existing_field').value))
self.assertEquals('new_value', json.loads(self.storage_class.objects.get(field_name='missing_field').value))
def test_delete_existing_field(self):
"Test that deleting an existing field removes it"
self.kvs.delete(self.key_factory('existing_field'))
self.assertEquals(0, self.storage_class.objects.all().count())
def test_delete_missing_field(self):
"Test that deleting a missing field from an existing Storage Field raises a KeyError"
self.assertRaises(KeyError, self.kvs.delete, self.key_factory('missing_field'))
self.assertEquals(1, self.storage_class.objects.all().count())
def test_has_existing_field(self):
"Test that `has` returns True for an existing Storage Field"
self.assertTrue(self.kvs.has(self.key_factory('existing_field')))
def test_has_missing_field(self):
"Test that `has` return False for an existing Storage Field"
self.assertFalse(self.kvs.has(self.key_factory('missing_field')))
class TestSettingsStorage(StorageTestBase, TestCase):
factory = SettingsFactory
scope = Scope.settings
key_factory = settings_key
storage_class = XModuleSettingsField
class TestContentStorage(StorageTestBase, TestCase):
factory = ContentFactory
scope = Scope.content
key_factory = content_key
storage_class = XModuleContentField
class TestStudentPrefsStorage(StorageTestBase, TestCase):
factory = StudentPrefsFactory
scope = Scope.student_preferences
key_factory = student_prefs_key
storage_class = XModuleStudentPrefsField
class TestStudentInfoStorage(StorageTestBase, TestCase):
factory = StudentInfoFactory
scope = Scope.student_info
key_factory = student_info_key
storage_class = XModuleStudentInfoField