Adding and fixing tests
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
""" Unit tests for checklist methods in views.py. """
|
||||
from contentstore.utils import get_modulestore, get_url_reverse
|
||||
from contentstore.tests.test_course_settings import CourseTestCase
|
||||
from xmodule.modulestore.inheritance import own_metadata
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
from django.core.urlresolvers import reverse
|
||||
import json
|
||||
from .utils import CourseTestCase
|
||||
|
||||
|
||||
class ChecklistTestCase(CourseTestCase):
|
||||
|
||||
@@ -6,8 +6,6 @@ import json
|
||||
import copy
|
||||
import mock
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.test.client import Client
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.utils.timezone import UTC
|
||||
from django.test.utils import override_settings
|
||||
@@ -17,45 +15,12 @@ from models.settings.course_details import (CourseDetails, CourseSettingsEncoder
|
||||
from models.settings.course_grading import CourseGradingModel
|
||||
from contentstore.utils import get_modulestore
|
||||
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
|
||||
from models.settings.course_metadata import CourseMetadata
|
||||
from xmodule.modulestore.xml_importer import import_from_xml
|
||||
from xmodule.fields import Date
|
||||
|
||||
|
||||
class CourseTestCase(ModuleStoreTestCase):
|
||||
"""
|
||||
Base class for test classes below.
|
||||
"""
|
||||
def setUp(self):
|
||||
"""
|
||||
These tests need a user in the DB so that the django Test Client
|
||||
can log them in.
|
||||
They inherit from the ModuleStoreTestCase class so that the mongodb collection
|
||||
will be cleared out before each test case execution and deleted
|
||||
afterwards.
|
||||
"""
|
||||
uname = 'testuser'
|
||||
email = 'test+courses@edx.org'
|
||||
password = 'foo'
|
||||
|
||||
# Create the use so we can log them in.
|
||||
self.user = User.objects.create_user(uname, email, password)
|
||||
|
||||
# Note that we do not actually need to do anything
|
||||
# for registration if we directly mark them active.
|
||||
self.user.is_active = True
|
||||
# Staff has access to view all courses
|
||||
self.user.is_staff = True
|
||||
self.user.save()
|
||||
|
||||
self.client = Client()
|
||||
self.client.login(username=uname, password=password)
|
||||
|
||||
course = CourseFactory.create(template='i4x://edx/templates/course/Empty', org='MITx', number='999', display_name='Robot Super Course')
|
||||
self.course_location = course.location
|
||||
from .utils import CourseTestCase
|
||||
|
||||
|
||||
class CourseDetailsTestCase(CourseTestCase):
|
||||
@@ -63,8 +28,8 @@ class CourseDetailsTestCase(CourseTestCase):
|
||||
Tests the first course settings page (course dates, overview, etc.).
|
||||
"""
|
||||
def test_virgin_fetch(self):
|
||||
details = CourseDetails.fetch(self.course_location)
|
||||
self.assertEqual(details.course_location, self.course_location, "Location not copied into")
|
||||
details = CourseDetails.fetch(self.course.location)
|
||||
self.assertEqual(details.course_location, self.course.location, "Location not copied into")
|
||||
self.assertIsNotNone(details.start_date.tzinfo)
|
||||
self.assertIsNone(details.end_date, "end date somehow initialized " + str(details.end_date))
|
||||
self.assertIsNone(details.enrollment_start, "enrollment_start date somehow initialized " + str(details.enrollment_start))
|
||||
@@ -75,10 +40,10 @@ class CourseDetailsTestCase(CourseTestCase):
|
||||
self.assertIsNone(details.effort, "effort somehow initialized" + str(details.effort))
|
||||
|
||||
def test_encoder(self):
|
||||
details = CourseDetails.fetch(self.course_location)
|
||||
details = CourseDetails.fetch(self.course.location)
|
||||
jsondetails = json.dumps(details, cls=CourseSettingsEncoder)
|
||||
jsondetails = json.loads(jsondetails)
|
||||
self.assertTupleEqual(Location(jsondetails['course_location']), self.course_location, "Location !=")
|
||||
self.assertTupleEqual(Location(jsondetails['course_location']), self.course.location, "Location !=")
|
||||
self.assertIsNone(jsondetails['end_date'], "end date somehow initialized ")
|
||||
self.assertIsNone(jsondetails['enrollment_start'], "enrollment_start date somehow initialized ")
|
||||
self.assertIsNone(jsondetails['enrollment_end'], "enrollment_end date somehow initialized ")
|
||||
@@ -91,10 +56,12 @@ class CourseDetailsTestCase(CourseTestCase):
|
||||
"""
|
||||
Test the encoder out of its original constrained purpose to see if it functions for general use
|
||||
"""
|
||||
details = {'location': Location(['tag', 'org', 'course', 'category', 'name']),
|
||||
'number': 1,
|
||||
'string': 'string',
|
||||
'datetime': datetime.datetime.now(UTC())}
|
||||
details = {
|
||||
'location': Location(['tag', 'org', 'course', 'category', 'name']),
|
||||
'number': 1,
|
||||
'string': 'string',
|
||||
'datetime': datetime.datetime.now(UTC())
|
||||
}
|
||||
jsondetails = json.dumps(details, cls=CourseSettingsEncoder)
|
||||
jsondetails = json.loads(jsondetails)
|
||||
|
||||
@@ -105,7 +72,7 @@ class CourseDetailsTestCase(CourseTestCase):
|
||||
self.assertEqual(jsondetails['string'], 'string')
|
||||
|
||||
def test_update_and_fetch(self):
|
||||
jsondetails = CourseDetails.fetch(self.course_location)
|
||||
jsondetails = CourseDetails.fetch(self.course.location)
|
||||
jsondetails.syllabus = "<a href='foo'>bar</a>"
|
||||
# encode - decode to convert date fields and other data which changes form
|
||||
self.assertEqual(
|
||||
@@ -204,11 +171,12 @@ class CourseDetailsViewTest(CourseTestCase):
|
||||
return Date().to_json(dt)
|
||||
|
||||
def test_update_and_fetch(self):
|
||||
details = CourseDetails.fetch(self.course_location)
|
||||
loc = self.course.location
|
||||
details = CourseDetails.fetch(loc)
|
||||
|
||||
# resp s/b json from here on
|
||||
url = reverse('course_settings', kwargs={'org': self.course_location.org, 'course': self.course_location.course,
|
||||
'name': self.course_location.name, 'section': 'details'})
|
||||
url = reverse('course_settings', kwargs={'org': loc.org, 'course': loc.course,
|
||||
'name': loc.name, 'section': 'details'})
|
||||
resp = self.client.get(url)
|
||||
self.compare_details_with_encoding(json.loads(resp.content), details.__dict__, "virgin get")
|
||||
|
||||
@@ -251,49 +219,49 @@ class CourseGradingTest(CourseTestCase):
|
||||
Tests for the course settings grading page.
|
||||
"""
|
||||
def test_initial_grader(self):
|
||||
descriptor = get_modulestore(self.course_location).get_item(self.course_location)
|
||||
descriptor = get_modulestore(self.course.location).get_item(self.course.location)
|
||||
test_grader = CourseGradingModel(descriptor)
|
||||
# ??? How much should this test bake in expectations about defaults and thus fail if defaults change?
|
||||
self.assertEqual(self.course_location, test_grader.course_location, "Course locations")
|
||||
self.assertEqual(self.course.location, test_grader.course_location, "Course locations")
|
||||
self.assertIsNotNone(test_grader.graders, "No graders")
|
||||
self.assertIsNotNone(test_grader.grade_cutoffs, "No cutoffs")
|
||||
|
||||
def test_fetch_grader(self):
|
||||
test_grader = CourseGradingModel.fetch(self.course_location.url())
|
||||
self.assertEqual(self.course_location, test_grader.course_location, "Course locations")
|
||||
test_grader = CourseGradingModel.fetch(self.course.location.url())
|
||||
self.assertEqual(self.course.location, test_grader.course_location, "Course locations")
|
||||
self.assertIsNotNone(test_grader.graders, "No graders")
|
||||
self.assertIsNotNone(test_grader.grade_cutoffs, "No cutoffs")
|
||||
|
||||
test_grader = CourseGradingModel.fetch(self.course_location)
|
||||
self.assertEqual(self.course_location, test_grader.course_location, "Course locations")
|
||||
test_grader = CourseGradingModel.fetch(self.course.location)
|
||||
self.assertEqual(self.course.location, test_grader.course_location, "Course locations")
|
||||
self.assertIsNotNone(test_grader.graders, "No graders")
|
||||
self.assertIsNotNone(test_grader.grade_cutoffs, "No cutoffs")
|
||||
|
||||
for i, grader in enumerate(test_grader.graders):
|
||||
subgrader = CourseGradingModel.fetch_grader(self.course_location, i)
|
||||
subgrader = CourseGradingModel.fetch_grader(self.course.location, i)
|
||||
self.assertDictEqual(grader, subgrader, str(i) + "th graders not equal")
|
||||
|
||||
subgrader = CourseGradingModel.fetch_grader(self.course_location.list(), 0)
|
||||
subgrader = CourseGradingModel.fetch_grader(self.course.location.list(), 0)
|
||||
self.assertDictEqual(test_grader.graders[0], subgrader, "failed with location as list")
|
||||
|
||||
def test_fetch_cutoffs(self):
|
||||
test_grader = CourseGradingModel.fetch_cutoffs(self.course_location)
|
||||
test_grader = CourseGradingModel.fetch_cutoffs(self.course.location)
|
||||
# ??? should this check that it's at least a dict? (expected is { "pass" : 0.5 } I think)
|
||||
self.assertIsNotNone(test_grader, "No cutoffs via fetch")
|
||||
|
||||
test_grader = CourseGradingModel.fetch_cutoffs(self.course_location.url())
|
||||
test_grader = CourseGradingModel.fetch_cutoffs(self.course.location.url())
|
||||
self.assertIsNotNone(test_grader, "No cutoffs via fetch with url")
|
||||
|
||||
def test_fetch_grace(self):
|
||||
test_grader = CourseGradingModel.fetch_grace_period(self.course_location)
|
||||
test_grader = CourseGradingModel.fetch_grace_period(self.course.location)
|
||||
# almost a worthless test
|
||||
self.assertIn('grace_period', test_grader, "No grace via fetch")
|
||||
|
||||
test_grader = CourseGradingModel.fetch_grace_period(self.course_location.url())
|
||||
test_grader = CourseGradingModel.fetch_grace_period(self.course.location.url())
|
||||
self.assertIn('grace_period', test_grader, "No cutoffs via fetch with url")
|
||||
|
||||
def test_update_from_json(self):
|
||||
test_grader = CourseGradingModel.fetch(self.course_location)
|
||||
test_grader = CourseGradingModel.fetch(self.course.location)
|
||||
altered_grader = CourseGradingModel.update_from_json(test_grader.__dict__)
|
||||
self.assertDictEqual(test_grader.__dict__, altered_grader.__dict__, "Noop update")
|
||||
|
||||
@@ -307,11 +275,10 @@ class CourseGradingTest(CourseTestCase):
|
||||
|
||||
test_grader.grace_period = {'hours': 4, 'minutes': 5, 'seconds': 0}
|
||||
altered_grader = CourseGradingModel.update_from_json(test_grader.__dict__)
|
||||
print test_grader.grace_period, altered_grader.grace_period
|
||||
self.assertDictEqual(test_grader.__dict__, altered_grader.__dict__, "4 hour grace period")
|
||||
|
||||
def test_update_grader_from_json(self):
|
||||
test_grader = CourseGradingModel.fetch(self.course_location)
|
||||
test_grader = CourseGradingModel.fetch(self.course.location)
|
||||
altered_grader = CourseGradingModel.update_grader_from_json(test_grader.course_location, test_grader.graders[1])
|
||||
self.assertDictEqual(test_grader.graders[1], altered_grader, "Noop update")
|
||||
|
||||
@@ -331,11 +298,11 @@ class CourseMetadataEditingTest(CourseTestCase):
|
||||
def setUp(self):
|
||||
CourseTestCase.setUp(self)
|
||||
# add in the full class too
|
||||
import_from_xml(get_modulestore(self.course_location), 'common/test/data/', ['full'])
|
||||
import_from_xml(get_modulestore(self.course.location), 'common/test/data/', ['full'])
|
||||
self.fullcourse_location = Location(['i4x', 'edX', 'full', 'course', '6.002_Spring_2012', None])
|
||||
|
||||
def test_fetch_initial_fields(self):
|
||||
test_model = CourseMetadata.fetch(self.course_location)
|
||||
test_model = CourseMetadata.fetch(self.course.location)
|
||||
self.assertIn('display_name', test_model, 'Missing editable metadata field')
|
||||
self.assertEqual(test_model['display_name'], 'Robot Super Course', "not expected value")
|
||||
|
||||
@@ -348,17 +315,17 @@ class CourseMetadataEditingTest(CourseTestCase):
|
||||
self.assertIn('xqa_key', test_model, 'xqa_key field ')
|
||||
|
||||
def test_update_from_json(self):
|
||||
test_model = CourseMetadata.update_from_json(self.course_location, {
|
||||
test_model = CourseMetadata.update_from_json(self.course.location, {
|
||||
"advertised_start": "start A",
|
||||
"testcenter_info": {"c": "test"},
|
||||
"days_early_for_beta": 2
|
||||
})
|
||||
self.update_check(test_model)
|
||||
# try fresh fetch to ensure persistence
|
||||
test_model = CourseMetadata.fetch(self.course_location)
|
||||
test_model = CourseMetadata.fetch(self.course.location)
|
||||
self.update_check(test_model)
|
||||
# now change some of the existing metadata
|
||||
test_model = CourseMetadata.update_from_json(self.course_location, {
|
||||
test_model = CourseMetadata.update_from_json(self.course.location, {
|
||||
"advertised_start": "start B",
|
||||
"display_name": "jolly roger"}
|
||||
)
|
||||
|
||||
@@ -10,9 +10,9 @@ class CourseUpdateTest(CourseTestCase):
|
||||
'''Go through each interface and ensure it works.'''
|
||||
# first get the update to force the creation
|
||||
url = reverse('course_info',
|
||||
kwargs={'org': self.course_location.org,
|
||||
'course': self.course_location.course,
|
||||
'name': self.course_location.name})
|
||||
kwargs={'org': self.course.location.org,
|
||||
'course': self.course.location.course,
|
||||
'name': self.course.location.name})
|
||||
self.client.get(url)
|
||||
|
||||
init_content = '<iframe width="560" height="315" src="http://www.youtube.com/embed/RocY-Jd93XU" frameborder="0">'
|
||||
@@ -20,8 +20,8 @@ class CourseUpdateTest(CourseTestCase):
|
||||
payload = {'content': content,
|
||||
'date': 'January 8, 2013'}
|
||||
url = reverse('course_info_json',
|
||||
kwargs={'org': self.course_location.org,
|
||||
'course': self.course_location.course,
|
||||
kwargs={'org': self.course.location.org,
|
||||
'course': self.course.location.course,
|
||||
'provided_id': ''})
|
||||
|
||||
resp = self.client.post(url, json.dumps(payload), "application/json")
|
||||
@@ -31,8 +31,8 @@ class CourseUpdateTest(CourseTestCase):
|
||||
self.assertHTMLEqual(payload['content'], content)
|
||||
|
||||
first_update_url = reverse('course_info_json',
|
||||
kwargs={'org': self.course_location.org,
|
||||
'course': self.course_location.course,
|
||||
kwargs={'org': self.course.location.org,
|
||||
'course': self.course.location.course,
|
||||
'provided_id': payload['id']})
|
||||
content += '<div>div <p>p<br/></p></div>'
|
||||
payload['content'] = content
|
||||
@@ -47,8 +47,8 @@ class CourseUpdateTest(CourseTestCase):
|
||||
payload = {'content': content,
|
||||
'date': 'January 11, 2013'}
|
||||
url = reverse('course_info_json',
|
||||
kwargs={'org': self.course_location.org,
|
||||
'course': self.course_location.course,
|
||||
kwargs={'org': self.course.location.org,
|
||||
'course': self.course.location.course,
|
||||
'provided_id': ''})
|
||||
|
||||
resp = self.client.post(url, json.dumps(payload), "application/json")
|
||||
@@ -58,8 +58,8 @@ class CourseUpdateTest(CourseTestCase):
|
||||
self.assertHTMLEqual(content, payload['content'], "self closing ol")
|
||||
|
||||
url = reverse('course_info_json',
|
||||
kwargs={'org': self.course_location.org,
|
||||
'course': self.course_location.course,
|
||||
kwargs={'org': self.course.location.org,
|
||||
'course': self.course.location.course,
|
||||
'provided_id': ''})
|
||||
resp = self.client.get(url)
|
||||
payload = json.loads(resp.content)
|
||||
@@ -73,8 +73,8 @@ class CourseUpdateTest(CourseTestCase):
|
||||
|
||||
# now try to update a non-existent update
|
||||
url = reverse('course_info_json',
|
||||
kwargs={'org': self.course_location.org,
|
||||
'course': self.course_location.course,
|
||||
kwargs={'org': self.course.location.org,
|
||||
'course': self.course.location.course,
|
||||
'provided_id': '9'})
|
||||
content = 'blah blah'
|
||||
payload = {'content': content,
|
||||
@@ -87,8 +87,8 @@ class CourseUpdateTest(CourseTestCase):
|
||||
content = '<garbage tag No closing brace to force <span>error</span>'
|
||||
payload = {'content': content,
|
||||
'date': 'January 11, 2013'}
|
||||
url = reverse('course_info_json', kwargs={'org': self.course_location.org,
|
||||
'course': self.course_location.course,
|
||||
url = reverse('course_info_json', kwargs={'org': self.course.location.org,
|
||||
'course': self.course.location.course,
|
||||
'provided_id': ''})
|
||||
|
||||
self.assertContains(
|
||||
@@ -99,8 +99,8 @@ class CourseUpdateTest(CourseTestCase):
|
||||
content = "<p><br><br></p>"
|
||||
payload = {'content': content,
|
||||
'date': 'January 11, 2013'}
|
||||
url = reverse('course_info_json', kwargs={'org': self.course_location.org,
|
||||
'course': self.course_location.course,
|
||||
url = reverse('course_info_json', kwargs={'org': self.course.location.org,
|
||||
'course': self.course.location.course,
|
||||
'provided_id': ''})
|
||||
|
||||
resp = self.client.post(url, json.dumps(payload), "application/json")
|
||||
@@ -108,8 +108,8 @@ class CourseUpdateTest(CourseTestCase):
|
||||
self.assertHTMLEqual(content, json.loads(resp.content)['content'])
|
||||
|
||||
# now try to delete a non-existent update
|
||||
url = reverse('course_info_json', kwargs={'org': self.course_location.org,
|
||||
'course': self.course_location.course,
|
||||
url = reverse('course_info_json', kwargs={'org': self.course.location.org,
|
||||
'course': self.course.location.course,
|
||||
'provided_id': '19'})
|
||||
payload = {'content': content,
|
||||
'date': 'January 21, 2013'}
|
||||
@@ -119,8 +119,8 @@ class CourseUpdateTest(CourseTestCase):
|
||||
content = 'blah blah'
|
||||
payload = {'content': content,
|
||||
'date': 'January 28, 2013'}
|
||||
url = reverse('course_info_json', kwargs={'org': self.course_location.org,
|
||||
'course': self.course_location.course,
|
||||
url = reverse('course_info_json', kwargs={'org': self.course.location.org,
|
||||
'course': self.course.location.course,
|
||||
'provided_id': ''})
|
||||
resp = self.client.post(url, json.dumps(payload), "application/json")
|
||||
payload = json.loads(resp.content)
|
||||
@@ -128,16 +128,16 @@ class CourseUpdateTest(CourseTestCase):
|
||||
self.assertHTMLEqual(content, payload['content'], "single iframe")
|
||||
# first count the entries
|
||||
url = reverse('course_info_json',
|
||||
kwargs={'org': self.course_location.org,
|
||||
'course': self.course_location.course,
|
||||
kwargs={'org': self.course.location.org,
|
||||
'course': self.course.location.course,
|
||||
'provided_id': ''})
|
||||
resp = self.client.get(url)
|
||||
payload = json.loads(resp.content)
|
||||
before_delete = len(payload)
|
||||
|
||||
url = reverse('course_info_json',
|
||||
kwargs={'org': self.course_location.org,
|
||||
'course': self.course_location.course,
|
||||
kwargs={'org': self.course.location.org,
|
||||
'course': self.course.location.course,
|
||||
'provided_id': this_id})
|
||||
resp = self.client.delete(url)
|
||||
payload = json.loads(resp.content)
|
||||
|
||||
@@ -22,7 +22,3 @@ class DeleteItem(CourseTestCase):
|
||||
# Now delete it. There was a bug that the delete was failing (static tabs do not exist in draft modulestore).
|
||||
resp = self.client.post(reverse('delete_item'), resp.content, "application/json")
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
95
cms/djangoapps/contentstore/tests/test_textbooks.py
Normal file
95
cms/djangoapps/contentstore/tests/test_textbooks.py
Normal file
@@ -0,0 +1,95 @@
|
||||
import json
|
||||
import mock
|
||||
from unittest import TestCase
|
||||
from .utils import CourseTestCase
|
||||
from django.core.urlresolvers import reverse
|
||||
from contentstore.utils import get_modulestore
|
||||
|
||||
from contentstore.views.course import (
|
||||
validate_textbook_json, TextbookValidationError)
|
||||
|
||||
|
||||
class TextbookTestCase(CourseTestCase):
|
||||
def setUp(self):
|
||||
super(TextbookTestCase, self).setUp()
|
||||
self.url = reverse('textbook_index', kwargs={
|
||||
'org': self.course.location.org,
|
||||
'course': self.course.location.course,
|
||||
'name': self.course.location.name,
|
||||
})
|
||||
|
||||
def test_view_index(self):
|
||||
resp = self.client.get(self.url)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
# we don't have resp.context right now,
|
||||
# due to bugs in our testing harness :(
|
||||
if resp.context:
|
||||
self.assertEqual(resp.context['course'], self.course)
|
||||
|
||||
def test_view_index_xhr(self):
|
||||
resp = self.client.get(
|
||||
self.url,
|
||||
HTTP_ACCEPT="application/json",
|
||||
HTTP_X_REQUESTED_WITH='XMLHttpRequest'
|
||||
)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
obj = json.loads(resp.content)
|
||||
self.assertEqual(self.course.pdf_textbooks, obj)
|
||||
|
||||
def test_view_index_xhr_post(self):
|
||||
textbooks = [
|
||||
{"tab_title": "Hi, mom!"},
|
||||
{"tab_title": "Textbook 2"},
|
||||
]
|
||||
# import nose; nose.tools.set_trace()
|
||||
resp = self.client.post(
|
||||
self.url,
|
||||
data=json.dumps(textbooks),
|
||||
content_type="application/json",
|
||||
HTTP_ACCEPT="application/json",
|
||||
HTTP_X_REQUESTED_WITH='XMLHttpRequest'
|
||||
)
|
||||
self.assertEqual(resp.status_code, 204)
|
||||
self.assertEqual(resp.content, "")
|
||||
|
||||
# reload course
|
||||
store = get_modulestore(self.course.location)
|
||||
course = store.get_item(self.course.location)
|
||||
self.assertEqual(course.pdf_textbooks, textbooks)
|
||||
|
||||
|
||||
class TextbookValidationTestCase(TestCase):
|
||||
def test_happy_path(self):
|
||||
textbooks = [
|
||||
{
|
||||
"tab_title": "Hi, mom!",
|
||||
"url": "/mom.pdf"
|
||||
},
|
||||
{
|
||||
"tab_title": "Textbook 2",
|
||||
"chapters": [
|
||||
{
|
||||
"title": "Chapter 1",
|
||||
"url": "/ch1.pdf"
|
||||
}, {
|
||||
"title": "Chapter 2",
|
||||
"url": "/ch2.pdf"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
result = validate_textbook_json(json.dumps(textbooks))
|
||||
self.assertEqual(textbooks, result)
|
||||
|
||||
def test_invalid_json(self):
|
||||
with self.assertRaises(TextbookValidationError):
|
||||
validate_textbook_json("[{'abc'}]")
|
||||
|
||||
def test_wrong_json(self):
|
||||
with self.assertRaises(TextbookValidationError):
|
||||
validate_textbook_json('{"tab_title": "Hi, mom!"}')
|
||||
|
||||
def test_no_tab_title(self):
|
||||
with self.assertRaises(TextbookValidationError):
|
||||
validate_textbook_json('[{"url": "/textbook.pdf"}')
|
||||
@@ -6,6 +6,10 @@ import json
|
||||
|
||||
from student.models import Registration
|
||||
from django.contrib.auth.models import User
|
||||
from django.test.client import Client
|
||||
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
|
||||
|
||||
def parse_json(response):
|
||||
@@ -21,3 +25,37 @@ def user(email):
|
||||
def registration(email):
|
||||
"""look up registration object by email"""
|
||||
return Registration.objects.get(user__email=email)
|
||||
|
||||
|
||||
class CourseTestCase(ModuleStoreTestCase):
|
||||
def setUp(self):
|
||||
"""
|
||||
These tests need a user in the DB so that the django Test Client
|
||||
can log them in.
|
||||
They inherit from the ModuleStoreTestCase class so that the mongodb collection
|
||||
will be cleared out before each test case execution and deleted
|
||||
afterwards.
|
||||
"""
|
||||
uname = 'testuser'
|
||||
email = 'test+courses@edx.org'
|
||||
password = 'foo'
|
||||
|
||||
# Create the use so we can log them in.
|
||||
self.user = User.objects.create_user(uname, email, password)
|
||||
|
||||
# Note that we do not actually need to do anything
|
||||
# for registration if we directly mark them active.
|
||||
self.user.is_active = True
|
||||
# Staff has access to view all courses
|
||||
self.user.is_staff = True
|
||||
self.user.save()
|
||||
|
||||
self.client = Client()
|
||||
self.client.login(username=uname, password=password)
|
||||
|
||||
self.course = CourseFactory.create(
|
||||
template='i4x://edx/templates/course/Empty',
|
||||
org='MITx',
|
||||
number='999',
|
||||
display_name='Robot Super Course',
|
||||
)
|
||||
|
||||
@@ -416,6 +416,23 @@ def course_advanced_updates(request, org, course, name):
|
||||
return HttpResponse(response_json, mimetype="application/json")
|
||||
|
||||
|
||||
class TextbookValidationError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def validate_textbook_json(text):
|
||||
try:
|
||||
obj = json.loads(text)
|
||||
except ValueError:
|
||||
raise TextbookValidationError("invalid JSON")
|
||||
if not isinstance(obj, (list, tuple)):
|
||||
raise TextbookValidationError("must be JSON list")
|
||||
for textbook in obj:
|
||||
if not textbook.get("tab_title"):
|
||||
raise TextbookValidationError("every textbook must have a tab_title")
|
||||
return obj
|
||||
|
||||
|
||||
@login_required
|
||||
@ensure_csrf_cookie
|
||||
def textbook_index(request, org, course, name):
|
||||
@@ -433,18 +450,10 @@ def textbook_index(request, org, course, name):
|
||||
return HttpResponse(json.dumps(course_module.pdf_textbooks), content_type="application/json")
|
||||
elif request.method == 'POST':
|
||||
try:
|
||||
obj = json.loads(request.raw_post_data)
|
||||
except ValueError:
|
||||
msg = {"error": "invalid JSON"}
|
||||
course_module.pdf_textbooks = validate_textbook_json(request.body)
|
||||
except TextbookValidationError as e:
|
||||
msg = {"error": e.message}
|
||||
return HttpResponseBadRequest(json.dumps(msg), content_type="application/json")
|
||||
if not isinstance(obj, (list, tuple)):
|
||||
msg = {"error": "must be JSON list"}
|
||||
return HttpResponseBadRequest(json.dumps(msg), content_type="application/json")
|
||||
for textbook in obj:
|
||||
if not textbook.get("tab_title"):
|
||||
msg = {"error": "every textbook must have a tab_title"}
|
||||
return HttpResponseBadRequest(json.dumps(msg), content_type="application/json")
|
||||
course_module.pdf_textbooks = obj
|
||||
if not any(tab['type'] == 'pdf_textbooks' for tab in course_module.tabs):
|
||||
course_module.tabs.append({"type": "pdf_textbooks"})
|
||||
store.update_metadata(course_module.location, own_metadata(course_module))
|
||||
|
||||
0
cms/manage.py
Normal file → Executable file
0
cms/manage.py
Normal file → Executable file
Reference in New Issue
Block a user