Merge pull request #1755 from MITx/fix/brian/fix500v1
Fix/brian/fix500v1
This commit is contained in:
@@ -3,13 +3,11 @@ from django.test.utils import override_settings
|
||||
|
||||
import xmodule.modulestore.django
|
||||
|
||||
from courseware.tests.tests import PageLoader, TEST_DATA_XML_MODULESTORE
|
||||
from courseware.tests.tests import LoginEnrollmentTestCase, TEST_DATA_XML_MODULESTORE
|
||||
from xmodule.modulestore.django import modulestore
|
||||
from xmodule.modulestore.xml_importer import import_from_xml
|
||||
|
||||
|
||||
@override_settings(MODULESTORE=TEST_DATA_XML_MODULESTORE)
|
||||
class WikiRedirectTestCase(PageLoader):
|
||||
class WikiRedirectTestCase(LoginEnrollmentTestCase):
|
||||
def setUp(self):
|
||||
xmodule.modulestore.django._MODULESTORES = {}
|
||||
courses = modulestore().get_courses()
|
||||
@@ -30,8 +28,6 @@ class WikiRedirectTestCase(PageLoader):
|
||||
self.activate_user(self.student)
|
||||
self.activate_user(self.instructor)
|
||||
|
||||
|
||||
|
||||
def test_wiki_redirect(self):
|
||||
"""
|
||||
Test that requesting wiki URLs redirect properly to or out of classes.
|
||||
@@ -69,7 +65,6 @@ class WikiRedirectTestCase(PageLoader):
|
||||
self.assertEqual(resp.status_code, 302)
|
||||
self.assertEqual(resp['Location'], 'http://testserver' + destination)
|
||||
|
||||
|
||||
def create_course_page(self, course):
|
||||
"""
|
||||
Test that loading the course wiki page creates the wiki page.
|
||||
@@ -98,7 +93,6 @@ class WikiRedirectTestCase(PageLoader):
|
||||
self.assertTrue("course info" in resp.content.lower())
|
||||
self.assertTrue("courseware" in resp.content.lower())
|
||||
|
||||
|
||||
def test_course_navigator(self):
|
||||
""""
|
||||
Test that going from a course page to a wiki page contains the course navigator.
|
||||
@@ -108,7 +102,6 @@ class WikiRedirectTestCase(PageLoader):
|
||||
self.enroll(self.toy)
|
||||
self.create_course_page(self.toy)
|
||||
|
||||
|
||||
course_wiki_page = reverse('wiki:get', kwargs={'path': self.toy.wiki_slug + '/'})
|
||||
referer = reverse("courseware", kwargs={'course_id': self.toy.id})
|
||||
|
||||
|
||||
@@ -2,16 +2,16 @@ from mock import MagicMock
|
||||
import json
|
||||
|
||||
from django.http import Http404, HttpResponse
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.conf import settings
|
||||
from django.test import TestCase
|
||||
from django.test.client import RequestFactory
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.test.utils import override_settings
|
||||
|
||||
from xmodule.modulestore.exceptions import ItemNotFoundError
|
||||
import courseware.module_render as render
|
||||
from xmodule.modulestore.django import modulestore
|
||||
from courseware.tests.tests import PageLoader
|
||||
import courseware.module_render as render
|
||||
from courseware.tests.tests import LoginEnrollmentTestCase
|
||||
from courseware.model_data import ModelDataCache
|
||||
|
||||
from .factories import UserFactory
|
||||
@@ -38,7 +38,7 @@ TEST_DATA_XML_MODULESTORE = xml_store_config(TEST_DATA_DIR)
|
||||
|
||||
|
||||
@override_settings(MODULESTORE=TEST_DATA_XML_MODULESTORE)
|
||||
class ModuleRenderTestCase(PageLoader):
|
||||
class ModuleRenderTestCase(LoginEnrollmentTestCase):
|
||||
def setUp(self):
|
||||
self.location = ['i4x', 'edX', 'toy', 'chapter', 'Overview']
|
||||
self.course_id = 'edX/toy/2012_Fall'
|
||||
@@ -54,10 +54,9 @@ class ModuleRenderTestCase(PageLoader):
|
||||
mock_request = MagicMock()
|
||||
mock_request.FILES.keys.return_value = ['file_id']
|
||||
mock_request.FILES.getlist.return_value = ['file'] * (settings.MAX_FILEUPLOADS_PER_INPUT + 1)
|
||||
self.assertEquals(render.modx_dispatch(mock_request, 'dummy', self.location,
|
||||
'dummy').content,
|
||||
json.dumps({'success': 'Submission aborted! Maximum %d files may be submitted at once' %
|
||||
settings.MAX_FILEUPLOADS_PER_INPUT}))
|
||||
self.assertEquals(render.modx_dispatch(mock_request, 'dummy', self.location, 'dummy').content,
|
||||
json.dumps({'success': 'Submission aborted! Maximum %d files may be submitted at once' %
|
||||
settings.MAX_FILEUPLOADS_PER_INPUT}))
|
||||
mock_request_2 = MagicMock()
|
||||
mock_request_2.FILES.keys.return_value = ['file_id']
|
||||
inputfile = Stub()
|
||||
@@ -68,7 +67,7 @@ class ModuleRenderTestCase(PageLoader):
|
||||
self.assertEquals(render.modx_dispatch(mock_request_2, 'dummy', self.location,
|
||||
'dummy').content,
|
||||
json.dumps({'success': 'Submission aborted! Your file "%s" is too large (max size: %d MB)' %
|
||||
(inputfile.name, settings.STUDENT_FILEUPLOAD_MAX_SIZE / (1000 ** 2))}))
|
||||
(inputfile.name, settings.STUDENT_FILEUPLOAD_MAX_SIZE / (1000 ** 2))}))
|
||||
mock_request_3 = MagicMock()
|
||||
mock_request_3.POST.copy.return_value = {}
|
||||
mock_request_3.FILES = False
|
||||
@@ -79,10 +78,10 @@ class ModuleRenderTestCase(PageLoader):
|
||||
self.assertRaises(ItemNotFoundError, render.modx_dispatch,
|
||||
mock_request_3, 'dummy', self.location, 'toy')
|
||||
self.assertRaises(Http404, render.modx_dispatch, mock_request_3, 'dummy',
|
||||
self.location, self.course_id)
|
||||
self.location, self.course_id)
|
||||
mock_request_3.POST.copy.return_value = {'position': 1}
|
||||
self.assertIsInstance(render.modx_dispatch(mock_request_3, 'goto_position',
|
||||
self.location, self.course_id), HttpResponse)
|
||||
self.location, self.course_id), HttpResponse)
|
||||
|
||||
def test_get_score_bucket(self):
|
||||
self.assertEquals(render.get_score_bucket(0, 10), 'incorrect')
|
||||
@@ -124,19 +123,19 @@ class TestTOC(TestCase):
|
||||
self.toy_course.id, self.portal_user, self.toy_course, depth=2)
|
||||
|
||||
expected = ([{'active': True, 'sections':
|
||||
[{'url_name': 'Toy_Videos', 'display_name': u'Toy Videos', 'graded': True,
|
||||
'format': u'Lecture Sequence', 'due': '', 'active': False},
|
||||
{'url_name': 'Welcome', 'display_name': u'Welcome', 'graded': True,
|
||||
'format': '', 'due': '', 'active': False},
|
||||
{'url_name': 'video_123456789012', 'display_name': 'video 123456789012', 'graded': True,
|
||||
'format': '', 'due': '', 'active': False},
|
||||
{'url_name': 'video_4f66f493ac8f', 'display_name': 'video 4f66f493ac8f', 'graded': True,
|
||||
'format': '', 'due': '', 'active': False}],
|
||||
'url_name': 'Overview', 'display_name': u'Overview'},
|
||||
{'active': False, 'sections':
|
||||
[{'url_name': 'toyvideo', 'display_name': 'toyvideo', 'graded': True,
|
||||
'format': '', 'due': '', 'active': False}],
|
||||
'url_name': 'secret:magic', 'display_name': 'secret:magic'}])
|
||||
[{'url_name': 'Toy_Videos', 'display_name': u'Toy Videos', 'graded': True,
|
||||
'format': u'Lecture Sequence', 'due': '', 'active': False},
|
||||
{'url_name': 'Welcome', 'display_name': u'Welcome', 'graded': True,
|
||||
'format': '', 'due': '', 'active': False},
|
||||
{'url_name': 'video_123456789012', 'display_name': 'video 123456789012', 'graded': True,
|
||||
'format': '', 'due': '', 'active': False},
|
||||
{'url_name': 'video_4f66f493ac8f', 'display_name': 'video 4f66f493ac8f', 'graded': True,
|
||||
'format': '', 'due': '', 'active': False}],
|
||||
'url_name': 'Overview', 'display_name': u'Overview'},
|
||||
{'active': False, 'sections':
|
||||
[{'url_name': 'toyvideo', 'display_name': 'toyvideo', 'graded': True,
|
||||
'format': '', 'due': '', 'active': False}],
|
||||
'url_name': 'secret:magic', 'display_name': 'secret:magic'}])
|
||||
|
||||
actual = render.toc_for_course(self.portal_user, request, self.toy_course, chapter, None, model_data_cache)
|
||||
self.assertEqual(expected, actual)
|
||||
@@ -151,19 +150,19 @@ class TestTOC(TestCase):
|
||||
self.toy_course.id, self.portal_user, self.toy_course, depth=2)
|
||||
|
||||
expected = ([{'active': True, 'sections':
|
||||
[{'url_name': 'Toy_Videos', 'display_name': u'Toy Videos', 'graded': True,
|
||||
'format': u'Lecture Sequence', 'due': '', 'active': False},
|
||||
{'url_name': 'Welcome', 'display_name': u'Welcome', 'graded': True,
|
||||
'format': '', 'due': '', 'active': True},
|
||||
{'url_name': 'video_123456789012', 'display_name': 'video 123456789012', 'graded': True,
|
||||
'format': '', 'due': '', 'active': False},
|
||||
{'url_name': 'video_4f66f493ac8f', 'display_name': 'video 4f66f493ac8f', 'graded': True,
|
||||
'format': '', 'due': '', 'active': False}],
|
||||
'url_name': 'Overview', 'display_name': u'Overview'},
|
||||
{'active': False, 'sections':
|
||||
[{'url_name': 'toyvideo', 'display_name': 'toyvideo', 'graded': True,
|
||||
'format': '', 'due': '', 'active': False}],
|
||||
'url_name': 'secret:magic', 'display_name': 'secret:magic'}])
|
||||
[{'url_name': 'Toy_Videos', 'display_name': u'Toy Videos', 'graded': True,
|
||||
'format': u'Lecture Sequence', 'due': '', 'active': False},
|
||||
{'url_name': 'Welcome', 'display_name': u'Welcome', 'graded': True,
|
||||
'format': '', 'due': '', 'active': True},
|
||||
{'url_name': 'video_123456789012', 'display_name': 'video 123456789012', 'graded': True,
|
||||
'format': '', 'due': '', 'active': False},
|
||||
{'url_name': 'video_4f66f493ac8f', 'display_name': 'video 4f66f493ac8f', 'graded': True,
|
||||
'format': '', 'due': '', 'active': False}],
|
||||
'url_name': 'Overview', 'display_name': u'Overview'},
|
||||
{'active': False, 'sections':
|
||||
[{'url_name': 'toyvideo', 'display_name': 'toyvideo', 'graded': True,
|
||||
'format': '', 'due': '', 'active': False}],
|
||||
'url_name': 'secret:magic', 'display_name': 'secret:magic'}])
|
||||
|
||||
actual = render.toc_for_course(self.portal_user, request, self.toy_course, chapter, section, model_data_cache)
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import logging
|
||||
log = logging.getLogger("mitx." + __name__)
|
||||
|
||||
import json
|
||||
import time
|
||||
|
||||
from urlparse import urlsplit, urlunsplit
|
||||
|
||||
from django.contrib.auth.models import User, Group
|
||||
@@ -29,29 +26,30 @@ 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.timeparse import stringify_time
|
||||
|
||||
log = logging.getLogger("mitx." + __name__)
|
||||
|
||||
def parse_json(response):
|
||||
"""Parse response, which is assumed to be json"""
|
||||
return json.loads(response.content)
|
||||
|
||||
|
||||
def user(email):
|
||||
def get_user(email):
|
||||
'''look up a user by email'''
|
||||
return User.objects.get(email=email)
|
||||
|
||||
|
||||
def registration(email):
|
||||
def get_registration(email):
|
||||
'''look up registration object by email'''
|
||||
return Registration.objects.get(user__email=email)
|
||||
|
||||
# A bit of a hack--want mongo modulestore for these tests, until
|
||||
# jump_to works with the xmlmodulestore or we have an even better solution
|
||||
# NOTE: this means this test requires mongo to be running.
|
||||
|
||||
|
||||
def mongo_store_config(data_dir):
|
||||
'''
|
||||
Defines default module store using MongoModuleStore
|
||||
|
||||
Use of this config requires mongo to be running
|
||||
'''
|
||||
return {
|
||||
'default': {
|
||||
'ENGINE': 'xmodule.modulestore.mongo.MongoModuleStore',
|
||||
@@ -68,6 +66,7 @@ def mongo_store_config(data_dir):
|
||||
|
||||
|
||||
def draft_mongo_store_config(data_dir):
|
||||
'''Defines default module store using DraftMongoModuleStore'''
|
||||
return {
|
||||
'default': {
|
||||
'ENGINE': 'xmodule.modulestore.mongo.DraftMongoModuleStore',
|
||||
@@ -84,6 +83,7 @@ def draft_mongo_store_config(data_dir):
|
||||
|
||||
|
||||
def xml_store_config(data_dir):
|
||||
'''Defines default module store using XMLModuleStore'''
|
||||
return {
|
||||
'default': {
|
||||
'ENGINE': 'xmodule.modulestore.xml.XMLModuleStore',
|
||||
@@ -100,8 +100,8 @@ TEST_DATA_MONGO_MODULESTORE = mongo_store_config(TEST_DATA_DIR)
|
||||
TEST_DATA_DRAFT_MONGO_MODULESTORE = draft_mongo_store_config(TEST_DATA_DIR)
|
||||
|
||||
|
||||
class ActivateLoginTestCase(TestCase):
|
||||
'''Check that we can activate and log in'''
|
||||
class LoginEnrollmentTestCase(TestCase):
|
||||
'''Base TestCase providing support for user creation, activation, login, and course enrollment'''
|
||||
|
||||
def assertRedirectsNoFollow(self, response, expected_url):
|
||||
"""
|
||||
@@ -117,32 +117,33 @@ class ActivateLoginTestCase(TestCase):
|
||||
|
||||
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))
|
||||
|
||||
def setUp(self):
|
||||
email = 'view@test.com'
|
||||
password = 'foo'
|
||||
self.create_account('viewtest', email, password)
|
||||
self.activate_user(email)
|
||||
self.login(email, password)
|
||||
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, pw):
|
||||
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': pw})
|
||||
{'email': email, 'password': password})
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
return resp
|
||||
|
||||
def login(self, email, pw):
|
||||
def login(self, email, password):
|
||||
'''Login, check that it worked.'''
|
||||
resp = self._login(email, pw)
|
||||
resp = self._login(email, password)
|
||||
data = parse_json(resp)
|
||||
self.assertTrue(data['success'])
|
||||
return resp
|
||||
@@ -154,34 +155,34 @@ class ActivateLoginTestCase(TestCase):
|
||||
self.assertEqual(resp.status_code, 302)
|
||||
return resp
|
||||
|
||||
def _create_account(self, username, email, pw):
|
||||
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': pw,
|
||||
'password': password,
|
||||
'name': 'Fred Weasley',
|
||||
'terms_of_service': 'true',
|
||||
'honor_code': 'true',
|
||||
})
|
||||
return resp
|
||||
|
||||
def create_account(self, username, email, pw):
|
||||
def create_account(self, username, email, password):
|
||||
'''Create the account and check that it worked'''
|
||||
resp = self._create_account(username, email, pw)
|
||||
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(user(email).is_active)
|
||||
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 = registration(email).activation_key
|
||||
activation_key = get_registration(email).activation_key
|
||||
|
||||
# and now we try to activate
|
||||
resp = self.client.get(reverse('activate', kwargs={'key': activation_key}))
|
||||
@@ -191,19 +192,7 @@ class ActivateLoginTestCase(TestCase):
|
||||
resp = self._activate_user(email)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
# Now make sure that the user is now actually activated
|
||||
self.assertTrue(user(email).is_active)
|
||||
|
||||
def test_activate_login(self):
|
||||
'''The setup function does all the work'''
|
||||
pass
|
||||
|
||||
def test_logout(self):
|
||||
'''Setup function does login'''
|
||||
self.logout()
|
||||
|
||||
|
||||
class PageLoader(ActivateLoginTestCase):
|
||||
''' Base class that adds a function to load all pages in a modulestore '''
|
||||
self.assertTrue(get_user(email).is_active)
|
||||
|
||||
def _enroll(self, course):
|
||||
"""Post to the enrollment view, and return the parsed json response"""
|
||||
@@ -240,8 +229,7 @@ class PageLoader(ActivateLoginTestCase):
|
||||
"""
|
||||
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 {0} for url '{1}'. Expected code {2}".format(resp.status_code, url, code))
|
||||
return resp
|
||||
|
||||
def check_for_post_code(self, code, url, data={}):
|
||||
@@ -251,10 +239,27 @@ class PageLoader(ActivateLoginTestCase):
|
||||
"""
|
||||
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 {0} for url '{1}'. Expected code {2}".format(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_pages_load(self, module_store):
|
||||
"""Make all locations in course load"""
|
||||
# enroll in the course before trying to access pages
|
||||
@@ -264,14 +269,14 @@ class PageLoader(ActivateLoginTestCase):
|
||||
self.enroll(course)
|
||||
course_id = course.id
|
||||
|
||||
n = 0
|
||||
num = 0
|
||||
num_bad = 0
|
||||
all_ok = True
|
||||
|
||||
for descriptor in module_store.get_items(
|
||||
Location(None, None, None, None, None)):
|
||||
|
||||
n += 1
|
||||
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
|
||||
@@ -332,45 +337,43 @@ class PageLoader(ActivateLoginTestCase):
|
||||
print msg
|
||||
self.assertTrue(all_ok) # fail fast
|
||||
|
||||
print "{0}/{1} good".format(n - num_bad, n)
|
||||
log.info("{0}/{1} good".format(n - num_bad, n))
|
||||
print "{0}/{1} good".format(num - num_bad, num)
|
||||
log.info("{0}/{1} good".format(num - num_bad, num))
|
||||
self.assertTrue(all_ok)
|
||||
|
||||
|
||||
@override_settings(MODULESTORE=TEST_DATA_XML_MODULESTORE)
|
||||
class TestCoursesLoadTestCase_XmlModulestore(PageLoader):
|
||||
'''Check that all pages in test courses load properly'''
|
||||
class TestCoursesLoadTestCase_XmlModulestore(PageLoaderTestCase):
|
||||
'''Check that all pages in test courses load properly from XML'''
|
||||
|
||||
def setUp(self):
|
||||
ActivateLoginTestCase.setUp(self)
|
||||
self.setup_viewtest_user()
|
||||
xmodule.modulestore.django._MODULESTORES = {}
|
||||
|
||||
def test_toy_course_loads(self):
|
||||
module_store = XMLModuleStore(
|
||||
TEST_DATA_DIR,
|
||||
default_class='xmodule.hidden_module.HiddenDescriptor',
|
||||
course_dirs=['toy'],
|
||||
load_error_modules=True,
|
||||
module_store = XMLModuleStore(TEST_DATA_DIR,
|
||||
default_class='xmodule.hidden_module.HiddenDescriptor',
|
||||
course_dirs=['toy'],
|
||||
load_error_modules=True,
|
||||
)
|
||||
|
||||
self.check_pages_load(module_store)
|
||||
|
||||
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,
|
||||
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)
|
||||
|
||||
|
||||
@override_settings(MODULESTORE=TEST_DATA_MONGO_MODULESTORE)
|
||||
class TestCoursesLoadTestCase_MongoModulestore(PageLoader):
|
||||
'''Check that all pages in test courses load properly'''
|
||||
class TestCoursesLoadTestCase_MongoModulestore(PageLoaderTestCase):
|
||||
'''Check that all pages in test courses load properly from Mongo'''
|
||||
|
||||
def setUp(self):
|
||||
ActivateLoginTestCase.setUp(self)
|
||||
self.setup_viewtest_user()
|
||||
xmodule.modulestore.django._MODULESTORES = {}
|
||||
modulestore().collection.drop()
|
||||
|
||||
@@ -386,7 +389,7 @@ class TestCoursesLoadTestCase_MongoModulestore(PageLoader):
|
||||
|
||||
|
||||
@override_settings(MODULESTORE=TEST_DATA_XML_MODULESTORE)
|
||||
class TestNavigation(PageLoader):
|
||||
class TestNavigation(LoginEnrollmentTestCase):
|
||||
"""Check that navigation state is saved properly"""
|
||||
|
||||
def setUp(self):
|
||||
@@ -447,7 +450,7 @@ class TestDraftModuleStore(TestCase):
|
||||
|
||||
|
||||
@override_settings(MODULESTORE=TEST_DATA_XML_MODULESTORE)
|
||||
class TestViewAuth(PageLoader):
|
||||
class TestViewAuth(LoginEnrollmentTestCase):
|
||||
"""Check that view authentication works properly"""
|
||||
|
||||
# NOTE: setUpClass() runs before override_settings takes effect, so
|
||||
@@ -492,7 +495,7 @@ class TestViewAuth(PageLoader):
|
||||
'gradebook',
|
||||
'grade_summary',)]
|
||||
urls.append(reverse('student_progress', kwargs={'course_id': course.id,
|
||||
'student_id': user(self.student).id}))
|
||||
'student_id': get_user(self.student).id}))
|
||||
return urls
|
||||
|
||||
# shouldn't be able to get to the instructor pages
|
||||
@@ -502,8 +505,8 @@ class TestViewAuth(PageLoader):
|
||||
|
||||
# Make the instructor staff in the toy course
|
||||
group_name = _course_staff_group_name(self.toy.location)
|
||||
g = Group.objects.create(name=group_name)
|
||||
g.user_set.add(user(self.instructor))
|
||||
group = Group.objects.create(name=group_name)
|
||||
group.user_set.add(get_user(self.instructor))
|
||||
|
||||
self.logout()
|
||||
self.login(self.instructor, self.password)
|
||||
@@ -518,9 +521,9 @@ class TestViewAuth(PageLoader):
|
||||
self.check_for_get_code(404, url)
|
||||
|
||||
# now also make the instructor staff
|
||||
u = user(self.instructor)
|
||||
u.is_staff = True
|
||||
u.save()
|
||||
instructor = get_user(self.instructor)
|
||||
instructor.is_staff = True
|
||||
instructor.save()
|
||||
|
||||
# and now should be able to load both
|
||||
for url in instructor_urls(self.toy) + instructor_urls(self.full):
|
||||
@@ -627,7 +630,7 @@ class TestViewAuth(PageLoader):
|
||||
# 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': user(self.student).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)
|
||||
|
||||
@@ -648,8 +651,8 @@ class TestViewAuth(PageLoader):
|
||||
print '=== Testing course instructor access....'
|
||||
# Make the instructor staff in the toy course
|
||||
group_name = _course_staff_group_name(self.toy.location)
|
||||
g = Group.objects.create(name=group_name)
|
||||
g.user_set.add(user(self.instructor))
|
||||
group = Group.objects.create(name=group_name)
|
||||
group.user_set.add(get_user(self.instructor))
|
||||
|
||||
self.logout()
|
||||
self.login(self.instructor, self.password)
|
||||
@@ -663,9 +666,9 @@ class TestViewAuth(PageLoader):
|
||||
|
||||
print '=== Testing staff access....'
|
||||
# now also make the instructor staff
|
||||
u = user(self.instructor)
|
||||
u.is_staff = True
|
||||
u.save()
|
||||
instructor = get_user(self.instructor)
|
||||
instructor.is_staff = True
|
||||
instructor.save()
|
||||
|
||||
# and now should be able to load both
|
||||
check_staff(self.toy)
|
||||
@@ -698,8 +701,8 @@ class TestViewAuth(PageLoader):
|
||||
print '=== Testing course instructor access....'
|
||||
# Make the instructor staff in the toy course
|
||||
group_name = _course_staff_group_name(self.toy.location)
|
||||
g = Group.objects.create(name=group_name)
|
||||
g.user_set.add(user(self.instructor))
|
||||
group = Group.objects.create(name=group_name)
|
||||
group.user_set.add(get_user(self.instructor))
|
||||
|
||||
print "logout/login"
|
||||
self.logout()
|
||||
@@ -709,10 +712,10 @@ class TestViewAuth(PageLoader):
|
||||
|
||||
print '=== Testing staff access....'
|
||||
# now make the instructor global staff, but not in the instructor group
|
||||
g.user_set.remove(user(self.instructor))
|
||||
u = user(self.instructor)
|
||||
u.is_staff = True
|
||||
u.save()
|
||||
group.user_set.remove(get_user(self.instructor))
|
||||
instructor = get_user(self.instructor)
|
||||
instructor.is_staff = True
|
||||
instructor.save()
|
||||
|
||||
# unenroll and try again
|
||||
self.unenroll(self.toy)
|
||||
@@ -726,8 +729,8 @@ class TestViewAuth(PageLoader):
|
||||
|
||||
# Make courses start in the future
|
||||
tomorrow = time.time() + 24 * 3600
|
||||
nextday = tomorrow + 24 * 3600
|
||||
yesterday = time.time() - 24 * 3600
|
||||
# nextday = tomorrow + 24 * 3600
|
||||
# yesterday = time.time() - 24 * 3600
|
||||
|
||||
# toy course's hasn't started
|
||||
self.toy.lms.start = time.gmtime(tomorrow)
|
||||
@@ -737,20 +740,20 @@ class TestViewAuth(PageLoader):
|
||||
self.toy.lms.days_early_for_beta = 2
|
||||
|
||||
# student user shouldn't see it
|
||||
student_user = user(self.student)
|
||||
student_user = get_user(self.student)
|
||||
self.assertFalse(has_access(student_user, self.toy, 'load'))
|
||||
|
||||
# now add the student to the beta test group
|
||||
group_name = course_beta_test_group_name(self.toy.location)
|
||||
g = Group.objects.create(name=group_name)
|
||||
g.user_set.add(student_user)
|
||||
group = Group.objects.create(name=group_name)
|
||||
group.user_set.add(student_user)
|
||||
|
||||
# now the student should see it
|
||||
self.assertTrue(has_access(student_user, self.toy, 'load'))
|
||||
|
||||
|
||||
@override_settings(MODULESTORE=TEST_DATA_XML_MODULESTORE)
|
||||
class TestCourseGrader(PageLoader):
|
||||
class TestCourseGrader(LoginEnrollmentTestCase):
|
||||
"""Check that a course gets graded properly"""
|
||||
|
||||
# NOTE: setUpClass() runs before override_settings takes effect, so
|
||||
@@ -773,35 +776,39 @@ class TestCourseGrader(PageLoader):
|
||||
self.activate_user(self.student)
|
||||
self.enroll(self.graded_course)
|
||||
|
||||
self.student_user = user(self.student)
|
||||
self.student_user = get_user(self.student)
|
||||
|
||||
self.factory = RequestFactory()
|
||||
|
||||
def get_grade_summary(self):
|
||||
'''calls grades.grade for current user and course'''
|
||||
model_data_cache = ModelDataCache.cache_for_descriptor_descendents(
|
||||
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)
|
||||
|
||||
def get_homework_scores(self):
|
||||
'''get scores for homeworks'''
|
||||
return self.get_grade_summary()['totaled_scores']['Homework']
|
||||
|
||||
def get_progress_summary(self):
|
||||
'''return progress summary structure for current user and course'''
|
||||
model_data_cache = ModelDataCache.cache_for_descriptor_descendents(
|
||||
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)
|
||||
return progress_summary
|
||||
|
||||
def check_grade_percent(self, percent):
|
||||
'''assert that percent grade is as expected'''
|
||||
grade_summary = self.get_grade_summary()
|
||||
self.assertEqual(grade_summary['percent'], percent)
|
||||
|
||||
@@ -816,10 +823,9 @@ class TestCourseGrader(PageLoader):
|
||||
problem_location = "i4x://edX/graded/problem/{0}".format(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],
|
||||
@@ -831,16 +837,17 @@ class TestCourseGrader(PageLoader):
|
||||
return resp
|
||||
|
||||
def problem_location(self, problem_url_name):
|
||||
'''Get location string for problem, assuming hardcoded course_id'''
|
||||
return "i4x://edX/graded/problem/{0}".format(problem_url_name)
|
||||
|
||||
def reset_question_answer(self, problem_url_name):
|
||||
'''resets specified problem for current user'''
|
||||
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
|
||||
@@ -855,6 +862,7 @@ class TestCourseGrader(PageLoader):
|
||||
return [s.earned for s in self.get_homework_scores()]
|
||||
|
||||
def score_for_hw(hw_url_name):
|
||||
"""returns list of scores for a given url"""
|
||||
hw_section = [section for section
|
||||
in self.get_progress_summary()[0]['sections']
|
||||
if section.get('url_name') == hw_url_name][0]
|
||||
|
||||
@@ -1,28 +1,22 @@
|
||||
import json
|
||||
import logging
|
||||
import xml.sax.saxutils as saxutils
|
||||
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.views.decorators.http import require_POST
|
||||
from django.http import HttpResponse, Http404
|
||||
from django.utils import simplejson
|
||||
from django.http import Http404
|
||||
from django.core.context_processors import csrf
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from mitxmako.shortcuts import render_to_response, render_to_string
|
||||
from courseware.courses import get_course_with_access
|
||||
from course_groups.cohorts import is_course_cohorted, get_cohort_id, is_commentable_cohorted, get_cohorted_commentables, get_cohort, get_course_cohorts, get_cohort_by_id
|
||||
from course_groups.cohorts import (is_course_cohorted, get_cohort_id, is_commentable_cohorted,
|
||||
get_cohorted_commentables, get_course_cohorts, get_cohort_by_id)
|
||||
from courseware.access import has_access
|
||||
|
||||
from urllib import urlencode
|
||||
from operator import methodcaller
|
||||
from django_comment_client.permissions import check_permissions_by_view, cached_has_permission
|
||||
from django_comment_client.utils import (merge_dict, extract, strip_none,
|
||||
strip_blank, get_courseware_context)
|
||||
|
||||
from django_comment_client.permissions import cached_has_permission
|
||||
from django_comment_client.utils import (merge_dict, extract, strip_none, get_courseware_context)
|
||||
import django_comment_client.utils as utils
|
||||
import comment_client as cc
|
||||
import xml.sax.saxutils as saxutils
|
||||
|
||||
THREADS_PER_PAGE = 20
|
||||
INLINE_THREADS_PER_PAGE = 20
|
||||
@@ -31,6 +25,7 @@ escapedict = {'"': '"'}
|
||||
log = logging.getLogger("edx.discussions")
|
||||
|
||||
|
||||
@login_required
|
||||
def get_threads(request, course_id, discussion_id=None, per_page=THREADS_PER_PAGE):
|
||||
"""
|
||||
This may raise cc.utils.CommentClientError or
|
||||
@@ -60,7 +55,6 @@ def get_threads(request, course_id, discussion_id=None, per_page=THREADS_PER_PAG
|
||||
cc_user.default_sort_key = request.GET.get('sort_key')
|
||||
cc_user.save()
|
||||
|
||||
|
||||
#there are 2 dimensions to consider when executing a search with respect to group id
|
||||
#is user a moderator
|
||||
#did the user request a group
|
||||
@@ -91,18 +85,17 @@ def get_threads(request, course_id, discussion_id=None, per_page=THREADS_PER_PAG
|
||||
|
||||
#now add the group name if the thread has a group id
|
||||
for thread in threads:
|
||||
|
||||
|
||||
if thread.get('group_id'):
|
||||
thread['group_name'] = get_cohort_by_id(course_id, thread.get('group_id')).name
|
||||
thread['group_string'] = "This post visible only to Group %s." % (thread['group_name'])
|
||||
else:
|
||||
thread['group_name'] = ""
|
||||
thread['group_string'] = "This post visible to everyone."
|
||||
|
||||
|
||||
#patch for backward compatibility to comments service
|
||||
if not 'pinned' in thread:
|
||||
thread['pinned'] = False
|
||||
|
||||
|
||||
query_params['page'] = page
|
||||
query_params['num_pages'] = num_pages
|
||||
@@ -110,6 +103,7 @@ def get_threads(request, course_id, discussion_id=None, per_page=THREADS_PER_PAG
|
||||
return threads, query_params
|
||||
|
||||
|
||||
@login_required
|
||||
def inline_discussion(request, course_id, discussion_id):
|
||||
"""
|
||||
Renders JSON for DiscussionModules
|
||||
@@ -142,14 +136,14 @@ def inline_discussion(request, course_id, discussion_id):
|
||||
cohorts_list = list()
|
||||
|
||||
if is_cohorted:
|
||||
cohorts_list.append({'name':'All Groups','id':None})
|
||||
cohorts_list.append({'name': 'All Groups', 'id': None})
|
||||
|
||||
#if you're a mod, send all cohorts and let you pick
|
||||
|
||||
if is_moderator:
|
||||
cohorts = get_course_cohorts(course_id)
|
||||
for c in cohorts:
|
||||
cohorts_list.append({'name':c.name, 'id':c.id})
|
||||
cohorts_list.append({'name': c.name, 'id': c.id})
|
||||
|
||||
else:
|
||||
#students don't get to choose
|
||||
@@ -216,9 +210,6 @@ def forum_form_discussion(request, course_id):
|
||||
|
||||
user_cohort_id = get_cohort_id(request.user, course_id)
|
||||
|
||||
|
||||
|
||||
|
||||
context = {
|
||||
'csrf': csrf(request)['csrf_token'],
|
||||
'course': course,
|
||||
@@ -242,6 +233,7 @@ def forum_form_discussion(request, course_id):
|
||||
|
||||
return render_to_response('discussion/index.html', context)
|
||||
|
||||
|
||||
@login_required
|
||||
def single_thread(request, course_id, discussion_id, thread_id):
|
||||
course = get_course_with_access(request.user, course_id, 'load')
|
||||
@@ -250,11 +242,11 @@ def single_thread(request, course_id, discussion_id, thread_id):
|
||||
|
||||
try:
|
||||
thread = cc.Thread.find(thread_id).retrieve(recursive=True, user_id=request.user.id)
|
||||
|
||||
|
||||
#patch for backward compatibility with comments service
|
||||
if not 'pinned' in thread.attributes:
|
||||
thread['pinned'] = False
|
||||
|
||||
|
||||
except (cc.utils.CommentClientError, cc.utils.CommentClientUnknownError) as err:
|
||||
log.error("Error loading single thread.")
|
||||
raise Http404
|
||||
@@ -352,7 +344,7 @@ def user_profile(request, course_id, user_id):
|
||||
query_params = {
|
||||
'page': request.GET.get('page', 1),
|
||||
'per_page': THREADS_PER_PAGE, # more than threads_per_page to show more activities
|
||||
}
|
||||
}
|
||||
|
||||
threads, page, num_pages = profiled_user.active_threads(query_params)
|
||||
query_params['page'] = page
|
||||
@@ -369,8 +361,6 @@ def user_profile(request, course_id, user_id):
|
||||
'annotated_content_info': saxutils.escape(json.dumps(annotated_content_info), escapedict),
|
||||
})
|
||||
else:
|
||||
|
||||
|
||||
context = {
|
||||
'course': course,
|
||||
'user': request.user,
|
||||
@@ -426,5 +416,5 @@ def followed_threads(request, course_id, user_id):
|
||||
}
|
||||
|
||||
return render_to_response('discussion/user_profile.html', context)
|
||||
except (cc.utils.CommentClientError, cc.utils.CommentClientUnknownError) as err:
|
||||
except (cc.utils.CommentClientError, cc.utils.CommentClientUnknownError):
|
||||
raise Http404
|
||||
|
||||
@@ -1,73 +1,12 @@
|
||||
from django.contrib.auth.models import User, Group
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.test import TestCase
|
||||
from django.test.client import RequestFactory
|
||||
from django.conf import settings
|
||||
|
||||
from mock import Mock
|
||||
|
||||
from django.test.utils import override_settings
|
||||
|
||||
import xmodule.modulestore.django
|
||||
|
||||
from student.models import CourseEnrollment
|
||||
|
||||
from django.db.models.signals import m2m_changed, pre_delete, pre_save, post_delete, post_save
|
||||
from django.dispatch.dispatcher import _make_id
|
||||
import string
|
||||
import random
|
||||
from .permissions import has_permission
|
||||
from .models import Role, Permission
|
||||
|
||||
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 comment_client
|
||||
|
||||
from courseware.tests.tests import PageLoader, TEST_DATA_XML_MODULESTORE
|
||||
|
||||
#@override_settings(MODULESTORE=TEST_DATA_XML_MODULESTORE)
|
||||
#class TestCohorting(PageLoader):
|
||||
# """Check that cohorting works properly"""
|
||||
#
|
||||
# def setUp(self):
|
||||
# xmodule.modulestore.django._MODULESTORES = {}
|
||||
#
|
||||
# # Assume courses are there
|
||||
# self.toy = modulestore().get_course("edX/toy/2012_Fall")
|
||||
#
|
||||
# # 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)
|
||||
#
|
||||
# def test_create_thread(self):
|
||||
# my_save = Mock()
|
||||
# comment_client.perform_request = my_save
|
||||
#
|
||||
# resp = self.client.post(
|
||||
# reverse('django_comment_client.base.views.create_thread',
|
||||
# kwargs={'course_id': 'edX/toy/2012_Fall',
|
||||
# 'commentable_id': 'General'}),
|
||||
# {'some': "some",
|
||||
# 'data': 'data'})
|
||||
# self.assertTrue(my_save.called)
|
||||
#
|
||||
# #self.assertEqual(resp.status_code, 200)
|
||||
# #self.assertEqual(my_save.something, "expected", "complaint if not true")
|
||||
#
|
||||
# self.toy.cohort_config = {"cohorted": True}
|
||||
#
|
||||
# # call the view again ...
|
||||
#
|
||||
# # assert that different things happened
|
||||
from django.contrib.auth.models import User
|
||||
from django.test import TestCase
|
||||
|
||||
from student.models import CourseEnrollment
|
||||
from django_comment_client.permissions import has_permission
|
||||
from django_comment_client.models import Role
|
||||
|
||||
|
||||
class PermissionsTestCase(TestCase):
|
||||
|
||||
@@ -8,13 +8,6 @@ Notes for running by hand:
|
||||
django-admin.py test --settings=lms.envs.test --pythonpath=. lms/djangoapps/instructor
|
||||
"""
|
||||
|
||||
import courseware.tests.tests as ct
|
||||
|
||||
import json
|
||||
|
||||
from nose import SkipTest
|
||||
from mock import patch, Mock
|
||||
|
||||
from django.test.utils import override_settings
|
||||
|
||||
# Need access to internal func to put users in the right group
|
||||
@@ -26,13 +19,13 @@ from django_comment_client.models import Role, FORUM_ROLE_ADMINISTRATOR, \
|
||||
from django_comment_client.utils import has_forum_access
|
||||
|
||||
from courseware.access import _course_staff_group_name
|
||||
import courseware.tests.tests as ct
|
||||
from courseware.tests.tests import LoginEnrollmentTestCase, TEST_DATA_XML_MODULESTORE, get_user
|
||||
from xmodule.modulestore.django import modulestore
|
||||
import xmodule.modulestore.django
|
||||
|
||||
|
||||
@override_settings(MODULESTORE=ct.TEST_DATA_XML_MODULESTORE)
|
||||
class TestInstructorDashboardGradeDownloadCSV(ct.PageLoader):
|
||||
@override_settings(MODULESTORE=TEST_DATA_XML_MODULESTORE)
|
||||
class TestInstructorDashboardGradeDownloadCSV(LoginEnrollmentTestCase):
|
||||
'''
|
||||
Check for download of csv
|
||||
'''
|
||||
@@ -55,7 +48,7 @@ class TestInstructorDashboardGradeDownloadCSV(ct.PageLoader):
|
||||
def make_instructor(course):
|
||||
group_name = _course_staff_group_name(course.location)
|
||||
g = Group.objects.create(name=group_name)
|
||||
g.user_set.add(ct.user(self.instructor))
|
||||
g.user_set.add(get_user(self.instructor))
|
||||
|
||||
make_instructor(self.toy)
|
||||
|
||||
@@ -63,7 +56,6 @@ class TestInstructorDashboardGradeDownloadCSV(ct.PageLoader):
|
||||
self.login(self.instructor, self.password)
|
||||
self.enroll(self.toy)
|
||||
|
||||
|
||||
def test_download_grades_csv(self):
|
||||
course = self.toy
|
||||
url = reverse('instructor_dashboard', kwargs={'course_id': course.id})
|
||||
@@ -101,9 +93,8 @@ def action_name(operation, rolename):
|
||||
return '{0} forum {1}'.format(operation, FORUM_ADMIN_ACTION_SUFFIX[rolename])
|
||||
|
||||
|
||||
|
||||
@override_settings(MODULESTORE=ct.TEST_DATA_XML_MODULESTORE)
|
||||
class TestInstructorDashboardForumAdmin(ct.PageLoader):
|
||||
@override_settings(MODULESTORE=TEST_DATA_XML_MODULESTORE)
|
||||
class TestInstructorDashboardForumAdmin(LoginEnrollmentTestCase):
|
||||
'''
|
||||
Check for change in forum admin role memberships
|
||||
'''
|
||||
@@ -112,7 +103,6 @@ class TestInstructorDashboardForumAdmin(ct.PageLoader):
|
||||
xmodule.modulestore.django._MODULESTORES = {}
|
||||
courses = modulestore().get_courses()
|
||||
|
||||
|
||||
self.course_id = "edX/toy/2012_Fall"
|
||||
self.toy = modulestore().get_course(self.course_id)
|
||||
|
||||
@@ -127,14 +117,12 @@ class TestInstructorDashboardForumAdmin(ct.PageLoader):
|
||||
|
||||
group_name = _course_staff_group_name(self.toy.location)
|
||||
g = Group.objects.create(name=group_name)
|
||||
g.user_set.add(ct.user(self.instructor))
|
||||
g.user_set.add(get_user(self.instructor))
|
||||
|
||||
self.logout()
|
||||
self.login(self.instructor, self.password)
|
||||
self.enroll(self.toy)
|
||||
|
||||
|
||||
|
||||
def initialize_roles(self, course_id):
|
||||
self.admin_role = Role.objects.get_or_create(name=FORUM_ROLE_ADMINISTRATOR, course_id=course_id)[0]
|
||||
self.moderator_role = Role.objects.get_or_create(name=FORUM_ROLE_MODERATOR, course_id=course_id)[0]
|
||||
|
||||
@@ -1,22 +1,138 @@
|
||||
"""Tests for License package"""
|
||||
import logging
|
||||
import json
|
||||
|
||||
from uuid import uuid4
|
||||
from random import shuffle
|
||||
from tempfile import NamedTemporaryFile
|
||||
from factory import Factory, SubFactory
|
||||
|
||||
from django.test import TestCase
|
||||
from django.core.management import call_command
|
||||
|
||||
from .models import CourseSoftware, UserLicense
|
||||
from django.core.urlresolvers import reverse
|
||||
from licenses.models import CourseSoftware, UserLicense
|
||||
from courseware.tests.tests import LoginEnrollmentTestCase, get_user
|
||||
|
||||
COURSE_1 = 'edX/toy/2012_Fall'
|
||||
|
||||
SOFTWARE_1 = 'matlab'
|
||||
SOFTWARE_2 = 'stata'
|
||||
|
||||
SERIAL_1 = '123456abcde'
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class CourseSoftwareFactory(Factory):
|
||||
'''Factory for generating CourseSoftware objects in database'''
|
||||
FACTORY_FOR = CourseSoftware
|
||||
|
||||
name = SOFTWARE_1
|
||||
full_name = SOFTWARE_1
|
||||
url = SOFTWARE_1
|
||||
course_id = COURSE_1
|
||||
|
||||
|
||||
class UserLicenseFactory(Factory):
|
||||
'''
|
||||
Factory for generating UserLicense objects in database
|
||||
|
||||
By default, the user assigned is null, indicating that the
|
||||
serial number has not yet been assigned.
|
||||
'''
|
||||
FACTORY_FOR = UserLicense
|
||||
|
||||
software = SubFactory(CourseSoftwareFactory)
|
||||
serial = SERIAL_1
|
||||
|
||||
|
||||
class LicenseTestCase(LoginEnrollmentTestCase):
|
||||
'''Tests for licenses.views'''
|
||||
def setUp(self):
|
||||
'''creates a user and logs in'''
|
||||
self.setup_viewtest_user()
|
||||
self.software = CourseSoftwareFactory()
|
||||
|
||||
def test_get_license(self):
|
||||
UserLicenseFactory(user=get_user(self.viewtest_email), software=self.software)
|
||||
response = self.client.post(reverse('user_software_license'),
|
||||
{'software': SOFTWARE_1, 'generate': 'false'},
|
||||
HTTP_X_REQUESTED_WITH='XMLHttpRequest',
|
||||
HTTP_REFERER='/courses/{0}/some_page'.format(COURSE_1))
|
||||
self.assertEqual(200, response.status_code)
|
||||
json_returned = json.loads(response.content)
|
||||
self.assertFalse('error' in json_returned)
|
||||
self.assertTrue('serial' in json_returned)
|
||||
self.assertEquals(json_returned['serial'], SERIAL_1)
|
||||
|
||||
def test_get_nonexistent_license(self):
|
||||
response = self.client.post(reverse('user_software_license'),
|
||||
{'software': SOFTWARE_1, 'generate': 'false'},
|
||||
HTTP_X_REQUESTED_WITH='XMLHttpRequest',
|
||||
HTTP_REFERER='/courses/{0}/some_page'.format(COURSE_1))
|
||||
self.assertEqual(200, response.status_code)
|
||||
json_returned = json.loads(response.content)
|
||||
self.assertFalse('serial' in json_returned)
|
||||
self.assertTrue('error' in json_returned)
|
||||
|
||||
def test_create_nonexistent_license(self):
|
||||
'''Should not assign a license to an unlicensed user when none are available'''
|
||||
response = self.client.post(reverse('user_software_license'),
|
||||
{'software': SOFTWARE_1, 'generate': 'true'},
|
||||
HTTP_X_REQUESTED_WITH='XMLHttpRequest',
|
||||
HTTP_REFERER='/courses/{0}/some_page'.format(COURSE_1))
|
||||
self.assertEqual(200, response.status_code)
|
||||
json_returned = json.loads(response.content)
|
||||
self.assertFalse('serial' in json_returned)
|
||||
self.assertTrue('error' in json_returned)
|
||||
|
||||
def test_create_license(self):
|
||||
'''Should assign a license to an unlicensed user if one is unassigned'''
|
||||
# create an unassigned license
|
||||
UserLicenseFactory(software=self.software)
|
||||
response = self.client.post(reverse('user_software_license'),
|
||||
{'software': SOFTWARE_1, 'generate': 'true'},
|
||||
HTTP_X_REQUESTED_WITH='XMLHttpRequest',
|
||||
HTTP_REFERER='/courses/{0}/some_page'.format(COURSE_1))
|
||||
self.assertEqual(200, response.status_code)
|
||||
json_returned = json.loads(response.content)
|
||||
self.assertFalse('error' in json_returned)
|
||||
self.assertTrue('serial' in json_returned)
|
||||
self.assertEquals(json_returned['serial'], SERIAL_1)
|
||||
|
||||
def test_get_license_from_wrong_course(self):
|
||||
response = self.client.post(reverse('user_software_license'),
|
||||
{'software': SOFTWARE_1, 'generate': 'false'},
|
||||
HTTP_X_REQUESTED_WITH='XMLHttpRequest',
|
||||
HTTP_REFERER='/courses/{0}/some_page'.format('some/other/course'))
|
||||
self.assertEqual(404, response.status_code)
|
||||
|
||||
def test_get_license_from_non_ajax(self):
|
||||
response = self.client.post(reverse('user_software_license'),
|
||||
{'software': SOFTWARE_1, 'generate': 'false'},
|
||||
HTTP_REFERER='/courses/{0}/some_page'.format(COURSE_1))
|
||||
self.assertEqual(404, response.status_code)
|
||||
|
||||
def test_get_license_without_software(self):
|
||||
response = self.client.post(reverse('user_software_license'),
|
||||
{'generate': 'false'},
|
||||
HTTP_X_REQUESTED_WITH='XMLHttpRequest',
|
||||
HTTP_REFERER='/courses/{0}/some_page'.format(COURSE_1))
|
||||
self.assertEqual(404, response.status_code)
|
||||
|
||||
def test_get_license_without_login(self):
|
||||
self.logout()
|
||||
response = self.client.post(reverse('user_software_license'),
|
||||
{'software': SOFTWARE_1, 'generate': 'false'},
|
||||
HTTP_X_REQUESTED_WITH='XMLHttpRequest',
|
||||
HTTP_REFERER='/courses/{0}/some_page'.format(COURSE_1))
|
||||
# if we're not logged in, we should be referred to the login page
|
||||
self.assertEqual(302, response.status_code)
|
||||
|
||||
|
||||
class CommandTest(TestCase):
|
||||
'''Test management command for importing serial numbers'''
|
||||
|
||||
def test_import_serial_numbers(self):
|
||||
size = 20
|
||||
|
||||
@@ -51,31 +167,33 @@ class CommandTest(TestCase):
|
||||
licenses_count = UserLicense.objects.all().count()
|
||||
self.assertEqual(3 * size, licenses_count)
|
||||
|
||||
cs = CourseSoftware.objects.get(pk=1)
|
||||
software = CourseSoftware.objects.get(pk=1)
|
||||
|
||||
lics = UserLicense.objects.filter(software=cs)[:size]
|
||||
lics = UserLicense.objects.filter(software=software)[:size]
|
||||
known_serials = list(l.serial for l in lics)
|
||||
known_serials.extend(generate_serials(10))
|
||||
|
||||
shuffle(known_serials)
|
||||
|
||||
log.debug('Adding some new and old serials to {0}'.format(SOFTWARE_1))
|
||||
with NamedTemporaryFile() as f:
|
||||
f.write('\n'.join(known_serials))
|
||||
f.flush()
|
||||
args = [COURSE_1, SOFTWARE_1, f.name]
|
||||
with NamedTemporaryFile() as tmpfile:
|
||||
tmpfile.write('\n'.join(known_serials))
|
||||
tmpfile.flush()
|
||||
args = [COURSE_1, SOFTWARE_1, tmpfile.name]
|
||||
call_command('import_serial_numbers', *args)
|
||||
|
||||
log.debug('Check if we added only the new ones')
|
||||
licenses_count = UserLicense.objects.filter(software=cs).count()
|
||||
licenses_count = UserLicense.objects.filter(software=software).count()
|
||||
self.assertEqual((2 * size) + 10, licenses_count)
|
||||
|
||||
|
||||
def generate_serials(size=20):
|
||||
'''generate a list of serial numbers'''
|
||||
return [str(uuid4()) for _ in range(size)]
|
||||
|
||||
|
||||
def generate_serials_file(size=20):
|
||||
'''output list of generated serial numbers to a temp file'''
|
||||
serials = generate_serials(size)
|
||||
|
||||
temp_file = NamedTemporaryFile()
|
||||
|
||||
@@ -7,12 +7,13 @@ from collections import namedtuple, defaultdict
|
||||
|
||||
from mitxmako.shortcuts import render_to_string
|
||||
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib.auth.models import User
|
||||
from django.http import HttpResponse, Http404
|
||||
from django.views.decorators.csrf import requires_csrf_token, csrf_protect
|
||||
from django.views.decorators.csrf import requires_csrf_token
|
||||
|
||||
from .models import CourseSoftware
|
||||
from .models import get_courses_licenses, get_or_create_license, get_license
|
||||
from licenses.models import CourseSoftware
|
||||
from licenses.models import get_courses_licenses, get_or_create_license, get_license
|
||||
|
||||
|
||||
log = logging.getLogger("mitx.licenses")
|
||||
@@ -44,6 +45,7 @@ def get_licenses_by_course(user, courses):
|
||||
return data_by_course
|
||||
|
||||
|
||||
@login_required
|
||||
@requires_csrf_token
|
||||
def user_software_license(request):
|
||||
if request.method != 'POST' or not request.is_ajax():
|
||||
@@ -65,19 +67,21 @@ def user_software_license(request):
|
||||
try:
|
||||
software = CourseSoftware.objects.get(name=software_name,
|
||||
course_id=course_id)
|
||||
print software
|
||||
except CourseSoftware.DoesNotExist:
|
||||
raise Http404
|
||||
|
||||
user = User.objects.get(id=user_id)
|
||||
try:
|
||||
user = User.objects.get(id=user_id)
|
||||
except User.DoesNotExist:
|
||||
raise Http404
|
||||
|
||||
if generate:
|
||||
license = get_or_create_license(user, software)
|
||||
software_license = get_or_create_license(user, software)
|
||||
else:
|
||||
license = get_license(user, software)
|
||||
software_license = get_license(user, software)
|
||||
|
||||
if license:
|
||||
response = {'serial': license.serial}
|
||||
if software_license:
|
||||
response = {'serial': software_license.serial}
|
||||
else:
|
||||
response = {'error': 'No serial number found'}
|
||||
|
||||
|
||||
@@ -4,22 +4,22 @@ Tests for open ended grading interfaces
|
||||
django-admin.py test --settings=lms.envs.test --pythonpath=. lms/djangoapps/open_ended_grading
|
||||
"""
|
||||
|
||||
from django.test import TestCase
|
||||
from open_ended_grading import staff_grading_service
|
||||
from xmodule.open_ended_grading_classes import peer_grading_service
|
||||
from xmodule import peer_grading_module
|
||||
import json
|
||||
from mock import MagicMock
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.contrib.auth.models import Group
|
||||
from mitxmako.shortcuts import render_to_string
|
||||
|
||||
from courseware.access import _course_staff_group_name
|
||||
import courseware.tests.tests as ct
|
||||
from xmodule.open_ended_grading_classes import peer_grading_service
|
||||
from xmodule import peer_grading_module
|
||||
from xmodule.modulestore.django import modulestore
|
||||
import xmodule.modulestore.django
|
||||
from nose import SkipTest
|
||||
from mock import patch, Mock, MagicMock
|
||||
import json
|
||||
from xmodule.x_module import ModuleSystem
|
||||
from mitxmako.shortcuts import render_to_string
|
||||
|
||||
from open_ended_grading import staff_grading_service
|
||||
from courseware.access import _course_staff_group_name
|
||||
from courseware.tests.tests import LoginEnrollmentTestCase, TEST_DATA_XML_MODULESTORE, get_user
|
||||
|
||||
import logging
|
||||
|
||||
@@ -30,8 +30,8 @@ from django.http import QueryDict
|
||||
from xmodule.tests import test_util_open_ended
|
||||
|
||||
|
||||
@override_settings(MODULESTORE=ct.TEST_DATA_XML_MODULESTORE)
|
||||
class TestStaffGradingService(ct.PageLoader):
|
||||
@override_settings(MODULESTORE=TEST_DATA_XML_MODULESTORE)
|
||||
class TestStaffGradingService(LoginEnrollmentTestCase):
|
||||
'''
|
||||
Check that staff grading service proxy works. Basically just checking the
|
||||
access control and error handling logic -- all the actual work is on the
|
||||
@@ -56,7 +56,7 @@ class TestStaffGradingService(ct.PageLoader):
|
||||
def make_instructor(course):
|
||||
group_name = _course_staff_group_name(course.location)
|
||||
g = Group.objects.create(name=group_name)
|
||||
g.user_set.add(ct.user(self.instructor))
|
||||
g.user_set.add(get_user(self.instructor))
|
||||
|
||||
make_instructor(self.toy)
|
||||
|
||||
@@ -126,8 +126,8 @@ class TestStaffGradingService(ct.PageLoader):
|
||||
self.assertIsNotNone(d['problem_list'])
|
||||
|
||||
|
||||
@override_settings(MODULESTORE=ct.TEST_DATA_XML_MODULESTORE)
|
||||
class TestPeerGradingService(ct.PageLoader):
|
||||
@override_settings(MODULESTORE=TEST_DATA_XML_MODULESTORE)
|
||||
class TestPeerGradingService(LoginEnrollmentTestCase):
|
||||
'''
|
||||
Check that staff grading service proxy works. Basically just checking the
|
||||
access control and error handling logic -- all the actual work is on the
|
||||
|
||||
Reference in New Issue
Block a user