Features coming down the pipe will want to be able to: * Refer to enrollments before they are actually activated (approval step). * See what courses a user used to be enrolled in for when they re-enroll in the same course, or a different run of that course. * Have different "modes" of enrolling in a course, representing things like honor certificate enrollment, auditing (no certs), etc. This change adds an is_active flag and mode (with default being "honor"). The commit is only as large as it is because many parts of the codebase were manipulating enrollments by adding and removing CourseEnrollment objects directly. It was necessary to create classmethods on CourseEnrollment to encapsulate this functionality and then port everything over to using them. The migration to add columns has been tested on a prod replica, and seems to be fine for running on a live system with single digit millions of rows of enrollments.
190 lines
7.7 KiB
Python
190 lines
7.7 KiB
Python
from mock import MagicMock
|
|
import datetime
|
|
|
|
from django.test import TestCase
|
|
from django.http import Http404
|
|
from django.test.utils import override_settings
|
|
from django.contrib.auth.models import User
|
|
from django.test.client import RequestFactory
|
|
|
|
from django.conf import settings
|
|
from django.core.urlresolvers import reverse
|
|
|
|
from student.models import CourseEnrollment
|
|
from student.tests.factories import AdminFactory
|
|
from xmodule.modulestore.django import modulestore
|
|
|
|
import courseware.views as views
|
|
from xmodule.modulestore import Location
|
|
from pytz import UTC
|
|
from modulestore_config import TEST_DATA_XML_MODULESTORE
|
|
|
|
|
|
class Stub():
|
|
pass
|
|
|
|
|
|
@override_settings(MODULESTORE=TEST_DATA_XML_MODULESTORE)
|
|
class TestJumpTo(TestCase):
|
|
"""Check the jumpto link for a course"""
|
|
def setUp(self):
|
|
self._MODULESTORES = {}
|
|
|
|
# Toy courses should be loaded
|
|
self.course_name = 'edX/toy/2012_Fall'
|
|
self.toy_course = modulestore().get_course('edX/toy/2012_Fall')
|
|
|
|
def test_jumpto_invalid_location(self):
|
|
location = Location('i4x', 'edX', 'toy', 'NoSuchPlace', None)
|
|
jumpto_url = '{0}/{1}/jump_to/{2}'.format('/courses', self.course_name, location)
|
|
response = self.client.get(jumpto_url)
|
|
self.assertEqual(response.status_code, 404)
|
|
|
|
def test_jumpto_from_chapter(self):
|
|
location = Location('i4x', 'edX', 'toy', 'chapter', 'Overview')
|
|
jumpto_url = '{0}/{1}/jump_to/{2}'.format('/courses', self.course_name, location)
|
|
expected = 'courses/edX/toy/2012_Fall/courseware/Overview/'
|
|
response = self.client.get(jumpto_url)
|
|
self.assertRedirects(response, expected, status_code=302, target_status_code=302)
|
|
|
|
def test_jumpto_id(self):
|
|
location = Location('i4x', 'edX', 'toy', 'chapter', 'Overview')
|
|
jumpto_url = '{0}/{1}/jump_to_id/{2}'.format('/courses', self.course_name, location.name)
|
|
expected = 'courses/edX/toy/2012_Fall/courseware/Overview/'
|
|
response = self.client.get(jumpto_url)
|
|
self.assertRedirects(response, expected, status_code=302, target_status_code=302)
|
|
|
|
def test_jumpto_id_invalid_location(self):
|
|
location = Location('i4x', 'edX', 'toy', 'NoSuchPlace', None)
|
|
jumpto_url = '{0}/{1}/jump_to_id/{2}'.format('/courses', self.course_name, location.name)
|
|
response = self.client.get(jumpto_url)
|
|
self.assertEqual(response.status_code, 404)
|
|
|
|
|
|
class ViewsTestCase(TestCase):
|
|
def setUp(self):
|
|
self.user = User.objects.create(username='dummy', password='123456',
|
|
email='test@mit.edu')
|
|
self.date = datetime.datetime(2013, 1, 22, tzinfo=UTC)
|
|
self.course_id = 'edX/toy/2012_Fall'
|
|
self.enrollment = CourseEnrollment.enroll(self.user, self.course_id)
|
|
self.enrollment.created = self.date
|
|
self.enrollment.save()
|
|
self.location = ['tag', 'org', 'course', 'category', 'name']
|
|
self._MODULESTORES = {}
|
|
# This is a CourseDescriptor object
|
|
self.toy_course = modulestore().get_course('edX/toy/2012_Fall')
|
|
self.request_factory = RequestFactory()
|
|
chapter = 'Overview'
|
|
self.chapter_url = '%s/%s/%s' % ('/courses', self.course_id, chapter)
|
|
|
|
def test_user_groups(self):
|
|
# depreciated function
|
|
mock_user = MagicMock()
|
|
mock_user.is_authenticated.return_value = False
|
|
self.assertEquals(views.user_groups(mock_user), [])
|
|
|
|
def test_get_current_child(self):
|
|
self.assertIsNone(views.get_current_child(Stub()))
|
|
mock_xmodule = MagicMock()
|
|
mock_xmodule.position = -1
|
|
mock_xmodule.get_display_items.return_value = ['one', 'two']
|
|
self.assertEquals(views.get_current_child(mock_xmodule), 'one')
|
|
mock_xmodule_2 = MagicMock()
|
|
mock_xmodule_2.position = 3
|
|
mock_xmodule_2.get_display_items.return_value = []
|
|
self.assertIsNone(views.get_current_child(mock_xmodule_2))
|
|
|
|
def test_redirect_to_course_position(self):
|
|
mock_module = MagicMock()
|
|
mock_module.descriptor.id = 'Underwater Basketweaving'
|
|
mock_module.position = 3
|
|
mock_module.get_display_items.return_value = []
|
|
self.assertRaises(Http404, views.redirect_to_course_position,
|
|
mock_module)
|
|
|
|
def test_registered_for_course(self):
|
|
self.assertFalse(views.registered_for_course('Basketweaving', None))
|
|
mock_user = MagicMock()
|
|
mock_user.is_authenticated.return_value = False
|
|
self.assertFalse(views.registered_for_course('dummy', mock_user))
|
|
mock_course = MagicMock()
|
|
mock_course.id = self.course_id
|
|
self.assertTrue(views.registered_for_course(mock_course, self.user))
|
|
|
|
def test_jump_to_invalid(self):
|
|
request = self.request_factory.get(self.chapter_url)
|
|
self.assertRaisesRegexp(Http404, 'Invalid location', views.jump_to,
|
|
request, 'bar', ())
|
|
self.assertRaisesRegexp(Http404, 'No data*', views.jump_to, request,
|
|
'dummy', self.location)
|
|
|
|
def test_no_end_on_about_page(self):
|
|
# Toy course has no course end date or about/end_date blob
|
|
self.verify_end_date('edX/toy/TT_2012_Fall')
|
|
|
|
def test_no_end_about_blob(self):
|
|
# test_end has a course end date, no end_date HTML blob
|
|
self.verify_end_date("edX/test_end/2012_Fall", "Sep 17, 2015")
|
|
|
|
def test_about_blob_end_date(self):
|
|
# test_about_blob_end_date has both a course end date and an end_date HTML blob.
|
|
# HTML blob wins
|
|
self.verify_end_date("edX/test_about_blob_end_date/2012_Fall", "Learning never ends")
|
|
|
|
def verify_end_date(self, course_id, expected_end_text=None):
|
|
request = self.request_factory.get("foo")
|
|
request.user = self.user
|
|
result = views.course_about(request, course_id)
|
|
if expected_end_text is not None:
|
|
self.assertContains(result, "Classes End")
|
|
self.assertContains(result, expected_end_text)
|
|
else:
|
|
self.assertNotContains(result, "Classes End")
|
|
|
|
def test_chat_settings(self):
|
|
mock_user = MagicMock()
|
|
mock_user.username = "johndoe"
|
|
|
|
mock_course = MagicMock()
|
|
mock_course.id = "a/b/c"
|
|
|
|
# Stub this out in the case that it's not in the settings
|
|
domain = "jabber.edx.org"
|
|
settings.JABBER_DOMAIN = domain
|
|
|
|
chat_settings = views.chat_settings(mock_course, mock_user)
|
|
|
|
# Test the proper format of all chat settings
|
|
self.assertEquals(chat_settings['domain'], domain)
|
|
self.assertEquals(chat_settings['room'], "a-b-c_class")
|
|
self.assertEquals(chat_settings['username'], "johndoe@%s" % domain)
|
|
|
|
# TODO: this needs to be changed once we figure out how to
|
|
# generate/store a real password.
|
|
self.assertEquals(chat_settings['password'], "johndoe@%s" % domain)
|
|
|
|
def test_submission_history_xss(self):
|
|
# log into a staff account
|
|
admin = AdminFactory()
|
|
|
|
self.client.login(username=admin.username, password='test')
|
|
|
|
# try it with an existing user and a malicious location
|
|
url = reverse('submission_history', kwargs={
|
|
'course_id': self.course_id,
|
|
'student_username': 'dummy',
|
|
'location': '<script>alert("hello");</script>'
|
|
})
|
|
response = self.client.get(url)
|
|
self.assertFalse('<script>' in response.content)
|
|
|
|
# try it with a malicious user and a non-existent location
|
|
url = reverse('submission_history', kwargs={
|
|
'course_id': self.course_id,
|
|
'student_username': '<script>alert("hello");</script>',
|
|
'location': 'dummy'
|
|
})
|
|
response = self.client.get(url)
|
|
self.assertFalse('<script>' in response.content)
|