From 1afb32c7755a1e6dd1fcba3698d998a886cc7d32 Mon Sep 17 00:00:00 2001 From: Agrendalath Date: Fri, 8 Jul 2022 15:13:05 +0200 Subject: [PATCH] fix: move service initialization from LMS runtime init to module render --- lms/djangoapps/courseware/module_render.py | 31 ++- .../courseware/tests/test_module_render.py | 233 +++++++++++++++--- lms/djangoapps/lms_xblock/runtime.py | 38 +-- .../lms_xblock/test/test_runtime.py | 172 +------------ xmodule/tests/__init__.py | 23 +- xmodule/tests/test_lti_unit.py | 10 +- xmodule/tests/test_poll.py | 6 +- 7 files changed, 243 insertions(+), 270 deletions(-) diff --git a/lms/djangoapps/courseware/module_render.py b/lms/djangoapps/courseware/module_render.py index 7ff9ad7bfc..04015787c6 100644 --- a/lms/djangoapps/courseware/module_render.py +++ b/lms/djangoapps/courseware/module_render.py @@ -7,10 +7,12 @@ import json import logging import textwrap from collections import OrderedDict + from functools import partial from completion.waffle import ENABLE_COMPLETION_TRACKING_SWITCH from completion.models import BlockCompletion +from completion.services import CompletionService from django.conf import settings from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user from django.core.cache import cache @@ -22,7 +24,7 @@ from django.urls import reverse from django.utils.text import slugify from django.views.decorators.clickjacking import xframe_options_exempt from django.views.decorators.csrf import csrf_exempt -from edx_django_utils.cache import RequestCache +from edx_django_utils.cache import DEFAULT_REQUEST_CACHE, RequestCache from edx_django_utils.monitoring import set_custom_attributes_for_course_key, set_monitoring_transaction_name from edx_proctoring.api import get_attempt_status_summary from edx_proctoring.services import ProctoringService @@ -39,12 +41,18 @@ from xblock.exceptions import NoSuchHandlerError, NoSuchViewError from xblock.reference.plugins import FSService from xblock.runtime import KvsFieldData +from lms.djangoapps.badges.service import BadgingService +from lms.djangoapps.badges.utils import badges_enabled +from lms.djangoapps.teams.services import TeamsService +from openedx.core.lib.xblock_services.call_to_action import CallToActionService from xmodule.contentstore.django import contentstore from xmodule.exceptions import NotFoundError, ProcessingError -from xmodule.modulestore.django import modulestore +from xmodule.library_tools import LibraryToolsService +from xmodule.modulestore.django import ModuleI18nService, modulestore from xmodule.modulestore.exceptions import ItemNotFoundError +from xmodule.partitions.partitions_service import PartitionService from xmodule.util.sandboxing import SandboxService -from xmodule.services import RebindUserService +from xmodule.services import RebindUserService, SettingsService, TeamsConfigurationService from common.djangoapps.static_replace.services import ReplaceURLService from common.djangoapps.static_replace.wrapper import replace_urls_wrapper from common.djangoapps.xblock_django.constants import ATTR_KEY_USER_ID @@ -63,7 +71,7 @@ from lms.djangoapps.courseware.services import UserStateService from lms.djangoapps.grades.api import GradesUtilService from lms.djangoapps.grades.api import signals as grades_signals from lms.djangoapps.lms_xblock.field_data import LmsFieldData -from lms.djangoapps.lms_xblock.runtime import LmsModuleSystem +from lms.djangoapps.lms_xblock.runtime import LmsModuleSystem, UserTagsService from lms.djangoapps.verify_student.services import XBlockVerificationService from openedx.core.djangoapps.bookmarks.services import BookmarksService from openedx.core.djangoapps.crawlers.models import CrawlersConfig @@ -678,10 +686,11 @@ def get_module_system_for_user( field_data = DateLookupFieldData(descriptor._field_data, course_id, user) # pylint: disable=protected-access field_data = LmsFieldData(field_data, student_data) + store = modulestore() + system = LmsModuleSystem( track_function=track_function, get_module=inner_get_module, - user=user, publish=publish, # TODO: When we merge the descriptor and module systems, we can stop reaching into the mixologist (cpennington) mixins=descriptor.runtime.mixologist._mixins, # pylint: disable=protected-access @@ -705,6 +714,18 @@ def get_module_system_for_user( 'xqueue': xqueue_service, 'replace_urls': replace_url_service, 'rebind_user': rebind_user_service, + 'completion': CompletionService(user=user, context_key=course_id) + if user and user.is_authenticated + else None, + 'i18n': ModuleI18nService, + 'library_tools': LibraryToolsService(store, user_id=user.id if user else None), + 'partitions': PartitionService(course_id=course_id, cache=DEFAULT_REQUEST_CACHE.data), + 'settings': SettingsService(), + 'user_tags': UserTagsService(user=user, course_id=course_id), + 'badging': BadgingService(course_id=course_id, modulestore=store) if badges_enabled() else None, + 'teams': TeamsService(), + 'teams_configuration': TeamsConfigurationService(), + 'call_to_action': CallToActionService(), }, descriptor_runtime=descriptor._runtime, # pylint: disable=protected-access request_token=request_token, diff --git a/lms/djangoapps/courseware/tests/test_module_render.py b/lms/djangoapps/courseware/tests/test_module_render.py index 7be2e339b7..9e187fd1eb 100644 --- a/lms/djangoapps/courseware/tests/test_module_render.py +++ b/lms/djangoapps/courseware/tests/test_module_render.py @@ -34,6 +34,7 @@ from pyquery import PyQuery # lint-amnesty, pylint: disable=wrong-import-order from web_fragments.fragment import Fragment # lint-amnesty, pylint: disable=wrong-import-order from xblock.completable import CompletableXBlockMixin # lint-amnesty, pylint: disable=wrong-import-order from xblock.core import XBlock, XBlockAside # lint-amnesty, pylint: disable=wrong-import-order +from xblock.exceptions import NoSuchServiceError from xblock.field_data import FieldData # lint-amnesty, pylint: disable=wrong-import-order from xblock.fields import ScopeIds # lint-amnesty, pylint: disable=wrong-import-order from xblock.runtime import DictKeyValueStore, KvsFieldData, Runtime # lint-amnesty, pylint: disable=wrong-import-order @@ -46,7 +47,7 @@ from xmodule.contentstore.django import contentstore from xmodule.html_module import AboutBlock, CourseInfoBlock, HtmlBlock, StaticTabBlock from xmodule.lti_module import LTIBlock from xmodule.modulestore import ModuleStoreEnum -from xmodule.modulestore.django import modulestore +from xmodule.modulestore.django import ModuleI18nService, modulestore from xmodule.modulestore.tests.django_utils import ( TEST_DATA_MONGO_AMNESTY_MODULESTORE, ModuleStoreTestCase, @@ -64,6 +65,8 @@ from common.djangoapps.student.tests.factories import GlobalStaffFactory from common.djangoapps.student.tests.factories import RequestFactoryNoCsrf from common.djangoapps.student.tests.factories import UserFactory from common.djangoapps.xblock_django.constants import ATTR_KEY_ANONYMOUS_USER_ID +from lms.djangoapps.badges.tests.factories import BadgeClassFactory +from lms.djangoapps.badges.tests.test_models import get_image from lms.djangoapps.courseware import module_render as render from lms.djangoapps.courseware.access_response import AccessResponse from lms.djangoapps.courseware.courses import get_course_info_section, get_course_with_access @@ -91,11 +94,34 @@ from common.djangoapps.xblock_django.models import XBlockConfiguration TEST_DATA_DIR = settings.COMMON_TEST_DATA_ROOT -@XBlock.needs("field-data") -@XBlock.needs("i18n") -@XBlock.needs("fs") -@XBlock.needs("user") -@XBlock.needs("bookmarks") +@XBlock.needs('fs') +@XBlock.needs('field-data') +@XBlock.needs('mako') +@XBlock.needs('user') +@XBlock.needs('verification') +@XBlock.needs('proctoring') +@XBlock.needs('milestones') +@XBlock.needs('credit') +@XBlock.needs('bookmarks') +@XBlock.needs('gating') +@XBlock.needs('grade_utils') +@XBlock.needs('user_state') +@XBlock.needs('content_type_gating') +@XBlock.needs('cache') +@XBlock.needs('sandbox') +@XBlock.needs('xqueue') +@XBlock.needs('replace_urls') +@XBlock.needs('rebind_user') +@XBlock.needs('completion') +@XBlock.needs('i18n') +@XBlock.needs('library_tools') +@XBlock.needs('partitions') +@XBlock.needs('settings') +@XBlock.needs('user_tags') +@XBlock.needs('badging') +@XBlock.needs('teams') +@XBlock.needs('teams_configuration') +@XBlock.needs('call_to_action') class PureXBlock(XBlock): """ Pure XBlock to use in tests. @@ -2232,12 +2258,25 @@ class TestEventPublishing(ModuleStoreTestCase, LoginEnrollmentTestCase): mock_track_function.return_value.assert_called_once_with(event_type, event) -@ddt.ddt -class LMSXBlockServiceBindingTest(SharedModuleStoreTestCase): +class LMSXBlockServiceMixin(SharedModuleStoreTestCase): """ - Tests that the LMS Module System (XBlock Runtime) provides an expected set of services. + Helper class that initializes the LmsModuleSystem. """ + def _prepare_runtime(self): + """ + Instantiate the LmsModuleSystem. + """ + self.runtime, _ = render.get_module_system_for_user( + self.user, + self.student_data, + self.descriptor, + self.course.id, + self.track_function, + self.request_token, + course=self.course + ) + @XBlock.register_temp_plugin(PureXBlock, identifier='pure') def setUp(self): """ Set up the user and other fields that will be used to instantiate the runtime. @@ -2248,46 +2287,168 @@ class LMSXBlockServiceBindingTest(SharedModuleStoreTestCase): self.student_data = Mock() self.track_function = Mock() self.request_token = Mock() + self.descriptor = ItemFactory(category="pure", parent=self.course) + self._prepare_runtime() - @XBlock.register_temp_plugin(PureXBlock, identifier='pure') - @ddt.data("user", "i18n", "fs", "field-data", "bookmarks") + +@ddt.ddt +class LMSXBlockServiceBindingTest(LMSXBlockServiceMixin): + """ + Tests that the LMS Module System (XBlock Runtime) provides an expected set of services. + """ + + @ddt.data( + 'fs', + 'field-data', + 'mako', + 'user', + 'verification', + 'proctoring', + 'milestones', + 'credit', + 'bookmarks', + 'gating', + 'grade_utils', + 'user_state', + 'content_type_gating', + 'cache', + 'sandbox', + 'xqueue', + 'replace_urls', + 'rebind_user', + 'completion', + 'i18n', + 'library_tools', + 'partitions', + 'settings', + 'user_tags', + 'teams', + 'teams_configuration', + 'call_to_action', + ) def test_expected_services_exist(self, expected_service): """ Tests that the 'user', 'i18n', and 'fs' services are provided by the LMS runtime. """ - descriptor = ItemFactory(category="pure", parent=self.course) - runtime, _ = render.get_module_system_for_user( - self.user, - self.student_data, - descriptor, - self.course.id, - self.track_function, - self.request_token, - course=self.course - ) - service = runtime.service(descriptor, expected_service) + service = self.runtime.service(self.descriptor, expected_service) assert service is not None - @XBlock.register_temp_plugin(PureXBlock, identifier='pure') def test_beta_tester_fields_added(self): """ Tests that the beta tester fields are set on LMS runtime. """ - descriptor = ItemFactory(category="pure", parent=self.course) - descriptor.days_early_for_beta = 5 - runtime, _ = render.get_module_system_for_user( - self.user, - self.student_data, - descriptor, - self.course.id, - self.track_function, - self.request_token, - course=self.course - ) + self.descriptor.days_early_for_beta = 5 + self._prepare_runtime() # pylint: disable=no-member - assert not runtime.user_is_beta_tester - assert runtime.days_early_for_beta == 5 + assert not self.runtime.user_is_beta_tester + assert self.runtime.days_early_for_beta == 5 + + def test_get_set_tag(self): + """ + Tests the user service interface. + """ + scope = 'course' + key = 'key1' + + # test for when we haven't set the tag yet + tag = self.runtime.service(self.descriptor, 'user_tags').get_tag(scope, key) + assert tag is None + + # set the tag + set_value = 'value' + self.runtime.service(self.descriptor, 'user_tags').set_tag(scope, key, set_value) + tag = self.runtime.service(self.descriptor, 'user_tags').get_tag(scope, key) + + assert tag == set_value + + # Try to set tag in wrong scope + with pytest.raises(ValueError): + self.runtime.service(self.descriptor, 'user_tags').set_tag('fake_scope', key, set_value) + + # Try to get tag in wrong scope + with pytest.raises(ValueError): + self.runtime.service(self.descriptor, 'user_tags').get_tag('fake_scope', key) + + +@ddt.ddt +class TestBadgingService(LMSXBlockServiceMixin): + """Test the badging service interface""" + + @patch.dict(settings.FEATURES, {'ENABLE_OPENBADGES': True}) + def test_service_rendered(self): + self._prepare_runtime() + assert self.runtime.service(self.descriptor, 'badging') + + def test_no_service_rendered(self): + with pytest.raises(NoSuchServiceError): + self.runtime.service(self.descriptor, 'badging') + + @ddt.data(True, False) + @patch.dict(settings.FEATURES, {'ENABLE_OPENBADGES': True}) + def test_course_badges_toggle(self, toggle): + self.course = CourseFactory.create(metadata={'issue_badges': toggle}) + self._prepare_runtime() + assert self.runtime.service(self.descriptor, 'badging').course_badges_enabled is toggle + + @patch.dict(settings.FEATURES, {'ENABLE_OPENBADGES': True}) + def test_get_badge_class(self): + self._prepare_runtime() + badge_service = self.runtime.service(self.descriptor, '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. + with get_image('good') as image_handle: + 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=image_handle + ) + # These defaults are set on the factory. + assert badge_class.criteria == 'https://example.com/syllabus' + assert badge_class.display_name == 'Test Badge' + assert badge_class.description == "Yay! It's a test badge." + # File name won't always be the same. + assert badge_class.image.path == premade_badge_class.image.path + + +class TestI18nService(LMSXBlockServiceMixin): + """ Test ModuleI18nService """ + + def test_module_i18n_lms_service(self): + """ + Test: module i18n service in LMS + """ + i18n_service = self.runtime.service(self.descriptor, 'i18n') + assert i18n_service is not None + assert isinstance(i18n_service, ModuleI18nService) + + def test_no_service_exception_with_none_declaration_(self): + """ + Test: NoSuchServiceError should be raised block declaration returns none + """ + self.descriptor.service_declaration = Mock(return_value=None) + with pytest.raises(NoSuchServiceError): + self.runtime.service(self.descriptor, '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 pytest.raises(NoSuchServiceError): + self.runtime.service(self.descriptor, 'i18n') + + def test_i18n_service_callable(self): + """ + Test: _services dict should contain the callable i18n service in LMS. + """ + assert 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. + """ + assert not callable(self.runtime.service(self.descriptor, 'i18n')) class PureXBlockWithChildren(PureXBlock): diff --git a/lms/djangoapps/lms_xblock/runtime.py b/lms/djangoapps/lms_xblock/runtime.py index 4b5fec0308..f79f75da08 100644 --- a/lms/djangoapps/lms_xblock/runtime.py +++ b/lms/djangoapps/lms_xblock/runtime.py @@ -2,25 +2,13 @@ Module implementing `xblock.runtime.Runtime` functionality for the LMS """ - -import xblock.reference.plugins -from completion.services import CompletionService from django.conf import settings from django.urls import reverse -from edx_django_utils.cache import DEFAULT_REQUEST_CACHE -from lms.djangoapps.badges.service import BadgingService -from lms.djangoapps.badges.utils import badges_enabled from lms.djangoapps.lms_xblock.models import XBlockAsidesConfig -from lms.djangoapps.teams.services import TeamsService from openedx.core.djangoapps.user_api.course_tag import api as user_course_tag_api from openedx.core.lib.url_utils import quote_slashes -from openedx.core.lib.xblock_services.call_to_action import CallToActionService from openedx.core.lib.xblock_utils import wrap_xblock_aside, xblock_local_resource_url -from xmodule.library_tools import LibraryToolsService # lint-amnesty, pylint: disable=wrong-import-order -from xmodule.modulestore.django import ModuleI18nService, modulestore # lint-amnesty, pylint: disable=wrong-import-order -from xmodule.partitions.partitions_service import PartitionService # lint-amnesty, pylint: disable=wrong-import-order -from xmodule.services import SettingsService, TeamsConfigurationService # lint-amnesty, pylint: disable=wrong-import-order from xmodule.x_module import ModuleSystem # lint-amnesty, pylint: disable=wrong-import-order @@ -132,32 +120,8 @@ class LmsModuleSystem(ModuleSystem): # pylint: disable=abstract-method """ ModuleSystem specialized to the LMS """ - def __init__(self, user, **kwargs): - request_cache_dict = DEFAULT_REQUEST_CACHE.data - store = modulestore() - course_id = kwargs.get('course_id') - - services = kwargs.setdefault('services', {}) - if user and user.is_authenticated: - services['completion'] = CompletionService(user=user, context_key=course_id) - services['fs'] = xblock.reference.plugins.FSService() - services['i18n'] = ModuleI18nService - services['library_tools'] = LibraryToolsService(store, user_id=user.id if user else None) - services['partitions'] = PartitionService( - course_id=course_id, - cache=request_cache_dict - ) - services['settings'] = SettingsService() - services['user_tags'] = UserTagsService( - user=user, - course_id=course_id, - ) - if badges_enabled(): - services['badging'] = BadgingService(course_id=course_id, modulestore=store) + def __init__(self, **kwargs): self.request_token = kwargs.pop('request_token', None) - services['teams'] = TeamsService() - services['teams_configuration'] = TeamsConfigurationService() - services['call_to_action'] = CallToActionService() super().__init__(**kwargs) def handler_url(self, *args, **kwargs): # lint-amnesty, pylint: disable=signature-differs diff --git a/lms/djangoapps/lms_xblock/test/test_runtime.py b/lms/djangoapps/lms_xblock/test/test_runtime.py index 58b90a8c10..08a7ff47a0 100644 --- a/lms/djangoapps/lms_xblock/test/test_runtime.py +++ b/lms/djangoapps/lms_xblock/test/test_runtime.py @@ -3,25 +3,15 @@ Tests of the LMS XBlock Runtime and associated utilities """ -from unittest.mock import Mock, patch +from unittest.mock import Mock from urllib.parse import urlparse -import pytest -from ddt import data, ddt from django.conf import settings from django.test import TestCase -from opaque_keys.edx.keys import CourseKey from opaque_keys.edx.locations import BlockUsageLocator, CourseLocator -from xblock.exceptions import NoSuchServiceError from xblock.fields import ScopeIds -from common.djangoapps.student.tests.factories import UserFactory -from lms.djangoapps.badges.tests.factories import BadgeClassFactory -from lms.djangoapps.badges.tests.test_models import get_image from lms.djangoapps.lms_xblock.runtime import LmsModuleSystem -from xmodule.modulestore.django import ModuleI18nService # lint-amnesty, pylint: disable=wrong-import-order -from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase # lint-amnesty, pylint: disable=wrong-import-order -from xmodule.modulestore.tests.factories import CourseFactory # lint-amnesty, pylint: disable=wrong-import-order class BlockMock(Mock): @@ -63,8 +53,6 @@ class TestHandlerUrl(TestCase): self.runtime = LmsModuleSystem( track_function=Mock(), get_module=Mock(), - course_id=self.course_key, - user=Mock(), descriptor_runtime=Mock(), ) @@ -114,161 +102,3 @@ class TestHandlerUrl(TestCase): parsed_fq_url = urlparse(self.runtime.handler_url(self.block, 'handler', thirdparty=False)) assert parsed_fq_url.scheme == '' assert parsed_fq_url.hostname is None - - -class TestUserServiceAPI(TestCase): - """Test the user service interface""" - - def setUp(self): - super().setUp() - self.course_id = CourseLocator("org", "course", "run") - self.user = UserFactory.create() - - self.runtime = LmsModuleSystem( - track_function=Mock(), - get_module=Mock(), - user=self.user, - course_id=self.course_id, - descriptor_runtime=Mock(), - ) - 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) - assert tag is None - - # 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) - - assert tag == set_value - - # Try to set tag in wrong scope - with pytest.raises(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 pytest.raises(ValueError): - self.runtime.service(self.mock_block, 'user_tags').get_tag('fake_scope', self.key) - - -@ddt -class TestBadgingService(ModuleStoreTestCase): - """Test the badging service interface""" - - def setUp(self): - super().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. - """ - return LmsModuleSystem( - track_function=Mock(), - get_module=Mock(), - course_id=self.course_id, - user=self.user, - descriptor_runtime=Mock(), - ) - - @patch.dict(settings.FEATURES, {'ENABLE_OPENBADGES': True}) - def test_service_rendered(self): - runtime = self.create_runtime() - assert runtime.service(self.mock_block, 'badging') - - @patch.dict(settings.FEATURES, {'ENABLE_OPENBADGES': False}) - def test_no_service_rendered(self): - runtime = self.create_runtime() - assert not runtime.service(self.mock_block, 'badging') - - @data(True, False) - @patch.dict(settings.FEATURES, {'ENABLE_OPENBADGES': True}) - def test_course_badges_toggle(self, toggle): - self.course_id = CourseFactory.create(metadata={'issue_badges': toggle}).location.course_key - runtime = self.create_runtime() - assert runtime.service(self.mock_block, 'badging').course_badges_enabled is toggle - - @patch.dict(settings.FEATURES, {'ENABLE_OPENBADGES': True}) - 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. - with get_image('good') as image_handle: - 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=image_handle - ) - # These defaults are set on the factory. - assert badge_class.criteria == 'https://example.com/syllabus' - assert badge_class.display_name == 'Test Badge' - assert badge_class.description == "Yay! It's a test badge." - # File name won't always be the same. - assert badge_class.image.path == premade_badge_class.image.path - - -class TestI18nService(ModuleStoreTestCase): - """ Test ModuleI18nService """ - - def setUp(self): - """ Setting up tests """ - super().setUp() - self.course = CourseFactory.create() - self.test_language = 'dummy language' - self.runtime = LmsModuleSystem( - track_function=Mock(), - get_module=Mock(), - course_id=self.course.id, - user=Mock(), - 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') - assert i18n_service is not None - assert isinstance(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 pytest.raises(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 pytest.raises(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. - """ - assert 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. - """ - assert not callable(self.runtime.service(self.mock_block, 'i18n')) diff --git a/xmodule/tests/__init__.py b/xmodule/tests/__init__.py index c5808ab5a3..4554d2e19f 100644 --- a/xmodule/tests/__init__.py +++ b/xmodule/tests/__init__.py @@ -50,19 +50,6 @@ class TestModuleSystem(ModuleSystem): # pylint: disable=abstract-method """ ModuleSystem for testing """ - def __init__(self, **kwargs): - course_id = kwargs['course_id'] - id_manager = CourseLocationManager(course_id) - kwargs.setdefault('id_reader', id_manager) - kwargs.setdefault('id_generator', id_manager) - - services = kwargs.get('services', {}) - services.setdefault('cache', CacheService(DoNothingCache())) - services.setdefault('field-data', DictFieldData({})) - services.setdefault('sandbox', SandboxService(contentstore, course_id)) - kwargs['services'] = services - super().__init__(**kwargs) - def handler_url(self, block, handler, suffix='', query='', thirdparty=False): # lint-amnesty, pylint: disable=arguments-differ return '{usage_id}/{handler}{suffix}?{query}'.format( usage_id=str(block.scope_ids.usage_id), @@ -132,6 +119,8 @@ def get_test_system( descriptor_system = get_test_descriptor_system() + id_manager = CourseLocationManager(course_id) + def get_module(descriptor): """Mocks module_system get_module function""" @@ -162,10 +151,14 @@ def get_test_system( waittime=10, construct_callback=Mock(name='get_test_system.xqueue.construct_callback', side_effect="/"), ), - 'replace_urls': replace_url_service + 'replace_urls': replace_url_service, + 'cache': CacheService(DoNothingCache()), + 'field-data': DictFieldData({}), + 'sandbox': SandboxService(contentstore, course_id), }, - course_id=course_id, descriptor_runtime=descriptor_system, + id_reader=id_manager, + id_generator=id_manager, ) diff --git a/xmodule/tests/test_lti_unit.py b/xmodule/tests/test_lti_unit.py index 2ca2a6492c..6ef8cb846e 100644 --- a/xmodule/tests/test_lti_unit.py +++ b/xmodule/tests/test_lti_unit.py @@ -12,6 +12,7 @@ import pytest from django.conf import settings from django.test import TestCase, override_settings from lxml import etree +from opaque_keys.edx.keys import CourseKey from opaque_keys.edx.locator import BlockUsageLocator from pytz import UTC from webob.request import Request @@ -61,14 +62,15 @@ class LTIBlockTest(TestCase): """) - self.system = get_test_system() + self.course_id = CourseKey.from_string('org/course/run') + self.system = get_test_system(self.course_id) self.system.publish = Mock() self.system._services['rebind_user'] = Mock() # pylint: disable=protected-access self.xmodule = LTIBlock( self.system, DictFieldData({}), - ScopeIds(None, None, None, BlockUsageLocator(self.system.course_id, 'lti', 'name')) + ScopeIds(None, None, None, BlockUsageLocator(self.course_id, 'lti', 'name')) ) current_user = self.system.service(self.xmodule, 'user').get_current_user() self.user_id = current_user.opt_attrs.get(ATTR_KEY_ANONYMOUS_USER_ID) @@ -319,7 +321,7 @@ class LTIBlockTest(TestCase): def test_lis_result_sourcedid(self): expected_sourced_id = ':'.join(parse.quote(i) for i in ( - str(self.system.course_id), + str(self.course_id), self.xmodule.get_resource_link_id(), self.user_id )) @@ -539,4 +541,4 @@ class LTIBlockTest(TestCase): """ Tests that LTI parameter context_id is equal to course_id. """ - assert str(self.system.course_id) == self.xmodule.context_id + assert str(self.course_id) == self.xmodule.context_id diff --git a/xmodule/tests/test_poll.py b/xmodule/tests/test_poll.py index b7a58bda94..ea0f6049db 100644 --- a/xmodule/tests/test_poll.py +++ b/xmodule/tests/test_poll.py @@ -5,6 +5,7 @@ import unittest from unittest.mock import Mock +from opaque_keys.edx.keys import CourseKey from xblock.field_data import DictFieldData from xblock.fields import ScopeIds from xmodule.poll_module import PollBlock @@ -24,8 +25,9 @@ class PollBlockTest(unittest.TestCase): def setUp(self): super().setUp() - self.system = get_test_system() - usage_key = self.system.course_id.make_usage_key(PollBlock.category, 'test_loc') + course_key = CourseKey.from_string('org/course/run') + self.system = get_test_system(course_key) + usage_key = course_key.make_usage_key(PollBlock.category, 'test_loc') # ScopeIds has 4 fields: user_id, block_type, def_id, usage_id scope_ids = ScopeIds(1, PollBlock.category, usage_key, usage_key) self.xmodule = PollBlock(