diff --git a/eox_nelp/pearson_vue/api/v1/views.py b/eox_nelp/pearson_vue/api/v1/views.py index 19abc231..60d97122 100644 --- a/eox_nelp/pearson_vue/api/v1/views.py +++ b/eox_nelp/pearson_vue/api/v1/views.py @@ -19,7 +19,7 @@ from edx_rest_framework_extensions.auth.jwt.authentication import JwtAuthentication from eox_core.edxapp_wrapper.bearer_authentication import BearerAuthentication from rest_framework import status -from rest_framework.mixins import CreateModelMixin, ListModelMixin +from rest_framework.mixins import CreateModelMixin, ListModelMixin, RetrieveModelMixin from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from rest_framework.viewsets import GenericViewSet @@ -174,7 +174,7 @@ def get_course(self): return enrollment.course if enrollment else None -class ResultNotificationView(PearsonRTENBaseView): +class ResultNotificationView(RetrieveModelMixin, PearsonRTENBaseView): """ View for handling Result Notification events. @@ -182,6 +182,7 @@ class ResultNotificationView(PearsonRTENBaseView): The `event_type` attribute is set to "resultNotification". """ event_type = RESULT_NOTIFICATION + lookup_field = "course" def create(self, request, *args, **kwargs): """ @@ -201,6 +202,30 @@ def create(self, request, *args, **kwargs): return response + def get_object(self): + """ + Retrieves the latest object for the given candidate and course. + + This method filters the queryset to find objects matching the current user + and the course specified by the lookup field in the URL. If no objects are found, + it raises a 404 error. If objects are found, it returns the most recently created object. + + Returns: + object: The most recently created object that matches the given candidate and course. + + Raises: + Http404: If no objects match the given candidate and course. + """ + objects = self.get_queryset().filter( + candidate=self.request.user, + course=self.kwargs[self.lookup_field], + ) + + if not objects: + raise Http404 + + return objects.latest("created_at") + class PlaceHoldView(PearsonRTENBaseView): """ diff --git a/eox_nelp/pearson_vue/tests/api/v1/test_views.py b/eox_nelp/pearson_vue/tests/api/v1/test_views.py index 4faad7a3..03257a90 100644 --- a/eox_nelp/pearson_vue/tests/api/v1/test_views.py +++ b/eox_nelp/pearson_vue/tests/api/v1/test_views.py @@ -11,6 +11,7 @@ TestUnrevokeResultView: Unit tests for the UnrevokeResultView. """ import unittest +from datetime import datetime, timedelta from unittest.mock import Mock, patch from django.contrib.auth import get_user_model @@ -263,6 +264,47 @@ def test_pipeline_execution(self, result_notification_mock): result_notification_mock.assert_called_once_with(request_data=payload) result_notification_mock.return_value.run_pipeline.assert_called_once() + def test_detail_view(self): + """ + Test that the detail view correctly retrieves the latest event for a candidate and course. + + Expected behavior: + - Response returns a 200 status code. + - Response data contains the correct event details. + """ + PearsonRTENEvent.objects.create( # pylint: disable=no-member + event_type=self.event_type, + candidate=self.user, + course=self.course, + created_at=datetime.now(), + content={}, + ) + latest_event = PearsonRTENEvent.objects.create( # pylint: disable=no-member + event_type=self.event_type, + candidate=self.user, + course=self.course, + created_at=datetime.now() + timedelta(seconds=1), # Ensure this is the latest event + content={}, + ) + + response = self.client.get(reverse(f"pearson-vue-api:v1:{self.event_type}-detail", args=[str(self.course_key)])) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data['created_at'], latest_event.created_at.isoformat()) + + def test_detail_not_found(self): + """ + Test that the detail view returns a 404 status when no event is found for the given candidate and course. + + Expected behavior: + - Response returns a 404 status code. + """ + PearsonRTENEvent.objects.filter(candidate=self.user, course=self.course).delete() # pylint: disable=no-member + + response = self.client.get(reverse(f"pearson-vue-api:v1:{self.event_type}-detail", args=[str(self.course_key)])) + + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + class TestPlaceHoldView(RTENMixin, unittest.TestCase): """