test_runtime.py 11.1 KB
Newer Older
1 2 3 4
"""
Tests of the LMS XBlock Runtime and associated utilities
"""

5 6 7
from urlparse import urlparse

from ddt import data, ddt
8
from django.conf import settings
9 10 11
from django.test import TestCase
from mock import Mock, patch
from opaque_keys.edx.keys import CourseKey
12
from opaque_keys.edx.locations import BlockUsageLocator, CourseLocator
13 14
from xblock.exceptions import NoSuchServiceError
from xblock.fields import ScopeIds
15 16 17

from badges.tests.factories import BadgeClassFactory
from badges.tests.test_models import get_image
18
from lms.djangoapps.lms_xblock.runtime import LmsModuleSystem
19
from student.tests.factories import UserFactory
20 21
from xmodule.modulestore.django import ModuleI18nService
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
22
from xmodule.modulestore.tests.factories import CourseFactory
23

24

25 26 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
class BlockMock(Mock):
    """Mock class that we fill with our "handler" methods."""

    def handler(self, _context):
        """
        A test handler method.
        """
        pass

    def handler1(self, _context):
        """
        A test handler method.
        """
        pass

    def handler_a(self, _context):
        """
        A test handler method.
        """
        pass

    @property
    def location(self):
        """Create a functional BlockUsageLocator for testing URL generation."""
        course_key = CourseLocator(org="mockx", course="100", run="2015")
        return BlockUsageLocator(course_key, block_type='mock_type', block_id="mock_id")


53 54 55 56
class TestHandlerUrl(TestCase):
    """Test the LMS handler_url"""

    def setUp(self):
57
        super(TestHandlerUrl, self).setUp()
58
        self.block = BlockMock(name='block', scope_ids=ScopeIds(None, None, None, 'dummy'))
59
        self.course_key = CourseLocator("org", "course", "run")
60 61
        self.runtime = LmsModuleSystem(
            static_url='/static',
62 63 64
            track_function=Mock(),
            get_module=Mock(),
            render_template=Mock(),
65
            replace_urls=str,
66
            course_id=self.course_key,
67
            descriptor_runtime=Mock(),
68
        )
69 70

    def test_trailing_characters(self):
71 72
        self.assertFalse(self.runtime.handler_url(self.block, 'handler').endswith('?'))
        self.assertFalse(self.runtime.handler_url(self.block, 'handler').endswith('/'))
73

74 75
        self.assertFalse(self.runtime.handler_url(self.block, 'handler', 'suffix').endswith('?'))
        self.assertFalse(self.runtime.handler_url(self.block, 'handler', 'suffix').endswith('/'))
76

77 78
        self.assertFalse(self.runtime.handler_url(self.block, 'handler', 'suffix', 'query').endswith('?'))
        self.assertFalse(self.runtime.handler_url(self.block, 'handler', 'suffix', 'query').endswith('/'))
79

80 81
        self.assertFalse(self.runtime.handler_url(self.block, 'handler', query='query').endswith('?'))
        self.assertFalse(self.runtime.handler_url(self.block, 'handler', query='query').endswith('/'))
82 83 84

    def _parsed_query(self, query_string):
        """Return the parsed query string from a handler_url generated with the supplied query_string"""
85
        return urlparse(self.runtime.handler_url(self.block, 'handler', query=query_string)).query
86 87 88 89 90 91 92 93

    def test_query_string(self):
        self.assertIn('foo=bar', self._parsed_query('foo=bar'))
        self.assertIn('foo=bar&baz=true', self._parsed_query('foo=bar&baz=true'))
        self.assertIn('foo&bar&baz', self._parsed_query('foo&bar&baz'))

    def _parsed_path(self, handler_name='handler', suffix=''):
        """Return the parsed path from a handler_url with the supplied handler_name and suffix"""
94
        return urlparse(self.runtime.handler_url(self.block, handler_name, suffix=suffix)).path
95 96 97 98 99 100 101 102 103

    def test_suffix(self):
        self.assertTrue(self._parsed_path(suffix="foo").endswith('foo'))
        self.assertTrue(self._parsed_path(suffix="foo/bar").endswith('foo/bar'))
        self.assertTrue(self._parsed_path(suffix="/foo/bar").endswith('/foo/bar'))

    def test_handler_name(self):
        self.assertIn('handler1', self._parsed_path('handler1'))
        self.assertIn('handler_a', self._parsed_path('handler_a'))
104

105 106 107 108 109 110 111 112 113 114 115 116
    def test_thirdparty_fq(self):
        """Testing the Fully-Qualified URL returned by thirdparty=True"""
        parsed_fq_url = urlparse(self.runtime.handler_url(self.block, 'handler', thirdparty=True))
        self.assertEqual(parsed_fq_url.scheme, 'https')
        self.assertEqual(parsed_fq_url.hostname, settings.SITE_NAME)

    def test_not_thirdparty_rel(self):
        """Testing the Fully-Qualified URL returned by thirdparty=False"""
        parsed_fq_url = urlparse(self.runtime.handler_url(self.block, 'handler', thirdparty=False))
        self.assertEqual(parsed_fq_url.scheme, '')
        self.assertIsNone(parsed_fq_url.hostname)

117

118 119
class TestUserServiceAPI(TestCase):
    """Test the user service interface"""
120 121

    def setUp(self):
122
        super(TestUserServiceAPI, self).setUp()
123
        self.course_id = CourseLocator("org", "course", "run")
124
        self.user = UserFactory.create()
125 126

        def mock_get_real_user(_anon_id):
127
            """Just returns the test user"""
128 129 130 131
            return self.user

        self.runtime = LmsModuleSystem(
            static_url='/static',
132 133 134
            track_function=Mock(),
            get_module=Mock(),
            render_template=Mock(),
135 136 137
            replace_urls=str,
            course_id=self.course_id,
            get_real_user=mock_get_real_user,
138
            descriptor_runtime=Mock(),
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
        )
        self.scope = 'course'
        self.key = 'key1'

        self.mock_block = Mock()
        self.mock_block.service_declaration.return_value = 'needs'

    def test_get_set_tag(self):
        # test for when we haven't set the tag yet
        tag = self.runtime.service(self.mock_block, 'user_tags').get_tag(self.scope, self.key)
        self.assertIsNone(tag)

        # set the tag
        set_value = 'value'
        self.runtime.service(self.mock_block, 'user_tags').set_tag(self.scope, self.key, set_value)
        tag = self.runtime.service(self.mock_block, 'user_tags').get_tag(self.scope, self.key)

        self.assertEqual(tag, set_value)
157 158 159 160 161 162 163 164

        # Try to set tag in wrong scope
        with self.assertRaises(ValueError):
            self.runtime.service(self.mock_block, 'user_tags').set_tag('fake_scope', self.key, set_value)

        # Try to get tag in wrong scope
        with self.assertRaises(ValueError):
            self.runtime.service(self.mock_block, 'user_tags').get_tag('fake_scope', self.key)
165 166


Jonathan Piacenti committed
167
@ddt
168
class TestBadgingService(ModuleStoreTestCase):
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
    """Test the badging service interface"""

    def setUp(self):
        super(TestBadgingService, self).setUp()
        self.course_id = CourseKey.from_string('course-v1:org+course+run')

        self.mock_block = Mock()
        self.mock_block.service_declaration.return_value = 'needs'

    def create_runtime(self):
        """
        Create the testing runtime.
        """
        def mock_get_real_user(_anon_id):
            """Just returns the test user"""
            return self.user

        return LmsModuleSystem(
            static_url='/static',
            track_function=Mock(),
            get_module=Mock(),
            render_template=Mock(),
            replace_urls=str,
            course_id=self.course_id,
            get_real_user=mock_get_real_user,
            descriptor_runtime=Mock(),
        )

    @patch.dict(settings.FEATURES, {'ENABLE_OPENBADGES': True})
    def test_service_rendered(self):
        runtime = self.create_runtime()
        self.assertTrue(runtime.service(self.mock_block, 'badging'))

    @patch.dict(settings.FEATURES, {'ENABLE_OPENBADGES': False})
    def test_no_service_rendered(self):
        runtime = self.create_runtime()
        self.assertFalse(runtime.service(self.mock_block, 'badging'))

Jonathan Piacenti committed
207
    @data(True, False)
208
    @patch.dict(settings.FEATURES, {'ENABLE_OPENBADGES': True})
Jonathan Piacenti committed
209 210
    def test_course_badges_toggle(self, toggle):
        self.course_id = CourseFactory.create(metadata={'issue_badges': toggle}).location.course_key
211
        runtime = self.create_runtime()
Jonathan Piacenti committed
212
        self.assertIs(runtime.service(self.mock_block, 'badging').course_badges_enabled, toggle)
213 214

    @patch.dict(settings.FEATURES, {'ENABLE_OPENBADGES': True})
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
    def test_get_badge_class(self):
        runtime = self.create_runtime()
        badge_service = runtime.service(self.mock_block, 'badging')
        premade_badge_class = BadgeClassFactory.create()
        # Ignore additional parameters. This class already exists.
        # We should get back the first class we created, rather than a new one.
        badge_class = badge_service.get_badge_class(
            slug='test_slug', issuing_component='test_component', description='Attempted override',
            criteria='test', display_name='Testola', image_file_handle=get_image('good')
        )
        # These defaults are set on the factory.
        self.assertEqual(badge_class.criteria, 'https://example.com/syllabus')
        self.assertEqual(badge_class.display_name, 'Test Badge')
        self.assertEqual(badge_class.description, "Yay! It's a test badge.")
        # File name won't always be the same.
        self.assertEqual(badge_class.image.path, premade_badge_class.image.path)


233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288
class TestI18nService(ModuleStoreTestCase):
    """ Test ModuleI18nService """

    def setUp(self):
        """ Setting up tests """
        super(TestI18nService, self).setUp()
        self.course = CourseFactory.create()
        self.test_language = 'dummy language'
        self.runtime = LmsModuleSystem(
            static_url='/static',
            track_function=Mock(),
            get_module=Mock(),
            render_template=Mock(),
            replace_urls=str,
            course_id=self.course.id,
            descriptor_runtime=Mock(),
        )

        self.mock_block = Mock()
        self.mock_block.service_declaration.return_value = 'need'

    def test_module_i18n_lms_service(self):
        """
        Test: module i18n service in LMS
        """
        i18n_service = self.runtime.service(self.mock_block, 'i18n')
        self.assertIsNotNone(i18n_service)
        self.assertIsInstance(i18n_service, ModuleI18nService)

    def test_no_service_exception_with_none_declaration_(self):
        """
        Test: NoSuchServiceError should be raised block declaration returns none
        """
        self.mock_block.service_declaration.return_value = None
        with self.assertRaises(NoSuchServiceError):
            self.runtime.service(self.mock_block, 'i18n')

    def test_no_service_exception_(self):
        """
        Test: NoSuchServiceError should be raised if i18n service is none.
        """
        self.runtime._services['i18n'] = None  # pylint: disable=protected-access
        with self.assertRaises(NoSuchServiceError):
            self.runtime.service(self.mock_block, 'i18n')

    def test_i18n_service_callable(self):
        """
        Test: _services dict should contain the callable i18n service in LMS.
        """
        self.assertTrue(callable(self.runtime._services.get('i18n')))  # pylint: disable=protected-access

    def test_i18n_service_not_callable(self):
        """
        Test: i18n service should not be callable in LMS after initialization.
        """
        self.assertFalse(callable(self.runtime.service(self.mock_block, 'i18n')))