Use runtime-provided XQueueService instead of constructing it in ProblemBlock (#37998)

* fix: move xqueue services
This commit is contained in:
Irtaza Akram
2026-02-19 11:02:00 +05:00
committed by GitHub
parent c70bfe980a
commit 76018183d4
11 changed files with 215 additions and 193 deletions

View File

@@ -21,13 +21,13 @@ from xblock.core import XBlock
from xblock.field_data import DictFieldData
from xblock.fields import Reference, ReferenceList, ReferenceValueDict, ScopeIds
from xmodule.capa.xqueue_interface import XQueueService
from xmodule.assetstore import AssetMetadata
from xmodule.contentstore.django import contentstore
from xmodule.modulestore import ModuleStoreEnum
from xmodule.modulestore.draft_and_published import ModuleStoreDraftAndPublished
from xmodule.modulestore.inheritance import InheritanceMixin
from xmodule.modulestore.xml import CourseLocationManager
from xmodule.services import XQueueService
from xmodule.tests.helpers import StubReplaceURLService, mock_render_template, StubMakoService, StubUserService
from xmodule.util.sandboxing import SandboxService
from xmodule.x_module import DoNothingCache, XModuleMixin, ModuleStoreRuntime
@@ -161,7 +161,8 @@ def get_test_system(
'field-data': DictFieldData({}),
'sandbox': SandboxService(contentstore, course_id),
'video_config': VideoConfigService(),
'discussion_config_service': DiscussionConfigService()
'discussion_config_service': DiscussionConfigService(),
'xqueue': XQueueService,
}
descriptor_system.get_block_for_descriptor = get_block # lint-amnesty, pylint: disable=attribute-defined-outside-init
@@ -218,7 +219,8 @@ def prepare_block_runtime(
'field-data': DictFieldData({}),
'sandbox': SandboxService(contentstore, course_id),
'video_config': VideoConfigService(),
'discussion_config_service': DiscussionConfigService()
'discussion_config_service': DiscussionConfigService(),
'xqueue': XQueueService,
}
if add_overrides:

View File

@@ -203,6 +203,7 @@ if submission[0] == '':
@ddt.ddt
@skip_unless_lms
@pytest.mark.django_db
class ProblemBlockTest(unittest.TestCase): # pylint: disable=too-many-public-methods
"""Tests for various problem types in XBlocks."""
@@ -2844,6 +2845,7 @@ class ProblemBlockTest(unittest.TestCase): # pylint: disable=too-many-public-me
@ddt.ddt
@pytest.mark.django_db
class ProblemBlockXMLTest(unittest.TestCase):
"""Tests XML strings for various problem types in XBlocks."""
@@ -3709,6 +3711,7 @@ class ComplexEncoderTest(unittest.TestCase):
@skip_unless_lms
@UseUnsafeCodejail()
@pytest.mark.django_db
class ProblemCheckTrackingTest(unittest.TestCase):
"""
Ensure correct tracking information is included in events emitted during problem checks.

View File

@@ -119,6 +119,7 @@ class CapaFactoryWithDelay:
return block
@pytest.mark.django_db
class XModuleQuizAttemptsDelayTest(unittest.TestCase):
"""
Class to test delay between quiz attempts.

View File

@@ -2,20 +2,22 @@
Tests for SettingsService
"""
import unittest
from unittest import mock
from unittest import TestCase, mock
from unittest.mock import Mock, patch
import pytest
from django.test import TestCase
import ddt
import pytest
from config_models.models import ConfigurationModel
from django.conf import settings
from django.test.utils import override_settings
from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator
from xblock.fields import ScopeIds
from xblock.runtime import Mixologist
from opaque_keys.edx.locator import CourseLocator
from xmodule.services import ConfigurationService, SettingsService, TeamsConfigurationService
from openedx.core.djangolib.testing.utils import skip_unless_lms
from openedx.core.lib.teams_config import TeamsConfig
from xmodule.capa.xqueue_interface import XQueueInterface
from xmodule.services import ConfigurationService, SettingsService, TeamsConfigurationService, XQueueService
class _DummyBlock:
@@ -163,3 +165,71 @@ class TestTeamsConfigurationService(ConfigurationServiceBaseClass):
def test_get_teamsconfiguration(self):
teams_config = self.configuration_service.get_teams_configuration(self.course.id)
assert teams_config == self.teams_config
@pytest.mark.django_db
@skip_unless_lms
class XQueueServiceTest(TestCase):
"""Test the XQueue service methods."""
def setUp(self):
super().setUp()
location = BlockUsageLocator(
CourseLocator("test_org", "test_course", "test_run"),
"problem",
"ExampleProblem",
)
self.block = Mock(scope_ids=ScopeIds("user1", "mock_problem", location, location))
self.block.max_score = Mock(return_value=10) # Mock max_score method
self.service = XQueueService(self.block)
def test_interface(self):
"""Test that the `XQUEUE_INTERFACE` settings are passed from the service to the interface."""
assert isinstance(self.service.interface, XQueueInterface)
assert self.service.interface.url == "http://sandbox-xqueue.edx.org"
assert self.service.interface.auth["username"] == "lms"
assert self.service.interface.auth["password"] == "***REMOVED***"
assert self.service.interface.session.auth.username == "anant"
assert self.service.interface.session.auth.password == "agarwal"
@patch("xmodule.services.XQueueService.use_edx_submissions_for_xqueue", return_value=True)
def test_construct_callback_with_flag_enabled(self, mock_flag): # pylint: disable=unused-argument
"""Test construct_callback when the waffle flag is enabled."""
self.service = XQueueService(self.block)
usage_id = self.block.scope_ids.usage_id
course_id = str(usage_id.course_key)
callback_url = f"courses/{course_id}/xqueue/user1/{usage_id}"
assert self.service.construct_callback() == f"{settings.LMS_ROOT_URL}/{callback_url}/score_update"
assert self.service.construct_callback("alt_dispatch") == (
f"{settings.LMS_ROOT_URL}/{callback_url}/alt_dispatch"
)
custom_callback_url = "http://alt.url"
with override_settings(XQUEUE_INTERFACE={**settings.XQUEUE_INTERFACE, "callback_url": custom_callback_url}):
assert self.service.construct_callback() == f"{custom_callback_url}/{callback_url}/score_update"
@patch("xmodule.services.XQueueService.use_edx_submissions_for_xqueue", return_value=False)
def test_construct_callback_with_flag_disabled(self, mock_flag): # pylint: disable=unused-argument
"""Test construct_callback when the waffle flag is disabled."""
self.service = XQueueService(self.block)
usage_id = self.block.scope_ids.usage_id
callback_url = f"courses/{usage_id.context_key}/xqueue/user1/{usage_id}"
assert self.service.construct_callback() == f"{settings.LMS_ROOT_URL}/{callback_url}/score_update"
assert self.service.construct_callback("alt_dispatch") == f"{settings.LMS_ROOT_URL}/{callback_url}/alt_dispatch"
custom_callback_url = "http://alt.url"
with override_settings(XQUEUE_INTERFACE={**settings.XQUEUE_INTERFACE, "callback_url": custom_callback_url}):
assert self.service.construct_callback() == f"{custom_callback_url}/{callback_url}/score_update"
def test_default_queuename(self):
"""Check the format of the default queue name."""
assert self.service.default_queuename == "test_org-test_course"
def test_waittime(self):
"""Check that the time between requests is retrieved correctly from the settings."""
assert self.service.waittime == 5
with override_settings(XQUEUE_WAITTIME_BETWEEN_REQUESTS=15):
assert self.service.waittime == 15