373 lines
12 KiB
Python
373 lines
12 KiB
Python
"""
|
|
Unit tests for stub EdxNotes implementation.
|
|
"""
|
|
|
|
|
|
import json
|
|
import unittest
|
|
from uuid import uuid4
|
|
|
|
import ddt
|
|
import requests
|
|
import six
|
|
|
|
from ..edxnotes import StubEdxNotesService
|
|
|
|
|
|
@ddt.ddt
|
|
class StubEdxNotesServiceTest(unittest.TestCase):
|
|
"""
|
|
Test cases for the stub EdxNotes service.
|
|
"""
|
|
def setUp(self):
|
|
"""
|
|
Start the stub server.
|
|
"""
|
|
super().setUp()
|
|
self.server = StubEdxNotesService()
|
|
dummy_notes = self._get_dummy_notes(count=5)
|
|
self.server.add_notes(dummy_notes)
|
|
self.addCleanup(self.server.shutdown)
|
|
|
|
def _get_dummy_notes(self, count=1):
|
|
"""
|
|
Returns a list of dummy notes.
|
|
"""
|
|
return [self._get_dummy_note(i) for i in range(count)]
|
|
|
|
def _get_dummy_note(self, uid=0):
|
|
"""
|
|
Returns a single dummy note.
|
|
"""
|
|
nid = uuid4().hex
|
|
return {
|
|
"id": nid,
|
|
"created": "2014-10-31T10:05:00.000000",
|
|
"updated": "2014-10-31T10:50:00.101010",
|
|
"user": "dummy-user-id",
|
|
"usage_id": "dummy-usage-id-" + str(uid),
|
|
"course_id": "dummy-course-id",
|
|
"text": "dummy note text " + nid,
|
|
"quote": "dummy note quote",
|
|
"ranges": [
|
|
{
|
|
"start": "/p[1]",
|
|
"end": "/p[1]",
|
|
"startOffset": 0,
|
|
"endOffset": 10,
|
|
}
|
|
],
|
|
}
|
|
|
|
def test_note_create(self):
|
|
dummy_note = {
|
|
"user": "dummy-user-id",
|
|
"usage_id": "dummy-usage-id",
|
|
"course_id": "dummy-course-id",
|
|
"text": "dummy note text",
|
|
"quote": "dummy note quote",
|
|
"ranges": [
|
|
{
|
|
"start": "/p[1]",
|
|
"end": "/p[1]",
|
|
"startOffset": 0,
|
|
"endOffset": 10,
|
|
}
|
|
],
|
|
}
|
|
response = requests.post(self._get_url("api/v1/annotations"), data=json.dumps(dummy_note))
|
|
assert response.ok
|
|
response_content = response.json()
|
|
assert 'id' in response_content
|
|
assert 'created' in response_content
|
|
assert 'updated' in response_content
|
|
assert 'annotator_schema_version' in response_content
|
|
self.assertDictContainsSubset(dummy_note, response_content)
|
|
|
|
def test_note_read(self):
|
|
notes = self._get_notes()
|
|
for note in notes:
|
|
response = requests.get(self._get_url("api/v1/annotations/" + note["id"]))
|
|
assert response.ok
|
|
self.assertDictEqual(note, response.json())
|
|
|
|
response = requests.get(self._get_url("api/v1/annotations/does_not_exist"))
|
|
assert response.status_code == 404
|
|
|
|
def test_note_update(self):
|
|
notes = self._get_notes()
|
|
for note in notes:
|
|
response = requests.get(self._get_url("api/v1/annotations/" + note["id"]))
|
|
assert response.ok
|
|
self.assertDictEqual(note, response.json())
|
|
|
|
response = requests.get(self._get_url("api/v1/annotations/does_not_exist"))
|
|
assert response.status_code == 404
|
|
|
|
def test_search(self):
|
|
# Without user
|
|
response = requests.get(self._get_url("api/v1/search"))
|
|
assert response.status_code == 400
|
|
|
|
# get response with default page and page size
|
|
response = requests.get(self._get_url("api/v1/search"), params={
|
|
"user": "dummy-user-id",
|
|
"course_id": "dummy-course-id",
|
|
})
|
|
|
|
assert response.ok
|
|
self._verify_pagination_info(
|
|
response=response.json(),
|
|
total_notes=5,
|
|
num_pages=3,
|
|
notes_per_page=2,
|
|
start=0,
|
|
current_page=1,
|
|
next_page=2,
|
|
previous_page=None
|
|
)
|
|
|
|
# search notes with text that don't exist
|
|
response = requests.get(self._get_url("api/v1/search"), params={
|
|
"user": "dummy-user-id",
|
|
"course_id": "dummy-course-id",
|
|
"text": "world war 2"
|
|
})
|
|
|
|
assert response.ok
|
|
self._verify_pagination_info(
|
|
response=response.json(),
|
|
total_notes=0,
|
|
num_pages=0,
|
|
notes_per_page=0,
|
|
start=0,
|
|
current_page=1,
|
|
next_page=None,
|
|
previous_page=None
|
|
)
|
|
|
|
@ddt.data(
|
|
'?usage_id=dummy-usage-id-0',
|
|
'?usage_id=dummy-usage-id-0&usage_id=dummy-usage-id-1&dummy-usage-id-2&dummy-usage-id-3&dummy-usage-id-4'
|
|
)
|
|
def test_search_usage_ids(self, usage_ids):
|
|
"""
|
|
Test search with usage ids.
|
|
"""
|
|
url = self._get_url('api/v1/search') + usage_ids
|
|
response = requests.get(url, params={
|
|
'user': 'dummy-user-id',
|
|
'course_id': 'dummy-course-id'
|
|
})
|
|
assert response.ok
|
|
response = response.json()
|
|
parsed = six.moves.urllib.parse.urlparse(url)
|
|
query_params = six.moves.urllib.parse.parse_qs(parsed.query)
|
|
query_params['usage_id'].reverse()
|
|
assert len(response) == len(query_params['usage_id'])
|
|
for index, usage_id in enumerate(query_params['usage_id']):
|
|
assert response[index]['usage_id'] == usage_id
|
|
|
|
def test_delete(self):
|
|
notes = self._get_notes()
|
|
response = requests.delete(self._get_url("api/v1/annotations/does_not_exist"))
|
|
assert response.status_code == 404
|
|
|
|
for note in notes:
|
|
response = requests.delete(self._get_url("api/v1/annotations/" + note["id"]))
|
|
assert response.status_code == 204
|
|
remaining_notes = self.server.get_all_notes()
|
|
assert note['id'] not in [note['id'] for note in remaining_notes]
|
|
|
|
assert len(remaining_notes) == 0
|
|
|
|
def test_update(self):
|
|
note = self._get_notes()[0]
|
|
response = requests.put(self._get_url("api/v1/annotations/" + note["id"]), data=json.dumps({
|
|
"text": "new test text"
|
|
}))
|
|
assert response.status_code == 200
|
|
|
|
updated_note = self._get_notes()[0]
|
|
assert 'new test text' == updated_note['text']
|
|
assert note['id'] == updated_note['id']
|
|
self.assertCountEqual(note, updated_note)
|
|
|
|
response = requests.get(self._get_url("api/v1/annotations/does_not_exist"))
|
|
assert response.status_code == 404
|
|
|
|
# pylint: disable=too-many-arguments
|
|
def _verify_pagination_info(
|
|
self,
|
|
response,
|
|
total_notes,
|
|
num_pages,
|
|
notes_per_page,
|
|
current_page,
|
|
previous_page,
|
|
next_page,
|
|
start
|
|
):
|
|
"""
|
|
Verify the pagination information.
|
|
|
|
Argument:
|
|
response: response from api
|
|
total_notes: total notes in the response
|
|
num_pages: total number of pages in response
|
|
notes_per_page: number of notes in the response
|
|
current_page: current page number
|
|
previous_page: previous page number
|
|
next_page: next page number
|
|
start: start of the current page
|
|
"""
|
|
def get_page_value(url):
|
|
"""
|
|
Return page value extracted from url.
|
|
"""
|
|
if url is None:
|
|
return None
|
|
|
|
parsed = six.moves.urllib.parse.urlparse(url)
|
|
query_params = six.moves.urllib.parse.parse_qs(parsed.query)
|
|
|
|
page = query_params["page"][0]
|
|
return page if page is None else int(page)
|
|
|
|
assert response['total'] == total_notes
|
|
assert response['num_pages'] == num_pages
|
|
assert len(response['rows']) == notes_per_page
|
|
assert response['current_page'] == current_page
|
|
assert get_page_value(response['previous']) == previous_page
|
|
assert get_page_value(response['next']) == next_page
|
|
assert response['start'] == start
|
|
|
|
def test_notes_collection(self):
|
|
"""
|
|
Test paginated response of notes api
|
|
"""
|
|
|
|
# Without user
|
|
response = requests.get(self._get_url("api/v1/annotations"))
|
|
assert response.status_code == 400
|
|
|
|
# Without any pagination parameters
|
|
response = requests.get(self._get_url("api/v1/annotations"), params={"user": "dummy-user-id"})
|
|
|
|
assert response.ok
|
|
self._verify_pagination_info(
|
|
response=response.json(),
|
|
total_notes=5,
|
|
num_pages=3,
|
|
notes_per_page=2,
|
|
start=0,
|
|
current_page=1,
|
|
next_page=2,
|
|
previous_page=None
|
|
)
|
|
|
|
# With pagination parameters
|
|
response = requests.get(self._get_url("api/v1/annotations"), params={
|
|
"user": "dummy-user-id",
|
|
"page": 2,
|
|
"page_size": 3
|
|
})
|
|
|
|
assert response.ok
|
|
self._verify_pagination_info(
|
|
response=response.json(),
|
|
total_notes=5,
|
|
num_pages=2,
|
|
notes_per_page=2,
|
|
start=3,
|
|
current_page=2,
|
|
next_page=None,
|
|
previous_page=1
|
|
)
|
|
|
|
def test_notes_collection_next_previous_with_one_page(self):
|
|
"""
|
|
Test next and previous urls of paginated response of notes api
|
|
when number of pages are 1
|
|
"""
|
|
response = requests.get(self._get_url("api/v1/annotations"), params={
|
|
"user": "dummy-user-id",
|
|
"page_size": 10
|
|
})
|
|
|
|
assert response.ok
|
|
self._verify_pagination_info(
|
|
response=response.json(),
|
|
total_notes=5,
|
|
num_pages=1,
|
|
notes_per_page=5,
|
|
start=0,
|
|
current_page=1,
|
|
next_page=None,
|
|
previous_page=None
|
|
)
|
|
|
|
def test_notes_collection_when_no_notes(self):
|
|
"""
|
|
Test paginated response of notes api when there's no note present
|
|
"""
|
|
|
|
# Delete all notes
|
|
self.test_cleanup()
|
|
|
|
# Get default page
|
|
response = requests.get(self._get_url("api/v1/annotations"), params={"user": "dummy-user-id"})
|
|
assert response.ok
|
|
self._verify_pagination_info(
|
|
response=response.json(),
|
|
total_notes=0,
|
|
num_pages=0,
|
|
notes_per_page=0,
|
|
start=0,
|
|
current_page=1,
|
|
next_page=None,
|
|
previous_page=None
|
|
)
|
|
|
|
def test_cleanup(self):
|
|
response = requests.put(self._get_url("cleanup"))
|
|
assert response.ok
|
|
assert len(self.server.get_all_notes()) == 0
|
|
|
|
def test_create_notes(self):
|
|
dummy_notes = self._get_dummy_notes(count=2)
|
|
response = requests.post(self._get_url("create_notes"), data=json.dumps(dummy_notes))
|
|
assert response.ok
|
|
assert len(self._get_notes()) == 7
|
|
|
|
response = requests.post(self._get_url("create_notes"))
|
|
assert response.status_code == 400
|
|
|
|
def test_headers(self):
|
|
note = self._get_notes()[0]
|
|
response = requests.get(self._get_url("api/v1/annotations/" + note["id"]))
|
|
assert response.ok
|
|
assert response.headers.get('access-control-allow-origin') == '*'
|
|
|
|
response = requests.options(self._get_url("api/v1/annotations/"))
|
|
assert response.ok
|
|
assert response.headers.get('access-control-allow-origin') == '*'
|
|
assert response.headers.get('access-control-allow-methods') == 'GET, POST, PUT, DELETE, OPTIONS'
|
|
assert 'X-CSRFToken' in response.headers.get('access-control-allow-headers')
|
|
|
|
def _get_notes(self):
|
|
"""
|
|
Return a list of notes from the stub EdxNotes service.
|
|
"""
|
|
notes = self.server.get_all_notes()
|
|
assert len(notes) > 0, 'Notes are empty.'
|
|
return notes
|
|
|
|
def _get_url(self, path):
|
|
"""
|
|
Construt a URL to the stub EdxNotes service.
|
|
"""
|
|
return "http://127.0.0.1:{port}/{path}/".format(
|
|
port=self.server.port, path=path
|
|
)
|