Commit 05cb13b1 by David Baumgold

Adding and fixing tests

parent 77db1f8a
""" Unit tests for checklist methods in views.py. """ """ Unit tests for checklist methods in views.py. """
from contentstore.utils import get_modulestore, get_url_reverse 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.inheritance import own_metadata
from xmodule.modulestore.tests.factories import CourseFactory from xmodule.modulestore.tests.factories import CourseFactory
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
import json import json
from .utils import CourseTestCase
class ChecklistTestCase(CourseTestCase): class ChecklistTestCase(CourseTestCase):
......
...@@ -6,8 +6,6 @@ import json ...@@ -6,8 +6,6 @@ import json
import copy import copy
import mock import mock
from django.contrib.auth.models import User
from django.test.client import Client
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.utils.timezone import UTC from django.utils.timezone import UTC
from django.test.utils import override_settings from django.test.utils import override_settings
...@@ -17,45 +15,12 @@ from models.settings.course_details import (CourseDetails, CourseSettingsEncoder ...@@ -17,45 +15,12 @@ from models.settings.course_details import (CourseDetails, CourseSettingsEncoder
from models.settings.course_grading import CourseGradingModel from models.settings.course_grading import CourseGradingModel
from contentstore.utils import get_modulestore 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 models.settings.course_metadata import CourseMetadata
from xmodule.modulestore.xml_importer import import_from_xml from xmodule.modulestore.xml_importer import import_from_xml
from xmodule.fields import Date from xmodule.fields import Date
from .utils import CourseTestCase
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
class CourseDetailsTestCase(CourseTestCase): class CourseDetailsTestCase(CourseTestCase):
...@@ -63,8 +28,8 @@ class CourseDetailsTestCase(CourseTestCase): ...@@ -63,8 +28,8 @@ class CourseDetailsTestCase(CourseTestCase):
Tests the first course settings page (course dates, overview, etc.). Tests the first course settings page (course dates, overview, etc.).
""" """
def test_virgin_fetch(self): def test_virgin_fetch(self):
details = CourseDetails.fetch(self.course_location) details = CourseDetails.fetch(self.course.location)
self.assertEqual(details.course_location, self.course_location, "Location not copied into") self.assertEqual(details.course_location, self.course.location, "Location not copied into")
self.assertIsNotNone(details.start_date.tzinfo) self.assertIsNotNone(details.start_date.tzinfo)
self.assertIsNone(details.end_date, "end date somehow initialized " + str(details.end_date)) 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)) self.assertIsNone(details.enrollment_start, "enrollment_start date somehow initialized " + str(details.enrollment_start))
...@@ -75,10 +40,10 @@ class CourseDetailsTestCase(CourseTestCase): ...@@ -75,10 +40,10 @@ class CourseDetailsTestCase(CourseTestCase):
self.assertIsNone(details.effort, "effort somehow initialized" + str(details.effort)) self.assertIsNone(details.effort, "effort somehow initialized" + str(details.effort))
def test_encoder(self): def test_encoder(self):
details = CourseDetails.fetch(self.course_location) details = CourseDetails.fetch(self.course.location)
jsondetails = json.dumps(details, cls=CourseSettingsEncoder) jsondetails = json.dumps(details, cls=CourseSettingsEncoder)
jsondetails = json.loads(jsondetails) 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['end_date'], "end date somehow initialized ")
self.assertIsNone(jsondetails['enrollment_start'], "enrollment_start date somehow initialized ") self.assertIsNone(jsondetails['enrollment_start'], "enrollment_start date somehow initialized ")
self.assertIsNone(jsondetails['enrollment_end'], "enrollment_end date somehow initialized ") self.assertIsNone(jsondetails['enrollment_end'], "enrollment_end date somehow initialized ")
...@@ -91,10 +56,12 @@ class CourseDetailsTestCase(CourseTestCase): ...@@ -91,10 +56,12 @@ class CourseDetailsTestCase(CourseTestCase):
""" """
Test the encoder out of its original constrained purpose to see if it functions for general use 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']), details = {
'location': Location(['tag', 'org', 'course', 'category', 'name']),
'number': 1, 'number': 1,
'string': 'string', 'string': 'string',
'datetime': datetime.datetime.now(UTC())} 'datetime': datetime.datetime.now(UTC())
}
jsondetails = json.dumps(details, cls=CourseSettingsEncoder) jsondetails = json.dumps(details, cls=CourseSettingsEncoder)
jsondetails = json.loads(jsondetails) jsondetails = json.loads(jsondetails)
...@@ -105,7 +72,7 @@ class CourseDetailsTestCase(CourseTestCase): ...@@ -105,7 +72,7 @@ class CourseDetailsTestCase(CourseTestCase):
self.assertEqual(jsondetails['string'], 'string') self.assertEqual(jsondetails['string'], 'string')
def test_update_and_fetch(self): 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>" jsondetails.syllabus = "<a href='foo'>bar</a>"
# encode - decode to convert date fields and other data which changes form # encode - decode to convert date fields and other data which changes form
self.assertEqual( self.assertEqual(
...@@ -204,11 +171,12 @@ class CourseDetailsViewTest(CourseTestCase): ...@@ -204,11 +171,12 @@ class CourseDetailsViewTest(CourseTestCase):
return Date().to_json(dt) return Date().to_json(dt)
def test_update_and_fetch(self): 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 # resp s/b json from here on
url = reverse('course_settings', kwargs={'org': self.course_location.org, 'course': self.course_location.course, url = reverse('course_settings', kwargs={'org': loc.org, 'course': loc.course,
'name': self.course_location.name, 'section': 'details'}) 'name': loc.name, 'section': 'details'})
resp = self.client.get(url) resp = self.client.get(url)
self.compare_details_with_encoding(json.loads(resp.content), details.__dict__, "virgin get") self.compare_details_with_encoding(json.loads(resp.content), details.__dict__, "virgin get")
...@@ -251,49 +219,49 @@ class CourseGradingTest(CourseTestCase): ...@@ -251,49 +219,49 @@ class CourseGradingTest(CourseTestCase):
Tests for the course settings grading page. Tests for the course settings grading page.
""" """
def test_initial_grader(self): 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) test_grader = CourseGradingModel(descriptor)
# ??? How much should this test bake in expectations about defaults and thus fail if defaults change? # ??? 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.graders, "No graders")
self.assertIsNotNone(test_grader.grade_cutoffs, "No cutoffs") self.assertIsNotNone(test_grader.grade_cutoffs, "No cutoffs")
def test_fetch_grader(self): def test_fetch_grader(self):
test_grader = CourseGradingModel.fetch(self.course_location.url()) test_grader = CourseGradingModel.fetch(self.course.location.url())
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.graders, "No graders")
self.assertIsNotNone(test_grader.grade_cutoffs, "No cutoffs") self.assertIsNotNone(test_grader.grade_cutoffs, "No cutoffs")
test_grader = CourseGradingModel.fetch(self.course_location) test_grader = CourseGradingModel.fetch(self.course.location)
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.graders, "No graders")
self.assertIsNotNone(test_grader.grade_cutoffs, "No cutoffs") self.assertIsNotNone(test_grader.grade_cutoffs, "No cutoffs")
for i, grader in enumerate(test_grader.graders): 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") 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") self.assertDictEqual(test_grader.graders[0], subgrader, "failed with location as list")
def test_fetch_cutoffs(self): 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) # ??? 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") 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") self.assertIsNotNone(test_grader, "No cutoffs via fetch with url")
def test_fetch_grace(self): 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 # almost a worthless test
self.assertIn('grace_period', test_grader, "No grace via fetch") 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") self.assertIn('grace_period', test_grader, "No cutoffs via fetch with url")
def test_update_from_json(self): 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__) altered_grader = CourseGradingModel.update_from_json(test_grader.__dict__)
self.assertDictEqual(test_grader.__dict__, altered_grader.__dict__, "Noop update") self.assertDictEqual(test_grader.__dict__, altered_grader.__dict__, "Noop update")
...@@ -307,11 +275,10 @@ class CourseGradingTest(CourseTestCase): ...@@ -307,11 +275,10 @@ class CourseGradingTest(CourseTestCase):
test_grader.grace_period = {'hours': 4, 'minutes': 5, 'seconds': 0} test_grader.grace_period = {'hours': 4, 'minutes': 5, 'seconds': 0}
altered_grader = CourseGradingModel.update_from_json(test_grader.__dict__) 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") self.assertDictEqual(test_grader.__dict__, altered_grader.__dict__, "4 hour grace period")
def test_update_grader_from_json(self): 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]) 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") self.assertDictEqual(test_grader.graders[1], altered_grader, "Noop update")
...@@ -331,11 +298,11 @@ class CourseMetadataEditingTest(CourseTestCase): ...@@ -331,11 +298,11 @@ class CourseMetadataEditingTest(CourseTestCase):
def setUp(self): def setUp(self):
CourseTestCase.setUp(self) CourseTestCase.setUp(self)
# add in the full class too # 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]) self.fullcourse_location = Location(['i4x', 'edX', 'full', 'course', '6.002_Spring_2012', None])
def test_fetch_initial_fields(self): 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.assertIn('display_name', test_model, 'Missing editable metadata field')
self.assertEqual(test_model['display_name'], 'Robot Super Course', "not expected value") self.assertEqual(test_model['display_name'], 'Robot Super Course', "not expected value")
...@@ -348,17 +315,17 @@ class CourseMetadataEditingTest(CourseTestCase): ...@@ -348,17 +315,17 @@ class CourseMetadataEditingTest(CourseTestCase):
self.assertIn('xqa_key', test_model, 'xqa_key field ') self.assertIn('xqa_key', test_model, 'xqa_key field ')
def test_update_from_json(self): 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", "advertised_start": "start A",
"testcenter_info": {"c": "test"}, "testcenter_info": {"c": "test"},
"days_early_for_beta": 2 "days_early_for_beta": 2
}) })
self.update_check(test_model) self.update_check(test_model)
# try fresh fetch to ensure persistence # 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) self.update_check(test_model)
# now change some of the existing metadata # 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", "advertised_start": "start B",
"display_name": "jolly roger"} "display_name": "jolly roger"}
) )
......
...@@ -10,9 +10,9 @@ class CourseUpdateTest(CourseTestCase): ...@@ -10,9 +10,9 @@ class CourseUpdateTest(CourseTestCase):
'''Go through each interface and ensure it works.''' '''Go through each interface and ensure it works.'''
# first get the update to force the creation # first get the update to force the creation
url = reverse('course_info', url = reverse('course_info',
kwargs={'org': self.course_location.org, kwargs={'org': self.course.location.org,
'course': self.course_location.course, 'course': self.course.location.course,
'name': self.course_location.name}) 'name': self.course.location.name})
self.client.get(url) self.client.get(url)
init_content = '<iframe width="560" height="315" src="http://www.youtube.com/embed/RocY-Jd93XU" frameborder="0">' init_content = '<iframe width="560" height="315" src="http://www.youtube.com/embed/RocY-Jd93XU" frameborder="0">'
...@@ -20,8 +20,8 @@ class CourseUpdateTest(CourseTestCase): ...@@ -20,8 +20,8 @@ class CourseUpdateTest(CourseTestCase):
payload = {'content': content, payload = {'content': content,
'date': 'January 8, 2013'} 'date': 'January 8, 2013'}
url = reverse('course_info_json', url = reverse('course_info_json',
kwargs={'org': self.course_location.org, kwargs={'org': self.course.location.org,
'course': self.course_location.course, 'course': self.course.location.course,
'provided_id': ''}) 'provided_id': ''})
resp = self.client.post(url, json.dumps(payload), "application/json") resp = self.client.post(url, json.dumps(payload), "application/json")
...@@ -31,8 +31,8 @@ class CourseUpdateTest(CourseTestCase): ...@@ -31,8 +31,8 @@ class CourseUpdateTest(CourseTestCase):
self.assertHTMLEqual(payload['content'], content) self.assertHTMLEqual(payload['content'], content)
first_update_url = reverse('course_info_json', first_update_url = reverse('course_info_json',
kwargs={'org': self.course_location.org, kwargs={'org': self.course.location.org,
'course': self.course_location.course, 'course': self.course.location.course,
'provided_id': payload['id']}) 'provided_id': payload['id']})
content += '<div>div <p>p<br/></p></div>' content += '<div>div <p>p<br/></p></div>'
payload['content'] = content payload['content'] = content
...@@ -47,8 +47,8 @@ class CourseUpdateTest(CourseTestCase): ...@@ -47,8 +47,8 @@ class CourseUpdateTest(CourseTestCase):
payload = {'content': content, payload = {'content': content,
'date': 'January 11, 2013'} 'date': 'January 11, 2013'}
url = reverse('course_info_json', url = reverse('course_info_json',
kwargs={'org': self.course_location.org, kwargs={'org': self.course.location.org,
'course': self.course_location.course, 'course': self.course.location.course,
'provided_id': ''}) 'provided_id': ''})
resp = self.client.post(url, json.dumps(payload), "application/json") resp = self.client.post(url, json.dumps(payload), "application/json")
...@@ -58,8 +58,8 @@ class CourseUpdateTest(CourseTestCase): ...@@ -58,8 +58,8 @@ class CourseUpdateTest(CourseTestCase):
self.assertHTMLEqual(content, payload['content'], "self closing ol") self.assertHTMLEqual(content, payload['content'], "self closing ol")
url = reverse('course_info_json', url = reverse('course_info_json',
kwargs={'org': self.course_location.org, kwargs={'org': self.course.location.org,
'course': self.course_location.course, 'course': self.course.location.course,
'provided_id': ''}) 'provided_id': ''})
resp = self.client.get(url) resp = self.client.get(url)
payload = json.loads(resp.content) payload = json.loads(resp.content)
...@@ -73,8 +73,8 @@ class CourseUpdateTest(CourseTestCase): ...@@ -73,8 +73,8 @@ class CourseUpdateTest(CourseTestCase):
# now try to update a non-existent update # now try to update a non-existent update
url = reverse('course_info_json', url = reverse('course_info_json',
kwargs={'org': self.course_location.org, kwargs={'org': self.course.location.org,
'course': self.course_location.course, 'course': self.course.location.course,
'provided_id': '9'}) 'provided_id': '9'})
content = 'blah blah' content = 'blah blah'
payload = {'content': content, payload = {'content': content,
...@@ -87,8 +87,8 @@ class CourseUpdateTest(CourseTestCase): ...@@ -87,8 +87,8 @@ class CourseUpdateTest(CourseTestCase):
content = '<garbage tag No closing brace to force <span>error</span>' content = '<garbage tag No closing brace to force <span>error</span>'
payload = {'content': content, payload = {'content': content,
'date': 'January 11, 2013'} 'date': 'January 11, 2013'}
url = reverse('course_info_json', kwargs={'org': self.course_location.org, url = reverse('course_info_json', kwargs={'org': self.course.location.org,
'course': self.course_location.course, 'course': self.course.location.course,
'provided_id': ''}) 'provided_id': ''})
self.assertContains( self.assertContains(
...@@ -99,8 +99,8 @@ class CourseUpdateTest(CourseTestCase): ...@@ -99,8 +99,8 @@ class CourseUpdateTest(CourseTestCase):
content = "<p><br><br></p>" content = "<p><br><br></p>"
payload = {'content': content, payload = {'content': content,
'date': 'January 11, 2013'} 'date': 'January 11, 2013'}
url = reverse('course_info_json', kwargs={'org': self.course_location.org, url = reverse('course_info_json', kwargs={'org': self.course.location.org,
'course': self.course_location.course, 'course': self.course.location.course,
'provided_id': ''}) 'provided_id': ''})
resp = self.client.post(url, json.dumps(payload), "application/json") resp = self.client.post(url, json.dumps(payload), "application/json")
...@@ -108,8 +108,8 @@ class CourseUpdateTest(CourseTestCase): ...@@ -108,8 +108,8 @@ class CourseUpdateTest(CourseTestCase):
self.assertHTMLEqual(content, json.loads(resp.content)['content']) self.assertHTMLEqual(content, json.loads(resp.content)['content'])
# now try to delete a non-existent update # now try to delete a non-existent update
url = reverse('course_info_json', kwargs={'org': self.course_location.org, url = reverse('course_info_json', kwargs={'org': self.course.location.org,
'course': self.course_location.course, 'course': self.course.location.course,
'provided_id': '19'}) 'provided_id': '19'})
payload = {'content': content, payload = {'content': content,
'date': 'January 21, 2013'} 'date': 'January 21, 2013'}
...@@ -119,8 +119,8 @@ class CourseUpdateTest(CourseTestCase): ...@@ -119,8 +119,8 @@ class CourseUpdateTest(CourseTestCase):
content = 'blah blah' content = 'blah blah'
payload = {'content': content, payload = {'content': content,
'date': 'January 28, 2013'} 'date': 'January 28, 2013'}
url = reverse('course_info_json', kwargs={'org': self.course_location.org, url = reverse('course_info_json', kwargs={'org': self.course.location.org,
'course': self.course_location.course, 'course': self.course.location.course,
'provided_id': ''}) 'provided_id': ''})
resp = self.client.post(url, json.dumps(payload), "application/json") resp = self.client.post(url, json.dumps(payload), "application/json")
payload = json.loads(resp.content) payload = json.loads(resp.content)
...@@ -128,16 +128,16 @@ class CourseUpdateTest(CourseTestCase): ...@@ -128,16 +128,16 @@ class CourseUpdateTest(CourseTestCase):
self.assertHTMLEqual(content, payload['content'], "single iframe") self.assertHTMLEqual(content, payload['content'], "single iframe")
# first count the entries # first count the entries
url = reverse('course_info_json', url = reverse('course_info_json',
kwargs={'org': self.course_location.org, kwargs={'org': self.course.location.org,
'course': self.course_location.course, 'course': self.course.location.course,
'provided_id': ''}) 'provided_id': ''})
resp = self.client.get(url) resp = self.client.get(url)
payload = json.loads(resp.content) payload = json.loads(resp.content)
before_delete = len(payload) before_delete = len(payload)
url = reverse('course_info_json', url = reverse('course_info_json',
kwargs={'org': self.course_location.org, kwargs={'org': self.course.location.org,
'course': self.course_location.course, 'course': self.course.location.course,
'provided_id': this_id}) 'provided_id': this_id})
resp = self.client.delete(url) resp = self.client.delete(url)
payload = json.loads(resp.content) payload = json.loads(resp.content)
......
...@@ -22,7 +22,3 @@ class DeleteItem(CourseTestCase): ...@@ -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). # 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") resp = self.client.post(reverse('delete_item'), resp.content, "application/json")
self.assertEqual(resp.status_code, 200) self.assertEqual(resp.status_code, 200)
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 ...@@ -6,6 +6,10 @@ import json
from student.models import Registration from student.models import Registration
from django.contrib.auth.models import User 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): def parse_json(response):
...@@ -21,3 +25,37 @@ def user(email): ...@@ -21,3 +25,37 @@ def user(email):
def registration(email): def registration(email):
"""look up registration object by email""" """look up registration object by email"""
return Registration.objects.get(user__email=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): ...@@ -416,6 +416,23 @@ def course_advanced_updates(request, org, course, name):
return HttpResponse(response_json, mimetype="application/json") 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 @login_required
@ensure_csrf_cookie @ensure_csrf_cookie
def textbook_index(request, org, course, name): def textbook_index(request, org, course, name):
...@@ -433,18 +450,10 @@ 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") return HttpResponse(json.dumps(course_module.pdf_textbooks), content_type="application/json")
elif request.method == 'POST': elif request.method == 'POST':
try: try:
obj = json.loads(request.raw_post_data) course_module.pdf_textbooks = validate_textbook_json(request.body)
except ValueError: except TextbookValidationError as e:
msg = {"error": "invalid JSON"} 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") 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): if not any(tab['type'] == 'pdf_textbooks' for tab in course_module.tabs):
course_module.tabs.append({"type": "pdf_textbooks"}) course_module.tabs.append({"type": "pdf_textbooks"})
store.update_metadata(course_module.location, own_metadata(course_module)) store.update_metadata(course_module.location, own_metadata(course_module))
......
File mode changed from 100644 to 100755
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