* Use full LMS imports paths in LMS settings and urls modules * Use full LMS import paths in Studio settings and urls modules * Import from lms.djangoapps.badges instead of badges * Import from lms.djangoapps.branding instead of branding * Import from lms.djangoapps.bulk_email instead of bulk_email * Import from lms.djangoapps.bulk_enroll instead of bulk_enroll * Import from lms.djangoapps.ccx instead of ccx * Import from lms.djangoapps.course_api instead of course_api * Import from lms.djangoapps.course_blocks instead of course_blocks * Import from lms.djangoapps.course_wiki instead of course_wiki * Import from lms.djangoapps.courseware instead of courseware * Import from lms.djangoapps.dashboard instead of dashboard * Import from lms.djangoapps.discussion import discussion * Import from lms.djangoapps.email_marketing instead of email_marketing * Import from lms.djangoapps.experiments instead of experiments * Import from lms.djangoapps.gating instead of gating * Import from lms.djangoapps.grades instead of grades * Import from lms.djangoapps.instructor_analytics instead of instructor_analytics * Import form lms.djangoapps.lms_xblock instead of lms_xblock * Import from lms.djangoapps.lti_provider instead of lti_provider * Import from lms.djangoapps.mobile_api instead of mobile_api * Import from lms.djangoapps.rss_proxy instead of rss_proxy * Import from lms.djangoapps.static_template_view instead of static_template_view * Import from lms.djangoapps.survey instead of survey * Import from lms.djangoapps.verify_student instead of verify_student * Stop suppressing EdxPlatformDeprecatedImportWarnings
137 lines
4.3 KiB
Python
137 lines
4.3 KiB
Python
"""
|
|
Tests for the LTI outcome service handlers, both in outcomes.py and in tasks.py
|
|
"""
|
|
|
|
|
|
import ddt
|
|
import six
|
|
from django.test import TestCase
|
|
from mock import MagicMock, patch
|
|
from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator
|
|
|
|
import lms.djangoapps.lti_provider.tasks as tasks
|
|
from lms.djangoapps.lti_provider.models import GradedAssignment, LtiConsumer, OutcomeService
|
|
from student.tests.factories import UserFactory
|
|
|
|
|
|
class BaseOutcomeTest(TestCase):
|
|
"""
|
|
Super type for tests of both the leaf and composite outcome celery tasks.
|
|
"""
|
|
def setUp(self):
|
|
super(BaseOutcomeTest, self).setUp()
|
|
self.course_key = CourseLocator(
|
|
org='some_org',
|
|
course='some_course',
|
|
run='some_run'
|
|
)
|
|
self.usage_key = BlockUsageLocator(
|
|
course_key=self.course_key,
|
|
block_type='problem',
|
|
block_id='block_id'
|
|
)
|
|
self.user = UserFactory.create()
|
|
self.consumer = LtiConsumer(
|
|
consumer_name='Lti Consumer Name',
|
|
consumer_key='consumer_key',
|
|
consumer_secret='consumer_secret',
|
|
instance_guid='tool_instance_guid'
|
|
)
|
|
self.consumer.save()
|
|
outcome = OutcomeService(
|
|
lis_outcome_service_url='http://example.com/service_url',
|
|
lti_consumer=self.consumer
|
|
)
|
|
outcome.save()
|
|
self.assignment = GradedAssignment(
|
|
user=self.user,
|
|
course_key=self.course_key,
|
|
usage_key=self.usage_key,
|
|
outcome_service=outcome,
|
|
lis_result_sourcedid='sourcedid',
|
|
version_number=1,
|
|
)
|
|
self.assignment.save()
|
|
|
|
self.send_score_update_mock = self.setup_patch(
|
|
'lms.djangoapps.lti_provider.outcomes.send_score_update', None
|
|
)
|
|
|
|
def setup_patch(self, function_name, return_value):
|
|
"""
|
|
Patch a method with a given return value, and return the mock
|
|
"""
|
|
mock = MagicMock(return_value=return_value)
|
|
new_patch = patch(function_name, new=mock)
|
|
new_patch.start()
|
|
self.addCleanup(new_patch.stop)
|
|
return mock
|
|
|
|
|
|
@ddt.ddt
|
|
class SendLeafOutcomeTest(BaseOutcomeTest):
|
|
"""
|
|
Tests for the send_leaf_outcome method in tasks.py
|
|
"""
|
|
|
|
@ddt.data(
|
|
(2.0, 2.0, 1.0),
|
|
(2.0, 0.0, 0.0),
|
|
(1, 2, 0.5),
|
|
)
|
|
@ddt.unpack
|
|
def test_outcome_with_score(self, earned, possible, expected):
|
|
tasks.send_leaf_outcome(
|
|
self.assignment.id,
|
|
earned,
|
|
possible
|
|
)
|
|
self.send_score_update_mock.assert_called_once_with(self.assignment, expected)
|
|
|
|
|
|
@ddt.ddt
|
|
class SendCompositeOutcomeTest(BaseOutcomeTest):
|
|
"""
|
|
Tests for the send_composite_outcome method in tasks.py
|
|
"""
|
|
|
|
def setUp(self):
|
|
super(SendCompositeOutcomeTest, self).setUp()
|
|
self.descriptor = MagicMock()
|
|
self.descriptor.location = BlockUsageLocator(
|
|
course_key=self.course_key,
|
|
block_type='problem',
|
|
block_id='problem',
|
|
)
|
|
self.course_grade = MagicMock()
|
|
self.course_grade_mock = self.setup_patch(
|
|
'lms.djangoapps.lti_provider.tasks.CourseGradeFactory.read', self.course_grade
|
|
)
|
|
self.module_store = MagicMock()
|
|
self.module_store.get_item = MagicMock(return_value=self.descriptor)
|
|
self.check_result_mock = self.setup_patch(
|
|
'lms.djangoapps.lti_provider.tasks.modulestore',
|
|
self.module_store
|
|
)
|
|
|
|
@ddt.data(
|
|
(2.0, 2.0, 1.0),
|
|
(2.0, 0.0, 0.0),
|
|
(1, 2, 0.5),
|
|
)
|
|
@ddt.unpack
|
|
def test_outcome_with_score_score(self, earned, possible, expected):
|
|
self.course_grade.score_for_module = MagicMock(return_value=(earned, possible))
|
|
tasks.send_composite_outcome(
|
|
self.user.id, six.text_type(self.course_key), self.assignment.id, 1
|
|
)
|
|
self.send_score_update_mock.assert_called_once_with(self.assignment, expected)
|
|
|
|
def test_outcome_with_outdated_version(self):
|
|
self.assignment.version_number = 2
|
|
self.assignment.save()
|
|
tasks.send_composite_outcome(
|
|
self.user.id, six.text_type(self.course_key), self.assignment.id, 1
|
|
)
|
|
self.assertEqual(self.course_grade_mock.call_count, 0)
|