From 8d9369ff10b0b8fc897ae0e287de5028ecb8cb39 Mon Sep 17 00:00:00 2001 From: Zachary Hancock Date: Mon, 4 Dec 2023 09:05:33 -0500 Subject: [PATCH] feat: make roster endpoint permanent (#218) --- edx_exams/apps/lti/tests/test_views.py | 54 ++++++++++++++++++++++++++ edx_exams/apps/lti/views.py | 7 ++-- 2 files changed, 57 insertions(+), 4 deletions(-) diff --git a/edx_exams/apps/lti/tests/test_views.py b/edx_exams/apps/lti/tests/test_views.py index 80d5eeb9..0a00efb3 100644 --- a/edx_exams/apps/lti/tests/test_views.py +++ b/edx_exams/apps/lti/tests/test_views.py @@ -665,3 +665,57 @@ def test_requires_staff_user(self, mock_create_launch_url): # pylint: disable=u response = self.client.get(self._get_launch_url(self.exam.id), **headers) self.assertEqual(response.status_code, 403) + + +class ExamRosterTestCase(ExamsAPITestCase): + """ + Test exam_roster() + """ + def setUp(self): + super().setUp() + self.course_id = 'course-v1:edx+test+f19' + self.exam = ExamFactory(course_id=self.course_id) + + def _get_response(self, exam_id): + """ + GET roster endpoint + """ + return self.client.get(reverse('lti:exam_roster', kwargs={'exam_id': exam_id})) + + def test_course_staff_access(self): + """ + Test the endpoint requires course staff access. + """ + non_staff_user = UserFactory(password='test') + self.client.login(username=non_staff_user.username, password='test') + response = self._get_response(self.exam.id) + self.assertEqual(response.status_code, 403) + + course_staff_user = UserFactory(password='test') + CourseStaffRole.objects.create(user=course_staff_user, course_id=self.course_id) + self.client.login(username=course_staff_user.username, password='test') + response = self._get_response(self.exam.id) + self.assertEqual(response.status_code, 200) + + def test_get_roster(self): + """ + Test endpoint returns the expected usernames. + """ + # by convention we'd normally mock get_attempts but we need to test + # database behavior for select_related() + user1 = UserFactory(username='user1') + user2 = UserFactory(username='user2') + user3 = UserFactory(username='user3') + ExamAttemptFactory(exam=self.exam, user=user1) + ExamAttemptFactory(exam=self.exam, user=user2) + ExamAttemptFactory(exam=self.exam, user=user3) + + with self.assertNumQueries(4): + response = self._get_response(self.exam.id) + + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json(), [ + [str(user1.anonymous_user_id), 'user1'], + [str(user2.anonymous_user_id), 'user2'], + [str(user3.anonymous_user_id), 'user3'], + ]) diff --git a/edx_exams/apps/lti/views.py b/edx_exams/apps/lti/views.py index b525dedb..3983bc4e 100644 --- a/edx_exams/apps/lti/views.py +++ b/edx_exams/apps/lti/views.py @@ -378,19 +378,18 @@ def launch_instructor_tool(request, exam_id): @csrf_exempt +@api_view(['GET']) @require_http_methods(['GET']) def exam_roster(request, exam_id): - # pragma: no cover """ - Temporary endpoint to prove we can authenticate this request properly + Returns a list of users who have an attempt for the exam. """ user = request.user exam = get_exam_by_id(exam_id) if not user.is_staff and not user.has_course_staff_permission(exam.course_id): return Response(status=status.HTTP_403_FORBIDDEN) - attempts = get_exam_attempts(exam_id) - attempts.select_related('user') + attempts = get_exam_attempts(exam_id).select_related('user') users = set(attempt.user for attempt in attempts) roster = [