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