def student_view(self, context=None):
        user_allowed = False
        user_state = {}
        message = ""
        lti_parameters = {}
        fragment = Fragment()
        context.update(self._get_context_for_template())
        child_fragments = self.runtime.render_children(block=self, view_name='student_view', context=context)
        context.update({"child_fragments": child_fragments})
        if self._is_studio():  # studio view
            context["lms_link"] = get_lms_link_for_item(self.location) if get_lms_link_for_item else ""
            fragment.add_content(self._render_template('static/html/studio.html', context))
        else:  # Student view
            if self.launch_url and self._get_exam_id():
                try:
                    lti_consumer = LtiConsumer(self)
                    lti_parameters = lti_consumer.get_signed_lti_parameters()
                    exam_id = self._get_exam_id()
                    user_state = self.get_proctorexam_user_state(
                        self._get_exam_id(),
                        lti_parameters
                    )
                    context["user_state"] = user_state
                except LtiError:
                    message = _("Proctor Exam xblock configuration is incomplete, LTI passport is invalid")
            else:
                message = _("Proctor Exam xblock configuration is incomplete, exam URL is missing")

            if user_state and "student" in user_state and (user_state["student"].get("status") == "exam_started"):
                # User have completed Proctor Exam indentification process,
                # we show him exam content
                html = self._render_template('static/html/sequence.html', context)
                fragment.add_content(html)
                fragment.add_frags_resources(child_fragments)
            else:
                if self._allowed_verified():
                    # User have to complete Proctor Exam indentification process
                    context.update({'lti_parameters': lti_parameters, "message": message})
                    html = self._render_template("static/html/student.html", context)
                else:
                    html = self._render_template("static/html/honor.html", context)
                fragment.add_content(html)
                fragment.add_css(self.resource_string('static/css/student.css'))

        return fragment
 def setUp(self):
     super(TestLtiConsumer, self).setUp()
     self.lti_consumer = LtiConsumer(self.xblock)
class TestLtiConsumer(TestLtiConsumerXBlock):
    """
    Unit tests for LtiConsumer
    """

    def setUp(self):
        super(TestLtiConsumer, self).setUp()
        self.lti_consumer = LtiConsumer(self.xblock)

    @patch(
        'lti_consumer.lti.get_oauth_request_signature',
        Mock(return_value=(
            'OAuth oauth_nonce="fake_nonce", '
            'oauth_timestamp="fake_timestamp", oauth_version="fake_version", oauth_signature_method="fake_method", '
            'oauth_consumer_key="fake_consumer_key", oauth_signature="fake_signature"'
        ))
    )
    @patch(
        'lti_consumer.lti_consumer.LtiConsumerXBlock.prefixed_custom_parameters',
        PropertyMock(return_value={u'custom_param_1': 'custom1', u'custom_param_2': 'custom2'})
    )
    @patch(
        'lti_consumer.lti_consumer.LtiConsumerXBlock.lti_provider_key_secret',
        PropertyMock(return_value=('t', 's'))
    )
    @patch('lti_consumer.lti_consumer.LtiConsumerXBlock.user_id', PropertyMock(return_value=FAKE_USER_ID))
    def test_get_signed_lti_parameters(self):
        """
        Test `get_signed_lti_parameters` returns the correct dict
        """
        expected_lti_parameters = {
            u'user_id': self.lti_consumer.xblock.user_id,
            u'oauth_callback': u'about:blank',
            u'launch_presentation_return_url': '',
            u'lti_message_type': u'basic-lti-launch-request',
            u'lti_version': 'LTI-1p0',
            u'roles': self.lti_consumer.xblock.role,
            u'resource_link_id': self.lti_consumer.xblock.resource_link_id,
            u'lis_result_sourcedid': self.lti_consumer.xblock.lis_result_sourcedid,
            u'context_id': self.lti_consumer.xblock.context_id,
            u'lis_outcome_service_url': self.lti_consumer.xblock.outcome_service_url,
            'lis_person_sourcedid': 'edx',
            'lis_person_contact_email_primary': '*****@*****.**',
            u'custom_param_1': 'custom1',
            u'custom_param_2': 'custom2',
            u'oauth_nonce': 'fake_nonce',
            'oauth_timestamp': 'fake_timestamp',
            'oauth_version': 'fake_version',
            'oauth_signature_method': 'fake_method',
            'oauth_consumer_key': 'fake_consumer_key',
            'oauth_signature': u'fake_signature'
        }
        self.lti_consumer.xblock.has_score = True
        self.lti_consumer.xblock.ask_to_send_username = True
        self.lti_consumer.xblock.ask_to_send_email = True

        self.lti_consumer.xblock.runtime.get_real_user.return_value = Mock(email='*****@*****.**', username='******')
        self.assertEqual(self.lti_consumer.get_signed_lti_parameters(), expected_lti_parameters)

        # Test that `lis_person_sourcedid` and `lis_person_contact_email_primary` are not included
        # in the returned LTI parameters when a user cannot be found
        self.lti_consumer.xblock.runtime.get_real_user.return_value = {}
        del expected_lti_parameters['lis_person_sourcedid']
        del expected_lti_parameters['lis_person_contact_email_primary']
        self.assertEqual(self.lti_consumer.get_signed_lti_parameters(), expected_lti_parameters)

    def test_get_result(self):
        """
        Test `get_result` returns valid json response
        """
        self.xblock.module_score = 0.9
        self.xblock.score_comment = 'Great Job!'
        response = dict(GET_RESULT_RESPONSE)
        response.update({
            "resultScore": self.xblock.module_score,
            "comment": self.xblock.score_comment
        })
        self.assertEqual(self.lti_consumer.get_result(Mock()), response)

        self.xblock.module_score = None
        self.xblock.score_comment = ''
        self.assertEqual(self.lti_consumer.get_result(Mock()), GET_RESULT_RESPONSE)

    @patch('lti_consumer.lti_consumer.LtiConsumerXBlock.clear_user_module_score')
    def test_delete_result(self, mock_clear):
        """
        Test `delete_result` calls `LtiConsumerXBlock.clear_user_module_score`
        """
        user = Mock()
        response = self.lti_consumer.delete_result(user)

        mock_clear.assert_called_with(user)
        self.assertEqual(response, {})

    @patch('lti_consumer.lti_consumer.LtiConsumerXBlock.max_score', Mock(return_value=1.0))
    @patch('lti_consumer.lti_consumer.LtiConsumerXBlock.set_user_module_score')
    @patch('lti_consumer.lti_consumer.LtiConsumerXBlock.clear_user_module_score')
    @patch('lti_consumer.lti.parse_result_json')
    def test_put_result(self, mock_parse, mock_clear, mock_set):
        """
        Test `put_result` calls `LtiConsumerXBlock.set_user_module_score`
        or `LtiConsumerXblock.clear_user_module_score` if resultScore not included in request
        """
        user = Mock()
        score = 0.9
        comment = 'Great Job!'

        mock_parse.return_value = (score, comment)
        response = self.lti_consumer.put_result(user, '')
        mock_set.assert_called_with(user, score, 1.0, comment)
        self.assertEqual(response, {})

        mock_parse.return_value = (None, '')
        response = self.lti_consumer.put_result(user, '')
        mock_clear.assert_called_with(user)
        self.assertEqual(response, {})

    @patch('lti_consumer.lti.log')
    def test_verify_result_headers_verify_content_type_true(self, mock_log):
        """
        Test wrong content type raises exception if `verify_content_type` is True
        """
        request = make_request('')

        with self.assertRaises(LtiError):
            self.lti_consumer.verify_result_headers(request)
            self.assertTrue(mock_log.called)

    @patch('lti_consumer.lti.verify_oauth_body_signature', Mock(return_value=True))
    @patch('lti_consumer.lti_consumer.LtiConsumerXBlock.lti_provider_key_secret', PropertyMock(return_value=('t', 's')))
    def test_verify_result_headers_verify_content_type_false(self):
        """
        Test content type check skipped if `verify_content_type` is False
        """
        request = make_request('')
        request.environ['CONTENT_TYPE'] = LtiConsumer.CONTENT_TYPE_RESULT_JSON
        response = self.lti_consumer.verify_result_headers(request, False)

        self.assertTrue(response)

    @patch('lti_consumer.lti.verify_oauth_body_signature', Mock(return_value=True))
    @patch('lti_consumer.lti_consumer.LtiConsumerXBlock.lti_provider_key_secret', PropertyMock(return_value=('t', 's')))
    def test_verify_result_headers_valid(self):
        """
        Test True is returned if request is valid
        """
        request = make_request('')
        request.environ['CONTENT_TYPE'] = LtiConsumer.CONTENT_TYPE_RESULT_JSON
        response = self.lti_consumer.verify_result_headers(request)

        self.assertTrue(response)

    @patch('lti_consumer.lti.verify_oauth_body_signature', Mock(side_effect=LtiError))
    @patch('lti_consumer.lti_consumer.LtiConsumerXBlock.lti_provider_key_secret', PropertyMock(return_value=('t', 's')))
    @patch('lti_consumer.lti.log')
    def test_verify_result_headers_lti_error(self, mock_log):
        """
        Test exception raised if request header verification raises error
        """
        request = make_request('')
        request.environ['CONTENT_TYPE'] = LtiConsumer.CONTENT_TYPE_RESULT_JSON

        with self.assertRaises(LtiError):
            self.lti_consumer.verify_result_headers(request)
            self.assertTrue(mock_log.called)

    @patch('lti_consumer.lti.verify_oauth_body_signature', Mock(side_effect=ValueError))
    @patch('lti_consumer.lti_consumer.LtiConsumerXBlock.lti_provider_key_secret', PropertyMock(return_value=('t', 's')))
    @patch('lti_consumer.lti.log')
    def test_verify_result_headers_value_error(self, mock_log):
        """
        Test exception raised if request header verification raises error
        """
        request = make_request('')
        request.environ['CONTENT_TYPE'] = LtiConsumer.CONTENT_TYPE_RESULT_JSON

        with self.assertRaises(LtiError):
            self.lti_consumer.verify_result_headers(request)
            self.assertTrue(mock_log.called)
Example #4
0
 def setUp(self):
     super(TestLtiConsumer, self).setUp()
     self.lti_consumer = LtiConsumer(self.xblock)
Example #5
0
class TestLtiConsumer(TestLtiConsumerXBlock):
    """
    Unit tests for LtiConsumer
    """

    def setUp(self):
        super(TestLtiConsumer, self).setUp()
        self.lti_consumer = LtiConsumer(self.xblock)

    @patch(
        'lti_consumer.lti.get_oauth_request_signature',
        Mock(return_value=(
            'OAuth oauth_nonce="fake_nonce", '
            'oauth_timestamp="fake_timestamp", oauth_version="fake_version", oauth_signature_method="fake_method", '
            'oauth_consumer_key="fake_consumer_key", oauth_signature="fake_signature"'
        ))
    )
    @patch(
        'lti_consumer.lti_consumer.LtiConsumerXBlock.prefixed_custom_parameters',
        PropertyMock(return_value={u'custom_param_1': 'custom1', u'custom_param_2': 'custom2'})
    )
    @patch(
        'lti_consumer.lti_consumer.LtiConsumerXBlock.lti_provider_key_secret',
        PropertyMock(return_value=('t', 's'))
    )
    @patch('lti_consumer.lti_consumer.LtiConsumerXBlock.user_id', PropertyMock(return_value=FAKE_USER_ID))
    def test_get_signed_lti_parameters(self):
        """
        Test `get_signed_lti_parameters` returns the correct dict
        """
        self.lti_consumer.xblock.due = timezone.now()
        self.lti_consumer.xblock.graceperiod = timedelta(days=1)

        expected_lti_parameters = {
            u'user_id': self.lti_consumer.xblock.user_id,
            u'oauth_callback': u'about:blank',
            u'launch_presentation_return_url': '',
            u'lti_message_type': u'basic-lti-launch-request',
            u'lti_version': 'LTI-1p0',
            u'roles': self.lti_consumer.xblock.role,
            u'resource_link_id': self.lti_consumer.xblock.resource_link_id,
            u'lis_result_sourcedid': self.lti_consumer.xblock.lis_result_sourcedid,
            u'context_id': self.lti_consumer.xblock.context_id,
            u'lis_outcome_service_url': self.lti_consumer.xblock.outcome_service_url,
            u'custom_component_display_name': self.lti_consumer.xblock.display_name,
            u'custom_component_due_date': self.lti_consumer.xblock.due.strftime('%Y-%m-%d %H:%M:%S'),
            u'custom_component_graceperiod': str(self.lti_consumer.xblock.graceperiod.total_seconds()),
            u'custom_user_id': unicode(5),
            'lis_person_sourcedid': 'edx',
            'lis_person_contact_email_primary': '*****@*****.**',
            'lis_person_name_given': 'Jane',
            'lis_person_name_family': 'Doe',
            'lis_person_name_full': 'Jane Doe',
            'launch_presentation_locale': 'en',
            u'custom_param_1': 'custom1',
            u'custom_param_2': 'custom2',
            u'oauth_nonce': 'fake_nonce',
            'oauth_timestamp': 'fake_timestamp',
            'oauth_version': 'fake_version',
            'oauth_signature_method': 'fake_method',
            'oauth_consumer_key': 'fake_consumer_key',
            'oauth_signature': u'fake_signature'
        }
        self.lti_consumer.xblock.has_score = True
        self.lti_consumer.xblock.ask_to_send_username = True
        self.lti_consumer.xblock.ask_to_send_email = True
        self.lti_consumer.xblock.ask_to_send_first_name = True
        self.lti_consumer.xblock.ask_to_send_last_name = True
        self.lti_consumer.xblock.ask_to_send_full_name = True
        self.lti_consumer.xblock.runtime.get_real_user.return_value = Mock(
            id=5,
            email='*****@*****.**',
            username='******',
            profile=Mock(filter=Mock(return_value=[Mock(value='Jane Doe')])),
            preferences=Mock(filter=Mock(return_value=[Mock(value='en')]))
        )
        self.assertEqual(self.lti_consumer.get_signed_lti_parameters(), expected_lti_parameters)

        # Test that `lis_person_name_family` returns empty string when `lis_person_name_full`
        # contains only one name
        expected_lti_parameters.update({
            "lis_person_name_family": "",
            "lis_person_name_full": "Jane"
        })
        self.lti_consumer.xblock.runtime.get_real_user.return_value = Mock(
            id=5,
            email='*****@*****.**',
            username='******',
            profile=Mock(filter=Mock(return_value=[Mock(value='Jane')])),
            preferences=Mock(filter=Mock(return_value=[Mock(value='en')]))
        )
        self.assertEqual(self.lti_consumer.get_signed_lti_parameters(), expected_lti_parameters)

        # Test that `custom_user_id`, `lis_person_sourcedid`, `lis_person_contact_email_primary`,
        # `lis_person_name_given`, `lis_person_name_family`, `lis_person_name_full`
        # and `launch_presentation_locale` are not included in the returned
        # LTI parameters when a user cannot be found
        self.lti_consumer.xblock.runtime.get_real_user.return_value = {}
        del expected_lti_parameters['custom_user_id']
        del expected_lti_parameters['lis_person_sourcedid']
        del expected_lti_parameters['lis_person_contact_email_primary']
        del expected_lti_parameters['lis_person_name_given']
        del expected_lti_parameters['lis_person_name_family']
        del expected_lti_parameters['lis_person_name_full']
        del expected_lti_parameters['launch_presentation_locale']
        self.assertEqual(self.lti_consumer.get_signed_lti_parameters(), expected_lti_parameters)

    def test_get_result(self):
        """
        Test `get_result` returns valid json response
        """
        self.xblock.module_score = 0.9
        self.xblock.score_comment = 'Great Job!'
        response = dict(GET_RESULT_RESPONSE)
        response.update({
            "resultScore": self.xblock.module_score,
            "comment": self.xblock.score_comment
        })
        self.assertEqual(self.lti_consumer.get_result(Mock()), response)

        self.xblock.module_score = None
        self.xblock.score_comment = ''
        self.assertEqual(self.lti_consumer.get_result(Mock()), GET_RESULT_RESPONSE)

    @patch('lti_consumer.lti_consumer.LtiConsumerXBlock.clear_user_module_score')
    def test_delete_result(self, mock_clear):
        """
        Test `delete_result` calls `LtiConsumerXBlock.clear_user_module_score`
        """
        user = Mock()
        response = self.lti_consumer.delete_result(user)

        mock_clear.assert_called_with(user)
        self.assertEqual(response, {})

    @patch('lti_consumer.lti_consumer.LtiConsumerXBlock.max_score', Mock(return_value=1.0))
    @patch('lti_consumer.lti_consumer.LtiConsumerXBlock.set_user_module_score')
    @patch('lti_consumer.lti_consumer.LtiConsumerXBlock.clear_user_module_score')
    @patch('lti_consumer.lti.parse_result_json')
    def test_put_result(self, mock_parse, mock_clear, mock_set):
        """
        Test `put_result` calls `LtiConsumerXBlock.set_user_module_score`
        or `LtiConsumerXblock.clear_user_module_score` if resultScore not included in request
        """
        user = Mock()
        score = 0.9
        comment = 'Great Job!'

        mock_parse.return_value = (score, comment)
        response = self.lti_consumer.put_result(user, '')
        mock_set.assert_called_with(user, score, 1.0, comment)
        self.assertEqual(response, {})

        mock_parse.return_value = (None, '')
        response = self.lti_consumer.put_result(user, '')
        mock_clear.assert_called_with(user)
        self.assertEqual(response, {})

    @patch('lti_consumer.lti.log')
    def test_verify_result_headers_verify_content_type_true(self, mock_log):
        """
        Test wrong content type raises exception if `verify_content_type` is True
        """
        request = make_request('')

        with self.assertRaises(LtiError):
            self.lti_consumer.verify_result_headers(request)
            self.assertTrue(mock_log.called)

    @patch('lti_consumer.lti.verify_oauth_body_signature', Mock(return_value=True))
    @patch('lti_consumer.lti_consumer.LtiConsumerXBlock.lti_provider_key_secret', PropertyMock(return_value=('t', 's')))
    def test_verify_result_headers_verify_content_type_false(self):
        """
        Test content type check skipped if `verify_content_type` is False
        """
        request = make_request('')
        request.environ['CONTENT_TYPE'] = LtiConsumer.CONTENT_TYPE_RESULT_JSON
        response = self.lti_consumer.verify_result_headers(request, False)

        self.assertTrue(response)

    @patch('lti_consumer.lti.verify_oauth_body_signature', Mock(return_value=True))
    @patch('lti_consumer.lti_consumer.LtiConsumerXBlock.lti_provider_key_secret', PropertyMock(return_value=('t', 's')))
    def test_verify_result_headers_valid(self):
        """
        Test True is returned if request is valid
        """
        request = make_request('')
        request.environ['CONTENT_TYPE'] = LtiConsumer.CONTENT_TYPE_RESULT_JSON
        response = self.lti_consumer.verify_result_headers(request)

        self.assertTrue(response)

    @patch('lti_consumer.lti.verify_oauth_body_signature', Mock(side_effect=LtiError))
    @patch('lti_consumer.lti_consumer.LtiConsumerXBlock.lti_provider_key_secret', PropertyMock(return_value=('t', 's')))
    @patch('lti_consumer.lti.log')
    def test_verify_result_headers_lti_error(self, mock_log):
        """
        Test exception raised if request header verification raises error
        """
        request = make_request('')
        request.environ['CONTENT_TYPE'] = LtiConsumer.CONTENT_TYPE_RESULT_JSON

        with self.assertRaises(LtiError):
            self.lti_consumer.verify_result_headers(request)
            self.assertTrue(mock_log.called)

    @patch('lti_consumer.lti.verify_oauth_body_signature', Mock(side_effect=ValueError))
    @patch('lti_consumer.lti_consumer.LtiConsumerXBlock.lti_provider_key_secret', PropertyMock(return_value=('t', 's')))
    @patch('lti_consumer.lti.log')
    def test_verify_result_headers_value_error(self, mock_log):
        """
        Test exception raised if request header verification raises error
        """
        request = make_request('')
        request.environ['CONTENT_TYPE'] = LtiConsumer.CONTENT_TYPE_RESULT_JSON

        with self.assertRaises(LtiError):
            self.lti_consumer.verify_result_headers(request)
            self.assertTrue(mock_log.called)
Example #6
0
class TestLtiConsumer(TestLtiConsumerXBlock):
    """
    Unit tests for LtiConsumer
    """
    def setUp(self):
        super(TestLtiConsumer, self).setUp()
        self.lti_consumer = LtiConsumer(self.xblock)

    def _update_xblock_for_signed_parameters(self):
        """
        Prepare the LTI XBlock for signing the parameters.
        """
        self.lti_consumer.xblock.due = timezone.now()
        self.lti_consumer.xblock.graceperiod = timedelta(days=1)
        self.lti_consumer.xblock.has_score = True
        self.lti_consumer.xblock.ask_to_send_username = True
        self.lti_consumer.xblock.ask_to_send_email = True
        self.lti_consumer.xblock.runtime.get_real_user.return_value = Mock(
            email='*****@*****.**',
            username='******',
            preferences=Mock(filter=Mock(return_value=[Mock(value='en')])))

    @patch_signed_parameters
    def test_get_signed_lti_parameters(self):
        """
        Test `get_signed_lti_parameters` returns the correct dict
        """
        self._update_xblock_for_signed_parameters()
        expected_lti_parameters = {
            text_type('user_id'):
            self.lti_consumer.xblock.user_id,
            text_type('oauth_callback'):
            'about:blank',
            text_type('launch_presentation_return_url'):
            '',
            text_type('lti_message_type'):
            'basic-lti-launch-request',
            text_type('lti_version'):
            'LTI-1p0',
            text_type('roles'):
            self.lti_consumer.xblock.role,
            text_type('resource_link_id'):
            self.lti_consumer.xblock.resource_link_id,
            text_type('lis_result_sourcedid'):
            self.lti_consumer.xblock.lis_result_sourcedid,
            text_type('context_id'):
            self.lti_consumer.xblock.context_id,
            text_type('lis_outcome_service_url'):
            self.lti_consumer.xblock.outcome_service_url,
            text_type('custom_component_display_name'):
            self.lti_consumer.xblock.display_name,
            text_type('custom_component_due_date'):
            self.lti_consumer.xblock.due.strftime('%Y-%m-%d %H:%M:%S'),
            text_type('custom_component_graceperiod'):
            str(self.lti_consumer.xblock.graceperiod.total_seconds()),
            'lis_person_sourcedid':
            'edx',
            'lis_person_contact_email_primary':
            '*****@*****.**',
            'launch_presentation_locale':
            'en',
            text_type('custom_param_1'):
            'custom1',
            text_type('custom_param_2'):
            'custom2',
            text_type('oauth_nonce'):
            'fake_nonce',
            'oauth_timestamp':
            'fake_timestamp',
            'oauth_version':
            'fake_version',
            'oauth_signature_method':
            'fake_method',
            'oauth_consumer_key':
            'fake_consumer_key',
            'oauth_signature':
            'fake_signature',
            text_type('context_label'):
            self.lti_consumer.xblock.course.display_org_with_default,
            text_type('context_title'):
            self.lti_consumer.xblock.course.display_name_with_default,
        }
        self.assertEqual(self.lti_consumer.get_signed_lti_parameters(),
                         expected_lti_parameters)

        # Test that `lis_person_sourcedid`, `lis_person_contact_email_primary`, and `launch_presentation_locale`
        # are not included in the returned LTI parameters when a user cannot be found
        self.lti_consumer.xblock.runtime.get_real_user.return_value = {}
        del expected_lti_parameters['lis_person_sourcedid']
        del expected_lti_parameters['lis_person_contact_email_primary']
        del expected_lti_parameters['launch_presentation_locale']
        self.assertEqual(self.lti_consumer.get_signed_lti_parameters(),
                         expected_lti_parameters)

    @patch_signed_parameters
    @patch('lti_consumer.lti.log')
    def test_parameter_processors(self, mock_log):
        self._update_xblock_for_signed_parameters()
        self.xblock.enable_processors = True

        mock_value = {
            'parameter_processors':
            ['lti_consumer.tests.unit.test_utils:dummy_processor']
        }
        with patch('lti_consumer.lti_consumer.LtiConsumerXBlock.get_settings',
                   return_value=mock_value):
            params = self.lti_consumer.get_signed_lti_parameters()
            assert params['custom_author_country'] == u''
            assert params['custom_author_email'] == u'*****@*****.**'
            assert not mock_log.exception.called

    @patch_signed_parameters
    @patch('lti_consumer.lti.log')
    def test_default_params(self, mock_log):
        self._update_xblock_for_signed_parameters()
        self.xblock.enable_processors = True

        mock_value = {
            'parameter_processors':
            ['lti_consumer.tests.unit.test_utils:defaulting_processor']
        }
        with patch('lti_consumer.lti_consumer.LtiConsumerXBlock.get_settings',
                   return_value=mock_value):
            params = self.lti_consumer.get_signed_lti_parameters()
            assert params['custom_country'] == u''
            assert params['custom_name'] == u'Lex'
            assert not mock_log.exception.called

    @patch_signed_parameters
    @patch('lti_consumer.lti.log')
    def test_default_params_with_error(self, mock_log):
        self._update_xblock_for_signed_parameters()
        self.xblock.enable_processors = True

        mock_value = {
            'parameter_processors':
            ['lti_consumer.tests.unit.test_utils:faulty_processor']
        }
        with patch('lti_consumer.lti_consumer.LtiConsumerXBlock.get_settings',
                   return_value=mock_value):
            params = self.lti_consumer.get_signed_lti_parameters()
            assert params['custom_name'] == u'Lex'
            assert mock_log.exception.called

    def test_get_result(self):
        """
        Test `get_result` returns valid json response
        """
        self.xblock.module_score = 0.9
        self.xblock.score_comment = 'Great Job!'
        response = dict(GET_RESULT_RESPONSE)
        response.update({
            "resultScore": self.xblock.module_score,
            "comment": self.xblock.score_comment
        })
        self.assertEqual(self.lti_consumer.get_result(Mock()), response)

        self.xblock.module_score = None
        self.xblock.score_comment = ''
        self.assertEqual(self.lti_consumer.get_result(Mock()),
                         GET_RESULT_RESPONSE)

    @patch(
        'lti_consumer.lti_consumer.LtiConsumerXBlock.clear_user_module_score')
    def test_delete_result(self, mock_clear):
        """
        Test `delete_result` calls `LtiConsumerXBlock.clear_user_module_score`
        """
        user = Mock()
        response = self.lti_consumer.delete_result(user)

        mock_clear.assert_called_with(user)
        self.assertEqual(response, {})

    @patch('lti_consumer.lti_consumer.LtiConsumerXBlock.max_score',
           Mock(return_value=1.0))
    @patch('lti_consumer.lti_consumer.LtiConsumerXBlock.set_user_module_score')
    @patch(
        'lti_consumer.lti_consumer.LtiConsumerXBlock.clear_user_module_score')
    @patch('lti_consumer.lti.parse_result_json')
    def test_put_result(self, mock_parse, mock_clear, mock_set):
        """
        Test `put_result` calls `LtiConsumerXBlock.set_user_module_score`
        or `LtiConsumerXblock.clear_user_module_score` if resultScore not included in request
        """
        user = Mock()
        score = 0.9
        comment = 'Great Job!'

        mock_parse.return_value = (score, comment)
        response = self.lti_consumer.put_result(user, '')
        mock_set.assert_called_with(user, score, 1.0, comment)
        self.assertEqual(response, {})

        mock_parse.return_value = (None, '')
        response = self.lti_consumer.put_result(user, '')
        mock_clear.assert_called_with(user)
        self.assertEqual(response, {})

    @patch('lti_consumer.lti.log')
    def test_verify_result_headers_verify_content_type_true(self, mock_log):
        """
        Test wrong content type raises exception if `verify_content_type` is True
        """
        request = make_request('')

        with self.assertRaises(LtiError):
            self.lti_consumer.verify_result_headers(request)

        assert mock_log.error.called

    @patch('lti_consumer.lti.verify_oauth_body_signature',
           Mock(return_value=True))
    @patch(
        'lti_consumer.lti_consumer.LtiConsumerXBlock.lti_provider_key_secret',
        PropertyMock(return_value=('t', 's')))
    def test_verify_result_headers_verify_content_type_false(self):
        """
        Test content type check skipped if `verify_content_type` is False
        """
        request = make_request('')
        request.environ['CONTENT_TYPE'] = LtiConsumer.CONTENT_TYPE_RESULT_JSON
        response = self.lti_consumer.verify_result_headers(request, False)

        self.assertTrue(response)

    @patch('lti_consumer.lti.verify_oauth_body_signature',
           Mock(return_value=True))
    @patch(
        'lti_consumer.lti_consumer.LtiConsumerXBlock.lti_provider_key_secret',
        PropertyMock(return_value=('t', 's')))
    def test_verify_result_headers_valid(self):
        """
        Test True is returned if request is valid
        """
        request = make_request('')
        request.environ['CONTENT_TYPE'] = LtiConsumer.CONTENT_TYPE_RESULT_JSON
        response = self.lti_consumer.verify_result_headers(request)

        self.assertTrue(response)

    @patch('lti_consumer.lti.verify_oauth_body_signature',
           Mock(side_effect=LtiError))
    @patch(
        'lti_consumer.lti_consumer.LtiConsumerXBlock.lti_provider_key_secret',
        PropertyMock(return_value=('t', 's')))
    @patch('lti_consumer.lti.log')
    def test_verify_result_headers_lti_error(self, mock_log):
        """
        Test exception raised if request header verification raises error
        """
        request = make_request('')
        request.environ['CONTENT_TYPE'] = LtiConsumer.CONTENT_TYPE_RESULT_JSON

        with self.assertRaises(LtiError):
            self.lti_consumer.verify_result_headers(request)

        assert mock_log.error.called

    @patch('lti_consumer.lti.verify_oauth_body_signature',
           Mock(side_effect=ValueError))
    @patch(
        'lti_consumer.lti_consumer.LtiConsumerXBlock.lti_provider_key_secret',
        PropertyMock(return_value=('t', 's')))
    @patch('lti_consumer.lti.log')
    def test_verify_result_headers_value_error(self, mock_log):
        """
        Test exception raised if request header verification raises error
        """
        request = make_request('')
        request.environ['CONTENT_TYPE'] = LtiConsumer.CONTENT_TYPE_RESULT_JSON

        with self.assertRaises(LtiError):
            self.lti_consumer.verify_result_headers(request)

        assert mock_log.error.called
Example #7
0
class TestLtiConsumer(TestLtiConsumerXBlock):
    """
    Unit tests for LtiConsumer
    """

    def setUp(self):
        super(TestLtiConsumer, self).setUp()
        self.lti_consumer = LtiConsumer(self.xblock)

    @patch(
        "lti_consumer.lti.get_oauth_request_signature",
        Mock(
            return_value=(
                'OAuth oauth_nonce="fake_nonce", '
                'oauth_timestamp="fake_timestamp", oauth_version="fake_version", oauth_signature_method="fake_method", '
                'oauth_consumer_key="fake_consumer_key", oauth_signature="fake_signature"'
            )
        ),
    )
    @patch(
        "lti_consumer.lti_consumer.LtiConsumerXBlock.prefixed_custom_parameters",
        PropertyMock(return_value={u"custom_param_1": "custom1", u"custom_param_2": "custom2"}),
    )
    @patch("lti_consumer.lti_consumer.LtiConsumerXBlock.lti_provider_key_secret", PropertyMock(return_value=("t", "s")))
    @patch("lti_consumer.lti_consumer.LtiConsumerXBlock.user_id", PropertyMock(return_value=FAKE_USER_ID))
    def test_get_signed_lti_parameters(self):
        """
        Test `get_signed_lti_parameters` returns the correct dict
        """
        self.lti_consumer.xblock.due = timezone.now()
        self.lti_consumer.xblock.graceperiod = timedelta(days=1)

        expected_lti_parameters = {
            u"user_id": self.lti_consumer.xblock.user_id,
            u"oauth_callback": u"about:blank",
            u"launch_presentation_return_url": "",
            u"lti_message_type": u"basic-lti-launch-request",
            u"lti_version": "LTI-1p0",
            u"roles": self.lti_consumer.xblock.role,
            u"resource_link_id": self.lti_consumer.xblock.resource_link_id,
            u"lis_result_sourcedid": self.lti_consumer.xblock.lis_result_sourcedid,
            u"context_id": self.lti_consumer.xblock.context_id,
            u"lis_outcome_service_url": self.lti_consumer.xblock.outcome_service_url,
            u"custom_component_display_name": self.lti_consumer.xblock.display_name,
            u"custom_component_due_date": self.lti_consumer.xblock.due.strftime("%Y-%m-%d %H:%M:%S"),
            u"custom_component_graceperiod": str(self.lti_consumer.xblock.graceperiod.total_seconds()),
            "lis_person_sourcedid": "edx",
            "lis_person_contact_email_primary": "*****@*****.**",
            "launch_presentation_locale": "en",
            u"custom_param_1": "custom1",
            u"custom_param_2": "custom2",
            u"oauth_nonce": "fake_nonce",
            "oauth_timestamp": "fake_timestamp",
            "oauth_version": "fake_version",
            "oauth_signature_method": "fake_method",
            "oauth_consumer_key": "fake_consumer_key",
            "oauth_signature": u"fake_signature",
        }
        self.lti_consumer.xblock.has_score = True
        self.lti_consumer.xblock.ask_to_send_username = True
        self.lti_consumer.xblock.ask_to_send_email = True
        self.lti_consumer.xblock.runtime.get_real_user.return_value = Mock(
            email="*****@*****.**", username="******", preferences=Mock(filter=Mock(return_value=[Mock(value="en")]))
        )
        self.assertEqual(self.lti_consumer.get_signed_lti_parameters(), expected_lti_parameters)

        # Test that `lis_person_sourcedid`, `lis_person_contact_email_primary`, and `launch_presentation_locale`
        # are not included in the returned LTI parameters when a user cannot be found
        self.lti_consumer.xblock.runtime.get_real_user.return_value = {}
        del expected_lti_parameters["lis_person_sourcedid"]
        del expected_lti_parameters["lis_person_contact_email_primary"]
        del expected_lti_parameters["launch_presentation_locale"]
        self.assertEqual(self.lti_consumer.get_signed_lti_parameters(), expected_lti_parameters)

    def test_get_result(self):
        """
        Test `get_result` returns valid json response
        """
        self.xblock.module_score = 0.9
        self.xblock.score_comment = "Great Job!"
        response = dict(GET_RESULT_RESPONSE)
        response.update({"resultScore": self.xblock.module_score, "comment": self.xblock.score_comment})
        self.assertEqual(self.lti_consumer.get_result(Mock()), response)

        self.xblock.module_score = None
        self.xblock.score_comment = ""
        self.assertEqual(self.lti_consumer.get_result(Mock()), GET_RESULT_RESPONSE)

    @patch("lti_consumer.lti_consumer.LtiConsumerXBlock.clear_user_module_score")
    def test_delete_result(self, mock_clear):
        """
        Test `delete_result` calls `LtiConsumerXBlock.clear_user_module_score`
        """
        user = Mock()
        response = self.lti_consumer.delete_result(user)

        mock_clear.assert_called_with(user)
        self.assertEqual(response, {})

    @patch("lti_consumer.lti_consumer.LtiConsumerXBlock.max_score", Mock(return_value=1.0))
    @patch("lti_consumer.lti_consumer.LtiConsumerXBlock.set_user_module_score")
    @patch("lti_consumer.lti_consumer.LtiConsumerXBlock.clear_user_module_score")
    @patch("lti_consumer.lti.parse_result_json")
    def test_put_result(self, mock_parse, mock_clear, mock_set):
        """
        Test `put_result` calls `LtiConsumerXBlock.set_user_module_score`
        or `LtiConsumerXblock.clear_user_module_score` if resultScore not included in request
        """
        user = Mock()
        score = 0.9
        comment = "Great Job!"

        mock_parse.return_value = (score, comment)
        response = self.lti_consumer.put_result(user, "")
        mock_set.assert_called_with(user, score, 1.0, comment)
        self.assertEqual(response, {})

        mock_parse.return_value = (None, "")
        response = self.lti_consumer.put_result(user, "")
        mock_clear.assert_called_with(user)
        self.assertEqual(response, {})

    @patch("lti_consumer.lti.log")
    def test_verify_result_headers_verify_content_type_true(self, mock_log):
        """
        Test wrong content type raises exception if `verify_content_type` is True
        """
        request = make_request("")

        with self.assertRaises(LtiError):
            self.lti_consumer.verify_result_headers(request)
            self.assertTrue(mock_log.called)

    @patch("lti_consumer.lti.verify_oauth_body_signature", Mock(return_value=True))
    @patch("lti_consumer.lti_consumer.LtiConsumerXBlock.lti_provider_key_secret", PropertyMock(return_value=("t", "s")))
    def test_verify_result_headers_verify_content_type_false(self):
        """
        Test content type check skipped if `verify_content_type` is False
        """
        request = make_request("")
        request.environ["CONTENT_TYPE"] = LtiConsumer.CONTENT_TYPE_RESULT_JSON
        response = self.lti_consumer.verify_result_headers(request, False)

        self.assertTrue(response)

    @patch("lti_consumer.lti.verify_oauth_body_signature", Mock(return_value=True))
    @patch("lti_consumer.lti_consumer.LtiConsumerXBlock.lti_provider_key_secret", PropertyMock(return_value=("t", "s")))
    def test_verify_result_headers_valid(self):
        """
        Test True is returned if request is valid
        """
        request = make_request("")
        request.environ["CONTENT_TYPE"] = LtiConsumer.CONTENT_TYPE_RESULT_JSON
        response = self.lti_consumer.verify_result_headers(request)

        self.assertTrue(response)

    @patch("lti_consumer.lti.verify_oauth_body_signature", Mock(side_effect=LtiError))
    @patch("lti_consumer.lti_consumer.LtiConsumerXBlock.lti_provider_key_secret", PropertyMock(return_value=("t", "s")))
    @patch("lti_consumer.lti.log")
    def test_verify_result_headers_lti_error(self, mock_log):
        """
        Test exception raised if request header verification raises error
        """
        request = make_request("")
        request.environ["CONTENT_TYPE"] = LtiConsumer.CONTENT_TYPE_RESULT_JSON

        with self.assertRaises(LtiError):
            self.lti_consumer.verify_result_headers(request)
            self.assertTrue(mock_log.called)

    @patch("lti_consumer.lti.verify_oauth_body_signature", Mock(side_effect=ValueError))
    @patch("lti_consumer.lti_consumer.LtiConsumerXBlock.lti_provider_key_secret", PropertyMock(return_value=("t", "s")))
    @patch("lti_consumer.lti.log")
    def test_verify_result_headers_value_error(self, mock_log):
        """
        Test exception raised if request header verification raises error
        """
        request = make_request("")
        request.environ["CONTENT_TYPE"] = LtiConsumer.CONTENT_TYPE_RESULT_JSON

        with self.assertRaises(LtiError):
            self.lti_consumer.verify_result_headers(request)
            self.assertTrue(mock_log.called)