Commit a5b1428b by Jay Zoldak

Merge pull request #1760 from MITx/feature/will/speed_up_unit_tests

Feature/will/speed up unit tests
parents f6e9b2ed d044d5c4
'''
Utilities for contentstore tests
'''
#pylint: disable=W0603
import json
import copy
from uuid import uuid4
......@@ -17,36 +23,89 @@ class ModuleStoreTestCase(TestCase):
collection with templates before running the TestCase
and drops it they are finished. """
def _pre_setup(self):
super(ModuleStoreTestCase, self)._pre_setup()
@staticmethod
def flush_mongo_except_templates():
'''
Delete everything in the module store except templates
'''
modulestore = xmodule.modulestore.django.modulestore()
# This query means: every item in the collection
# that is not a template
query = {"_id.course": {"$ne": "templates"}}
# Remove everything except templates
modulestore.collection.remove(query)
@staticmethod
def load_templates_if_necessary():
'''
Load templates into the modulestore only if they do not already exist.
We need the templates, because they are copied to create
XModules such as sections and problems
'''
modulestore = xmodule.modulestore.django.modulestore()
# Count the number of templates
query = {"_id.course": "templates"}
num_templates = modulestore.collection.find(query).count()
if num_templates < 1:
update_templates()
@classmethod
def setUpClass(cls):
'''
Flush the mongo store and set up templates
'''
# Use a uuid to differentiate
# the mongo collections on jenkins.
self.orig_MODULESTORE = copy.deepcopy(settings.MODULESTORE)
self.test_MODULESTORE = self.orig_MODULESTORE
self.test_MODULESTORE['default']['OPTIONS']['collection'] = 'modulestore_%s' % uuid4().hex
self.test_MODULESTORE['direct']['OPTIONS']['collection'] = 'modulestore_%s' % uuid4().hex
settings.MODULESTORE = self.test_MODULESTORE
# Flush and initialize the module store
# It needs the templates because it creates new records
# by cloning from the template.
# Note that if your test module gets in some weird state
# (though it shouldn't), do this manually
# from the bash shell to drop it:
# $ mongo test_xmodule --eval "db.dropDatabase()"
cls.orig_modulestore = copy.deepcopy(settings.MODULESTORE)
test_modulestore = cls.orig_modulestore
test_modulestore['default']['OPTIONS']['collection'] = 'modulestore_%s' % uuid4().hex
test_modulestore['direct']['OPTIONS']['collection'] = 'modulestore_%s' % uuid4().hex
xmodule.modulestore.django._MODULESTORES = {}
update_templates()
settings.MODULESTORE = test_modulestore
TestCase.setUpClass()
@classmethod
def tearDownClass(cls):
'''
Revert to the old modulestore settings
'''
# Clean up by dropping the collection
modulestore = xmodule.modulestore.django.modulestore()
modulestore.collection.drop()
# Restore the original modulestore settings
settings.MODULESTORE = cls.orig_modulestore
def _pre_setup(self):
'''
Remove everything but the templates before each test
'''
# Flush anything that is not a template
ModuleStoreTestCase.flush_mongo_except_templates()
# Check that we have templates loaded; if not, load them
ModuleStoreTestCase.load_templates_if_necessary()
# Call superclass implementation
super(ModuleStoreTestCase, self)._pre_setup()
def _post_teardown(self):
# Make sure you flush out the modulestore.
# Drop the collection at the end of the test,
# otherwise there will be lingering collections leftover
# from executing the tests.
xmodule.modulestore.django._MODULESTORES = {}
xmodule.modulestore.django.modulestore().collection.drop()
settings.MODULESTORE = self.orig_MODULESTORE
'''
Flush everything we created except the templates
'''
# Flush anything that is not a template
ModuleStoreTestCase.flush_mongo_except_templates()
# Call superclass implementation
super(ModuleStoreTestCase, self)._post_teardown()
......
......@@ -3,10 +3,11 @@ import unittest
import threading
import json
import urllib
import urlparse
import time
from mock_xqueue_server import MockXQueueServer, MockXQueueRequestHandler
from nose.plugins.skip import SkipTest
class MockXQueueServerTest(unittest.TestCase):
'''
......@@ -22,11 +23,16 @@ class MockXQueueServerTest(unittest.TestCase):
def setUp(self):
# This is a test of the test setup,
# so it does not need to run as part of the unit test suite
# You can re-enable it by commenting out the line below
raise SkipTest
# Create the server
server_port = 8034
self.server_url = 'http://127.0.0.1:%d' % server_port
self.server = MockXQueueServer(server_port,
{'correct': True, 'score': 1, 'msg': ''})
{'correct': True, 'score': 1, 'msg': ''})
# Start the server in a separate daemon thread
server_thread = threading.Thread(target=self.server.serve_forever)
......@@ -48,18 +54,18 @@ class MockXQueueServerTest(unittest.TestCase):
callback_url = 'http://127.0.0.1:8000/test_callback'
grade_header = json.dumps({'lms_callback_url': callback_url,
'lms_key': 'test_queuekey',
'queue_name': 'test_queue'})
'lms_key': 'test_queuekey',
'queue_name': 'test_queue'})
grade_body = json.dumps({'student_info': 'test',
'grader_payload': 'test',
'student_response': 'test'})
grade_request = {'xqueue_header': grade_header,
'xqueue_body': grade_body}
'xqueue_body': grade_body}
response_handle = urllib.urlopen(self.server_url + '/xqueue/submit',
urllib.urlencode(grade_request))
urllib.urlencode(grade_request))
response_dict = json.loads(response_handle.read())
......@@ -71,8 +77,8 @@ class MockXQueueServerTest(unittest.TestCase):
# Expect that the server tries to post back the grading info
xqueue_body = json.dumps({'correct': True, 'score': 1,
'msg': '<div></div>'})
'msg': '<div></div>'})
expected_callback_dict = {'xqueue_header': grade_header,
'xqueue_body': xqueue_body}
'xqueue_body': xqueue_body}
MockXQueueRequestHandler.post_to_url.assert_called_with(callback_url,
expected_callback_dict)
expected_callback_dict)
'''
Test for lms courseware app
'''
import logging
import json
import time
import random
from urlparse import urlsplit, urlunsplit
from django.contrib.auth.models import User, Group
......@@ -11,8 +17,6 @@ from django.core.urlresolvers import reverse
from django.test.utils import override_settings
import xmodule.modulestore.django
from xmodule.modulestore.mongo import MongoModuleStore
# Need access to internal func to put users in the right group
from courseware import grades
......@@ -29,6 +33,7 @@ from xmodule.modulestore.xml import XMLModuleStore
log = logging.getLogger("mitx." + __name__)
def parse_json(response):
"""Parse response, which is assumed to be json"""
return json.loads(response.content)
......@@ -47,7 +52,7 @@ def get_registration(email):
def mongo_store_config(data_dir):
'''
Defines default module store using MongoModuleStore
Use of this config requires mongo to be running
'''
return {
......@@ -101,7 +106,10 @@ TEST_DATA_DRAFT_MONGO_MODULESTORE = draft_mongo_store_config(TEST_DATA_DIR)
class LoginEnrollmentTestCase(TestCase):
'''Base TestCase providing support for user creation, activation, login, and course enrollment'''
'''
Base TestCase providing support for user creation,
activation, login, and course enrollment
'''
def assertRedirectsNoFollow(self, response, expected_url):
"""
......@@ -112,22 +120,26 @@ class LoginEnrollmentTestCase(TestCase):
Some of the code taken from django.test.testcases.py
"""
self.assertEqual(response.status_code, 302,
'Response status code was {0} instead of 302'.format(response.status_code))
'Response status code was %d instead of 302'
% (response.status_code))
url = response['Location']
e_scheme, e_netloc, e_path, e_query, e_fragment = urlsplit(expected_url)
if not (e_scheme or e_netloc):
expected_url = urlunsplit(('http', 'testserver', e_path, e_query, e_fragment))
expected_url = urlunsplit(('http', 'testserver',
e_path, e_query, e_fragment))
self.assertEqual(url, expected_url, "Response redirected to '{0}', expected '{1}'".format(
url, expected_url))
self.assertEqual(url, expected_url,
"Response redirected to '%s', expected '%s'" %
(url, expected_url))
def setup_viewtest_user(self):
'''create a user account, activate, and log in'''
self.viewtest_email = 'view@test.com'
self.viewtest_password = 'foo'
self.viewtest_username = 'viewtest'
self.create_account(self.viewtest_username, self.viewtest_email, self.viewtest_password)
self.create_account(self.viewtest_username,
self.viewtest_email, self.viewtest_password)
self.activate_user(self.viewtest_email)
self.login(self.viewtest_email, self.viewtest_password)
......@@ -185,7 +197,8 @@ class LoginEnrollmentTestCase(TestCase):
activation_key = get_registration(email).activation_key
# and now we try to activate
resp = self.client.get(reverse('activate', kwargs={'key': activation_key}))
url = reverse('activate', kwargs={'key': activation_key})
resp = self.client.get(url)
return resp
def activate_user(self, email):
......@@ -205,7 +218,8 @@ class LoginEnrollmentTestCase(TestCase):
def try_enroll(self, course):
"""Try to enroll. Return bool success instead of asserting it."""
data = self._enroll(course)
print 'Enrollment in {0} result: {1}'.format(course.location.url(), data)
print ('Enrollment in %s result: %s'
% (course.location.url(), str(data)))
return data['success']
def enroll(self, course):
......@@ -229,7 +243,8 @@ class LoginEnrollmentTestCase(TestCase):
"""
resp = self.client.get(url)
self.assertEqual(resp.status_code, code,
"got code {0} for url '{1}'. Expected code {2}".format(resp.status_code, url, code))
"got code %d for url '%s'. Expected code %d"
% (resp.status_code, url, code))
return resp
def check_for_post_code(self, code, url, data={}):
......@@ -239,7 +254,8 @@ class LoginEnrollmentTestCase(TestCase):
"""
resp = self.client.post(url, data)
self.assertEqual(resp.status_code, code,
"got code {0} for url '{1}'. Expected code {2}".format(resp.status_code, url, code))
"got code %d for url '%s'. Expected code %d"
% (resp.status_code, url, code))
return resp
......@@ -260,8 +276,10 @@ class ActivateLoginTest(LoginEnrollmentTestCase):
class PageLoaderTestCase(LoginEnrollmentTestCase):
''' Base class that adds a function to load all pages in a modulestore '''
def check_pages_load(self, module_store):
"""Make all locations in course load"""
def check_random_page_loads(self, module_store):
'''
Choose a page in the course randomly, and assert that it loads
'''
# enroll in the course before trying to access pages
courses = module_store.get_courses()
self.assertEqual(len(courses), 1)
......@@ -269,77 +287,71 @@ class PageLoaderTestCase(LoginEnrollmentTestCase):
self.enroll(course)
course_id = course.id
num = 0
num_bad = 0
all_ok = True
for descriptor in module_store.get_items(
Location(None, None, None, None, None)):
num += 1
print "Checking ", descriptor.location.url()
# We have ancillary course information now as modules and we can't simply use 'jump_to' to view them
if descriptor.location.category == 'about':
resp = self.client.get(reverse('about_course', kwargs={'course_id': course_id}))
msg = str(resp.status_code)
if resp.status_code != 200:
msg = "ERROR " + msg
all_ok = False
num_bad += 1
elif descriptor.location.category == 'static_tab':
resp = self.client.get(reverse('static_tab', kwargs={'course_id': course_id, 'tab_slug': descriptor.location.name}))
msg = str(resp.status_code)
if resp.status_code != 200:
msg = "ERROR " + msg
all_ok = False
num_bad += 1
elif descriptor.location.category == 'course_info':
resp = self.client.get(reverse('info', kwargs={'course_id': course_id}))
msg = str(resp.status_code)
if resp.status_code != 200:
msg = "ERROR " + msg
all_ok = False
num_bad += 1
elif descriptor.location.category == 'custom_tag_template':
pass
else:
#print descriptor.__class__, descriptor.location
resp = self.client.get(reverse('jump_to',
kwargs={'course_id': course_id,
'location': descriptor.location.url()}), follow=True)
msg = str(resp.status_code)
if resp.status_code != 200:
msg = "ERROR " + msg + ": " + descriptor.location.url()
all_ok = False
num_bad += 1
elif resp.redirect_chain[0][1] != 302:
msg = "ERROR on redirect from " + descriptor.location.url()
all_ok = False
num_bad += 1
# check content to make sure there were no rendering failures
content = resp.content
if content.find("this module is temporarily unavailable") >= 0:
msg = "ERROR unavailable module "
all_ok = False
num_bad += 1
elif isinstance(descriptor, ErrorDescriptor):
msg = "ERROR error descriptor loaded: "
msg = msg + descriptor.error_msg
all_ok = False
num_bad += 1
print msg
self.assertTrue(all_ok) # fail fast
print "{0}/{1} good".format(num - num_bad, num)
log.info("{0}/{1} good".format(num - num_bad, num))
self.assertTrue(all_ok)
# Search for items in the course
# None is treated as a wildcard
course_loc = course.location
location_query = Location(course_loc.tag, course_loc.org,
course_loc.course, None, None, None)
items = module_store.get_items(location_query)
if len(items) < 1:
self.fail('Could not retrieve any items from course')
else:
descriptor = random.choice(items)
# We have ancillary course information now as modules
# and we can't simply use 'jump_to' to view them
if descriptor.location.category == 'about':
self._assert_loads('about_course',
{'course_id': course_id},
descriptor)
elif descriptor.location.category == 'static_tab':
kwargs = {'course_id': course_id,
'tab_slug': descriptor.location.name}
self._assert_loads('static_tab', kwargs, descriptor)
elif descriptor.location.category == 'course_info':
self._assert_loads('info', {'course_id': course_id},
descriptor)
elif descriptor.location.category == 'custom_tag_template':
pass
else:
kwargs = {'course_id': course_id,
'location': descriptor.location.url()}
self._assert_loads('jump_to', kwargs, descriptor,
expect_redirect=True,
check_content=True)
def _assert_loads(self, django_url, kwargs, descriptor,
expect_redirect=False,
check_content=False):
'''
Assert that the url loads correctly.
If expect_redirect, then also check that we were redirected.
If check_content, then check that we don't get
an error message about unavailable modules.
'''
url = reverse(django_url, kwargs=kwargs)
response = self.client.get(url, follow=True)
if response.status_code != 200:
self.fail('Status %d for page %s' %
(response.status_code, descriptor.location.url()))
if expect_redirect:
self.assertEqual(response.redirect_chain[0][1], 302)
if check_content:
unavailable_msg = "this module is temporarily unavailable"
self.assertEqual(response.content.find(unavailable_msg), -1)
self.assertFalse(isinstance(descriptor, ErrorDescriptor))
@override_settings(MODULESTORE=TEST_DATA_XML_MODULESTORE)
......@@ -351,21 +363,13 @@ class TestCoursesLoadTestCase_XmlModulestore(PageLoaderTestCase):
xmodule.modulestore.django._MODULESTORES = {}
def test_toy_course_loads(self):
module_class = 'xmodule.hidden_module.HiddenDescriptor'
module_store = XMLModuleStore(TEST_DATA_DIR,
default_class='xmodule.hidden_module.HiddenDescriptor',
default_class=module_class,
course_dirs=['toy'],
load_error_modules=True,
)
self.check_pages_load(module_store)
load_error_modules=True)
def test_full_course_loads(self):
module_store = XMLModuleStore(TEST_DATA_DIR,
default_class='xmodule.hidden_module.HiddenDescriptor',
course_dirs=['full'],
load_error_modules=True,
)
self.check_pages_load(module_store)
self.check_random_page_loads(module_store)
@override_settings(MODULESTORE=TEST_DATA_MONGO_MODULESTORE)
......@@ -380,12 +384,7 @@ class TestCoursesLoadTestCase_MongoModulestore(PageLoaderTestCase):
def test_toy_course_loads(self):
module_store = modulestore()
import_from_xml(module_store, TEST_DATA_DIR, ['toy'])
self.check_pages_load(module_store)
def test_full_course_loads(self):
module_store = modulestore()
import_from_xml(module_store, TEST_DATA_DIR, ['full'])
self.check_pages_load(module_store)
self.check_random_page_loads(module_store)
@override_settings(MODULESTORE=TEST_DATA_XML_MODULESTORE)
......@@ -415,37 +414,51 @@ class TestNavigation(LoginEnrollmentTestCase):
self.enroll(self.full)
# First request should redirect to ToyVideos
resp = self.client.get(reverse('courseware', kwargs={'course_id': self.toy.id}))
resp = self.client.get(reverse('courseware',
kwargs={'course_id': self.toy.id}))
# Don't use no-follow, because state should only be saved once we actually hit the section
# Don't use no-follow, because state should
# only be saved once we actually hit the section
self.assertRedirects(resp, reverse(
'courseware_section', kwargs={'course_id': self.toy.id,
'chapter': 'Overview',
'section': 'Toy_Videos'}))
# Hitting the couseware tab again should redirect to the first chapter: 'Overview'
resp = self.client.get(reverse('courseware', kwargs={'course_id': self.toy.id}))
# Hitting the couseware tab again should
# redirect to the first chapter: 'Overview'
resp = self.client.get(reverse('courseware',
kwargs={'course_id': self.toy.id}))
self.assertRedirectsNoFollow(resp, reverse('courseware_chapter',
kwargs={'course_id': self.toy.id, 'chapter': 'Overview'}))
kwargs={'course_id': self.toy.id,
'chapter': 'Overview'}))
# Now we directly navigate to a section in a different chapter
self.check_for_get_code(200, reverse('courseware_section',
kwargs={'course_id': self.toy.id,
'chapter': 'secret:magic', 'section': 'toyvideo'}))
'chapter': 'secret:magic',
'section': 'toyvideo'}))
# And now hitting the courseware tab should redirect to 'secret:magic'
resp = self.client.get(reverse('courseware', kwargs={'course_id': self.toy.id}))
resp = self.client.get(reverse('courseware',
kwargs={'course_id': self.toy.id}))
self.assertRedirectsNoFollow(resp, reverse('courseware_chapter',
kwargs={'course_id': self.toy.id, 'chapter': 'secret:magic'}))
kwargs={'course_id': self.toy.id,
'chapter': 'secret:magic'}))
@override_settings(MODULESTORE=TEST_DATA_DRAFT_MONGO_MODULESTORE)
class TestDraftModuleStore(TestCase):
def test_get_items_with_course_items(self):
store = modulestore()
# fix was to allow get_items() to take the course_id parameter
store.get_items(Location(None, None, 'vertical', None, None), course_id='abc', depth=0)
# test success is just getting through the above statement. The bug was that 'course_id' argument was
store.get_items(Location(None, None, 'vertical', None, None),
course_id='abc', depth=0)
# test success is just getting through the above statement.
# The bug was that 'course_id' argument was
# not allowed to be passed in (i.e. was throwing exception)
......@@ -472,21 +485,29 @@ class TestViewAuth(LoginEnrollmentTestCase):
self.activate_user(self.instructor)
def test_instructor_pages(self):
"""Make sure only instructors for the course or staff can load the instructor
"""Make sure only instructors for the course
or staff can load the instructor
dashboard, the grade views, and student profile pages"""
# First, try with an enrolled student
self.login(self.student, self.password)
# shouldn't work before enroll
response = self.client.get(reverse('courseware', kwargs={'course_id': self.toy.id}))
self.assertRedirectsNoFollow(response, reverse('about_course', args=[self.toy.id]))
response = self.client.get(reverse('courseware',
kwargs={'course_id': self.toy.id}))
self.assertRedirectsNoFollow(response,
reverse('about_course',
args=[self.toy.id]))
self.enroll(self.toy)
self.enroll(self.full)
# should work now -- redirect to first page
response = self.client.get(reverse('courseware', kwargs={'course_id': self.toy.id}))
self.assertRedirectsNoFollow(response, reverse('courseware_section', kwargs={'course_id': self.toy.id,
'chapter': 'Overview',
'section': 'Toy_Videos'}))
response = self.client.get(reverse('courseware',
kwargs={'course_id': self.toy.id}))
self.assertRedirectsNoFollow(response,
reverse('courseware_section',
kwargs={'course_id': self.toy.id,
'chapter': 'Overview',
'section': 'Toy_Videos'}))
def instructor_urls(course):
"list of urls that only instructors/staff should be able to see"
......@@ -494,14 +515,19 @@ class TestViewAuth(LoginEnrollmentTestCase):
'instructor_dashboard',
'gradebook',
'grade_summary',)]
urls.append(reverse('student_progress', kwargs={'course_id': course.id,
'student_id': get_user(self.student).id}))
urls.append(reverse('student_progress',
kwargs={'course_id': course.id,
'student_id': get_user(self.student).id}))
return urls
# shouldn't be able to get to the instructor pages
for url in instructor_urls(self.toy) + instructor_urls(self.full):
print 'checking for 404 on {0}'.format(url)
self.check_for_get_code(404, url)
# Randomly sample an instructor page
url = random.choice(instructor_urls(self.toy) +
instructor_urls(self.full))
# Shouldn't be able to get to the instructor pages
print 'checking for 404 on {0}'.format(url)
self.check_for_get_code(404, url)
# Make the instructor staff in the toy course
group_name = _course_staff_group_name(self.toy.location)
......@@ -512,13 +538,13 @@ class TestViewAuth(LoginEnrollmentTestCase):
self.login(self.instructor, self.password)
# Now should be able to get to the toy course, but not the full course
for url in instructor_urls(self.toy):
print 'checking for 200 on {0}'.format(url)
self.check_for_get_code(200, url)
url = random.choice(instructor_urls(self.toy))
print 'checking for 200 on {0}'.format(url)
self.check_for_get_code(200, url)
for url in instructor_urls(self.full):
print 'checking for 404 on {0}'.format(url)
self.check_for_get_code(404, url)
url = random.choice(instructor_urls(self.full))
print 'checking for 404 on {0}'.format(url)
self.check_for_get_code(404, url)
# now also make the instructor staff
instructor = get_user(self.instructor)
......@@ -526,9 +552,10 @@ class TestViewAuth(LoginEnrollmentTestCase):
instructor.save()
# and now should be able to load both
for url in instructor_urls(self.toy) + instructor_urls(self.full):
print 'checking for 200 on {0}'.format(url)
self.check_for_get_code(200, url)
url = random.choice(instructor_urls(self.toy) +
instructor_urls(self.full))
print 'checking for 200 on {0}'.format(url)
self.check_for_get_code(200, url)
def run_wrapped(self, test):
"""
......@@ -572,7 +599,8 @@ class TestViewAuth(LoginEnrollmentTestCase):
def reverse_urls(names, course):
"""Reverse a list of course urls"""
return [reverse(name, kwargs={'course_id': course.id}) for name in names]
return [reverse(name, kwargs={'course_id': course.id})
for name in names]
def dark_student_urls(course):
"""
......@@ -581,7 +609,8 @@ class TestViewAuth(LoginEnrollmentTestCase):
"""
urls = reverse_urls(['info', 'progress'], course)
urls.extend([
reverse('book', kwargs={'course_id': course.id, 'book_index': book.title})
reverse('book', kwargs={'course_id': course.id,
'book_index': book.title})
for book in course.textbooks
])
return urls
......@@ -600,37 +629,46 @@ class TestViewAuth(LoginEnrollmentTestCase):
def instructor_urls(course):
"""list of urls that only instructors/staff should be able to see"""
urls = reverse_urls(['instructor_dashboard', 'gradebook', 'grade_summary'],
course)
urls = reverse_urls(['instructor_dashboard',
'gradebook', 'grade_summary'], course)
return urls
def check_non_staff(course):
"""Check that access is right for non-staff in course"""
print '=== Checking non-staff access for {0}'.format(course.id)
for url in instructor_urls(course) + dark_student_urls(course) + reverse_urls(['courseware'], course):
print 'checking for 404 on {0}'.format(url)
self.check_for_get_code(404, url)
for url in light_student_urls(course):
print 'checking for 200 on {0}'.format(url)
self.check_for_get_code(200, url)
# Randomly sample a dark url
url = random.choice(instructor_urls(course) +
dark_student_urls(course) +
reverse_urls(['courseware'], course))
print 'checking for 404 on {0}'.format(url)
self.check_for_get_code(404, url)
# Randomly sample a light url
url = random.choice(light_student_urls(course))
print 'checking for 200 on {0}'.format(url)
self.check_for_get_code(200, url)
def check_staff(course):
"""Check that access is right for staff in course"""
print '=== Checking staff access for {0}'.format(course.id)
for url in (instructor_urls(course) +
dark_student_urls(course) +
light_student_urls(course)):
print 'checking for 200 on {0}'.format(url)
self.check_for_get_code(200, url)
# Randomly sample a url
url = random.choice(instructor_urls(course) +
dark_student_urls(course) +
light_student_urls(course))
print 'checking for 200 on {0}'.format(url)
self.check_for_get_code(200, url)
# The student progress tab is not accessible to a student
# before launch, so the instructor view-as-student feature should return a 404 as well.
# before launch, so the instructor view-as-student feature
# should return a 404 as well.
# TODO (vshnayder): If this is not the behavior we want, will need
# to make access checking smarter and understand both the effective
# user (the student), and the requesting user (the prof)
url = reverse('student_progress', kwargs={'course_id': course.id,
'student_id': get_user(self.student).id})
url = reverse('student_progress',
kwargs={'course_id': course.id,
'student_id': get_user(self.student).id})
print 'checking for 404 on view-as-student: {0}'.format(url)
self.check_for_get_code(404, url)
......@@ -786,7 +824,7 @@ class TestCourseGrader(LoginEnrollmentTestCase):
self.graded_course.id, self.student_user, self.graded_course)
fake_request = self.factory.get(reverse('progress',
kwargs={'course_id': self.graded_course.id}))
kwargs={'course_id': self.graded_course.id}))
return grades.grade(self.student_user, fake_request,
self.graded_course, model_data_cache)
......@@ -801,10 +839,12 @@ class TestCourseGrader(LoginEnrollmentTestCase):
self.graded_course.id, self.student_user, self.graded_course)
fake_request = self.factory.get(reverse('progress',
kwargs={'course_id': self.graded_course.id}))
kwargs={'course_id': self.graded_course.id}))
progress_summary = grades.progress_summary(self.student_user, fake_request,
self.graded_course, model_data_cache)
progress_summary = grades.progress_summary(self.student_user,
fake_request,
self.graded_course,
model_data_cache)
return progress_summary
def check_grade_percent(self, percent):
......@@ -820,17 +860,17 @@ class TestCourseGrader(LoginEnrollmentTestCase):
input_i4x-edX-graded-problem-H1P3_2_1
input_i4x-edX-graded-problem-H1P3_2_2
"""
problem_location = "i4x://edX/graded/problem/{0}".format(problem_url_name)
problem_location = "i4x://edX/graded/problem/%s" % problem_url_name
modx_url = reverse('modx_dispatch',
kwargs={'course_id': self.graded_course.id,
'location': problem_location,
'dispatch': 'problem_check', })
kwargs={'course_id': self.graded_course.id,
'location': problem_location,
'dispatch': 'problem_check', })
resp = self.client.post(modx_url, {
'input_i4x-edX-graded-problem-{0}_2_1'.format(problem_url_name): responses[0],
'input_i4x-edX-graded-problem-{0}_2_2'.format(problem_url_name): responses[1],
})
'input_i4x-edX-graded-problem-%s_2_1' % problem_url_name: responses[0],
'input_i4x-edX-graded-problem-%s_2_2' % problem_url_name: responses[1],
})
print "modx_url", modx_url, "responses", responses
print "resp", resp
......@@ -845,9 +885,9 @@ class TestCourseGrader(LoginEnrollmentTestCase):
problem_location = self.problem_location(problem_url_name)
modx_url = reverse('modx_dispatch',
kwargs={'course_id': self.graded_course.id,
'location': problem_location,
'dispatch': 'problem_reset', })
kwargs={'course_id': self.graded_course.id,
'location': problem_location,
'dispatch': 'problem_reset', })
resp = self.client.post(modx_url)
return resp
......@@ -887,7 +927,8 @@ class TestCourseGrader(LoginEnrollmentTestCase):
self.assertEqual(earned_hw_scores(), [4.0, 0.0, 0])
self.assertEqual(score_for_hw('Homework1'), [2.0, 2.0])
# This problem is hidden in an ABTest. Getting it correct doesn't change total grade
# This problem is hidden in an ABTest.
# Getting it correct doesn't change total grade
self.submit_question_answer('H1P3', ['Correct', 'Correct'])
self.check_grade_percent(0.25)
self.assertEqual(score_for_hw('Homework1'), [2.0, 2.0])
......
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