260 lines
11 KiB
Python
260 lines
11 KiB
Python
import json
|
|
from mock import Mock
|
|
from functools import partial
|
|
|
|
from courseware.model_data import LmsKeyValueStore, InvalidWriteError
|
|
from courseware.model_data import InvalidScopeError, ModelDataCache
|
|
from courseware.models import StudentModule, XModuleContentField, XModuleSettingsField
|
|
from courseware.models import XModuleStudentInfoField, XModuleStudentPrefsField
|
|
|
|
from student.tests.factories import UserFactory
|
|
from courseware.tests.factories import StudentModuleFactory as cmfStudentModuleFactory
|
|
from courseware.tests.factories import ContentFactory, SettingsFactory
|
|
from courseware.tests.factories import StudentPrefsFactory, StudentInfoFactory
|
|
|
|
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'))
|
|
user_state_key = partial(LmsKeyValueStore.Key, Scope.user_state, 'user', location('def_id'))
|
|
prefs_key = partial(LmsKeyValueStore.Key, Scope.preferences, 'user', 'problem')
|
|
user_info_key = partial(LmsKeyValueStore.Key, Scope.user_info, 'user', None)
|
|
|
|
|
|
class StudentModuleFactory(cmfStudentModuleFactory):
|
|
module_state_key = location('def_id').url()
|
|
course_id = course_id
|
|
|
|
|
|
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(username='user')
|
|
self.mdc = ModelDataCache([mock_descriptor([mock_field(Scope.user_state, 'a_field')])], course_id, self.user)
|
|
self.kvs = LmsKeyValueStore(self.desc_md, self.mdc)
|
|
|
|
def test_invalid_scopes(self):
|
|
for scope in (Scope(user=True, block=BlockScope.DEFINITION),
|
|
Scope(user=False, block=BlockScope.TYPE),
|
|
Scope(user=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.user_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(user_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, user_state_key('not_a_field'))
|
|
|
|
def test_set_existing_field(self):
|
|
"Test that setting an existing user_state field changes the value"
|
|
self.kvs.set(user_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 user_state field changes the value"
|
|
self.kvs.set(user_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(user_state_key('a_field'))
|
|
self.assertEquals(1, StudentModule.objects.all().count())
|
|
self.assertRaises(KeyError, self.kvs.get, user_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, user_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(user_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(user_state_key('not_a_field')))
|
|
|
|
|
|
class TestMissingStudentModule(TestCase):
|
|
def setUp(self):
|
|
self.user = UserFactory.create(username='user')
|
|
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, user_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(user_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, user_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(user_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.preferences
|
|
key_factory = prefs_key
|
|
storage_class = XModuleStudentPrefsField
|
|
|
|
|
|
class TestStudentInfoStorage(StorageTestBase, TestCase):
|
|
factory = StudentInfoFactory
|
|
scope = Scope.user_info
|
|
key_factory = user_info_key
|
|
storage_class = XModuleStudentInfoField
|