diff --git a/common/lib/xmodule/xmodule/modulestore/tests/factories.py b/common/lib/xmodule/xmodule/modulestore/tests/factories.py index 247122fa31..b91e9be700 100644 --- a/common/lib/xmodule/xmodule/modulestore/tests/factories.py +++ b/common/lib/xmodule/xmodule/modulestore/tests/factories.py @@ -1,12 +1,13 @@ from factory import Factory, lazy_attribute_sequence, lazy_attribute from uuid import uuid4 +import datetime + from xmodule.modulestore import Location from xmodule.modulestore.django import modulestore from xmodule.modulestore.inheritance import own_metadata from xmodule.x_module import ModuleSystem from mitxmako.shortcuts import render_to_string from xblock.runtime import InvalidScopeError -import datetime from pytz import UTC @@ -149,6 +150,8 @@ class XModuleItemFactory(Factory): if new_item.location.category not in DETACHED_CATEGORIES: store.update_children(parent_location, parent.children + [new_item.location.url()]) + new_item = store.get_item(new_item.location) + return new_item diff --git a/lms/djangoapps/courseware/access.py b/lms/djangoapps/courseware/access.py index 07987a8edf..9e6a371552 100644 --- a/lms/djangoapps/courseware/access.py +++ b/lms/djangoapps/courseware/access.py @@ -245,7 +245,8 @@ 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) - if now > effective_start: + difference = (now - effective_start).total_seconds() + if difference > 3600: # after start date, everyone can see it debug("Allow: now > effective start date") return True @@ -508,6 +509,7 @@ def _adjust_start_date_for_beta_testers(user, descriptor): start_as_datetime = descriptor.lms.start delta = timedelta(descriptor.lms.days_early_for_beta) effective = start_as_datetime - delta + # ...and back to time_struct return effective @@ -570,7 +572,6 @@ def _has_access_to_location(user, location, access_level, course_context): debug("Deny: user not in groups %s", instructor_groups) else: log.debug("Error in access._has_access_to_location access_level=%s unknown" % access_level) - return False diff --git a/lms/djangoapps/courseware/tests/test_navigation.py b/lms/djangoapps/courseware/tests/test_navigation.py index 7d7406f30c..242379d8ca 100644 --- a/lms/djangoapps/courseware/tests/test_navigation.py +++ b/lms/djangoapps/courseware/tests/test_navigation.py @@ -80,7 +80,6 @@ def xml_store_config(data_dir): 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): diff --git a/lms/djangoapps/courseware/tests/test_view_authentication.py b/lms/djangoapps/courseware/tests/test_view_authentication.py index e739c25c93..da4f40e0db 100644 --- a/lms/djangoapps/courseware/tests/test_view_authentication.py +++ b/lms/djangoapps/courseware/tests/test_view_authentication.py @@ -1,5 +1,4 @@ import logging -import time import datetime import pytz import random @@ -80,10 +79,14 @@ class TestViewAuth(MongoLoginHelpers): def setUp(self): xmodule.modulestore.django._MODULESTORES = {} - self.full = CourseFactory.create(display_name='Robot_Sub_Course') + self.full = CourseFactory.create(number='666', display_name='Robot_Sub_Course') self.course = CourseFactory.create() - self.overview_chapter = ItemFactory.create(display_name='Overview') + self.courseware_chapter = ItemFactory.create(display_name='courseware') + self.sub_courseware_chapter = ItemFactory.create(parent_location=self.full.location, + 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, @@ -121,6 +124,7 @@ class TestViewAuth(MongoLoginHelpers): # 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, @@ -210,8 +214,8 @@ class TestViewAuth(MongoLoginHelpers): # Make courses start in the future now = datetime.datetime.now(pytz.UTC) tomorrow = now + datetime.timedelta(days=1) - self.course.start = tomorrow - self.full.start = tomorrow + self.course.lms.start = tomorrow + self.full.lms.start = tomorrow self.assertFalse(self.course.has_started()) self.assertFalse(self.full.has_started()) @@ -344,7 +348,6 @@ class TestViewAuth(MongoLoginHelpers): print "changing" # self.course's enrollment period hasn't started - print self.course.enrollment_start self.course = update_course(self.course, course_data) # full course's has self.full = update_course(self.full, full_data) @@ -391,7 +394,7 @@ class TestViewAuth(MongoLoginHelpers): # nextday = tomorrow + 24 * 3600 # yesterday = time.time() - 24 * 3600 - # toy course's hasn't started + # self.course's hasn't started self.course.lms.start = tomorrow self.assertFalse(self.course.has_started()) diff --git a/lms/djangoapps/courseware/tests/tests.py b/lms/djangoapps/courseware/tests/tests.py index c6e5a5f5b7..d7883d88d3 100644 --- a/lms/djangoapps/courseware/tests/tests.py +++ b/lms/djangoapps/courseware/tests/tests.py @@ -272,144 +272,6 @@ class LoginEnrollmentTestCase(TestCase): 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) - - -@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_viewtest_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) class TestDraftModuleStore(TestCase): def test_get_items_with_course_items(self): @@ -424,327 +286,6 @@ class TestDraftModuleStore(TestCase): # not allowed to be passed in (i.e. was throwing exception) -@override_settings(MODULESTORE=TEST_DATA_XML_MODULESTORE) -class TestViewAuth(LoginEnrollmentTestCase): - """Check that view authentication works properly""" - - def setUp(self): - xmodule.modulestore.django._MODULESTORES = {} - - self.full = modulestore().get_course("edX/full/6.002_Spring_2012") - self.toy = modulestore().get_course("edX/toy/2012_Fall") - - # 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) - - 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""" - - # 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])) - 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'})) - - 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.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) - group = Group.objects.create(name=group_name) - group.user_set.add(get_user(self.instructor)) - - self.logout() - self.login(self.instructor, self.password) - - # Now should be able to get to the toy course, but not the full course - url = random.choice(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.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) - instructor.is_staff = True - instructor.save() - - # and now should be able to load both - 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): - """ - test.py turns off start dates. Enable them. - Because settings is global, be careful not to mess it up for other tests - (Can't use override_settings because we're only changing part of the - MITX_FEATURES dict) - """ - oldDSD = settings.MITX_FEATURES['DISABLE_START_DATES'] - - try: - settings.MITX_FEATURES['DISABLE_START_DATES'] = False - test() - finally: - 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""" - self.run_wrapped(self._do_test_dark_launch) - - def test_enrollment_period(self): - """Check that enrollment periods work""" - self.run_wrapped(self._do_test_enrollment_period) - - def test_beta_period(self): - """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.""" - - # Make courses start in the future - tomorrow = datetime.datetime.now(UTC()) + datetime.timedelta(days=1) - self.toy.lms.start = tomorrow - self.full.lms.start = tomorrow - - self.assertFalse(self.toy.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 - after launch, but staff should see before - """ - urls = reverse_urls(['info', 'progress'], course) - urls.extend([ - reverse('book', kwargs={'course_id': course.id, - 'book_index': index}) - for index, book in enumerate(course.textbooks) - ]) - return urls - - def light_student_urls(course): - """ - list of urls that students should be able to see before - launch. - """ - urls = 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) - 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) - - # 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) - - # 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. - # 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}) - print 'checking for 404 on view-as-student: {0}'.format(url) - self.check_for_get_code(404, url) - - # The courseware url should redirect, not 200 - url = reverse_urls(['courseware'], course)[0] - self.check_for_get_code(302, url) - - # First, try with an enrolled student - print '=== Testing student access....' - self.login(self.student, self.password) - self.enroll(self.toy) - self.enroll(self.full) - - # shouldn't be able to get to anything except the light pages - check_non_staff(self.toy) - check_non_staff(self.full) - - print '=== Testing course instructor access....' - # Make the instructor staff in the toy course - group_name = _course_staff_group_name(self.toy.location) - group = Group.objects.create(name=group_name) - group.user_set.add(get_user(self.instructor)) - - self.logout() - self.login(self.instructor, self.password) - # Enroll in the classes---can't see courseware otherwise. - self.enroll(self.toy) - self.enroll(self.full) - - # should now be able to get to everything for toy course - check_non_staff(self.full) - check_staff(self.toy) - - print '=== Testing staff access....' - # now also make the instructor staff - instructor = get_user(self.instructor) - instructor.is_staff = True - instructor.save() - - # and now should be able to load both - check_staff(self.toy) - check_staff(self.full) - - def _do_test_enrollment_period(self): - """Actually do the test, relying on settings to be right.""" - - # Make courses start in the future - tomorrow = datetime.datetime.now(UTC()) + datetime.timedelta(days=1) - nextday = tomorrow + datetime.timedelta(days=1) - yesterday = datetime.datetime.now(UTC()) - datetime.timedelta(days=1) - - print "changing" - # toy course's enrollment period hasn't started - self.toy.enrollment_start = tomorrow - self.toy.enrollment_end = nextday - - # full course's has - self.full.enrollment_start = yesterday - self.full.enrollment_end = tomorrow - - print "login" - # First, try with an enrolled student - print '=== Testing student access....' - self.login(self.student, self.password) - self.assertFalse(self.try_enroll(self.toy)) - self.assertTrue(self.try_enroll(self.full)) - - print '=== Testing course instructor access....' - # Make the instructor staff in the toy course - group_name = _course_staff_group_name(self.toy.location) - group = Group.objects.create(name=group_name) - group.user_set.add(get_user(self.instructor)) - - 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.toy)) - - 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) - instructor.is_staff = True - instructor.save() - - # unenroll and try again - self.unenroll(self.toy) - self.assertTrue(self.try_enroll(self.toy)) - - def _do_test_beta_period(self): - """Actually test beta periods, relying on settings to be right.""" - - # trust, but verify :) - self.assertFalse(settings.MITX_FEATURES['DISABLE_START_DATES']) - - # Make courses start in the future - tomorrow = datetime.datetime.now(UTC()) + datetime.timedelta(days=1) - - # toy course's hasn't started - self.toy.lms.start = tomorrow - self.assertFalse(self.toy.has_started()) - - # but should be accessible for beta testers - self.toy.lms.days_early_for_beta = 2 - - # student user shouldn't see it - 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) - 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 TestSubmittingProblems(LoginEnrollmentTestCase): """Check that a course gets graded properly"""