Addressed pull request feedback.
This commit is contained in:
@@ -14,6 +14,22 @@ class ModuleStoreTestCase(TestCase):
|
||||
collection with templates before running the TestCase
|
||||
and drops it they are finished. """
|
||||
|
||||
def update_course(self, course, data):
|
||||
"""
|
||||
Updates the version of course in the mongo modulestore
|
||||
with the metadata in data and returns the updated version.
|
||||
"""
|
||||
|
||||
store = xmodule.modulestore.django.modulestore()
|
||||
|
||||
store.update_item(course.location, data)
|
||||
|
||||
store.update_metadata(course.location, data)
|
||||
|
||||
updated_course = store.get_instance(course.id, course.location)
|
||||
|
||||
return updated_course
|
||||
|
||||
@staticmethod
|
||||
def flush_mongo_except_templates():
|
||||
'''
|
||||
|
||||
@@ -60,6 +60,8 @@ class XModuleCourseFactory(Factory):
|
||||
if data is not None:
|
||||
store.update_item(new_course.location, data)
|
||||
|
||||
'''update_item updates the the course as it exists in the modulestore, but doesn't
|
||||
update the instance we are working with, so have to refetch the course after updating it.'''
|
||||
new_course = store.get_instance(new_course.id, new_course.location)
|
||||
|
||||
return new_course
|
||||
@@ -150,6 +152,8 @@ class XModuleItemFactory(Factory):
|
||||
if new_item.location.category not in DETACHED_CATEGORIES:
|
||||
store.update_children(parent_location, parent.children + [new_item.location.url()])
|
||||
|
||||
'''update_children updates the the item as it exists in the modulestore, but doesn't
|
||||
update the instance we are working with, so have to refetch the item after updating it.'''
|
||||
new_item = store.get_item(new_item.location)
|
||||
|
||||
return new_item
|
||||
|
||||
@@ -245,8 +245,7 @@ def _has_access_descriptor(user, descriptor, action, course_context=None):
|
||||
if descriptor.lms.start is not None:
|
||||
now = datetime.now(UTC())
|
||||
effective_start = _adjust_start_date_for_beta_testers(user, descriptor)
|
||||
difference = (now - effective_start).total_seconds()
|
||||
if difference > 3600:
|
||||
if now > effective_start:
|
||||
# after start date, everyone can see it
|
||||
debug("Allow: now > effective start date")
|
||||
return True
|
||||
|
||||
24
lms/djangoapps/courseware/tests/check_request_code.py
Normal file
24
lms/djangoapps/courseware/tests/check_request_code.py
Normal file
@@ -0,0 +1,24 @@
|
||||
|
||||
|
||||
def check_for_get_code(code, url):
|
||||
"""
|
||||
Check that we got the expected code when accessing url via GET.
|
||||
Returns the response.
|
||||
"""
|
||||
resp = self.client.get(url)
|
||||
self.assertEqual(resp.status_code, code,
|
||||
"got code %d for url '%s'. Expected code %d"
|
||||
% (resp.status_code, url, code))
|
||||
return resp
|
||||
|
||||
|
||||
def check_for_post_code(code, url, data={}):
|
||||
"""
|
||||
Check that we got the expected code when accessing url via POST.
|
||||
Returns the response.
|
||||
"""
|
||||
resp = self.client.post(url, data)
|
||||
self.assertEqual(resp.status_code, code,
|
||||
"got code %d for url '%s'. Expected code %d"
|
||||
% (resp.status_code, url, code))
|
||||
return resp
|
||||
142
lms/djangoapps/courseware/tests/helpers.py
Normal file
142
lms/djangoapps/courseware/tests/helpers.py
Normal file
@@ -0,0 +1,142 @@
|
||||
import json
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
from student.models import Registration
|
||||
|
||||
from django.test import TestCase
|
||||
|
||||
|
||||
def check_for_get_code(self, code, url):
|
||||
"""
|
||||
Check that we got the expected code when accessing url via GET.
|
||||
Returns the HTTP response.
|
||||
'self' is a class that subclasses TestCase.
|
||||
"""
|
||||
resp = self.client.get(url)
|
||||
self.assertEqual(resp.status_code, 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={}):
|
||||
"""
|
||||
Check that we got the expected code when accessing url via POST.
|
||||
Returns the HTTP response.
|
||||
'self' is a class that subclasses TestCase.
|
||||
"""
|
||||
resp = self.client.post(url, data)
|
||||
self.assertEqual(resp.status_code, code,
|
||||
"got code %d for url '%s'. Expected code %d"
|
||||
% (resp.status_code, url, code))
|
||||
return resp
|
||||
|
||||
|
||||
class LoginEnrollmentTestCase(TestCase):
|
||||
|
||||
def setup_user(self):
|
||||
"""
|
||||
Create a user account, activate, and log in.
|
||||
"""
|
||||
self.email = 'foo@test.com'
|
||||
self.password = 'bar'
|
||||
self.username = 'test'
|
||||
self.create_account(self.username,
|
||||
self.email, self.password)
|
||||
self.activate_user(self.email)
|
||||
self.login(self.email, self.password)
|
||||
|
||||
# ============ User creation and login ==============
|
||||
|
||||
def login(self, email, password):
|
||||
"""
|
||||
Login, check that the corresponding view's response has a 200 status code.
|
||||
"""
|
||||
resp = resp = self.client.post(reverse('login'),
|
||||
{'email': email, 'password': password})
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
data = json.loads(resp.content)
|
||||
self.assertTrue(data['success'])
|
||||
|
||||
def logout(self):
|
||||
"""
|
||||
Logout, check that it worked.
|
||||
Returns an HTTP response which e
|
||||
"""
|
||||
resp = self.client.get(reverse('logout'), {})
|
||||
# should redirect
|
||||
self.assertEqual(resp.status_code, 302)
|
||||
|
||||
def create_account(self, username, email, password):
|
||||
"""
|
||||
Create the account and check that it worked.
|
||||
"""
|
||||
resp = self.client.post(reverse('create_account'), {
|
||||
'username': username,
|
||||
'email': email,
|
||||
'password': password,
|
||||
'name': 'username',
|
||||
'terms_of_service': 'true',
|
||||
'honor_code': 'true',
|
||||
})
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
data = json.loads(resp.content)
|
||||
self.assertEqual(data['success'], True)
|
||||
|
||||
# Check both that the user is created, and inactive
|
||||
self.assertFalse(User.objects.get(email=email).is_active)
|
||||
|
||||
def activate_user(self, email):
|
||||
"""
|
||||
Look up the activation key for the user, then hit the activate view.
|
||||
No error checking.
|
||||
"""
|
||||
activation_key = Registration.objects.get(user__email=email).activation_key
|
||||
|
||||
# and now we try to activate
|
||||
url = reverse('activate', kwargs={'key': activation_key})
|
||||
|
||||
resp = self.client.get(url)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
# Now make sure that the user is now actually activated
|
||||
self.assertTrue(User.objects.get(email=email).is_active)
|
||||
|
||||
def enroll(self, course, verify=False):
|
||||
"""
|
||||
Try to enroll and return boolean indicating result.
|
||||
'course' is an instance of CourseDescriptor.
|
||||
'verify' is an optional parameter specifying whether we
|
||||
want to verify that the student was successfully enrolled
|
||||
in the course.
|
||||
"""
|
||||
resp = self.client.post(reverse('change_enrollment'), {
|
||||
'enrollment_action': 'enroll',
|
||||
'course_id': course.id,
|
||||
})
|
||||
print ('Enrollment in %s result status code: %s'
|
||||
% (course.location.url(), str(resp.status_code)))
|
||||
result = resp.status_code == 200
|
||||
if verify:
|
||||
self.assertTrue(result)
|
||||
return result
|
||||
|
||||
# def enroll(self, course):
|
||||
# """
|
||||
# Enroll the currently logged-in user, and check that it worked.
|
||||
# """
|
||||
|
||||
# result = self.try_enroll(course)
|
||||
# self.assertTrue(result)
|
||||
|
||||
def unenroll(self, course):
|
||||
"""
|
||||
Unenroll the currently logged-in user, and check that it worked.
|
||||
'course' is an instance of CourseDescriptor.
|
||||
"""
|
||||
resp = self.client.post('/change_enrollment', {
|
||||
'enrollment_action': 'unenroll',
|
||||
'course_id': course.id,
|
||||
})
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
72
lms/djangoapps/courseware/tests/modulestore_config.py
Normal file
72
lms/djangoapps/courseware/tests/modulestore_config.py
Normal file
@@ -0,0 +1,72 @@
|
||||
from uuid import uuid4
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
def mongo_store_config(data_dir):
|
||||
'''
|
||||
Defines default module store using MongoModuleStore
|
||||
|
||||
Use of this config requires mongo to be running
|
||||
'''
|
||||
store = {
|
||||
'default': {
|
||||
'ENGINE': 'xmodule.modulestore.mongo.MongoModuleStore',
|
||||
'OPTIONS': {
|
||||
'default_class': 'xmodule.raw_module.RawDescriptor',
|
||||
'host': 'localhost',
|
||||
'db': 'test_xmodule',
|
||||
'collection': 'modulestore_%s' % uuid4().hex,
|
||||
'fs_root': data_dir,
|
||||
'render_template': 'mitxmako.shortcuts.render_to_string'
|
||||
}
|
||||
}
|
||||
}
|
||||
store['direct'] = store['default']
|
||||
return store
|
||||
|
||||
|
||||
def draft_mongo_store_config(data_dir):
|
||||
'''Defines default module store using DraftMongoModuleStore'''
|
||||
return {
|
||||
'default': {
|
||||
'ENGINE': 'xmodule.modulestore.mongo.DraftMongoModuleStore',
|
||||
'OPTIONS': {
|
||||
'default_class': 'xmodule.raw_module.RawDescriptor',
|
||||
'host': 'localhost',
|
||||
'db': 'test_xmodule',
|
||||
'collection': 'modulestore_%s' % uuid4().hex,
|
||||
'fs_root': data_dir,
|
||||
'render_template': 'mitxmako.shortcuts.render_to_string',
|
||||
}
|
||||
},
|
||||
'direct': {
|
||||
'ENGINE': 'xmodule.modulestore.mongo.MongoModuleStore',
|
||||
'OPTIONS': {
|
||||
'default_class': 'xmodule.raw_module.RawDescriptor',
|
||||
'host': 'localhost',
|
||||
'db': 'test_xmodule',
|
||||
'collection': 'modulestore_%s' % uuid4().hex,
|
||||
'fs_root': data_dir,
|
||||
'render_template': 'mitxmako.shortcuts.render_to_string',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def xml_store_config(data_dir):
|
||||
'''Defines default module store using XMLModuleStore'''
|
||||
return {
|
||||
'default': {
|
||||
'ENGINE': 'xmodule.modulestore.xml.XMLModuleStore',
|
||||
'OPTIONS': {
|
||||
'data_dir': data_dir,
|
||||
'default_class': 'xmodule.hidden_module.HiddenDescriptor',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_DATA_DIR = settings.COMMON_TEST_DATA_ROOT
|
||||
TEST_DATA_XML_MODULESTORE = xml_store_config(TEST_DATA_DIR)
|
||||
TEST_DATA_MONGO_MODULESTORE = mongo_store_config(TEST_DATA_DIR)
|
||||
TEST_DATA_DRAFT_MONGO_MODULESTORE = draft_mongo_store_config(TEST_DATA_DIR)
|
||||
@@ -1,172 +0,0 @@
|
||||
import logging
|
||||
import json
|
||||
|
||||
from urlparse import urlsplit, urlunsplit
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
from student.models import Registration
|
||||
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
|
||||
log = logging.getLogger("mitx." + __name__)
|
||||
|
||||
|
||||
def parse_json(response):
|
||||
"""Parse response, which is assumed to be json"""
|
||||
return json.loads(response.content)
|
||||
|
||||
|
||||
def get_user(email):
|
||||
'''look up a user by email'''
|
||||
return User.objects.get(email=email)
|
||||
|
||||
|
||||
def get_registration(email):
|
||||
'''look up registration object by email'''
|
||||
return Registration.objects.get(user__email=email)
|
||||
|
||||
|
||||
class MongoLoginHelpers(ModuleStoreTestCase):
|
||||
|
||||
def assertRedirectsNoFollow(self, response, expected_url):
|
||||
"""
|
||||
http://devblog.point2.com/2010/04/23/djangos-assertredirects-little-gotcha/
|
||||
|
||||
Don't check that the redirected-to page loads--there should be other tests for that.
|
||||
|
||||
Some of the code taken from django.test.testcases.py
|
||||
"""
|
||||
self.assertEqual(response.status_code, 302,
|
||||
'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))
|
||||
|
||||
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.activate_user(self.viewtest_email)
|
||||
self.login(self.viewtest_email, self.viewtest_password)
|
||||
|
||||
# ============ User creation and login ==============
|
||||
|
||||
def _login(self, email, password):
|
||||
'''Login. View should always return 200. The success/fail is in the
|
||||
returned json'''
|
||||
resp = self.client.post(reverse('login'),
|
||||
{'email': email, 'password': password})
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
return resp
|
||||
|
||||
def login(self, email, password):
|
||||
'''Login, check that it worked.'''
|
||||
resp = self._login(email, password)
|
||||
data = parse_json(resp)
|
||||
self.assertTrue(data['success'])
|
||||
return resp
|
||||
|
||||
def logout(self):
|
||||
'''Logout, check that it worked.'''
|
||||
resp = self.client.get(reverse('logout'), {})
|
||||
# should redirect
|
||||
self.assertEqual(resp.status_code, 302)
|
||||
return resp
|
||||
|
||||
def _create_account(self, username, email, password):
|
||||
'''Try to create an account. No error checking'''
|
||||
resp = self.client.post('/create_account', {
|
||||
'username': username,
|
||||
'email': email,
|
||||
'password': password,
|
||||
'name': 'Fred Weasley',
|
||||
'terms_of_service': 'true',
|
||||
'honor_code': 'true',
|
||||
})
|
||||
return resp
|
||||
|
||||
def create_account(self, username, email, password):
|
||||
'''Create the account and check that it worked'''
|
||||
resp = self._create_account(username, email, password)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
data = parse_json(resp)
|
||||
self.assertEqual(data['success'], True)
|
||||
|
||||
# Check both that the user is created, and inactive
|
||||
self.assertFalse(get_user(email).is_active)
|
||||
|
||||
return resp
|
||||
|
||||
def _activate_user(self, email):
|
||||
'''Look up the activation key for the user, then hit the activate view.
|
||||
No error checking'''
|
||||
activation_key = get_registration(email).activation_key
|
||||
|
||||
# and now we try to activate
|
||||
url = reverse('activate', kwargs={'key': activation_key})
|
||||
resp = self.client.get(url)
|
||||
return resp
|
||||
|
||||
def activate_user(self, email):
|
||||
resp = self._activate_user(email)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
# Now make sure that the user is now actually activated
|
||||
self.assertTrue(get_user(email).is_active)
|
||||
|
||||
def try_enroll(self, course):
|
||||
"""Try to enroll. Return bool success instead of asserting it."""
|
||||
resp = self.client.post('/change_enrollment', {
|
||||
'enrollment_action': 'enroll',
|
||||
'course_id': course.id,
|
||||
})
|
||||
print ('Enrollment in %s result status code: %s'
|
||||
% (course.location.url(), str(resp.status_code)))
|
||||
return resp.status_code == 200
|
||||
|
||||
def enroll(self, course):
|
||||
"""Enroll the currently logged-in user, and check that it worked."""
|
||||
result = self.try_enroll(course)
|
||||
self.assertTrue(result)
|
||||
|
||||
def unenroll(self, course):
|
||||
"""Unenroll the currently logged-in user, and check that it worked."""
|
||||
resp = self.client.post('/change_enrollment', {
|
||||
'enrollment_action': 'unenroll',
|
||||
'course_id': course.id,
|
||||
})
|
||||
self.assertTrue(resp.status_code == 200)
|
||||
|
||||
def check_for_get_code(self, code, url):
|
||||
"""
|
||||
Check that we got the expected code when accessing url via GET.
|
||||
Returns the response.
|
||||
"""
|
||||
resp = self.client.get(url)
|
||||
self.assertEqual(resp.status_code, 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={}):
|
||||
"""
|
||||
Check that we got the expected code when accessing url via POST.
|
||||
Returns the response.
|
||||
"""
|
||||
resp = self.client.post(url, data)
|
||||
self.assertEqual(resp.status_code, code,
|
||||
"got code %d for url '%s'. Expected code %d"
|
||||
% (resp.status_code, url, code))
|
||||
return resp
|
||||
21
lms/djangoapps/courseware/tests/test_draft_modulestore.py
Normal file
21
lms/djangoapps/courseware/tests/test_draft_modulestore.py
Normal file
@@ -0,0 +1,21 @@
|
||||
from django.test import TestCase
|
||||
from django.test.utils import override_settings
|
||||
|
||||
from xmodule.modulestore.django import modulestore
|
||||
from xmodule.modulestore import Location
|
||||
|
||||
from modulestore_config import TEST_DATA_DRAFT_MONGO_MODULESTORE
|
||||
|
||||
|
||||
@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
|
||||
# not allowed to be passed in (i.e. was throwing exception)
|
||||
@@ -14,11 +14,13 @@ from django.core.urlresolvers import reverse
|
||||
|
||||
from django.contrib.auth.models import User, Group
|
||||
from courseware.access import _course_staff_group_name
|
||||
from courseware.tests.tests import LoginEnrollmentTestCase, TEST_DATA_XML_MODULESTORE, get_user
|
||||
from courseware.tests.helpers import LoginEnrollmentTestCase
|
||||
from courseware.tests.modulestore_config import TEST_DATA_XML_MODULESTORE
|
||||
from xmodule.modulestore.django import modulestore
|
||||
import xmodule.modulestore.django
|
||||
import json
|
||||
|
||||
|
||||
@override_settings(MODULESTORE=TEST_DATA_XML_MODULESTORE)
|
||||
class TestStaffMasqueradeAsStudent(LoginEnrollmentTestCase):
|
||||
'''
|
||||
@@ -41,7 +43,7 @@ class TestStaffMasqueradeAsStudent(LoginEnrollmentTestCase):
|
||||
def make_instructor(course):
|
||||
group_name = _course_staff_group_name(course.location)
|
||||
g = Group.objects.create(name=group_name)
|
||||
g.user_set.add(get_user(self.instructor))
|
||||
g.user_set.add(User.objects.get(email=self.instructor))
|
||||
|
||||
make_instructor(self.graded_course)
|
||||
|
||||
@@ -67,7 +69,6 @@ class TestStaffMasqueradeAsStudent(LoginEnrollmentTestCase):
|
||||
|
||||
self.assertTrue(sdebug in resp.content)
|
||||
|
||||
|
||||
def toggle_masquerade(self):
|
||||
'''
|
||||
Toggle masquerade state
|
||||
|
||||
@@ -1,378 +1,24 @@
|
||||
import logging
|
||||
import json
|
||||
import random
|
||||
|
||||
from urlparse import urlsplit, urlunsplit
|
||||
from uuid import uuid4
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.test import TestCase
|
||||
from django.conf import settings
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.test.utils import override_settings
|
||||
|
||||
import xmodule.modulestore.django
|
||||
|
||||
from student.models import Registration
|
||||
from xmodule.error_module import ErrorDescriptor
|
||||
from xmodule.modulestore.django import modulestore
|
||||
from xmodule.modulestore import Location
|
||||
from xmodule.modulestore.xml_importer import import_from_xml
|
||||
from xmodule.modulestore.xml import XMLModuleStore
|
||||
|
||||
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
|
||||
|
||||
from mongo_login_helpers import *
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
|
||||
log = logging.getLogger("mitx." + __name__)
|
||||
import xmodule.modulestore.django
|
||||
|
||||
|
||||
def parse_json(response):
|
||||
"""Parse response, which is assumed to be json"""
|
||||
return json.loads(response.content)
|
||||
|
||||
|
||||
def get_user(email):
|
||||
'''look up a user by email'''
|
||||
return User.objects.get(email=email)
|
||||
|
||||
|
||||
def get_registration(email):
|
||||
'''look up registration object by email'''
|
||||
return Registration.objects.get(user__email=email)
|
||||
|
||||
|
||||
def mongo_store_config(data_dir):
|
||||
'''
|
||||
Defines default module store using MongoModuleStore
|
||||
|
||||
Use of this config requires mongo to be running
|
||||
'''
|
||||
store = {
|
||||
'default': {
|
||||
'ENGINE': 'xmodule.modulestore.mongo.MongoModuleStore',
|
||||
'OPTIONS': {
|
||||
'default_class': 'xmodule.raw_module.RawDescriptor',
|
||||
'host': 'localhost',
|
||||
'db': 'test_xmodule',
|
||||
'collection': 'modulestore_%s' % uuid4().hex,
|
||||
'fs_root': data_dir,
|
||||
'render_template': 'mitxmako.shortcuts.render_to_string',
|
||||
}
|
||||
}
|
||||
}
|
||||
store['direct'] = store['default']
|
||||
return store
|
||||
|
||||
|
||||
def xml_store_config(data_dir):
|
||||
'''Defines default module store using XMLModuleStore'''
|
||||
return {
|
||||
'default': {
|
||||
'ENGINE': 'xmodule.modulestore.xml.XMLModuleStore',
|
||||
'OPTIONS': {
|
||||
'data_dir': data_dir,
|
||||
'default_class': 'xmodule.hidden_module.HiddenDescriptor',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_DATA_DIR = settings.COMMON_TEST_DATA_ROOT
|
||||
TEST_DATA_XML_MODULESTORE = xml_store_config(TEST_DATA_DIR)
|
||||
TEST_DATA_MONGO_MODULESTORE = mongo_store_config(TEST_DATA_DIR)
|
||||
|
||||
|
||||
class LoginEnrollmentTestCase(TestCase):
|
||||
|
||||
'''
|
||||
Base TestCase providing support for user creation,
|
||||
activation, login, and course enrollment
|
||||
'''
|
||||
|
||||
def assertRedirectsNoFollow(self, response, expected_url):
|
||||
"""
|
||||
http://devblog.point2.com/2010/04/23/djangos-assertredirects-little-gotcha/
|
||||
|
||||
Don't check that the redirected-to page loads--there should be other tests for that.
|
||||
|
||||
Some of the code taken from django.test.testcases.py
|
||||
"""
|
||||
self.assertEqual(response.status_code, 302,
|
||||
'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))
|
||||
|
||||
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.activate_user(self.viewtest_email)
|
||||
self.login(self.viewtest_email, self.viewtest_password)
|
||||
|
||||
# ============ User creation and login ==============
|
||||
|
||||
def _login(self, email, password):
|
||||
'''Login. View should always return 200. The success/fail is in the
|
||||
returned json'''
|
||||
resp = self.client.post(reverse('login'),
|
||||
{'email': email, 'password': password})
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
return resp
|
||||
|
||||
def login(self, email, password):
|
||||
'''Login, check that it worked.'''
|
||||
resp = self._login(email, password)
|
||||
data = parse_json(resp)
|
||||
self.assertTrue(data['success'])
|
||||
return resp
|
||||
|
||||
def logout(self):
|
||||
'''Logout, check that it worked.'''
|
||||
resp = self.client.get(reverse('logout'), {})
|
||||
# should redirect
|
||||
self.assertEqual(resp.status_code, 302)
|
||||
return resp
|
||||
|
||||
def _create_account(self, username, email, password):
|
||||
'''Try to create an account. No error checking'''
|
||||
resp = self.client.post('/create_account', {
|
||||
'username': username,
|
||||
'email': email,
|
||||
'password': password,
|
||||
'name': 'Fred Weasley',
|
||||
'terms_of_service': 'true',
|
||||
'honor_code': 'true',
|
||||
})
|
||||
return resp
|
||||
|
||||
def create_account(self, username, email, password):
|
||||
'''Create the account and check that it worked'''
|
||||
resp = self._create_account(username, email, password)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
data = parse_json(resp)
|
||||
self.assertEqual(data['success'], True)
|
||||
|
||||
# Check both that the user is created, and inactive
|
||||
self.assertFalse(get_user(email).is_active)
|
||||
|
||||
return resp
|
||||
|
||||
def _activate_user(self, email):
|
||||
'''Look up the activation key for the user, then hit the activate view.
|
||||
No error checking'''
|
||||
activation_key = get_registration(email).activation_key
|
||||
|
||||
# and now we try to activate
|
||||
url = reverse('activate', kwargs={'key': activation_key})
|
||||
resp = self.client.get(url)
|
||||
return resp
|
||||
|
||||
def activate_user(self, email):
|
||||
resp = self._activate_user(email)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
# Now make sure that the user is now actually activated
|
||||
self.assertTrue(get_user(email).is_active)
|
||||
|
||||
def try_enroll(self, course):
|
||||
"""Try to enroll. Return bool success instead of asserting it."""
|
||||
resp = self.client.post('/change_enrollment', {
|
||||
'enrollment_action': 'enroll',
|
||||
'course_id': course.id,
|
||||
})
|
||||
print ('Enrollment in %s result status code: %s'
|
||||
% (course.location.url(), str(resp.status_code)))
|
||||
return resp.status_code == 200
|
||||
|
||||
def enroll(self, course):
|
||||
"""Enroll the currently logged-in user, and check that it worked."""
|
||||
result = self.try_enroll(course)
|
||||
self.assertTrue(result)
|
||||
|
||||
def unenroll(self, course):
|
||||
"""Unenroll the currently logged-in user, and check that it worked."""
|
||||
resp = self.client.post('/change_enrollment', {
|
||||
'enrollment_action': 'unenroll',
|
||||
'course_id': course.id,
|
||||
})
|
||||
self.assertTrue(resp.status_code == 200)
|
||||
|
||||
def check_for_get_code(self, code, url):
|
||||
"""
|
||||
Check that we got the expected code when accessing url via GET.
|
||||
Returns the response.
|
||||
"""
|
||||
resp = self.client.get(url)
|
||||
self.assertEqual(resp.status_code, 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={}):
|
||||
"""
|
||||
Check that we got the expected code when accessing url via POST.
|
||||
Returns the response.
|
||||
"""
|
||||
resp = self.client.post(url, data)
|
||||
self.assertEqual(resp.status_code, code,
|
||||
"got code %d for url '%s'. Expected code %d"
|
||||
% (resp.status_code, url, code))
|
||||
return resp
|
||||
|
||||
|
||||
class ActivateLoginTest(LoginEnrollmentTestCase):
|
||||
'''Test logging in and logging out'''
|
||||
def setUp(self):
|
||||
self.setup_viewtest_user()
|
||||
|
||||
def test_activate_login(self):
|
||||
'''Test login -- the setup function does all the work'''
|
||||
pass
|
||||
|
||||
def test_logout(self):
|
||||
'''Test logout -- setup function does login'''
|
||||
self.logout()
|
||||
|
||||
|
||||
class PageLoaderTestCase(LoginEnrollmentTestCase):
|
||||
''' Base class that adds a function to load all pages in a modulestore '''
|
||||
|
||||
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)
|
||||
course = courses[0]
|
||||
self.enroll(course)
|
||||
course_id = course.id
|
||||
|
||||
# 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)
|
||||
class TestCoursesLoadTestCase_XmlModulestore(PageLoaderTestCase):
|
||||
'''Check that all pages in test courses load properly from XML'''
|
||||
|
||||
def setUp(self):
|
||||
super(TestCoursesLoadTestCase_XmlModulestore, self).setUp()
|
||||
self.setup_viewtest_user()
|
||||
xmodule.modulestore.django._MODULESTORES = {}
|
||||
|
||||
def test_toy_course_loads(self):
|
||||
module_class = 'xmodule.hidden_module.HiddenDescriptor'
|
||||
module_store = XMLModuleStore(TEST_DATA_DIR,
|
||||
default_class=module_class,
|
||||
course_dirs=['toy'],
|
||||
load_error_modules=True)
|
||||
|
||||
self.check_random_page_loads(module_store)
|
||||
from helpers import LoginEnrollmentTestCase, check_for_get_code
|
||||
from modulestore_config import TEST_DATA_MONGO_MODULESTORE
|
||||
|
||||
|
||||
@override_settings(MODULESTORE=TEST_DATA_MONGO_MODULESTORE)
|
||||
class TestCoursesLoadTestCase_MongoModulestore(PageLoaderTestCase):
|
||||
'''Check that all pages in test courses load properly from Mongo'''
|
||||
class TestNavigation(ModuleStoreTestCase, LoginEnrollmentTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestCoursesLoadTestCase_MongoModulestore, self).setUp()
|
||||
self.setup_viewtest_user()
|
||||
xmodule.modulestore.django._MODULESTORES = {}
|
||||
modulestore().collection.drop()
|
||||
STUDENT_INFO = [('view@test.com', 'foo'), ('view2@test.com', 'foo')]
|
||||
|
||||
def test_toy_course_loads(self):
|
||||
module_store = modulestore()
|
||||
import_from_xml(module_store, TEST_DATA_DIR, ['toy'])
|
||||
self.check_random_page_loads(module_store)
|
||||
|
||||
def test_full_textbooks_loads(self):
|
||||
module_store = modulestore()
|
||||
import_from_xml(module_store, TEST_DATA_DIR, ['full'])
|
||||
|
||||
course = module_store.get_item(Location(['i4x', 'edX', 'full', 'course', '6.002_Spring_2012', None]))
|
||||
|
||||
self.assertGreater(len(course.textbooks), 0)
|
||||
|
||||
|
||||
@override_settings(MODULESTORE=TEST_DATA_MONGO_MODULESTORE)
|
||||
class TestNavigation(MongoLoginHelpers):
|
||||
|
||||
"""Check that navigation state is saved properly"""
|
||||
"""
|
||||
Check that navigation state is saved properly.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
xmodule.modulestore.django._MODULESTORES = {}
|
||||
@@ -388,52 +34,67 @@ class TestNavigation(MongoLoginHelpers):
|
||||
self.section9 = ItemFactory.create(parent_location=self.chapter9.location,
|
||||
display_name='factory_section')
|
||||
|
||||
#Create two accounts
|
||||
self.student = 'view@test.com'
|
||||
self.student2 = 'view2@test.com'
|
||||
self.password = 'foo'
|
||||
self.create_account('u1', self.student, self.password)
|
||||
self.create_account('u2', self.student2, self.password)
|
||||
self.activate_user(self.student)
|
||||
self.activate_user(self.student2)
|
||||
# Create student accounts and activate them.
|
||||
for i in range(len(self.STUDENT_INFO)):
|
||||
self.create_account('u{0}'.format(i), self.STUDENT_INFO[i][0], self.STUDENT_INFO[i][1])
|
||||
self.activate_user(self.STUDENT_INFO[i][0])
|
||||
|
||||
def test_accordion_state(self):
|
||||
"""Make sure that the accordion remembers where you were properly"""
|
||||
self.login(self.student, self.password)
|
||||
self.enroll(self.course)
|
||||
self.enroll(self.full)
|
||||
|
||||
# First request should redirect to ToyVideos
|
||||
def test_redirects_first_time(self):
|
||||
"""
|
||||
Verify that the first time we click on the courseware tab we are
|
||||
redirected to the 'Welcome' section.
|
||||
"""
|
||||
self.login(self.STUDENT_INFO[0][0], self.STUDENT_INFO[0][1])
|
||||
self.enroll(self.course, True)
|
||||
self.enroll(self.full, True)
|
||||
|
||||
resp = self.client.get(reverse('courseware',
|
||||
kwargs={'course_id': self.course.id}))
|
||||
|
||||
# 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.course.id,
|
||||
'chapter': 'Overview',
|
||||
'section': 'Welcome'}))
|
||||
|
||||
# Hitting the couseware tab again should
|
||||
# redirect to the first chapter: 'Overview'
|
||||
def test_redirects_second_time(self):
|
||||
"""
|
||||
Verify the accordion remembers we've already visited the Welcome section
|
||||
and redirects correpondingly.
|
||||
"""
|
||||
self.login(self.STUDENT_INFO[0][0], self.STUDENT_INFO[0][1])
|
||||
self.enroll(self.course, True)
|
||||
self.enroll(self.full, True)
|
||||
|
||||
self.client.get(reverse('courseware_section', kwargs={'course_id': self.course.id,
|
||||
'chapter': 'Overview',
|
||||
'section': 'Welcome'}))
|
||||
|
||||
resp = self.client.get(reverse('courseware',
|
||||
kwargs={'course_id': self.course.id}))
|
||||
|
||||
self.assertRedirectsNoFollow(resp, reverse('courseware_chapter',
|
||||
kwargs={'course_id': self.course.id,
|
||||
'chapter': 'Overview'}))
|
||||
self.assertRedirects(resp, reverse('courseware_chapter',
|
||||
kwargs={'course_id': self.course.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.course.id,
|
||||
'chapter': 'factory_chapter',
|
||||
'section': 'factory_section'}))
|
||||
def test_accordion_state(self):
|
||||
"""
|
||||
Verify the accordion remembers which chapter you were last viewing.
|
||||
"""
|
||||
|
||||
# And now hitting the courseware tab should redirect to 'secret:magic'
|
||||
self.login(self.STUDENT_INFO[0][0], self.STUDENT_INFO[0][1])
|
||||
self.enroll(self.course, True)
|
||||
self.enroll(self.full, True)
|
||||
|
||||
# Now we directly navigate to a section in a chapter other than 'Overview'.
|
||||
check_for_get_code(self, 200, reverse('courseware_section',
|
||||
kwargs={'course_id': self.course.id,
|
||||
'chapter': 'factory_chapter',
|
||||
'section': 'factory_section'}))
|
||||
|
||||
# And now hitting the courseware tab should redirect to 'factory_chapter'
|
||||
resp = self.client.get(reverse('courseware',
|
||||
kwargs={'course_id': self.course.id}))
|
||||
|
||||
self.assertRedirectsNoFollow(resp, reverse('courseware_chapter',
|
||||
kwargs={'course_id': self.course.id,
|
||||
'chapter': 'factory_chapter'}))
|
||||
self.assertRedirects(resp, reverse('courseware_chapter',
|
||||
kwargs={'course_id': self.course.id,
|
||||
'chapter': 'factory_chapter'}))
|
||||
|
||||
@@ -1,80 +1,56 @@
|
||||
import logging
|
||||
import datetime
|
||||
import pytz
|
||||
import random
|
||||
|
||||
from uuid import uuid4
|
||||
import xmodule.modulestore.django
|
||||
|
||||
from django.contrib.auth.models import User, Group
|
||||
from django.conf import settings
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.test.utils import override_settings
|
||||
|
||||
import xmodule.modulestore.django
|
||||
|
||||
# Need access to internal func to put users in the right group
|
||||
from courseware.access import (has_access, _course_staff_group_name,
|
||||
course_beta_test_group_name)
|
||||
|
||||
from mongo_login_helpers import MongoLoginHelpers
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
|
||||
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
|
||||
|
||||
log = logging.getLogger("mitx." + __name__)
|
||||
|
||||
|
||||
def get_user(email):
|
||||
'''look up a user by email'''
|
||||
return User.objects.get(email=email)
|
||||
|
||||
|
||||
def update_course(course, data):
|
||||
"""
|
||||
Updates the version of course in the mongo modulestore
|
||||
with the metadata in data and returns the updated version.
|
||||
"""
|
||||
|
||||
store = xmodule.modulestore.django.modulestore()
|
||||
|
||||
store.update_item(course.location, data)
|
||||
|
||||
store.update_metadata(course.location, data)
|
||||
|
||||
updated_course = store.get_instance(course.id, course.location)
|
||||
|
||||
return updated_course
|
||||
|
||||
|
||||
def mongo_store_config(data_dir):
|
||||
'''
|
||||
Defines default module store using MongoModuleStore
|
||||
|
||||
Use of this config requires mongo to be running
|
||||
'''
|
||||
store = {
|
||||
'default': {
|
||||
'ENGINE': 'xmodule.modulestore.mongo.MongoModuleStore',
|
||||
'OPTIONS': {
|
||||
'default_class': 'xmodule.raw_module.RawDescriptor',
|
||||
'host': 'localhost',
|
||||
'db': 'test_xmodule',
|
||||
'collection': 'modulestore_%s' % uuid4().hex,
|
||||
'fs_root': data_dir,
|
||||
'render_template': 'mitxmako.shortcuts.render_to_string',
|
||||
}
|
||||
}
|
||||
}
|
||||
store['direct'] = store['default']
|
||||
return store
|
||||
|
||||
|
||||
TEST_DATA_DIR = settings.COMMON_TEST_DATA_ROOT
|
||||
TEST_DATA_MONGO_MODULESTORE = mongo_store_config(TEST_DATA_DIR)
|
||||
from helpers import LoginEnrollmentTestCase, check_for_get_code
|
||||
from modulestore_config import TEST_DATA_MONGO_MODULESTORE
|
||||
|
||||
|
||||
@override_settings(MODULESTORE=TEST_DATA_MONGO_MODULESTORE)
|
||||
class TestViewAuth(MongoLoginHelpers):
|
||||
"""Check that view authentication works properly"""
|
||||
class TestViewAuth(ModuleStoreTestCase, LoginEnrollmentTestCase):
|
||||
"""
|
||||
Check that view authentication works properly.
|
||||
"""
|
||||
|
||||
ACCOUNT_INFO = [('view@test.com', 'foo'), ('view2@test.com', 'foo')]
|
||||
|
||||
@classmethod
|
||||
def _instructor_urls(self, course):
|
||||
"""
|
||||
List of urls that only instructors/staff should be able to see.
|
||||
"""
|
||||
urls = [reverse(name, kwargs={'course_id': course.id}) for name in (
|
||||
'instructor_dashboard',
|
||||
'gradebook',
|
||||
'grade_summary',)]
|
||||
|
||||
urls.append(reverse('student_progress',
|
||||
kwargs={'course_id': course.id,
|
||||
'student_id': User.objects.get(email=self.ACCOUNT_INFO[0][0]).id}))
|
||||
return urls
|
||||
|
||||
@staticmethod
|
||||
def _reverse_urls(names, course):
|
||||
"""
|
||||
Reverse a list of course urls.
|
||||
"""
|
||||
return [reverse(name, kwargs={'course_id': course.id})
|
||||
for name in names]
|
||||
|
||||
def setUp(self):
|
||||
xmodule.modulestore.django._MODULESTORES = {}
|
||||
@@ -87,98 +63,105 @@ class TestViewAuth(MongoLoginHelpers):
|
||||
display_name='courseware')
|
||||
self.sub_overview_chapter = ItemFactory.create(parent_location=self.sub_courseware_chapter.location,
|
||||
display_name='Overview')
|
||||
self.progress_chapter = ItemFactory.create(parent_location=self.course.location,
|
||||
display_name='progress')
|
||||
self.info_chapter = ItemFactory.create(parent_location=self.course.location,
|
||||
display_name='info')
|
||||
self.welcome_section = ItemFactory.create(parent_location=self.overview_chapter.location,
|
||||
display_name='Welcome')
|
||||
self.somewhere_in_progress = ItemFactory.create(parent_location=self.progress_chapter.location,
|
||||
display_name='1')
|
||||
|
||||
# Create two accounts
|
||||
self.student = 'view@test.com'
|
||||
self.instructor = 'view2@test.com'
|
||||
self.password = 'foo'
|
||||
self.create_account('u1', self.student, self.password)
|
||||
self.create_account('u2', self.instructor, self.password)
|
||||
self.activate_user(self.student)
|
||||
self.activate_user(self.instructor)
|
||||
# Create two accounts and activate them.
|
||||
for i in range(len(self.ACCOUNT_INFO)):
|
||||
self.create_account('u{0}'.format(i), self.ACCOUNT_INFO[i][0], self.ACCOUNT_INFO[i][1])
|
||||
self.activate_user(self.ACCOUNT_INFO[i][0])
|
||||
|
||||
def test_instructor_pages(self):
|
||||
"""Make sure only instructors for the course
|
||||
or staff can load the instructor
|
||||
dashboard, the grade views, and student profile pages"""
|
||||
def test_redirection_unenrolled(self):
|
||||
"""
|
||||
Verify unenrolled student is redirected to the 'about' section of the chapter
|
||||
instead of the 'Welcome' section after clicking on the courseware tab.
|
||||
"""
|
||||
|
||||
self.login(self.ACCOUNT_INFO[0][0], self.ACCOUNT_INFO[0][1])
|
||||
response = self.client.get(reverse('courseware',
|
||||
kwargs={'course_id': self.course.id}))
|
||||
self.assertRedirects(response,
|
||||
reverse('about_course',
|
||||
args=[self.course.id]))
|
||||
|
||||
def test_redirection_enrolled(self):
|
||||
"""
|
||||
Verify enrolled student is redirected to the 'Welcome' section of
|
||||
the chapter after clicking on the courseware tab.
|
||||
"""
|
||||
|
||||
self.login(self.ACCOUNT_INFO[0][0], self.ACCOUNT_INFO[0][1])
|
||||
self.enroll(self.course)
|
||||
|
||||
# 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.course.id}))
|
||||
|
||||
self.assertRedirectsNoFollow(response,
|
||||
reverse('about_course',
|
||||
args=[self.course.id]))
|
||||
self.assertRedirects(response,
|
||||
reverse('courseware_section',
|
||||
kwargs={'course_id': self.course.id,
|
||||
'chapter': 'Overview',
|
||||
'section': 'Welcome'}))
|
||||
|
||||
def test_instructor_page_access_nonstaff(self):
|
||||
"""
|
||||
Verify non-staff cannot load the instructor
|
||||
dashboard, the grade views, and student profile pages.
|
||||
"""
|
||||
|
||||
self.login(self.ACCOUNT_INFO[0][0], self.ACCOUNT_INFO[0][1])
|
||||
|
||||
self.enroll(self.course)
|
||||
self.enroll(self.full)
|
||||
# should work now -- redirect to first page
|
||||
response = self.client.get(reverse('courseware',
|
||||
kwargs={'course_id': self.course.id}))
|
||||
|
||||
self.assertRedirectsNoFollow(response,
|
||||
reverse('courseware_section',
|
||||
kwargs={'course_id': self.course.id,
|
||||
'chapter': 'Overview',
|
||||
'section': 'Welcome'}))
|
||||
|
||||
def instructor_urls(course):
|
||||
"list of urls that only instructors/staff should be able to see"
|
||||
urls = [reverse(name, kwargs={'course_id': course.id}) for name in (
|
||||
'instructor_dashboard',
|
||||
'gradebook',
|
||||
'grade_summary',)]
|
||||
|
||||
urls.append(reverse('student_progress',
|
||||
kwargs={'course_id': course.id,
|
||||
'student_id': get_user(self.student).id}))
|
||||
return urls
|
||||
|
||||
# Randomly sample an instructor page
|
||||
url = random.choice(instructor_urls(self.course) +
|
||||
instructor_urls(self.full))
|
||||
url = random.choice(self._instructor_urls(self.course) +
|
||||
self._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)
|
||||
check_for_get_code(self, 404, url)
|
||||
|
||||
# Make the instructor staff in the toy course
|
||||
def test_instructor_course_access(self):
|
||||
"""
|
||||
Verify instructor can load the instructor dashboard, the grade views,
|
||||
and student profile pages for their course.
|
||||
"""
|
||||
|
||||
# Make the instructor staff in self.course
|
||||
group_name = _course_staff_group_name(self.course.location)
|
||||
group = Group.objects.create(name=group_name)
|
||||
group.user_set.add(get_user(self.instructor))
|
||||
group.user_set.add(User.objects.get(email=self.ACCOUNT_INFO[1][0]))
|
||||
|
||||
self.logout()
|
||||
self.login(self.instructor, self.password)
|
||||
self.login(self.ACCOUNT_INFO[1][0], self.ACCOUNT_INFO[1][1])
|
||||
|
||||
# Now should be able to get to the toy course, but not the full course
|
||||
url = random.choice(instructor_urls(self.course))
|
||||
# Now should be able to get to self.course, but not self.full
|
||||
url = random.choice(self._instructor_urls(self.course))
|
||||
print 'checking for 200 on {0}'.format(url)
|
||||
self.check_for_get_code(200, url)
|
||||
check_for_get_code(self, 200, url)
|
||||
|
||||
url = random.choice(instructor_urls(self.full))
|
||||
url = random.choice(self._instructor_urls(self.full))
|
||||
print 'checking for 404 on {0}'.format(url)
|
||||
self.check_for_get_code(404, url)
|
||||
check_for_get_code(self, 404, url)
|
||||
|
||||
# now also make the instructor staff
|
||||
instructor = get_user(self.instructor)
|
||||
def test_instructor_as_staff_access(self):
|
||||
"""
|
||||
Verify the instructor can load staff pages if he is given
|
||||
staff permissions.
|
||||
"""
|
||||
|
||||
self.login(self.ACCOUNT_INFO[1][0], self.ACCOUNT_INFO[1][1])
|
||||
|
||||
# now make the instructor also staff
|
||||
instructor = User.objects.get(email=self.ACCOUNT_INFO[1][0])
|
||||
instructor.is_staff = True
|
||||
instructor.save()
|
||||
|
||||
# and now should be able to load both
|
||||
url = random.choice(instructor_urls(self.course) +
|
||||
instructor_urls(self.full))
|
||||
url = random.choice(self._instructor_urls(self.course) +
|
||||
self._instructor_urls(self.full))
|
||||
|
||||
print 'checking for 200 on {0}'.format(url)
|
||||
self.check_for_get_code(200, url)
|
||||
check_for_get_code(self, 200, url)
|
||||
|
||||
def run_wrapped(self, test):
|
||||
"""
|
||||
@@ -196,42 +179,47 @@ class TestViewAuth(MongoLoginHelpers):
|
||||
settings.MITX_FEATURES['DISABLE_START_DATES'] = oldDSD
|
||||
|
||||
def test_dark_launch(self):
|
||||
"""Make sure that before course start, students can't access course
|
||||
pages, but instructors can"""
|
||||
"""
|
||||
Make sure that before course start, students can't access course
|
||||
pages, but instructors can.
|
||||
"""
|
||||
self.run_wrapped(self._do_test_dark_launch)
|
||||
|
||||
def test_enrollment_period(self):
|
||||
"""Check that enrollment periods work"""
|
||||
"""
|
||||
Check that enrollment periods work.
|
||||
"""
|
||||
self.run_wrapped(self._do_test_enrollment_period)
|
||||
|
||||
def test_beta_period(self):
|
||||
"""Check that beta-test access works"""
|
||||
"""
|
||||
Check that beta-test access works.
|
||||
"""
|
||||
self.run_wrapped(self._do_test_beta_period)
|
||||
|
||||
def _do_test_dark_launch(self):
|
||||
"""Actually do the test, relying on settings to be right."""
|
||||
"""
|
||||
Actually do the test, relying on settings to be right.
|
||||
"""
|
||||
|
||||
# Make courses start in the future
|
||||
now = datetime.datetime.now(pytz.UTC)
|
||||
tomorrow = now + datetime.timedelta(days=1)
|
||||
self.course.lms.start = tomorrow
|
||||
self.full.lms.start = tomorrow
|
||||
course_data = {'start': tomorrow}
|
||||
full_data = {'start': tomorrow}
|
||||
self.course = self.update_course(self.course, course_data)
|
||||
self.full = self.update_course(self.full, full_data)
|
||||
|
||||
self.assertFalse(self.course.has_started())
|
||||
self.assertFalse(self.full.has_started())
|
||||
self.assertFalse(settings.MITX_FEATURES['DISABLE_START_DATES'])
|
||||
|
||||
def reverse_urls(names, course):
|
||||
"""Reverse a list of course urls"""
|
||||
return [reverse(name, kwargs={'course_id': course.id})
|
||||
for name in names]
|
||||
|
||||
def dark_student_urls(course):
|
||||
"""
|
||||
list of urls that students should be able to see only
|
||||
List of urls that students should be able to see only
|
||||
after launch, but staff should see before
|
||||
"""
|
||||
urls = reverse_urls(['info', 'progress'], course)
|
||||
urls = self._reverse_urls(['info', 'progress'], course)
|
||||
urls.extend([
|
||||
reverse('book', kwargs={'course_id': course.id,
|
||||
'book_index': index})
|
||||
@@ -241,38 +229,50 @@ class TestViewAuth(MongoLoginHelpers):
|
||||
|
||||
def light_student_urls(course):
|
||||
"""
|
||||
list of urls that students should be able to see before
|
||||
List of urls that students should be able to see before
|
||||
launch.
|
||||
"""
|
||||
urls = reverse_urls(['about_course'], course)
|
||||
urls = self._reverse_urls(['about_course'], course)
|
||||
urls.append(reverse('courses'))
|
||||
|
||||
return urls
|
||||
|
||||
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)
|
||||
"""
|
||||
List of urls that only instructors/staff should be able to see.
|
||||
"""
|
||||
urls = self._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"""
|
||||
def check_non_staff_light(course):
|
||||
"""
|
||||
Check that non-staff have access to light urls.
|
||||
"""
|
||||
print '=== Checking non-staff access for {0}'.format(course.id)
|
||||
|
||||
# Randomly sample a light url
|
||||
url = random.choice(light_student_urls(course))
|
||||
print 'checking for 200 on {0}'.format(url)
|
||||
check_for_get_code(self, 200, url)
|
||||
|
||||
def check_non_staff_dark(course):
|
||||
"""
|
||||
Check that non-staff don't have access to dark urls.
|
||||
"""
|
||||
print '=== Checking non-staff access for {0}'.format(course.id)
|
||||
|
||||
# Randomly sample a dark url
|
||||
url = random.choice(instructor_urls(course) +
|
||||
dark_student_urls(course) +
|
||||
reverse_urls(['courseware'], course))
|
||||
self._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)
|
||||
check_for_get_code(self, 404, url)
|
||||
|
||||
def check_staff(course):
|
||||
"""Check that access is right for staff in course"""
|
||||
"""
|
||||
Check that access is right for staff in course.
|
||||
"""
|
||||
print '=== Checking staff access for {0}'.format(course.id)
|
||||
|
||||
# Randomly sample a url
|
||||
@@ -280,7 +280,7 @@ class TestViewAuth(MongoLoginHelpers):
|
||||
dark_student_urls(course) +
|
||||
light_student_urls(course))
|
||||
print 'checking for 200 on {0}'.format(url)
|
||||
self.check_for_get_code(200, url)
|
||||
check_for_get_code(self, 200, url)
|
||||
|
||||
# The student progress tab is not accessible to a student
|
||||
# before launch, so the instructor view-as-student feature
|
||||
@@ -290,43 +290,46 @@ class TestViewAuth(MongoLoginHelpers):
|
||||
# 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})
|
||||
'student_id': User.objects.get(email=self.ACCOUNT_INFO[0][0]).id})
|
||||
print 'checking for 404 on view-as-student: {0}'.format(url)
|
||||
self.check_for_get_code(404, url)
|
||||
check_for_get_code(self, 404, url)
|
||||
|
||||
# The courseware url should redirect, not 200
|
||||
url = reverse_urls(['courseware'], course)[0]
|
||||
self.check_for_get_code(302, url)
|
||||
url = self._reverse_urls(['courseware'], course)[0]
|
||||
check_for_get_code(self, 302, url)
|
||||
|
||||
# First, try with an enrolled student
|
||||
print '=== Testing student access....'
|
||||
self.login(self.student, self.password)
|
||||
self.enroll(self.course)
|
||||
self.enroll(self.full)
|
||||
self.login(self.ACCOUNT_INFO[0][0], self.ACCOUNT_INFO[0][1])
|
||||
self.enroll(self.course, True)
|
||||
self.enroll(self.full, True)
|
||||
|
||||
# shouldn't be able to get to anything except the light pages
|
||||
check_non_staff(self.course)
|
||||
check_non_staff(self.full)
|
||||
check_non_staff_light(self.course)
|
||||
check_non_staff_dark(self.course)
|
||||
check_non_staff_light(self.full)
|
||||
check_non_staff_dark(self.full)
|
||||
|
||||
print '=== Testing course instructor access....'
|
||||
# Make the instructor staff in the toy course
|
||||
# Make the instructor staff in self.course
|
||||
group_name = _course_staff_group_name(self.course.location)
|
||||
group = Group.objects.create(name=group_name)
|
||||
group.user_set.add(get_user(self.instructor))
|
||||
group.user_set.add(User.objects.get(email=self.ACCOUNT_INFO[1][0]))
|
||||
|
||||
self.logout()
|
||||
self.login(self.instructor, self.password)
|
||||
self.login(self.ACCOUNT_INFO[1][0], self.ACCOUNT_INFO[1][1])
|
||||
# Enroll in the classes---can't see courseware otherwise.
|
||||
self.enroll(self.course)
|
||||
self.enroll(self.full)
|
||||
self.enroll(self.course, True)
|
||||
self.enroll(self.full, True)
|
||||
|
||||
# should now be able to get to everything for self.course
|
||||
check_non_staff(self.full)
|
||||
check_non_staff_light(self.full)
|
||||
check_non_staff_dark(self.full)
|
||||
check_staff(self.course)
|
||||
|
||||
print '=== Testing staff access....'
|
||||
# now also make the instructor staff
|
||||
instructor = get_user(self.instructor)
|
||||
instructor = User.objects.get(email=self.ACCOUNT_INFO[1][0])
|
||||
instructor.is_staff = True
|
||||
instructor.save()
|
||||
|
||||
@@ -335,7 +338,9 @@ class TestViewAuth(MongoLoginHelpers):
|
||||
check_staff(self.full)
|
||||
|
||||
def _do_test_enrollment_period(self):
|
||||
"""Actually do the test, relying on settings to be right."""
|
||||
"""
|
||||
Actually do the test, relying on settings to be right.
|
||||
"""
|
||||
|
||||
# Make courses start in the future
|
||||
now = datetime.datetime.now(pytz.UTC)
|
||||
@@ -348,42 +353,44 @@ class TestViewAuth(MongoLoginHelpers):
|
||||
|
||||
print "changing"
|
||||
# self.course's enrollment period hasn't started
|
||||
self.course = update_course(self.course, course_data)
|
||||
self.course = self.update_course(self.course, course_data)
|
||||
# full course's has
|
||||
self.full = update_course(self.full, full_data)
|
||||
self.full = self.update_course(self.full, full_data)
|
||||
|
||||
print "login"
|
||||
# First, try with an enrolled student
|
||||
print '=== Testing student access....'
|
||||
self.login(self.student, self.password)
|
||||
self.assertFalse(self.try_enroll(self.course))
|
||||
self.assertTrue(self.try_enroll(self.full))
|
||||
self.login(self.ACCOUNT_INFO[0][0], self.ACCOUNT_INFO[0][1])
|
||||
self.assertFalse(self.enroll(self.course))
|
||||
self.assertTrue(self.enroll(self.full))
|
||||
|
||||
print '=== Testing course instructor access....'
|
||||
# Make the instructor staff in the toy course
|
||||
# Make the instructor staff in the self.course
|
||||
group_name = _course_staff_group_name(self.course.location)
|
||||
group = Group.objects.create(name=group_name)
|
||||
group.user_set.add(get_user(self.instructor))
|
||||
group.user_set.add(User.objects.get(email=self.ACCOUNT_INFO[1][0]))
|
||||
|
||||
print "logout/login"
|
||||
self.logout()
|
||||
self.login(self.instructor, self.password)
|
||||
print "Instructor should be able to enroll in toy course"
|
||||
self.assertTrue(self.try_enroll(self.course))
|
||||
self.login(self.ACCOUNT_INFO[1][0], self.ACCOUNT_INFO[1][1])
|
||||
print "Instructor should be able to enroll in self.course"
|
||||
self.assertTrue(self.enroll(self.course))
|
||||
|
||||
print '=== Testing staff access....'
|
||||
# now make the instructor global staff, but not in the instructor group
|
||||
group.user_set.remove(get_user(self.instructor))
|
||||
instructor = get_user(self.instructor)
|
||||
group.user_set.remove(User.objects.get(email=self.ACCOUNT_INFO[1][0]))
|
||||
instructor = User.objects.get(email=self.ACCOUNT_INFO[1][0])
|
||||
instructor.is_staff = True
|
||||
instructor.save()
|
||||
|
||||
# unenroll and try again
|
||||
self.unenroll(self.course)
|
||||
self.assertTrue(self.try_enroll(self.course))
|
||||
self.assertTrue(self.enroll(self.course))
|
||||
|
||||
def _do_test_beta_period(self):
|
||||
"""Actually test beta periods, relying on settings to be right."""
|
||||
"""
|
||||
Actually test beta periods, relying on settings to be right.
|
||||
"""
|
||||
|
||||
# trust, but verify :)
|
||||
self.assertFalse(settings.MITX_FEATURES['DISABLE_START_DATES'])
|
||||
@@ -391,18 +398,17 @@ class TestViewAuth(MongoLoginHelpers):
|
||||
# Make courses start in the future
|
||||
now = datetime.datetime.now(pytz.UTC)
|
||||
tomorrow = now + datetime.timedelta(days=1)
|
||||
# nextday = tomorrow + 24 * 3600
|
||||
# yesterday = time.time() - 24 * 3600
|
||||
course_data = {'start': tomorrow}
|
||||
|
||||
# self.course's hasn't started
|
||||
self.course.lms.start = tomorrow
|
||||
self.course = self.update_course(self.course, course_data)
|
||||
self.assertFalse(self.course.has_started())
|
||||
|
||||
# but should be accessible for beta testers
|
||||
self.course.lms.days_early_for_beta = 2
|
||||
|
||||
# student user shouldn't see it
|
||||
student_user = get_user(self.student)
|
||||
student_user = User.objects.get(email=self.ACCOUNT_INFO[0][0])
|
||||
self.assertFalse(has_access(student_user, self.course, 'load'))
|
||||
|
||||
# now add the student to the beta test group
|
||||
|
||||
@@ -1,275 +1,172 @@
|
||||
'''
|
||||
Test for lms courseware app
|
||||
'''
|
||||
import logging
|
||||
import json
|
||||
import random
|
||||
|
||||
from urlparse import urlsplit, urlunsplit
|
||||
from uuid import uuid4
|
||||
|
||||
from django.contrib.auth.models import User, Group
|
||||
from django.test import TestCase
|
||||
from django.test.client import RequestFactory
|
||||
from django.conf import settings
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.test.utils import override_settings
|
||||
|
||||
import xmodule.modulestore.django
|
||||
|
||||
# Need access to internal func to put users in the right group
|
||||
from courseware import grades
|
||||
from courseware.model_data import ModelDataCache
|
||||
from courseware.access import (has_access, _course_staff_group_name,
|
||||
course_beta_test_group_name)
|
||||
|
||||
from student.models import Registration
|
||||
from xmodule.error_module import ErrorDescriptor
|
||||
from xmodule.modulestore.django import modulestore
|
||||
from xmodule.modulestore import Location
|
||||
from xmodule.modulestore.xml_importer import import_from_xml
|
||||
from xmodule.modulestore.xml import XMLModuleStore
|
||||
import datetime
|
||||
from django.utils.timezone import UTC
|
||||
|
||||
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from helpers import LoginEnrollmentTestCase
|
||||
from modulestore_config import TEST_DATA_DIR, TEST_DATA_XML_MODULESTORE, TEST_DATA_MONGO_MODULESTORE, TEST_DATA_DRAFT_MONGO_MODULESTORE
|
||||
|
||||
|
||||
log = logging.getLogger("mitx." + __name__)
|
||||
class ActivateLoginTest(LoginEnrollmentTestCase):
|
||||
"""
|
||||
Test logging in and logging out.
|
||||
"""
|
||||
def setUp(self):
|
||||
self.setup_user()
|
||||
|
||||
|
||||
def parse_json(response):
|
||||
"""Parse response, which is assumed to be json"""
|
||||
return json.loads(response.content)
|
||||
|
||||
|
||||
def get_user(email):
|
||||
'''look up a user by email'''
|
||||
return User.objects.get(email=email)
|
||||
|
||||
|
||||
def get_registration(email):
|
||||
'''look up registration object by email'''
|
||||
return Registration.objects.get(user__email=email)
|
||||
|
||||
|
||||
def mongo_store_config(data_dir):
|
||||
'''
|
||||
Defines default module store using MongoModuleStore
|
||||
|
||||
Use of this config requires mongo to be running
|
||||
'''
|
||||
store = {
|
||||
'default': {
|
||||
'ENGINE': 'xmodule.modulestore.mongo.MongoModuleStore',
|
||||
'OPTIONS': {
|
||||
'default_class': 'xmodule.raw_module.RawDescriptor',
|
||||
'host': 'localhost',
|
||||
'db': 'test_xmodule',
|
||||
'collection': 'modulestore_%s' % uuid4().hex,
|
||||
'fs_root': data_dir,
|
||||
'render_template': 'mitxmako.shortcuts.render_to_string'
|
||||
}
|
||||
}
|
||||
}
|
||||
store['direct'] = store['default']
|
||||
return store
|
||||
|
||||
|
||||
def draft_mongo_store_config(data_dir):
|
||||
'''Defines default module store using DraftMongoModuleStore'''
|
||||
return {
|
||||
'default': {
|
||||
'ENGINE': 'xmodule.modulestore.mongo.DraftMongoModuleStore',
|
||||
'OPTIONS': {
|
||||
'default_class': 'xmodule.raw_module.RawDescriptor',
|
||||
'host': 'localhost',
|
||||
'db': 'test_xmodule',
|
||||
'collection': 'modulestore_%s' % uuid4().hex,
|
||||
'fs_root': data_dir,
|
||||
'render_template': 'mitxmako.shortcuts.render_to_string',
|
||||
}
|
||||
},
|
||||
'direct': {
|
||||
'ENGINE': 'xmodule.modulestore.mongo.MongoModuleStore',
|
||||
'OPTIONS': {
|
||||
'default_class': 'xmodule.raw_module.RawDescriptor',
|
||||
'host': 'localhost',
|
||||
'db': 'test_xmodule',
|
||||
'collection': 'modulestore_%s' % uuid4().hex,
|
||||
'fs_root': data_dir,
|
||||
'render_template': 'mitxmako.shortcuts.render_to_string',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def xml_store_config(data_dir):
|
||||
'''Defines default module store using XMLModuleStore'''
|
||||
return {
|
||||
'default': {
|
||||
'ENGINE': 'xmodule.modulestore.xml.XMLModuleStore',
|
||||
'OPTIONS': {
|
||||
'data_dir': data_dir,
|
||||
'default_class': 'xmodule.hidden_module.HiddenDescriptor',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_DATA_DIR = settings.COMMON_TEST_DATA_ROOT
|
||||
TEST_DATA_XML_MODULESTORE = xml_store_config(TEST_DATA_DIR)
|
||||
TEST_DATA_MONGO_MODULESTORE = mongo_store_config(TEST_DATA_DIR)
|
||||
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
|
||||
'''
|
||||
|
||||
def assertRedirectsNoFollow(self, response, expected_url):
|
||||
def test_activate_login(self):
|
||||
"""
|
||||
http://devblog.point2.com/2010/04/23/djangos-assertredirects-little-gotcha/
|
||||
|
||||
Don't check that the redirected-to page loads--there should be other tests for that.
|
||||
|
||||
Some of the code taken from django.test.testcases.py
|
||||
Test login -- the setup function does all the work.
|
||||
"""
|
||||
self.assertEqual(response.status_code, 302,
|
||||
'Response status code was %d instead of 302'
|
||||
% (response.status_code))
|
||||
url = response['Location']
|
||||
pass
|
||||
|
||||
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))
|
||||
|
||||
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.activate_user(self.viewtest_email)
|
||||
self.login(self.viewtest_email, self.viewtest_password)
|
||||
|
||||
# ============ User creation and login ==============
|
||||
|
||||
def _login(self, email, password):
|
||||
'''Login. View should always return 200. The success/fail is in the
|
||||
returned json'''
|
||||
resp = self.client.post(reverse('login'),
|
||||
{'email': email, 'password': password})
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
return resp
|
||||
|
||||
def login(self, email, password):
|
||||
'''Login, check that it worked.'''
|
||||
resp = self._login(email, password)
|
||||
data = parse_json(resp)
|
||||
self.assertTrue(data['success'])
|
||||
return resp
|
||||
|
||||
def logout(self):
|
||||
'''Logout, check that it worked.'''
|
||||
resp = self.client.get(reverse('logout'), {})
|
||||
# should redirect
|
||||
self.assertEqual(resp.status_code, 302)
|
||||
return resp
|
||||
|
||||
def _create_account(self, username, email, password):
|
||||
'''Try to create an account. No error checking'''
|
||||
resp = self.client.post('/create_account', {
|
||||
'username': username,
|
||||
'email': email,
|
||||
'password': password,
|
||||
'name': 'Fred Weasley',
|
||||
'terms_of_service': 'true',
|
||||
'honor_code': 'true',
|
||||
})
|
||||
return resp
|
||||
|
||||
def create_account(self, username, email, password):
|
||||
'''Create the account and check that it worked'''
|
||||
resp = self._create_account(username, email, password)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
data = parse_json(resp)
|
||||
self.assertEqual(data['success'], True)
|
||||
|
||||
# Check both that the user is created, and inactive
|
||||
self.assertFalse(get_user(email).is_active)
|
||||
|
||||
return resp
|
||||
|
||||
def _activate_user(self, email):
|
||||
'''Look up the activation key for the user, then hit the activate view.
|
||||
No error checking'''
|
||||
activation_key = get_registration(email).activation_key
|
||||
|
||||
# and now we try to activate
|
||||
url = reverse('activate', kwargs={'key': activation_key})
|
||||
resp = self.client.get(url)
|
||||
return resp
|
||||
|
||||
def activate_user(self, email):
|
||||
resp = self._activate_user(email)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
# Now make sure that the user is now actually activated
|
||||
self.assertTrue(get_user(email).is_active)
|
||||
|
||||
def try_enroll(self, course):
|
||||
"""Try to enroll. Return bool success instead of asserting it."""
|
||||
resp = self.client.post('/change_enrollment', {
|
||||
'enrollment_action': 'enroll',
|
||||
'course_id': course.id,
|
||||
})
|
||||
print ('Enrollment in %s result status code: %s'
|
||||
% (course.location.url(), str(resp.status_code)))
|
||||
return resp.status_code == 200
|
||||
|
||||
def enroll(self, course):
|
||||
"""Enroll the currently logged-in user, and check that it worked."""
|
||||
result = self.try_enroll(course)
|
||||
self.assertTrue(result)
|
||||
|
||||
def unenroll(self, course):
|
||||
"""Unenroll the currently logged-in user, and check that it worked."""
|
||||
resp = self.client.post('/change_enrollment', {
|
||||
'enrollment_action': 'unenroll',
|
||||
'course_id': course.id,
|
||||
})
|
||||
self.assertTrue(resp.status_code == 200)
|
||||
|
||||
def check_for_get_code(self, code, url):
|
||||
def test_logout(self):
|
||||
"""
|
||||
Check that we got the expected code when accessing url via GET.
|
||||
Returns the response.
|
||||
Test logout -- setup function does login.
|
||||
"""
|
||||
resp = self.client.get(url)
|
||||
self.assertEqual(resp.status_code, code,
|
||||
"got code %d for url '%s'. Expected code %d"
|
||||
% (resp.status_code, url, code))
|
||||
return resp
|
||||
self.logout()
|
||||
|
||||
def check_for_post_code(self, code, url, data={}):
|
||||
|
||||
class PageLoaderTestCase(LoginEnrollmentTestCase):
|
||||
"""
|
||||
Base class that adds a function to load all pages in a modulestore.
|
||||
"""
|
||||
|
||||
def check_random_page_loads(self, module_store):
|
||||
"""
|
||||
Check that we got the expected code when accessing url via POST.
|
||||
Returns the response.
|
||||
Choose a page in the course randomly, and assert that it loads.
|
||||
"""
|
||||
resp = self.client.post(url, data)
|
||||
self.assertEqual(resp.status_code, code,
|
||||
"got code %d for url '%s'. Expected code %d"
|
||||
% (resp.status_code, url, code))
|
||||
return resp
|
||||
# enroll in the course before trying to access pages
|
||||
courses = module_store.get_courses()
|
||||
self.assertEqual(len(courses), 1)
|
||||
course = courses[0]
|
||||
self.enroll(course, True)
|
||||
course_id = course.id
|
||||
|
||||
# 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)
|
||||
class TestCoursesLoadTestCase_XmlModulestore(PageLoaderTestCase):
|
||||
"""
|
||||
Check that all pages in test courses load properly from XML.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(TestCoursesLoadTestCase_XmlModulestore, self).setUp()
|
||||
self.setup_user()
|
||||
xmodule.modulestore.django._MODULESTORES = {}
|
||||
|
||||
def test_toy_course_loads(self):
|
||||
module_class = 'xmodule.hidden_module.HiddenDescriptor'
|
||||
module_store = XMLModuleStore(TEST_DATA_DIR,
|
||||
default_class=module_class,
|
||||
course_dirs=['toy'],
|
||||
load_error_modules=True)
|
||||
|
||||
self.check_random_page_loads(module_store)
|
||||
|
||||
|
||||
@override_settings(MODULESTORE=TEST_DATA_MONGO_MODULESTORE)
|
||||
class TestCoursesLoadTestCase_MongoModulestore(PageLoaderTestCase):
|
||||
"""
|
||||
Check that all pages in test courses load properly from Mongo.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(TestCoursesLoadTestCase_MongoModulestore, self).setUp()
|
||||
self.setup_user()
|
||||
xmodule.modulestore.django._MODULESTORES = {}
|
||||
modulestore().collection.drop()
|
||||
|
||||
def test_toy_course_loads(self):
|
||||
module_store = modulestore()
|
||||
import_from_xml(module_store, TEST_DATA_DIR, ['toy'])
|
||||
self.check_random_page_loads(module_store)
|
||||
|
||||
def test_full_textbooks_loads(self):
|
||||
module_store = modulestore()
|
||||
import_from_xml(module_store, TEST_DATA_DIR, ['full'])
|
||||
|
||||
course = module_store.get_item(Location(['i4x', 'edX', 'full', 'course', '6.002_Spring_2012', None]))
|
||||
|
||||
self.assertGreater(len(course.textbooks), 0)
|
||||
|
||||
|
||||
@override_settings(MODULESTORE=TEST_DATA_DRAFT_MONGO_MODULESTORE)
|
||||
@@ -284,134 +181,3 @@ class TestDraftModuleStore(TestCase):
|
||||
# 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)
|
||||
|
||||
|
||||
@override_settings(MODULESTORE=TEST_DATA_XML_MODULESTORE)
|
||||
class TestSubmittingProblems(LoginEnrollmentTestCase):
|
||||
"""Check that a course gets graded properly"""
|
||||
|
||||
# Subclasses should specify the course slug
|
||||
course_slug = "UNKNOWN"
|
||||
course_when = "UNKNOWN"
|
||||
|
||||
def setUp(self):
|
||||
xmodule.modulestore.django._MODULESTORES = {}
|
||||
|
||||
course_name = "edX/%s/%s" % (self.course_slug, self.course_when)
|
||||
self.course = modulestore().get_course(course_name)
|
||||
assert self.course, "Couldn't load course %r" % course_name
|
||||
|
||||
# create a test student
|
||||
self.student = 'view@test.com'
|
||||
self.password = 'foo'
|
||||
self.create_account('u1', self.student, self.password)
|
||||
self.activate_user(self.student)
|
||||
self.enroll(self.course)
|
||||
|
||||
self.student_user = get_user(self.student)
|
||||
|
||||
self.factory = RequestFactory()
|
||||
|
||||
def problem_location(self, problem_url_name):
|
||||
return "i4x://edX/{}/problem/{}".format(self.course_slug, problem_url_name)
|
||||
|
||||
def modx_url(self, problem_location, dispatch):
|
||||
return reverse(
|
||||
'modx_dispatch',
|
||||
kwargs={
|
||||
'course_id': self.course.id,
|
||||
'location': problem_location,
|
||||
'dispatch': dispatch,
|
||||
}
|
||||
)
|
||||
|
||||
def submit_question_answer(self, problem_url_name, responses):
|
||||
"""
|
||||
Submit answers to a question.
|
||||
|
||||
Responses is a dict mapping problem ids (not sure of the right term)
|
||||
to answers:
|
||||
{'2_1': 'Correct', '2_2': 'Incorrect'}
|
||||
|
||||
"""
|
||||
problem_location = self.problem_location(problem_url_name)
|
||||
modx_url = self.modx_url(problem_location, 'problem_check')
|
||||
answer_key_prefix = 'input_i4x-edX-{}-problem-{}_'.format(self.course_slug, problem_url_name)
|
||||
resp = self.client.post(modx_url,
|
||||
{ (answer_key_prefix + k): v for k, v in responses.items() }
|
||||
)
|
||||
|
||||
return resp
|
||||
|
||||
def reset_question_answer(self, problem_url_name):
|
||||
'''resets specified problem for current user'''
|
||||
problem_location = self.problem_location(problem_url_name)
|
||||
modx_url = self.modx_url(problem_location, 'problem_reset')
|
||||
resp = self.client.post(modx_url)
|
||||
return resp
|
||||
|
||||
|
||||
@override_settings(MODULESTORE=TEST_DATA_XML_MODULESTORE)
|
||||
class TestSchematicResponse(TestSubmittingProblems):
|
||||
"""Check that we can submit a schematic response, and it answers properly."""
|
||||
|
||||
course_slug = "embedded_python"
|
||||
course_when = "2013_Spring"
|
||||
|
||||
def test_schematic(self):
|
||||
resp = self.submit_question_answer('schematic_problem',
|
||||
{ '2_1': json.dumps(
|
||||
[['transient', {'Z': [
|
||||
[0.0000004, 2.8],
|
||||
[0.0000009, 2.8],
|
||||
[0.0000014, 2.8],
|
||||
[0.0000019, 2.8],
|
||||
[0.0000024, 2.8],
|
||||
[0.0000029, 0.2],
|
||||
[0.0000034, 0.2],
|
||||
[0.0000039, 0.2]
|
||||
]}]]
|
||||
)
|
||||
})
|
||||
respdata = json.loads(resp.content)
|
||||
self.assertEqual(respdata['success'], 'correct')
|
||||
|
||||
self.reset_question_answer('schematic_problem')
|
||||
resp = self.submit_question_answer('schematic_problem',
|
||||
{ '2_1': json.dumps(
|
||||
[['transient', {'Z': [
|
||||
[0.0000004, 2.8],
|
||||
[0.0000009, 0.0], # wrong.
|
||||
[0.0000014, 2.8],
|
||||
[0.0000019, 2.8],
|
||||
[0.0000024, 2.8],
|
||||
[0.0000029, 0.2],
|
||||
[0.0000034, 0.2],
|
||||
[0.0000039, 0.2]
|
||||
]}]]
|
||||
)
|
||||
})
|
||||
respdata = json.loads(resp.content)
|
||||
self.assertEqual(respdata['success'], 'incorrect')
|
||||
|
||||
def test_check_function(self):
|
||||
resp = self.submit_question_answer('cfn_problem', {'2_1': "0, 1, 2, 3, 4, 5, 'Outside of loop', 6"})
|
||||
respdata = json.loads(resp.content)
|
||||
self.assertEqual(respdata['success'], 'correct')
|
||||
|
||||
self.reset_question_answer('cfn_problem')
|
||||
|
||||
resp = self.submit_question_answer('cfn_problem', {'2_1': "xyzzy!"})
|
||||
respdata = json.loads(resp.content)
|
||||
self.assertEqual(respdata['success'], 'incorrect')
|
||||
|
||||
def test_computed_answer(self):
|
||||
resp = self.submit_question_answer('computed_answer', {'2_1': "Xyzzy"})
|
||||
respdata = json.loads(resp.content)
|
||||
self.assertEqual(respdata['success'], 'correct')
|
||||
|
||||
self.reset_question_answer('computed_answer')
|
||||
|
||||
resp = self.submit_question_answer('computed_answer', {'2_1': "NO!"})
|
||||
respdata = json.loads(resp.content)
|
||||
self.assertEqual(respdata['success'], 'incorrect')
|
||||
|
||||
@@ -11,12 +11,13 @@ django-admin.py test --settings=lms.envs.test --pythonpath=. lms/djangoapps/inst
|
||||
from django.test.utils import override_settings
|
||||
|
||||
# Need access to internal func to put users in the right group
|
||||
from django.contrib.auth.models import Group
|
||||
from django.contrib.auth.models import Group, User
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
from courseware.access import _course_staff_group_name
|
||||
from courseware.tests.tests import LoginEnrollmentTestCase, TEST_DATA_XML_MODULESTORE, get_user
|
||||
from courseware.tests.helpers import LoginEnrollmentTestCase
|
||||
from courseware.tests.modulestore_config import TEST_DATA_XML_MODULESTORE
|
||||
from xmodule.modulestore.django import modulestore
|
||||
import xmodule.modulestore.django
|
||||
|
||||
@@ -45,7 +46,7 @@ class TestInstructorDashboardGradeDownloadCSV(LoginEnrollmentTestCase):
|
||||
def make_instructor(course):
|
||||
group_name = _course_staff_group_name(course.location)
|
||||
g = Group.objects.create(name=group_name)
|
||||
g.user_set.add(get_user(self.instructor))
|
||||
g.user_set.add(User.objects.get(email=self.instructor))
|
||||
|
||||
make_instructor(self.toy)
|
||||
|
||||
@@ -72,7 +73,7 @@ class TestInstructorDashboardGradeDownloadCSV(LoginEnrollmentTestCase):
|
||||
# All the not-actually-in-the-course hw and labs come from the
|
||||
# default grading policy string in graders.py
|
||||
expected_body = '''"ID","Username","Full Name","edX email","External email","HW 01","HW 02","HW 03","HW 04","HW 05","HW 06","HW 07","HW 08","HW 09","HW 10","HW 11","HW 12","HW Avg","Lab 01","Lab 02","Lab 03","Lab 04","Lab 05","Lab 06","Lab 07","Lab 08","Lab 09","Lab 10","Lab 11","Lab 12","Lab Avg","Midterm","Final"
|
||||
"2","u2","Fred Weasley","view2@test.com","","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0"
|
||||
"2","u2","username","view2@test.com","","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0"
|
||||
'''
|
||||
|
||||
self.assertEqual(body, expected_body, msg)
|
||||
|
||||
@@ -7,7 +7,8 @@ from django.test.utils import override_settings
|
||||
from django.contrib.auth.models import Group, User
|
||||
from django.core.urlresolvers import reverse
|
||||
from courseware.access import _course_staff_group_name
|
||||
from courseware.tests.tests import LoginEnrollmentTestCase, TEST_DATA_XML_MODULESTORE, get_user
|
||||
from courseware.tests.helpers import LoginEnrollmentTestCase
|
||||
from courseware.tests.modulestore_config import TEST_DATA_XML_MODULESTORE
|
||||
from xmodule.modulestore.django import modulestore
|
||||
import xmodule.modulestore.django
|
||||
from student.models import CourseEnrollment, CourseEnrollmentAllowed
|
||||
@@ -40,7 +41,7 @@ class TestInstructorEnrollsStudent(LoginEnrollmentTestCase):
|
||||
def make_instructor(course):
|
||||
group_name = _course_staff_group_name(course.location)
|
||||
g = Group.objects.create(name=group_name)
|
||||
g.user_set.add(get_user(self.instructor))
|
||||
g.user_set.add(User.objects.get(email=self.instructor))
|
||||
|
||||
make_instructor(self.toy)
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ Unit tests for instructor dashboard forum administration
|
||||
from django.test.utils import override_settings
|
||||
|
||||
# Need access to internal func to put users in the right group
|
||||
from django.contrib.auth.models import Group
|
||||
from django.contrib.auth.models import Group, User
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from django_comment_common.models import Role, FORUM_ROLE_ADMINISTRATOR, \
|
||||
@@ -14,7 +14,8 @@ from django_comment_common.models import Role, FORUM_ROLE_ADMINISTRATOR, \
|
||||
from django_comment_client.utils import has_forum_access
|
||||
|
||||
from courseware.access import _course_staff_group_name
|
||||
from courseware.tests.tests import LoginEnrollmentTestCase, TEST_DATA_XML_MODULESTORE, get_user
|
||||
from courseware.tests.helpers import LoginEnrollmentTestCase
|
||||
from courseware.tests.modulestore_config import TEST_DATA_XML_MODULESTORE
|
||||
from xmodule.modulestore.django import modulestore
|
||||
import xmodule.modulestore.django
|
||||
|
||||
@@ -55,7 +56,7 @@ class TestInstructorDashboardForumAdmin(LoginEnrollmentTestCase):
|
||||
|
||||
group_name = _course_staff_group_name(self.toy.location)
|
||||
g = Group.objects.create(name=group_name)
|
||||
g.user_set.add(get_user(self.instructor))
|
||||
g.user_set.add(User.objects.get(email=self.instructor))
|
||||
|
||||
self.logout()
|
||||
self.login(self.instructor, self.password)
|
||||
@@ -146,4 +147,4 @@ class TestInstructorDashboardForumAdmin(LoginEnrollmentTestCase):
|
||||
added_roles.append(rolename)
|
||||
added_roles.sort()
|
||||
roles = ', '.join(added_roles)
|
||||
self.assertTrue(response.content.find('<td>{0}</td>'.format(roles)) >= 0, 'not finding roles "{0}"'.format(roles))
|
||||
self.assertTrue(response.content.find('<td>{0}</td>'.format(roles)) >= 0, 'not finding roles "{0}"'.format(roles))
|
||||
|
||||
@@ -8,8 +8,7 @@ import json
|
||||
from mock import MagicMock, patch, Mock
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.contrib.auth.models import Group
|
||||
from django.http import HttpResponse
|
||||
from django.contrib.auth.models import Group, User
|
||||
from django.conf import settings
|
||||
from mitxmako.shortcuts import render_to_string
|
||||
|
||||
@@ -21,7 +20,6 @@ from xmodule.x_module import ModuleSystem
|
||||
|
||||
from open_ended_grading import staff_grading_service, views
|
||||
from courseware.access import _course_staff_group_name
|
||||
from courseware.tests.tests import LoginEnrollmentTestCase, TEST_DATA_XML_MODULESTORE, get_user
|
||||
|
||||
import logging
|
||||
|
||||
@@ -31,6 +29,9 @@ from django.test.utils import override_settings
|
||||
from xmodule.tests import test_util_open_ended
|
||||
|
||||
from courseware.tests import factories
|
||||
from courseware.tests.modulestore_config import TEST_DATA_XML_MODULESTORE
|
||||
from courseware.tests.helpers import LoginEnrollmentTestCase, check_for_get_code, check_for_post_code
|
||||
|
||||
|
||||
@override_settings(MODULESTORE=TEST_DATA_XML_MODULESTORE)
|
||||
class TestStaffGradingService(LoginEnrollmentTestCase):
|
||||
@@ -58,7 +59,7 @@ class TestStaffGradingService(LoginEnrollmentTestCase):
|
||||
def make_instructor(course):
|
||||
group_name = _course_staff_group_name(course.location)
|
||||
group = Group.objects.create(name=group_name)
|
||||
group.user_set.add(get_user(self.instructor))
|
||||
group.user_set.add(User.objects.get(email=self.instructor))
|
||||
|
||||
make_instructor(self.toy)
|
||||
|
||||
@@ -75,8 +76,8 @@ class TestStaffGradingService(LoginEnrollmentTestCase):
|
||||
# both get and post should return 404
|
||||
for view_name in ('staff_grading_get_next', 'staff_grading_save_grade'):
|
||||
url = reverse(view_name, kwargs={'course_id': self.course_id})
|
||||
self.check_for_get_code(404, url)
|
||||
self.check_for_post_code(404, url)
|
||||
check_for_get_code(self, 404, url)
|
||||
check_for_post_code(self, 404, url)
|
||||
|
||||
def test_get_next(self):
|
||||
self.login(self.instructor, self.password)
|
||||
@@ -84,7 +85,7 @@ class TestStaffGradingService(LoginEnrollmentTestCase):
|
||||
url = reverse('staff_grading_get_next', kwargs={'course_id': self.course_id})
|
||||
data = {'location': self.location}
|
||||
|
||||
response = self.check_for_post_code(200, url, data)
|
||||
response = check_for_post_code(self, 200, url, data)
|
||||
|
||||
content = json.loads(response.content)
|
||||
|
||||
@@ -113,7 +114,7 @@ class TestStaffGradingService(LoginEnrollmentTestCase):
|
||||
if skip:
|
||||
data.update({'skipped': True})
|
||||
|
||||
response = self.check_for_post_code(200, url, data)
|
||||
response = check_for_post_code(self, 200, url, data)
|
||||
content = json.loads(response.content)
|
||||
self.assertTrue(content['success'], str(content))
|
||||
self.assertEquals(content['submission_id'], self.mock_service.cnt)
|
||||
@@ -130,7 +131,7 @@ class TestStaffGradingService(LoginEnrollmentTestCase):
|
||||
url = reverse('staff_grading_get_problem_list', kwargs={'course_id': self.course_id})
|
||||
data = {}
|
||||
|
||||
response = self.check_for_post_code(200, url, data)
|
||||
response = check_for_post_code(self, 200, url, data)
|
||||
content = json.loads(response.content)
|
||||
|
||||
self.assertTrue(content['success'], str(content))
|
||||
|
||||
@@ -36,7 +36,7 @@ urlpatterns = ('', # nopep8
|
||||
url(r'^login_ajax$', 'student.views.login_user', name="login"),
|
||||
url(r'^login_ajax/(?P<error>[^/]*)$', 'student.views.login_user'),
|
||||
url(r'^logout$', 'student.views.logout_user', name='logout'),
|
||||
url(r'^create_account$', 'student.views.create_account'),
|
||||
url(r'^create_account$', 'student.views.create_account', name='create_account'),
|
||||
url(r'^activate/(?P<key>[^/]*)$', 'student.views.activate_account', name="activate"),
|
||||
|
||||
url(r'^begin_exam_registration/(?P<course_id>[^/]+/[^/]+/[^/]+)$', 'student.views.begin_exam_registration', name="begin_exam_registration"),
|
||||
|
||||
Reference in New Issue
Block a user