Commit e1ed5c3d by David Baumgold

docstrings

parent b97f5585
...@@ -10,7 +10,9 @@ from contentstore.views.course import ( ...@@ -10,7 +10,9 @@ from contentstore.views.course import (
class TextbookIndexTestCase(CourseTestCase): class TextbookIndexTestCase(CourseTestCase):
"Test cases for the textbook index page"
def setUp(self): def setUp(self):
"Set the URL for tests"
super(TextbookIndexTestCase, self).setUp() super(TextbookIndexTestCase, self).setUp()
self.url = reverse('textbook_index', kwargs={ self.url = reverse('textbook_index', kwargs={
'org': self.course.location.org, 'org': self.course.location.org,
...@@ -19,6 +21,7 @@ class TextbookIndexTestCase(CourseTestCase): ...@@ -19,6 +21,7 @@ class TextbookIndexTestCase(CourseTestCase):
}) })
def test_view_index(self): def test_view_index(self):
"Basic check that the textbook index page responds correctly"
resp = self.client.get(self.url) resp = self.client.get(self.url)
self.assert2XX(resp.status_code) self.assert2XX(resp.status_code)
# we don't have resp.context right now, # we don't have resp.context right now,
...@@ -27,6 +30,7 @@ class TextbookIndexTestCase(CourseTestCase): ...@@ -27,6 +30,7 @@ class TextbookIndexTestCase(CourseTestCase):
self.assertEqual(resp.context['course'], self.course) self.assertEqual(resp.context['course'], self.course)
def test_view_index_xhr(self): def test_view_index_xhr(self):
"Check that we get a JSON response when requested via AJAX"
resp = self.client.get( resp = self.client.get(
self.url, self.url,
HTTP_ACCEPT="application/json", HTTP_ACCEPT="application/json",
...@@ -37,6 +41,7 @@ class TextbookIndexTestCase(CourseTestCase): ...@@ -37,6 +41,7 @@ class TextbookIndexTestCase(CourseTestCase):
self.assertEqual(self.course.pdf_textbooks, obj) self.assertEqual(self.course.pdf_textbooks, obj)
def test_view_index_xhr_content(self): def test_view_index_xhr_content(self):
"Check that the response maps to the content of the modulestore"
content = [ content = [
{ {
"tab_title": "my textbook", "tab_title": "my textbook",
...@@ -70,6 +75,7 @@ class TextbookIndexTestCase(CourseTestCase): ...@@ -70,6 +75,7 @@ class TextbookIndexTestCase(CourseTestCase):
self.assertEqual(content, obj) self.assertEqual(content, obj)
def test_view_index_xhr_post(self): def test_view_index_xhr_post(self):
"Check that you can save information to the server"
textbooks = [ textbooks = [
{"tab_title": "Hi, mom!"}, {"tab_title": "Hi, mom!"},
{"tab_title": "Textbook 2"}, {"tab_title": "Textbook 2"},
...@@ -94,6 +100,7 @@ class TextbookIndexTestCase(CourseTestCase): ...@@ -94,6 +100,7 @@ class TextbookIndexTestCase(CourseTestCase):
self.assertEqual(no_ids, textbooks) self.assertEqual(no_ids, textbooks)
def test_view_index_xhr_post_invalid(self): def test_view_index_xhr_post_invalid(self):
"Check that you can't save invalid JSON"
resp = self.client.post( resp = self.client.post(
self.url, self.url,
data="invalid", data="invalid",
...@@ -107,7 +114,10 @@ class TextbookIndexTestCase(CourseTestCase): ...@@ -107,7 +114,10 @@ class TextbookIndexTestCase(CourseTestCase):
class TextbookCreateTestCase(CourseTestCase): class TextbookCreateTestCase(CourseTestCase):
"Test cases for creating a new PDF textbook"
def setUp(self): def setUp(self):
"Set up a url and some textbook content for tests"
super(TextbookCreateTestCase, self).setUp() super(TextbookCreateTestCase, self).setUp()
self.url = reverse('create_textbook', kwargs={ self.url = reverse('create_textbook', kwargs={
'org': self.course.location.org, 'org': self.course.location.org,
...@@ -123,6 +133,7 @@ class TextbookCreateTestCase(CourseTestCase): ...@@ -123,6 +133,7 @@ class TextbookCreateTestCase(CourseTestCase):
} }
def test_happy_path(self): def test_happy_path(self):
"Test that you can create a textbook"
resp = self.client.post( resp = self.client.post(
self.url, self.url,
data=json.dumps(self.textbook), data=json.dumps(self.textbook),
...@@ -138,6 +149,7 @@ class TextbookCreateTestCase(CourseTestCase): ...@@ -138,6 +149,7 @@ class TextbookCreateTestCase(CourseTestCase):
self.assertEqual(self.textbook, textbook) self.assertEqual(self.textbook, textbook)
def test_get(self): def test_get(self):
"Test that GET is not allowed"
resp = self.client.get( resp = self.client.get(
self.url, self.url,
HTTP_ACCEPT="application/json", HTTP_ACCEPT="application/json",
...@@ -146,6 +158,7 @@ class TextbookCreateTestCase(CourseTestCase): ...@@ -146,6 +158,7 @@ class TextbookCreateTestCase(CourseTestCase):
self.assertEqual(resp.status_code, 405) self.assertEqual(resp.status_code, 405)
def test_valid_id(self): def test_valid_id(self):
"Textbook IDs must begin with a number; try a valid one"
self.textbook["id"] = "7x5" self.textbook["id"] = "7x5"
resp = self.client.post( resp = self.client.post(
self.url, self.url,
...@@ -159,6 +172,7 @@ class TextbookCreateTestCase(CourseTestCase): ...@@ -159,6 +172,7 @@ class TextbookCreateTestCase(CourseTestCase):
self.assertEqual(self.textbook, textbook) self.assertEqual(self.textbook, textbook)
def test_invalid_id(self): def test_invalid_id(self):
"Textbook IDs must begin with a number; try an invalid one"
self.textbook["id"] = "xxx" self.textbook["id"] = "xxx"
resp = self.client.post( resp = self.client.post(
self.url, self.url,
...@@ -172,7 +186,10 @@ class TextbookCreateTestCase(CourseTestCase): ...@@ -172,7 +186,10 @@ class TextbookCreateTestCase(CourseTestCase):
class TextbookByIdTestCase(CourseTestCase): class TextbookByIdTestCase(CourseTestCase):
"Test cases for the `textbook_by_id` view"
def setUp(self): def setUp(self):
"Set some useful content and URLs for tests"
super(TextbookByIdTestCase, self).setUp() super(TextbookByIdTestCase, self).setUp()
self.textbook1 = { self.textbook1 = {
"tab_title": "Economics", "tab_title": "Economics",
...@@ -213,34 +230,40 @@ class TextbookByIdTestCase(CourseTestCase): ...@@ -213,34 +230,40 @@ class TextbookByIdTestCase(CourseTestCase):
}) })
def test_get_1(self): def test_get_1(self):
"Get the first textbook"
resp = self.client.get(self.url1) resp = self.client.get(self.url1)
self.assert2XX(resp.status_code) self.assert2XX(resp.status_code)
compare = json.loads(resp.content) compare = json.loads(resp.content)
self.assertEqual(compare, self.textbook1) self.assertEqual(compare, self.textbook1)
def test_get_2(self): def test_get_2(self):
"Get the second textbook"
resp = self.client.get(self.url2) resp = self.client.get(self.url2)
self.assert2XX(resp.status_code) self.assert2XX(resp.status_code)
compare = json.loads(resp.content) compare = json.loads(resp.content)
self.assertEqual(compare, self.textbook2) self.assertEqual(compare, self.textbook2)
def test_get_nonexistant(self): def test_get_nonexistant(self):
"Get a nonexistent textbook"
resp = self.client.get(self.url_nonexist) resp = self.client.get(self.url_nonexist)
self.assertEqual(resp.status_code, 404) self.assertEqual(resp.status_code, 404)
def test_delete(self): def test_delete(self):
"Delete a textbook by ID"
resp = self.client.delete(self.url1) resp = self.client.delete(self.url1)
self.assert2XX(resp.status_code) self.assert2XX(resp.status_code)
course = self.store.get_item(self.course.location) course = self.store.get_item(self.course.location)
self.assertEqual(course.pdf_textbooks, [self.textbook2]) self.assertEqual(course.pdf_textbooks, [self.textbook2])
def test_delete_nonexistant(self): def test_delete_nonexistant(self):
"Delete a textbook by ID, when the ID doesn't match an existing textbook"
resp = self.client.delete(self.url_nonexist) resp = self.client.delete(self.url_nonexist)
self.assertEqual(resp.status_code, 404) self.assertEqual(resp.status_code, 404)
course = self.store.get_item(self.course.location) course = self.store.get_item(self.course.location)
self.assertEqual(course.pdf_textbooks, [self.textbook1, self.textbook2]) self.assertEqual(course.pdf_textbooks, [self.textbook1, self.textbook2])
def test_create_new_by_id(self): def test_create_new_by_id(self):
"Create a textbook by ID"
textbook = { textbook = {
"tab_title": "a new textbook", "tab_title": "a new textbook",
"url": "supercool.pdf", "url": "supercool.pdf",
...@@ -269,6 +292,7 @@ class TextbookByIdTestCase(CourseTestCase): ...@@ -269,6 +292,7 @@ class TextbookByIdTestCase(CourseTestCase):
) )
def test_replace_by_id(self): def test_replace_by_id(self):
"Create a textbook by ID, overwriting an existing textbook ID"
replacement = { replacement = {
"tab_title": "You've been replaced!", "tab_title": "You've been replaced!",
"url": "supercool.pdf", "url": "supercool.pdf",
...@@ -292,7 +316,10 @@ class TextbookByIdTestCase(CourseTestCase): ...@@ -292,7 +316,10 @@ class TextbookByIdTestCase(CourseTestCase):
class TextbookValidationTestCase(TestCase): class TextbookValidationTestCase(TestCase):
"Tests for the code to validate the structure of a PDF textbook"
def setUp(self): def setUp(self):
"Set some useful content for tests"
self.tb1 = { self.tb1 = {
"tab_title": "Hi, mom!", "tab_title": "Hi, mom!",
"url": "/mom.pdf" "url": "/mom.pdf"
...@@ -312,52 +339,64 @@ class TextbookValidationTestCase(TestCase): ...@@ -312,52 +339,64 @@ class TextbookValidationTestCase(TestCase):
self.textbooks = [self.tb1, self.tb2] self.textbooks = [self.tb1, self.tb2]
def test_happy_path_plural(self): def test_happy_path_plural(self):
"Test that the plural validator works properly"
result = validate_textbooks_json(json.dumps(self.textbooks)) result = validate_textbooks_json(json.dumps(self.textbooks))
self.assertEqual(self.textbooks, result) self.assertEqual(self.textbooks, result)
def test_happy_path_singular_1(self): def test_happy_path_singular_1(self):
"Test that the singular validator works properly"
result = validate_textbook_json(json.dumps(self.tb1)) result = validate_textbook_json(json.dumps(self.tb1))
self.assertEqual(self.tb1, result) self.assertEqual(self.tb1, result)
def test_happy_path_singular_2(self): def test_happy_path_singular_2(self):
"Test that the singular validator works properly, with different data"
result = validate_textbook_json(json.dumps(self.tb2)) result = validate_textbook_json(json.dumps(self.tb2))
self.assertEqual(self.tb2, result) self.assertEqual(self.tb2, result)
def test_valid_id(self): def test_valid_id(self):
"Test that a valid ID doesn't trip the validator, and comes out unchanged"
self.tb1["id"] = 1 self.tb1["id"] = 1
result = validate_textbook_json(json.dumps(self.tb1)) result = validate_textbook_json(json.dumps(self.tb1))
self.assertEqual(self.tb1, result) self.assertEqual(self.tb1, result)
def test_invalid_id(self): def test_invalid_id(self):
"Test that an invalid ID trips the validator"
self.tb1["id"] = "abc" self.tb1["id"] = "abc"
with self.assertRaises(TextbookValidationError): with self.assertRaises(TextbookValidationError):
validate_textbook_json(json.dumps(self.tb1)) validate_textbook_json(json.dumps(self.tb1))
def test_invalid_json_plural(self): def test_invalid_json_plural(self):
"Test that invalid JSON trips the plural validator"
with self.assertRaises(TextbookValidationError): with self.assertRaises(TextbookValidationError):
validate_textbooks_json("[{'abc'}]") validate_textbooks_json("[{'abc'}]")
def test_invalid_json_singular(self): def test_invalid_json_singular(self):
"Test that invalid JSON trips the singluar validator"
with self.assertRaises(TextbookValidationError): with self.assertRaises(TextbookValidationError):
validate_textbook_json("[{1]}") validate_textbook_json("[{1]}")
def test_wrong_json_plural(self): def test_wrong_json_plural(self):
"Test that a JSON object trips the plural validators (requires a list)"
with self.assertRaises(TextbookValidationError): with self.assertRaises(TextbookValidationError):
validate_textbooks_json('{"tab_title": "Hi, mom!"}') validate_textbooks_json('{"tab_title": "Hi, mom!"}')
def test_wrong_json_singular(self): def test_wrong_json_singular(self):
"Test that a JSON list trips the plural validators (requires an object)"
with self.assertRaises(TextbookValidationError): with self.assertRaises(TextbookValidationError):
validate_textbook_json('[{"tab_title": "Hi, mom!"}, {"tab_title": "Hi, dad!"}]') validate_textbook_json('[{"tab_title": "Hi, mom!"}, {"tab_title": "Hi, dad!"}]')
def test_no_tab_title_plural(self): def test_no_tab_title_plural(self):
"Test that `tab_title` is required for the plural validator"
with self.assertRaises(TextbookValidationError): with self.assertRaises(TextbookValidationError):
validate_textbooks_json('[{"url": "/textbook.pdf"}]') validate_textbooks_json('[{"url": "/textbook.pdf"}]')
def test_no_tab_title_singular(self): def test_no_tab_title_singular(self):
"Test that `tab_title` is required for the singular validator"
with self.assertRaises(TextbookValidationError): with self.assertRaises(TextbookValidationError):
validate_textbook_json('{"url": "/textbook.pdf"}') validate_textbook_json('{"url": "/textbook.pdf"}')
def test_duplicate_ids(self): def test_duplicate_ids(self):
"Test that duplicate IDs in the plural validator trips the validator"
textbooks = [{ textbooks = [{
"tab_title": "name one", "tab_title": "name one",
"url": "one.pdf", "url": "one.pdf",
......
...@@ -38,6 +38,10 @@ __all__ = ['asset_index', 'upload_asset', 'import_course', 'generate_export_cour ...@@ -38,6 +38,10 @@ __all__ = ['asset_index', 'upload_asset', 'import_course', 'generate_export_cour
def assets_to_json_dict(assets): def assets_to_json_dict(assets):
"""
Transform the results of a contentstore query into something appropriate
for output via JSON.
"""
ret = [] ret = []
for asset in assets: for asset in assets:
obj = { obj = {
......
...@@ -509,6 +509,9 @@ def textbook_index(request, org, course, name): ...@@ -509,6 +509,9 @@ def textbook_index(request, org, course, name):
@login_required @login_required
@ensure_csrf_cookie @ensure_csrf_cookie
def create_textbook(request, org, course, name): def create_textbook(request, org, course, name):
"""
JSON API endpoint for creating a textbook. Used by the Backbone application.
"""
location = get_location_and_verify_access(request, org, course, name) location = get_location_and_verify_access(request, org, course, name)
store = get_modulestore(location) store = get_modulestore(location)
course_module = store.get_item(location, depth=0) course_module = store.get_item(location, depth=0)
...@@ -542,6 +545,10 @@ def create_textbook(request, org, course, name): ...@@ -542,6 +545,10 @@ def create_textbook(request, org, course, name):
@ensure_csrf_cookie @ensure_csrf_cookie
@require_http_methods(("GET", "POST", "DELETE")) @require_http_methods(("GET", "POST", "DELETE"))
def textbook_by_id(request, org, course, name, tid): def textbook_by_id(request, org, course, name, tid):
"""
JSON API endpoint for manipulating a textbook via its internal ID.
Used by the Backbone application.
"""
location = get_location_and_verify_access(request, org, course, name) location = get_location_and_verify_access(request, org, course, name)
store = get_modulestore(location) store = get_modulestore(location)
course_module = store.get_item(location, depth=3) course_module = store.get_item(location, depth=3)
......
...@@ -17,11 +17,13 @@ __all__ = ['edit_tabs', 'reorder_static_tabs', 'static_pages', 'edit_static'] ...@@ -17,11 +17,13 @@ __all__ = ['edit_tabs', 'reorder_static_tabs', 'static_pages', 'edit_static']
def initialize_course_tabs(course): def initialize_course_tabs(course):
# set up the default tabs """
# I've added this because when we add static tabs, the LMS either expects a None for the tabs list or set up the default tabs
# at least a list populated with the minimal times I've added this because when we add static tabs, the LMS either expects a None for the tabs list or
# @TODO: I don't like the fact that the presentation tier is away of these data related constraints, let's find a better at least a list populated with the minimal times
# place for this. Also rather than using a simple list of dictionaries a nice class model would be helpful here @TODO: I don't like the fact that the presentation tier is away of these data related constraints, let's find a better
place for this. Also rather than using a simple list of dictionaries a nice class model would be helpful here
"""
# This logic is repeated in xmodule/modulestore/tests/factories.py # This logic is repeated in xmodule/modulestore/tests/factories.py
# so if you change anything here, you need to also change it there. # so if you change anything here, you need to also change it there.
......
...@@ -186,21 +186,33 @@ class ModuleStoreTestCase(TestCase): ...@@ -186,21 +186,33 @@ class ModuleStoreTestCase(TestCase):
super(ModuleStoreTestCase, self)._post_teardown() super(ModuleStoreTestCase, self)._post_teardown()
def assert2XX(self, status_code, msg=None): def assert2XX(self, status_code, msg=None):
"""
Assert that the given value is a success status (between 200 and 299)
"""
if not 200 <= status_code < 300: if not 200 <= status_code < 300:
msg = self._formatMessage(msg, "%s is not a success status" % safe_repr(status_code)) msg = self._formatMessage(msg, "%s is not a success status" % safe_repr(status_code))
raise self.failureExecption(msg) raise self.failureExecption(msg)
def assert3XX(self, status_code, msg=None): def assert3XX(self, status_code, msg=None):
"""
Assert that the given value is a redirection status (between 300 and 399)
"""
if not 300 <= status_code < 400: if not 300 <= status_code < 400:
msg = self._formatMessage(msg, "%s is not a redirection status" % safe_repr(status_code)) msg = self._formatMessage(msg, "%s is not a redirection status" % safe_repr(status_code))
raise self.failureExecption(msg) raise self.failureExecption(msg)
def assert4XX(self, status_code, msg=None): def assert4XX(self, status_code, msg=None):
"""
Assert that the given value is a client error status (between 400 and 499)
"""
if not 400 <= status_code < 500: if not 400 <= status_code < 500:
msg = self._formatMessage(msg, "%s is not a client error status" % safe_repr(status_code)) msg = self._formatMessage(msg, "%s is not a client error status" % safe_repr(status_code))
raise self.failureExecption(msg) raise self.failureExecption(msg)
def assert5XX(self, status_code, msg=None): def assert5XX(self, status_code, msg=None):
"""
Assert that the given value is a server error status (between 500 and 599)
"""
if not 500 <= status_code < 600: if not 500 <= status_code < 600:
msg = self._formatMessage(msg, "%s is not a server error status" % safe_repr(status_code)) msg = self._formatMessage(msg, "%s is not a server error status" % safe_repr(status_code))
raise self.failureExecption(msg) raise self.failureExecption(msg)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment