Files
edx-platform/xmodule/capa/tests/test_xqueue_interface.py
gabrielC1409 db27ab6166 feat: Send xqueue submissions to edx-submission
fix: Restructuring to send course_id and score to edx submission

fix: Refactoring of sending information to sent_to_submission

fix: Elimination of unnecessary functions

fix: Added usage comment to ProblemBlock in XqueueInterface constructor

fix: update doc ADR

fix: setting for Quality Others test (ubuntu-24.04, 3.11, 20)

fix: Deprecation for django-trans-escape-filter-parse-error

test: Add @pytest.mark.django_db decorator to test functions

test: Fix for pylint being disabled

fix: updated changes for pylint disable

fix: update error from docs ADR-0005

update: xmodule/docs/decisions/0005-send-data-to-edx-submission.rst

Co-authored-by: Sarina Canelake <sarina@axim.org>

update: xmodule/docs/decisions/0005-send-data-to-edx-submission.rst

Co-authored-by: Sarina Canelake <sarina@axim.org>

fix: Adjusted correction

fix: update date for docs ADR

Revert "fix: update date for docs ADR"

This reverts commit 0b4229c51c4937f95cb407872645dd448df45418.

fix: replace call created_submission to create_external_grader_detail

fix: update test xqueue_submission

fix: add docstring in test_xqueue_submission

fix: update date doc ADR

fix: update version edx-submission 3.8.6

fix: add @pytest.mark.xfail

fix: add 20 chances in test_capa_block:

fix: increase retry attempts for seed generation in ProblemBlockTest

fix: change version to edx-submission lib

fix: new version edx-submission in testings

fix: replace parameter file to files

fix: update variable grader_file_name and points_possible

fix: Adjustment in the is_flag_active function to always take the last record edited in the waffle

fix: wrap large line of code

fix: update function is_flag_active

fix: code style adjustment

fix: changes for 60 retry

feat: use CourseWaffleFlag to determine xqueue callback path

fix: Code style adjustment

fix: remove deprecated xqueue callback route and simplify callback type logic

fix: Deleting a comment in the ADR document

fix: add log in self.block is None

fix: Code style adjustment in log
2025-04-15 11:59:32 -04:00

141 lines
6.1 KiB
Python

"""Test the XQueue service and interface."""
from unittest import TestCase
from unittest.mock import Mock, patch
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
import pytest
import json
from openedx.core.djangolib.testing.utils import skip_unless_lms
from xmodule.capa.xqueue_interface import XQueueInterface, XQueueService
@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.capa.xqueue_interface.use_edx_submissions_for_xqueue", return_value=True)
def test_construct_callback_with_flag_enabled(self, mock_flag):
"""Test construct_callback when the waffle flag is enabled."""
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.capa.xqueue_interface.use_edx_submissions_for_xqueue", return_value=False)
def test_construct_callback_with_flag_disabled(self, mock_flag):
"""Test construct_callback when the waffle flag is disabled."""
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
@pytest.mark.django_db
@patch("xmodule.capa.xqueue_interface.use_edx_submissions_for_xqueue", return_value=True)
@patch("xmodule.capa.xqueue_submission.XQueueInterfaceSubmission.send_to_submission")
def test_send_to_queue_with_flag_enabled(mock_send_to_submission, mock_flag):
"""Test send_to_queue when the waffle flag is enabled."""
url = "http://example.com/xqueue"
django_auth = {"username": "user", "password": "pass"}
block = Mock() # Mock block for the constructor
xqueue_interface = XQueueInterface(url, django_auth, block=block)
header = json.dumps({
"lms_callback_url": (
"http://example.com/courses/course-v1:test_org+test_course+test_run/"
"xqueue/block@item_id/type@problem"
),
})
body = json.dumps({
"student_info": json.dumps({"anonymous_student_id": "student_id"}),
"student_response": "student_answer",
})
files_to_upload = None
mock_send_to_submission.return_value = {"submission": "mock_submission"}
error, msg = xqueue_interface.send_to_queue(header, body, files_to_upload)
mock_send_to_submission.assert_called_once_with(header, body, {})
@pytest.mark.django_db
@patch("xmodule.capa.xqueue_interface.use_edx_submissions_for_xqueue", return_value=False)
@patch("xmodule.capa.xqueue_interface.XQueueInterface._http_post")
def test_send_to_queue_with_flag_disabled(mock_http_post, mock_flag):
"""Test send_to_queue when the waffle flag is disabled."""
url = "http://example.com/xqueue"
django_auth = {"username": "user", "password": "pass"}
block = Mock() # Mock block for the constructor
xqueue_interface = XQueueInterface(url, django_auth, block=block)
header = json.dumps({
"lms_callback_url": (
"http://example.com/courses/course-v1:test_org+test_course+test_run/"
"xqueue/block@item_id/type@problem"
),
})
body = json.dumps({
"student_info": json.dumps({"anonymous_student_id": "student_id"}),
"student_response": "student_answer",
})
files_to_upload = None
mock_http_post.return_value = (0, "Submission sent successfully")
error, msg = xqueue_interface.send_to_queue(header, body, files_to_upload)
mock_http_post.assert_called_once_with(
"http://example.com/xqueue/xqueue/submit/",
{"xqueue_header": header, "xqueue_body": body},
files={},
)