test_xqueue_stub.py 5.96 KB
Newer Older
1 2 3 4 5 6 7
"""
Unit tests for stub XQueue implementation.
"""

import mock
import unittest
import json
8
import requests
Ned Batchelder committed
9
from ..xqueue import StubXQueueService
10 11


12 13 14 15 16 17 18 19 20 21 22
class FakeTimer(object):
    """
    Fake timer implementation that executes immediately.
    """
    def __init__(self, delay, func):
        self.func = func

    def start(self):
        self.func()


23 24 25
class StubXQueueServiceTest(unittest.TestCase):

    def setUp(self):
26
        super(StubXQueueServiceTest, self).setUp()
27
        self.server = StubXQueueService()
28
        self.url = "http://127.0.0.1:{0}/xqueue/submit".format(self.server.port)
29 30
        self.addCleanup(self.server.shutdown)

31 32 33 34 35 36 37 38 39 40
        # Patch the timer async calls
        patcher = mock.patch('terrain.stubs.xqueue.post')
        self.post = patcher.start()
        self.addCleanup(patcher.stop)

        # Patch POST requests
        patcher = mock.patch('terrain.stubs.xqueue.Timer')
        timer = patcher.start()
        timer.side_effect = FakeTimer
        self.addCleanup(patcher.stop)
41

42
    def test_grade_request(self):
43

44
        # Post a submission to the stub XQueue
45
        callback_url = 'http://127.0.0.1:8000/test_callback'
46 47 48 49 50 51 52 53
        expected_header = self._post_submission(
            callback_url, 'test_queuekey', 'test_queue',
            json.dumps({
                'student_info': 'test',
                'grader_payload': 'test',
                'student_response': 'test'
            })
        )
54

55 56 57
        # Check the response we receive
        # (Should be the default grading response)
        expected_body = json.dumps({'correct': True, 'score': 1, 'msg': '<div></div>'})
58
        self._check_grade_response(callback_url, expected_header, expected_body)
59

60
    def test_configure_default_response(self):
61

62 63 64
        # Configure the default response for submissions to any queue
        response_content = {'test_response': 'test_content'}
        self.server.config['default'] = response_content
65

66 67 68 69 70 71 72 73 74
        # Post a submission to the stub XQueue
        callback_url = 'http://127.0.0.1:8000/test_callback'
        expected_header = self._post_submission(
            callback_url, 'test_queuekey', 'test_queue',
            json.dumps({
                'student_info': 'test',
                'grader_payload': 'test',
                'student_response': 'test'
            })
75 76
        )

77 78
        # Check the response we receive
        # (Should be the default grading response)
79
        self._check_grade_response(callback_url, expected_header, json.dumps(response_content))
80

81
    def test_configure_specific_response(self):
82 83 84 85

        # Configure the XQueue stub response to any submission to the test queue
        response_content = {'test_response': 'test_content'}
        self.server.config['This is only a test.'] = response_content
86

87 88 89 90 91
        # Post a submission to the XQueue stub
        callback_url = 'http://127.0.0.1:8000/test_callback'
        expected_header = self._post_submission(
            callback_url, 'test_queuekey', 'test_queue',
            json.dumps({'submission': 'This is only a test.'})
92 93
        )

94
        # Check that we receive the response we configured
95
        self._check_grade_response(callback_url, expected_header, json.dumps(response_content))
96

97
    def test_multiple_response_matches(self):
98 99 100 101 102 103 104 105 106 107

        # Configure the XQueue stub with two responses that
        # match the same submission
        self.server.config['test_1'] = {'response': True}
        self.server.config['test_2'] = {'response': False}

        with mock.patch('terrain.stubs.http.LOGGER') as logger:

            # Post a submission to the XQueue stub
            callback_url = 'http://127.0.0.1:8000/test_callback'
108
            self._post_submission(
109 110 111 112 113 114
                callback_url, 'test_queuekey', 'test_queue',
                json.dumps({'submission': 'test_1 and test_2'})
            )

            # Expect that we do NOT receive a response
            # and that an error message is logged
115
            self.assertFalse(self.post.called)
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
            self.assertTrue(logger.error.called)

    def _post_submission(self, callback_url, lms_key, queue_name, xqueue_body):
        """
        Post a submission to the stub XQueue implementation.
        `callback_url` is the URL at which we expect to receive a grade response
        `lms_key` is the authentication key sent in the header
        `queue_name` is the name of the queue in which to send put the submission
        `xqueue_body` is the content of the submission

        Returns the header (a string) we send with the submission, which can
        be used to validate the response we receive from the stub.
        """

        # Post a submission to the XQueue stub
        grade_request = {
            'xqueue_header': json.dumps({
                'lms_callback_url': callback_url,
                'lms_key': 'test_queuekey',
                'queue_name': 'test_queue'
            }),
137 138 139
            'xqueue_body': xqueue_body
        }

140 141 142 143 144 145 146 147
        resp = requests.post(self.url, data=grade_request)

        # Expect that the response is success
        self.assertEqual(resp.status_code, 200)

        # Return back the header, so we can authenticate the response we receive
        return grade_request['xqueue_header']

148
    def _check_grade_response(self, callback_url, expected_header, expected_body):
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
        """
        Verify that the stub sent a POST request back to us
        with the expected data.

        `callback_url` is the URL we expect the stub to POST to
        `expected_header` is the header (a string) we expect to receive with the grade.
        `expected_body` is the content (a string) we expect to receive with the grade.

        Raises an `AssertionError` if the check fails.
        """
        # Check the response posted back to us
        # This is the default response
        expected_callback_dict = {
            'xqueue_header': expected_header,
            'xqueue_body': expected_body,
        }
165 166

        # Check that the POST request was made with the correct params
167
        self.post.assert_called_with(callback_url, data=expected_callback_dict)