diff --git a/lms/djangoapps/courseware/courses.py b/lms/djangoapps/courseware/courses.py index c74b0f9e6f01..6bdbe3dc1eb6 100644 --- a/lms/djangoapps/courseware/courses.py +++ b/lms/djangoapps/courseware/courses.py @@ -185,8 +185,14 @@ def get_course_about_section(course, section_key): html = '' if about_module is not None: - html = about_module.render('student_view').content - + try: + html = about_module.render('student_view').content + except Exception: # pylint: disable=broad-except + html = "Error message" + log.exception("Error rendering course={course}, section_key={section_key}".format( + course=course, + section_key=section_key + )) return html except ItemNotFoundError: @@ -231,7 +237,13 @@ def get_course_info_section(request, course, section_key): html = '' if info_module is not None: - html = info_module.render('student_view').content + try: + html = info_module.render('student_view').content + except Exception: # pylint: disable=broad-except + log.exception("Error rendering course={course}, section_key={section_key}".format( + course=course, + section_key=section_key + )) return html diff --git a/lms/djangoapps/courseware/tabs.py b/lms/djangoapps/courseware/tabs.py index 7bc1c0473fb3..449db7d70ad3 100644 --- a/lms/djangoapps/courseware/tabs.py +++ b/lms/djangoapps/courseware/tabs.py @@ -409,8 +409,8 @@ def get_static_tab_by_slug(course, tab_slug): def get_static_tab_contents(request, course, tab): loc = Location(course.location.tag, course.location.org, course.location.course, 'static_tab', tab['url_slug']) - field_data_cache = FieldDataCache.cache_for_descriptor_descendents(course.id, - request.user, modulestore().get_instance(course.id, loc), depth=0) + field_data_cache = FieldDataCache.cache_for_descriptor_descendents( + course.id, request.user, modulestore().get_instance(course.id, loc), depth=0) tab_module = get_module(request.user, request, loc, field_data_cache, course.id, static_asset_path=course.static_asset_path) @@ -419,6 +419,13 @@ def get_static_tab_contents(request, course, tab): html = '' if tab_module is not None: - html = tab_module.render('student_view').content + try: + html = tab_module.render('student_view').content + except Exception: # pylint: disable=broad-except + html = "Error message" + log.exception("Error rendering course={course}, tab={tab_url}".format( + course=course, + tab_url=tab['url_slug'] + )) return html diff --git a/lms/djangoapps/courseware/tests/helpers.py b/lms/djangoapps/courseware/tests/helpers.py index 6890a6df2a48..53eb4499a10d 100644 --- a/lms/djangoapps/courseware/tests/helpers.py +++ b/lms/djangoapps/courseware/tests/helpers.py @@ -2,6 +2,7 @@ from django.contrib.auth.models import User from django.core.urlresolvers import reverse +from django.test.client import RequestFactory from student.models import Registration @@ -9,38 +10,49 @@ def check_for_get_code(self, code, url): - """ - Check that we got the expected code when accessing url via GET. - Returns the HTTP response. + """ + Check that we got the expected code when accessing url via GET. + Returns the HTTP response. - `self` is a class that subclasses TestCase. + `self` is a class that subclasses TestCase. - `code` is a status code for HTTP responses. + `code` is a status code for HTTP responses. - `url` is a url pattern for which we have to test the response. - """ - resp = self.client.get(url) - self.assertEqual(resp.status_code, code, - "got code %d for url '%s'. Expected code %d" - % (resp.status_code, url, code)) - return resp + `url` is a url pattern for which we have to test the response. + """ + resp = self.client.get(url) + self.assertEqual(resp.status_code, code, + "got code %d for url '%s'. Expected code %d" + % (resp.status_code, url, code)) + return resp def check_for_post_code(self, code, url, data={}): - """ - Check that we got the expected code when accessing url via POST. - Returns the HTTP response. - `self` is a class that subclasses TestCase. + """ + Check that we got the expected code when accessing url via POST. + Returns the HTTP response. + `self` is a class that subclasses TestCase. - `code` is a status code for HTTP responses. + `code` is a status code for HTTP responses. - `url` is a url pattern for which we want to test the response. - """ - resp = self.client.post(url, data) - self.assertEqual(resp.status_code, code, - "got code %d for url '%s'. Expected code %d" - % (resp.status_code, url, code)) - return resp + `url` is a url pattern for which we want to test the response. + """ + resp = self.client.post(url, data) + self.assertEqual(resp.status_code, code, + "got code %d for url '%s'. Expected code %d" + % (resp.status_code, url, code)) + return resp + + +def get_request_for_user(user): + """Create a request object for user.""" + + request = RequestFactory() + request.user = user + request.META = {} + request.is_secure = lambda: True + request.get_host = lambda: "edx.org" + return request class LoginEnrollmentTestCase(TestCase): diff --git a/lms/djangoapps/courseware/tests/test_courses.py b/lms/djangoapps/courseware/tests/test_courses.py index 8dcab808a351..ee721a3e2c48 100644 --- a/lms/djangoapps/courseware/tests/test_courses.py +++ b/lms/djangoapps/courseware/tests/test_courses.py @@ -7,11 +7,12 @@ from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from django.http import Http404 from django.test.utils import override_settings -from courseware.courses import get_course_by_id, get_course, get_cms_course_link +from courseware.courses import get_course_by_id, get_course, get_cms_course_link, get_course_info_section, get_course_about_section +from student.tests.factories import UserFactory from xmodule.modulestore.django import get_default_store_name_for_current_request from xmodule.modulestore.tests.factories import CourseFactory -from courseware.tests.tests import TEST_DATA_MONGO_MODULESTORE - +from courseware.tests.helpers import get_request_for_user +from courseware.tests.tests import TEST_DATA_MONGO_MODULESTORE, TEST_DATA_MIXED_MODULESTORE CMS_BASE_TEST = 'testcms' @@ -79,3 +80,43 @@ def test_default_modulestore_preview_mapping(self): ) def test_default_modulestore_published_mapping(self): self.assertEqual(get_default_store_name_for_current_request(), 'default') + + +class CoursesRenderTest(ModuleStoreTestCase): + """Test methods related to rendering courses content.""" + + @override_settings(MODULESTORE=TEST_DATA_MIXED_MODULESTORE) + def test_get_course_info_section_render(self): + course = get_course_by_id('edX/toy/2012_Fall') + request = get_request_for_user(UserFactory.create()) + + # Test render works okay + course_info = get_course_info_section(request, course, 'handouts') + self.assertEqual(course_info, "Sample") + + # Test when render raises an exception + with mock.patch('courseware.courses.get_module') as mock_module_render: + mock_module_render.return_value = mock.MagicMock( + render=mock.Mock(side_effect=Exception('Render failed!')) + ) + course_info = get_course_info_section(request, course, 'handouts') + self.assertIsInstance(course_info, basestring) + + @override_settings(MODULESTORE=TEST_DATA_MIXED_MODULESTORE) + @mock.patch('courseware.courses.get_request_for_thread') + def test_get_course_about_section_render(self, mock_get_request): + course = get_course_by_id('edX/toy/2012_Fall') + request = get_request_for_user(UserFactory.create()) + mock_get_request.return_value = request + + # Test render works okay + course_info = get_course_about_section(course, 'effort') + self.assertEqual(course_info, "6 hours") + + # Test when render raises an exception + with mock.patch('courseware.courses.get_module') as mock_module_render: + mock_module_render.return_value = mock.MagicMock( + render=mock.Mock(side_effect=Exception('Render failed!')) + ) + course_info = get_course_about_section(course, 'effort') + self.assertIsInstance(course_info, basestring) diff --git a/lms/djangoapps/courseware/tests/test_tabs.py b/lms/djangoapps/courseware/tests/test_tabs.py index 94ebe611f353..9276f944d27a 100644 --- a/lms/djangoapps/courseware/tests/test_tabs.py +++ b/lms/djangoapps/courseware/tests/test_tabs.py @@ -1,21 +1,26 @@ from django.test import TestCase -from mock import MagicMock -from mock import patch +from mock import MagicMock, Mock, patch -import courseware.tabs as tabs +from courseware import tabs +from courseware.courses import get_course_by_id from django.test.utils import override_settings from django.core.urlresolvers import reverse +from student.tests.factories import UserFactory from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory +from courseware.tests.helpers import get_request_for_user from courseware.tests.modulestore_config import TEST_DATA_MIXED_MODULESTORE + FAKE_REQUEST = None + def tab_constructor(active_page, course, user, tab={'name': 'same'}, generator=tabs._progress): return generator(tab, user, course, active_page, FAKE_REQUEST) + class ProgressTestCase(TestCase): def setUp(self): @@ -112,7 +117,7 @@ def test_external_link(self): self.assertEqual(tab_list[0].is_active, False) -class StaticTabTestCase(TestCase): +class StaticTabTestCase(ModuleStoreTestCase): def setUp(self): @@ -133,7 +138,7 @@ def test_static_tab(self): tab_list = tab_constructor( self.schmug, self.course, self.user, tab=self.tabby, generator=tabs._static_tab ) - expected_link = reverse('static_tab', args=[self.course.id,self.tabby['url_slug']]) + expected_link = reverse('static_tab', args=[self.course.id, self.tabby['url_slug']]) self.assertEqual(tab_list[0].link, expected_link) tab_list = tab_constructor( @@ -146,6 +151,26 @@ def test_static_tab(self): ) self.assertEqual(tab_list[0].is_active, False) + @override_settings(MODULESTORE=TEST_DATA_MIXED_MODULESTORE) + def test_get_static_tab_contents(self): + course = get_course_by_id('edX/toy/2012_Fall') + request = get_request_for_user(UserFactory.create()) + tab = tabs.get_static_tab_by_slug(course, 'resources') + + # Test render works okay + tab_content = tabs.get_static_tab_contents(request, course, tab) + self.assertIn('edX/toy/2012_Fall', tab_content) + self.assertIn('static_tab', tab_content) + + # Test when render raises an exception + with patch('courseware.tabs.get_module') as mock_module_render: + mock_module_render.return_value = MagicMock( + render=Mock(side_effect=Exception('Render failed!')) + ) + course_info = tabs.get_static_tab_contents(request, course, tab) + self.assertIsInstance(course_info, basestring) + + class TextbooksTestCase(TestCase): def setUp(self):