From d8464dbfb04d35980dc6216a5d710c18ede79fd4 Mon Sep 17 00:00:00 2001 From: Ayub-khan Date: Thu, 14 Apr 2016 15:57:39 +0500 Subject: [PATCH 1/5] -changed method name from "from_string_or_404" to "course_key_from_string_or_404". -Updated method "course_key_from_string_or_404" to raise message too. -Wrote tests for "course_key_from_string_or_404" when exception message is given. -Modified existing methods to use "ddt.data". -Used Splunk logs to find exectly where we were getting "Invalid Key Error" -Updated Views where we were getting "Invalid Key Error" in splunk logs. -Wrote tests for those View End points where we were getting "Invalid Key Error" --- cms/djangoapps/contentstore/views/tabs.py | 5 +- .../contentstore/views/tests/test_tabs.py | 8 +++ .../contentstore/views/tests/test_user.py | 10 ++++ cms/djangoapps/contentstore/views/user.py | 4 +- common/djangoapps/util/course_key_utils.py | 9 +-- .../util/tests/test_course_key_utils.py | 60 ++++++++++++------- .../django_comment_client/forum/tests.py | 8 +++ .../django_comment_client/forum/views.py | 4 +- 8 files changed, 75 insertions(+), 33 deletions(-) diff --git a/cms/djangoapps/contentstore/views/tabs.py b/cms/djangoapps/contentstore/views/tabs.py index f79d4d0721..38d1c6a228 100644 --- a/cms/djangoapps/contentstore/views/tabs.py +++ b/cms/djangoapps/contentstore/views/tabs.py @@ -14,7 +14,8 @@ from edxmako.shortcuts import render_to_response from xmodule.modulestore.django import modulestore from xmodule.modulestore import ModuleStoreEnum from xmodule.tabs import CourseTabList, CourseTab, InvalidTabsException, StaticTab -from opaque_keys.edx.keys import CourseKey, UsageKey +from opaque_keys.edx.keys import UsageKey +from util.course_key_utils import course_key_from_string_or_404 from ..utils import get_lms_link_for_item @@ -39,7 +40,7 @@ def tabs_handler(request, course_key_string): Creating a tab, deleting a tab, or changing its contents is not supported through this method. Instead use the general xblock URL (see item.xblock_handler). """ - course_key = CourseKey.from_string(course_key_string) + course_key = course_key_from_string_or_404(course_key_string) if not has_course_author_access(request.user, course_key): raise PermissionDenied() diff --git a/cms/djangoapps/contentstore/views/tests/test_tabs.py b/cms/djangoapps/contentstore/views/tests/test_tabs.py index fde68931a3..d79630b845 100644 --- a/cms/djangoapps/contentstore/views/tests/test_tabs.py +++ b/cms/djangoapps/contentstore/views/tests/test_tabs.py @@ -10,6 +10,7 @@ from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.tabs import CourseTabList from xmodule.modulestore.django import modulestore +from django.http import Http404 class TabsPageTests(CourseTestCase): @@ -191,6 +192,13 @@ class TabsPageTests(CourseTestCase): self.assertIn('Delete this component', html) self.assertIn('', html) + def test_invalid_course_id(self): + """ Asserts that Http404 is raised when the course id is not valid. """ + invalid_tab_url = reverse_course_url('tabs_handler', "/some.invalid.key/TTT/CS01/2015_T0") + with self.assertRaises(Http404): + self.client.get(invalid_tab_url) + + class PrimitiveTabEdit(ModuleStoreTestCase): """Tests for the primitive tab edit data manipulations""" diff --git a/cms/djangoapps/contentstore/views/tests/test_user.py b/cms/djangoapps/contentstore/views/tests/test_user.py index 725858e44d..8c26ff4515 100644 --- a/cms/djangoapps/contentstore/views/tests/test_user.py +++ b/cms/djangoapps/contentstore/views/tests/test_user.py @@ -9,6 +9,7 @@ from django.contrib.auth.models import User from student.models import CourseEnrollment from student.roles import CourseStaffRole, CourseInstructorRole from student import auth +from django.http import Http404 class UsersTestCase(CourseTestCase): @@ -315,3 +316,12 @@ class UsersTestCase(CourseTestCase): CourseEnrollment.is_enrolled(self.ext_user, self.course.id), 'User ext_user should have been enrolled in the course' ) + + def test_invalid_course_id(self): + """ Asserts that Http404 is raised when the course id is not valid. """ + wrong_url = reverse_course_url( + 'course_team_handler', "/some.invalid.key/TTT/CS01/2015_T0", + kwargs={'email': self.ext_user.email} + ) + with self.assertRaises(Http404): + self.client.get(wrong_url) diff --git a/cms/djangoapps/contentstore/views/user.py b/cms/djangoapps/contentstore/views/user.py index e5da0a8194..79208a535d 100644 --- a/cms/djangoapps/contentstore/views/user.py +++ b/cms/djangoapps/contentstore/views/user.py @@ -8,7 +8,7 @@ from django.views.decorators.csrf import ensure_csrf_cookie from edxmako.shortcuts import render_to_response from xmodule.modulestore.django import modulestore -from opaque_keys.edx.keys import CourseKey +from util.course_key_utils import course_key_from_string_or_404 from opaque_keys.edx.locator import LibraryLocator from util.json_request import JsonResponse, expect_json from student.roles import CourseInstructorRole, CourseStaffRole, LibraryUserRole @@ -49,7 +49,7 @@ def course_team_handler(request, course_key_string=None, email=None): DELETE: json: remove a particular course team member from the course team (email is required). """ - course_key = CourseKey.from_string(course_key_string) if course_key_string else None + course_key = course_key_from_string_or_404(course_key_string) if course_key_string else None # No permissions check here - each helper method does its own check. if 'application/json' in request.META.get('HTTP_ACCEPT', 'application/json'): diff --git a/common/djangoapps/util/course_key_utils.py b/common/djangoapps/util/course_key_utils.py index ab58a6558d..44328d7d78 100644 --- a/common/djangoapps/util/course_key_utils.py +++ b/common/djangoapps/util/course_key_utils.py @@ -6,14 +6,15 @@ from opaque_keys import InvalidKeyError from opaque_keys.edx.keys import CourseKey -def from_string_or_404(course_key_string): +def course_key_from_string_or_404(course_key_string, message=None): """ Gets CourseKey from the string passed as parameter. Parses course key from string(containing course key) or raises 404 if the string's format is invalid. Arguments: - course_key_string(str): It is string containing the course key + course_key_string(str): It contains the course key + message(str): It contains the exception message Returns: CourseKey: A key that uniquely identifies a course @@ -25,6 +26,6 @@ def from_string_or_404(course_key_string): try: course_key = CourseKey.from_string(course_key_string) except InvalidKeyError: - raise Http404 + raise Http404(message) - return course_key + return course_key \ No newline at end of file diff --git a/common/djangoapps/util/tests/test_course_key_utils.py b/common/djangoapps/util/tests/test_course_key_utils.py index efc88a09ee..709abac9a2 100644 --- a/common/djangoapps/util/tests/test_course_key_utils.py +++ b/common/djangoapps/util/tests/test_course_key_utils.py @@ -1,32 +1,46 @@ """ Tests for util.course_key_utils """ -from nose.tools import assert_equals, assert_raises # pylint: disable=no-name-in-module -from util.course_key_utils import from_string_or_404 +from util.course_key_utils import course_key_from_string_or_404 from opaque_keys.edx.keys import CourseKey from django.http import Http404 +import ddt +import unittest -def test_from_string_or_404(): +@ddt.ddt +class TestFromStringOr404(unittest.TestCase): + """ + Base Test class for course_key_from_string_or_404 utility tests + """ + @ddt.data( + ("/some.invalid.key/course-v1:TTT+CS01+2015_T0", "course-v1:TTT+CS01+2015_T0"), # split style course keys + ("/some.invalid.key/TTT/CS01/2015_T0", "TTT/CS01/2015_T0"), # mongo style course keys + ) + def test_from_string_or_404(self, (invalid_course_key, valid_course_key)): + """ + Tests course_key_from_string_or_404 for valid and invalid split style course keys and mongo style course keys. + """ + self.assertRaises( + Http404, + course_key_from_string_or_404, + invalid_course_key, + ) + self.assertEquals( + CourseKey.from_string(valid_course_key), + course_key_from_string_or_404(valid_course_key) + ) - #testing with split style course keys - assert_raises( - Http404, - from_string_or_404, - "/some.invalid.key/course-v1:TTT+CS01+2015_T0" - ) - assert_equals( - CourseKey.from_string("course-v1:TTT+CS01+2015_T0"), - from_string_or_404("course-v1:TTT+CS01+2015_T0") - ) - - #testing with mongo style course keys - assert_raises( - Http404, - from_string_or_404, - "/some.invalid.key/TTT/CS01/2015_T0" - ) - assert_equals( - CourseKey.from_string("TTT/CS01/2015_T0"), - from_string_or_404("TTT/CS01/2015_T0") + @ddt.data( + "/some.invalid.key/course-v1:TTT+CS01+2015_T0", # split style invalid course key + "/some.invalid.key/TTT/CS01/2015_T0" # mongo style invalid course key ) + def test_from_string_or_404_with_message(self, course_string): + """ + Tests course_key_from_string_or_404 with exception message for split style and monog style invalid course keys. + :return: + """ + try: + course_key_from_string_or_404(course_string, message="Invalid Keys") + except Http404 as exception: + self.assertEquals(str(exception), "Invalid Keys") \ No newline at end of file diff --git a/lms/djangoapps/django_comment_client/forum/tests.py b/lms/djangoapps/django_comment_client/forum/tests.py index 66323a1968..06399ea2c5 100644 --- a/lms/djangoapps/django_comment_client/forum/tests.py +++ b/lms/djangoapps/django_comment_client/forum/tests.py @@ -1312,6 +1312,14 @@ class InlineDiscussionUnicodeTestCase(SharedModuleStoreTestCase, UnicodeTestMixi self.assertEqual(response_data["discussion_data"][0]["title"], text) self.assertEqual(response_data["discussion_data"][0]["body"], text) + def _test_invalid_course_id(self): + """ Asserts that Http404 is raised when the course id is not valid. """ + request = RequestFactory().get("dummy_url") + request.user = self.student + with self.assertRaises(Http404): + views.inline_discussion( + request, self.course.id.to_deprecated_string(), self.course.discussion_topics['General']['id'] + ) class ForumFormDiscussionUnicodeTestCase(SharedModuleStoreTestCase, UnicodeTestMixin): @classmethod diff --git a/lms/djangoapps/django_comment_client/forum/views.py b/lms/djangoapps/django_comment_client/forum/views.py index 1459d4c39c..2e6efcc023 100644 --- a/lms/djangoapps/django_comment_client/forum/views.py +++ b/lms/djangoapps/django_comment_client/forum/views.py @@ -40,7 +40,7 @@ from django_comment_client.utils import ( import django_comment_client.utils as utils import lms.lib.comment_client as cc -from opaque_keys.edx.keys import CourseKey +from util.course_key_utils import course_key_from_string_or_404 THREADS_PER_PAGE = 20 INLINE_THREADS_PER_PAGE = 20 @@ -178,7 +178,7 @@ def use_bulk_ops(view_func): """ @wraps(view_func) def wrapped_view(request, course_id, *args, **kwargs): # pylint: disable=missing-docstring - course_key = CourseKey.from_string(course_id) + course_key = course_key_from_string_or_404(course_id) with modulestore().bulk_operations(course_key): return view_func(request, course_key, *args, **kwargs) return wrapped_view From ef70d017c80575b5d12f5c8839d741e7cacc8806 Mon Sep 17 00:00:00 2001 From: Ayub-khan Date: Fri, 15 Apr 2016 15:40:21 +0500 Subject: [PATCH 2/5] -fixed failing tests -Adressed all comments --- cms/djangoapps/contentstore/views/course.py | 4 +-- .../contentstore/views/tests/test_tabs.py | 1 - common/djangoapps/util/course_key_utils.py | 2 +- .../util/tests/test_course_key_utils.py | 31 ++++++++++++------- .../django_comment_client/forum/tests.py | 5 +-- 5 files changed, 26 insertions(+), 17 deletions(-) diff --git a/cms/djangoapps/contentstore/views/course.py b/cms/djangoapps/contentstore/views/course.py index aa60bcadca..530b2f9d6b 100644 --- a/cms/djangoapps/contentstore/views/course.py +++ b/cms/djangoapps/contentstore/views/course.py @@ -91,7 +91,7 @@ from util.organizations_helpers import ( organizations_enabled, ) from util.string_utils import _has_non_ascii_characters -from util.course_key_utils import from_string_or_404 +from util.course_key_utils import course_key_from_string_or_404 from xmodule.contentstore.content import StaticContent from xmodule.course_module import CourseFields from xmodule.course_module import DEFAULT_START_DATE @@ -875,7 +875,7 @@ def course_info_handler(request, course_key_string): GET html: return html for editing the course info handouts and updates. """ - course_key = from_string_or_404(course_key_string) + course_key = course_key_from_string_or_404(course_key_string) with modulestore().bulk_operations(course_key): course_module = get_course_and_check_access(course_key, request.user) diff --git a/cms/djangoapps/contentstore/views/tests/test_tabs.py b/cms/djangoapps/contentstore/views/tests/test_tabs.py index d79630b845..c6b60341c7 100644 --- a/cms/djangoapps/contentstore/views/tests/test_tabs.py +++ b/cms/djangoapps/contentstore/views/tests/test_tabs.py @@ -199,7 +199,6 @@ class TabsPageTests(CourseTestCase): self.client.get(invalid_tab_url) - class PrimitiveTabEdit(ModuleStoreTestCase): """Tests for the primitive tab edit data manipulations""" diff --git a/common/djangoapps/util/course_key_utils.py b/common/djangoapps/util/course_key_utils.py index 44328d7d78..d3768e300d 100644 --- a/common/djangoapps/util/course_key_utils.py +++ b/common/djangoapps/util/course_key_utils.py @@ -28,4 +28,4 @@ def course_key_from_string_or_404(course_key_string, message=None): except InvalidKeyError: raise Http404(message) - return course_key \ No newline at end of file + return course_key diff --git a/common/djangoapps/util/tests/test_course_key_utils.py b/common/djangoapps/util/tests/test_course_key_utils.py index 709abac9a2..fdebbef10c 100644 --- a/common/djangoapps/util/tests/test_course_key_utils.py +++ b/common/djangoapps/util/tests/test_course_key_utils.py @@ -14,22 +14,32 @@ class TestFromStringOr404(unittest.TestCase): Base Test class for course_key_from_string_or_404 utility tests """ @ddt.data( - ("/some.invalid.key/course-v1:TTT+CS01+2015_T0", "course-v1:TTT+CS01+2015_T0"), # split style course keys - ("/some.invalid.key/TTT/CS01/2015_T0", "TTT/CS01/2015_T0"), # mongo style course keys + "course-v1:TTT+CS01+2015_T0", # split style course keys + "TTT/CS01/2015_T0" # mongo style course keys ) - def test_from_string_or_404(self, (invalid_course_key, valid_course_key)): + def test_from_string_or_404_for_valid_course_key(self, valid_course_key): """ - Tests course_key_from_string_or_404 for valid and invalid split style course keys and mongo style course keys. + Tests course_key_from_string_or_404 for valid split style course keys and mongo style course keys. + """ + from nose.tools import set_trace ; set_trace() + self.assertEquals( + CourseKey.from_string(valid_course_key), + course_key_from_string_or_404(valid_course_key) + ) + + @ddt.data( + "/some.invalid.key/course-v1:TTT+CS01+2015_T0", # split style course keys + "/some.invalid.key/TTT/CS01/2015_T0" # mongo style course keys + ) + def test_from_string_or_404_for_invalid_course_key(self, invalid_course_key): + """ + Tests course_key_from_string_or_404 for valid split style course keys and mongo style course keys. """ self.assertRaises( Http404, course_key_from_string_or_404, invalid_course_key, ) - self.assertEquals( - CourseKey.from_string(valid_course_key), - course_key_from_string_or_404(valid_course_key) - ) @ddt.data( "/some.invalid.key/course-v1:TTT+CS01+2015_T0", # split style invalid course key @@ -40,7 +50,6 @@ class TestFromStringOr404(unittest.TestCase): Tests course_key_from_string_or_404 with exception message for split style and monog style invalid course keys. :return: """ - try: + with self.assertRaises(Http404) as context: course_key_from_string_or_404(course_string, message="Invalid Keys") - except Http404 as exception: - self.assertEquals(str(exception), "Invalid Keys") \ No newline at end of file + self.assertEquals(str(context.exception), "Invalid Keys") diff --git a/lms/djangoapps/django_comment_client/forum/tests.py b/lms/djangoapps/django_comment_client/forum/tests.py index 06399ea2c5..4df7a5ea21 100644 --- a/lms/djangoapps/django_comment_client/forum/tests.py +++ b/lms/djangoapps/django_comment_client/forum/tests.py @@ -1318,8 +1318,9 @@ class InlineDiscussionUnicodeTestCase(SharedModuleStoreTestCase, UnicodeTestMixi request.user = self.student with self.assertRaises(Http404): views.inline_discussion( - request, self.course.id.to_deprecated_string(), self.course.discussion_topics['General']['id'] - ) + request, unicode(self.course.id), self.course.discussion_topics['General']['id'] + ) + class ForumFormDiscussionUnicodeTestCase(SharedModuleStoreTestCase, UnicodeTestMixin): @classmethod From e687f1e754efdef2262dd015ea002e87a3dc49a1 Mon Sep 17 00:00:00 2001 From: Ayub-khan Date: Mon, 18 Apr 2016 11:57:31 +0500 Subject: [PATCH 3/5] -Adressed all comments --- cms/djangoapps/contentstore/views/tests/test_tabs.py | 2 +- cms/djangoapps/contentstore/views/tests/test_user.py | 2 +- common/djangoapps/util/tests/test_course_key_utils.py | 5 ++--- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/cms/djangoapps/contentstore/views/tests/test_tabs.py b/cms/djangoapps/contentstore/views/tests/test_tabs.py index c6b60341c7..358f146612 100644 --- a/cms/djangoapps/contentstore/views/tests/test_tabs.py +++ b/cms/djangoapps/contentstore/views/tests/test_tabs.py @@ -194,7 +194,7 @@ class TabsPageTests(CourseTestCase): def test_invalid_course_id(self): """ Asserts that Http404 is raised when the course id is not valid. """ - invalid_tab_url = reverse_course_url('tabs_handler', "/some.invalid.key/TTT/CS01/2015_T0") + invalid_tab_url = reverse_course_url('tabs_handler', "/some.invalid.key/course-v1:TTT+CS01+2015_T0") with self.assertRaises(Http404): self.client.get(invalid_tab_url) diff --git a/cms/djangoapps/contentstore/views/tests/test_user.py b/cms/djangoapps/contentstore/views/tests/test_user.py index 8c26ff4515..e6cafa6a54 100644 --- a/cms/djangoapps/contentstore/views/tests/test_user.py +++ b/cms/djangoapps/contentstore/views/tests/test_user.py @@ -320,7 +320,7 @@ class UsersTestCase(CourseTestCase): def test_invalid_course_id(self): """ Asserts that Http404 is raised when the course id is not valid. """ wrong_url = reverse_course_url( - 'course_team_handler', "/some.invalid.key/TTT/CS01/2015_T0", + 'course_team_handler', "/some.invalid.key/course-v1:TTT+CS01+2015_T0", kwargs={'email': self.ext_user.email} ) with self.assertRaises(Http404): diff --git a/common/djangoapps/util/tests/test_course_key_utils.py b/common/djangoapps/util/tests/test_course_key_utils.py index fdebbef10c..92025715e8 100644 --- a/common/djangoapps/util/tests/test_course_key_utils.py +++ b/common/djangoapps/util/tests/test_course_key_utils.py @@ -1,11 +1,11 @@ """ Tests for util.course_key_utils """ +import ddt +import unittest from util.course_key_utils import course_key_from_string_or_404 from opaque_keys.edx.keys import CourseKey from django.http import Http404 -import ddt -import unittest @ddt.ddt @@ -21,7 +21,6 @@ class TestFromStringOr404(unittest.TestCase): """ Tests course_key_from_string_or_404 for valid split style course keys and mongo style course keys. """ - from nose.tools import set_trace ; set_trace() self.assertEquals( CourseKey.from_string(valid_course_key), course_key_from_string_or_404(valid_course_key) From 9e3f68c0a0f67e76ddd5cb5bb3433a5e07a00d48 Mon Sep 17 00:00:00 2001 From: Ayub-khan Date: Mon, 18 Apr 2016 12:03:46 +0500 Subject: [PATCH 4/5] -Addressed wrong course id comment. --- lms/djangoapps/django_comment_client/forum/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lms/djangoapps/django_comment_client/forum/tests.py b/lms/djangoapps/django_comment_client/forum/tests.py index 4df7a5ea21..790b78f858 100644 --- a/lms/djangoapps/django_comment_client/forum/tests.py +++ b/lms/djangoapps/django_comment_client/forum/tests.py @@ -1318,7 +1318,7 @@ class InlineDiscussionUnicodeTestCase(SharedModuleStoreTestCase, UnicodeTestMixi request.user = self.student with self.assertRaises(Http404): views.inline_discussion( - request, unicode(self.course.id), self.course.discussion_topics['General']['id'] + request, "/some.invalid.key/course-v1:TTT+CS01+2015_T0", self.course.discussion_topics['General']['id'] ) From 66f52ba6e9cddff23ac60969cbfa3174a3009f44 Mon Sep 17 00:00:00 2001 From: Ayub-khan Date: Tue, 19 Apr 2016 16:04:39 +0500 Subject: [PATCH 5/5] -fixed Tests. --- cms/djangoapps/contentstore/views/tests/test_tabs.py | 8 ++++++-- cms/djangoapps/contentstore/views/tests/test_user.py | 11 ++++++----- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/cms/djangoapps/contentstore/views/tests/test_tabs.py b/cms/djangoapps/contentstore/views/tests/test_tabs.py index 358f146612..bb8342cd7a 100644 --- a/cms/djangoapps/contentstore/views/tests/test_tabs.py +++ b/cms/djangoapps/contentstore/views/tests/test_tabs.py @@ -7,10 +7,12 @@ from contentstore.tests.utils import CourseTestCase from contentstore.utils import reverse_course_url from xmodule.x_module import STUDENT_VIEW from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory +from django.test.client import RequestFactory from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.tabs import CourseTabList from xmodule.modulestore.django import modulestore from django.http import Http404 +from contentstore.views.tabs import tabs_handler class TabsPageTests(CourseTestCase): @@ -194,9 +196,11 @@ class TabsPageTests(CourseTestCase): def test_invalid_course_id(self): """ Asserts that Http404 is raised when the course id is not valid. """ - invalid_tab_url = reverse_course_url('tabs_handler', "/some.invalid.key/course-v1:TTT+CS01+2015_T0") + request_factory = RequestFactory() + request = request_factory.get('/dummy-url') + request.user = self.user with self.assertRaises(Http404): - self.client.get(invalid_tab_url) + tabs_handler(request, "/some.invalid.key/course-v1:TTT+CS01+2015_T0") class PrimitiveTabEdit(ModuleStoreTestCase): diff --git a/cms/djangoapps/contentstore/views/tests/test_user.py b/cms/djangoapps/contentstore/views/tests/test_user.py index e6cafa6a54..da82bdd00e 100644 --- a/cms/djangoapps/contentstore/views/tests/test_user.py +++ b/cms/djangoapps/contentstore/views/tests/test_user.py @@ -10,6 +10,8 @@ from student.models import CourseEnrollment from student.roles import CourseStaffRole, CourseInstructorRole from student import auth from django.http import Http404 +from contentstore.views.user import course_team_handler +from django.test.client import RequestFactory class UsersTestCase(CourseTestCase): @@ -319,9 +321,8 @@ class UsersTestCase(CourseTestCase): def test_invalid_course_id(self): """ Asserts that Http404 is raised when the course id is not valid. """ - wrong_url = reverse_course_url( - 'course_team_handler', "/some.invalid.key/course-v1:TTT+CS01+2015_T0", - kwargs={'email': self.ext_user.email} - ) + request_factory = RequestFactory() + request = request_factory.get('/dummy-url') + request.user = self.user with self.assertRaises(Http404): - self.client.get(wrong_url) + course_team_handler(request, "/some.invalid.key/course-v1:TTT+CS01+2015_T0")