This commit implements a comprehensive solution for test score integration in the enhancement system along with improvements to the score rendering mechanism. Key changes include: - Add event handler for rendering blocks with edx-submissions scores - Implement event-based mechanism to render XBlocks with scoring data - Create signal handlers in handlers.py to process external grader scores - Develop specialized XBlock loader for rendering without HTTP requests - Add queue_key propagation across the submission pipeline - Register submission URLs in LMS routing configuration - Add complete docstrings to score render module for better code maintainability - Add ADR for XBlock rendering with external grader integration - Add openedx-events fork branch as a dependency in testing.in - Upgrade edx submission dependency These changes support the migration from traditional XQueue callback HTTP requests to a more robust event-based architecture, improving performance and reliability when processing submission scores. The included ADR documents the architectural decision and implementation approach for this significant improvement to the external grading workflow.
124 lines
4.0 KiB
Python
124 lines
4.0 KiB
Python
"""
|
|
Unit tests for the XQueueInterfaceSubmission class.
|
|
"""
|
|
|
|
import json
|
|
from unittest.mock import Mock, patch
|
|
|
|
import pytest
|
|
from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator
|
|
from xblock.fields import ScopeIds
|
|
|
|
from xmodule.capa.xqueue_submission import XQueueInterfaceSubmission
|
|
|
|
|
|
@pytest.fixture
|
|
def xqueue_service():
|
|
"""
|
|
Fixture that returns an instance of XQueueInterfaceSubmission.
|
|
"""
|
|
location = BlockUsageLocator(CourseLocator("test_org", "test_course", "test_run"), "problem", "ExampleProblem")
|
|
block = Mock(scope_ids=ScopeIds("user1", "problem", location, location))
|
|
block.max_score = Mock(return_value=10)
|
|
return XQueueInterfaceSubmission(block)
|
|
|
|
|
|
def test_get_submission_params(xqueue_service):
|
|
"""
|
|
Test extracting item data from an xqueue submission.
|
|
"""
|
|
header = json.dumps({"lms_callback_url": "http://example.com/callback", "queue_name": "default"})
|
|
payload = json.dumps(
|
|
{
|
|
"student_info": json.dumps({"anonymous_student_id": "student_id"}),
|
|
"student_response": "student_answer",
|
|
"grader_payload": json.dumps({"grader": "test.py"}),
|
|
}
|
|
)
|
|
|
|
student_item, student_answer, queue_name, grader_file_name, points_possible = xqueue_service.get_submission_params(
|
|
header, payload
|
|
)
|
|
|
|
assert student_item == {
|
|
"item_id": "block-v1:test_org+test_course+test_run+type@problem+block@ExampleProblem",
|
|
"item_type": "problem",
|
|
"course_id": "course-v1:test_org+test_course+test_run",
|
|
"student_id": "student_id",
|
|
}
|
|
assert student_answer == "student_answer"
|
|
assert queue_name == "default"
|
|
assert grader_file_name == "test.py"
|
|
assert points_possible == 10
|
|
|
|
|
|
@pytest.mark.django_db
|
|
@patch("submissions.api.create_external_grader_detail")
|
|
def test_send_to_submission(mock_create_external_grader_detail, xqueue_service):
|
|
"""
|
|
Test sending a submission to the grading system.
|
|
"""
|
|
header = json.dumps(
|
|
{
|
|
"lms_callback_url": (
|
|
"http://example.com/courses/course-v1:test_org+test_course+test_run/xqueue/5/"
|
|
"block-v1:test_org+test_course+test_run+type@problem+block@ExampleProblem/"
|
|
),
|
|
}
|
|
)
|
|
body = json.dumps(
|
|
{
|
|
"student_info": json.dumps({"anonymous_student_id": "student_id"}),
|
|
"student_response": "student_answer",
|
|
"grader_payload": json.dumps({"grader": "test.py"}),
|
|
}
|
|
)
|
|
|
|
mock_response = {"submission": "mock_submission"}
|
|
mock_create_external_grader_detail.return_value = mock_response
|
|
|
|
result = xqueue_service.send_to_submission(header, body, queue_key="default")
|
|
|
|
assert result == mock_response
|
|
mock_create_external_grader_detail.assert_called_once_with(
|
|
{
|
|
"item_id": "block-v1:test_org+test_course+test_run+type@problem+block@ExampleProblem",
|
|
"item_type": "problem",
|
|
"course_id": "course-v1:test_org+test_course+test_run",
|
|
"student_id": "student_id",
|
|
},
|
|
'student_answer',
|
|
queue_name='default',
|
|
queue_key='default',
|
|
grader_file_name='test.py',
|
|
points_possible=10,
|
|
files=None,
|
|
)
|
|
|
|
|
|
@pytest.mark.django_db
|
|
@patch("submissions.api.create_external_grader_detail")
|
|
def test_send_to_submission_with_missing_fields(mock_create_external_grader_detail, xqueue_service):
|
|
"""
|
|
Test send_to_submission with missing required fields.
|
|
"""
|
|
header = json.dumps(
|
|
{
|
|
"lms_callback_url": (
|
|
"http://example.com/courses/course-v1:test_org+test_course+test_run/xqueue/5/" "block@item_id/"
|
|
)
|
|
}
|
|
)
|
|
body = json.dumps(
|
|
{
|
|
"student_info": json.dumps({"anonymous_student_id": "student_id"}),
|
|
"grader_payload": json.dumps({"grader": "test.py"}),
|
|
}
|
|
)
|
|
|
|
result = xqueue_service.send_to_submission(header, body, queue_key="default")
|
|
|
|
assert "error" in result
|
|
assert "Validation error" in result["error"]
|
|
mock_create_external_grader_detail.assert_not_called()
|