From efeafb8033904fe43dcc8f1a9fcd0a14f84db056 Mon Sep 17 00:00:00 2001 From: Shadi Naif Date: Tue, 28 May 2024 20:13:52 +0300 Subject: [PATCH] fix: missing request in serializer context --- futurex_openedx_extensions/__init__.py | 2 +- .../dashboard/serializers.py | 1 - futurex_openedx_extensions/dashboard/views.py | 5 ++- .../fake_models/functions.py | 10 ++--- tests/test_dashboard/test_views.py | 43 ++++++++++++++++++- 5 files changed, 52 insertions(+), 9 deletions(-) diff --git a/futurex_openedx_extensions/__init__.py b/futurex_openedx_extensions/__init__.py index 878c7759..5900858f 100644 --- a/futurex_openedx_extensions/__init__.py +++ b/futurex_openedx_extensions/__init__.py @@ -1,3 +1,3 @@ """One-line description for README and other doc files.""" -__version__ = '0.3.2' +__version__ = '0.3.5' diff --git a/futurex_openedx_extensions/dashboard/serializers.py b/futurex_openedx_extensions/dashboard/serializers.py index 69891a3b..83e2f07f 100644 --- a/futurex_openedx_extensions/dashboard/serializers.py +++ b/futurex_openedx_extensions/dashboard/serializers.py @@ -4,7 +4,6 @@ from lms.djangoapps.certificates.api import get_certificates_for_user_by_course_keys from lms.djangoapps.courseware.courses import get_course_blocks_completion_summary from lms.djangoapps.grades.api import CourseGradeFactory -from opaque_keys.edx.keys import CourseKey from openedx.core.djangoapps.content.block_structure.api import get_block_structure_manager from openedx.core.djangoapps.content.course_overviews.models import CourseOverview from openedx.core.djangoapps.user_api.accounts.serializers import AccountLegacyProfileSerializer diff --git a/futurex_openedx_extensions/dashboard/views.py b/futurex_openedx_extensions/dashboard/views.py index 2d954d38..15e02cfd 100644 --- a/futurex_openedx_extensions/dashboard/views.py +++ b/futurex_openedx_extensions/dashboard/views.py @@ -188,6 +188,7 @@ def get(self, request, username, *args, **kwargs): # pylint: disable=no-self-us class LearnerCoursesView(APIView): """View to get the list of courses for a learner""" permission_classes = [HasTenantAccess] + pagination_class = DefaultPagination def get(self, request, username, *args, **kwargs): # pylint: disable=no-self-use """ @@ -201,4 +202,6 @@ def get(self, request, username, *args, **kwargs): # pylint: disable=no-self-us courses = get_learner_courses_info_queryset(tenant_ids, user_id) - return Response(serializers.LearnerCoursesDetailsSerializer(courses, many=True).data) + return Response(serializers.LearnerCoursesDetailsSerializer( + courses, context={'request': request}, many=True + ).data) diff --git a/test_utils/edx_platform_mocks/fake_models/functions.py b/test_utils/edx_platform_mocks/fake_models/functions.py index 511069c9..261a74e8 100644 --- a/test_utils/edx_platform_mocks/fake_models/functions.py +++ b/test_utils/edx_platform_mocks/fake_models/functions.py @@ -3,17 +3,17 @@ from django.contrib.auth import get_user_model -def get_course_blocks_completion_summary(course_key, user): # pylint: disable=unused-argument +def get_course_blocks_completion_summary(course_key, user): # pylint: disable=unused-argument, useless-return """get_course_blocks_completion_summary Mock""" if not isinstance(user, get_user_model()): - raise Exception(f'Expects a user object but got "{user}" of type "{type(user)}"') + raise TypeError(f'Expects a user object but got "{user}" of type "{type(user)}"') return None -def get_block_structure_manager(course_key): # pylint: disable=unused-argument +def get_block_structure_manager(course_key): """get_block_structure_manager Mock""" if not isinstance(course_key, str): - raise Exception(f'Expects a coruse_key as string but got "{course_key}" of type "{type(course_key)}"') + raise TypeError(f'Expects a coruse_key as string but got "{course_key}" of type "{type(course_key)}"') class Dummy: # pylint: disable=too-few-public-methods """dummy class""" @@ -27,5 +27,5 @@ def get_collected(self): # pylint: disable=no-self-use def get_certificates_for_user_by_course_keys(user, course_keys): # pylint: disable=unused-argument """get_certificates_for_user_by_course_keys Mock""" if not isinstance(user, get_user_model()): - raise Exception(f'Expects a user object but got "{user}" of type "{type(user)}"') + raise TypeError(f'Expects a user object but got "{user}" of type "{type(user)}"') return {} diff --git a/tests/test_dashboard/test_views.py b/tests/test_dashboard/test_views.py index 11df60c1..f4464c61 100644 --- a/tests/test_dashboard/test_views.py +++ b/tests/test_dashboard/test_views.py @@ -9,7 +9,7 @@ from django.urls import resolve, reverse from django.utils.timezone import now, timedelta from openedx.core.djangoapps.content.course_overviews.models import CourseOverview -from rest_framework.test import APITestCase +from rest_framework.test import APIRequestFactory, APITestCase from futurex_openedx_extensions.dashboard import serializers from futurex_openedx_extensions.helpers.constants import COURSE_STATUSES @@ -36,6 +36,17 @@ def login_user(self, user_id): """Helper to login user""" self.client.force_login(get_user_model().objects.get(id=user_id)) + def _get_request_view_class(self): + """Helper to get the view class and a request""" + view_func, _, _ = resolve(self.url) + view_class = view_func.view_class + factory = APIRequestFactory() + request = factory.get(self.url) + request.query_params = {} + request.user = get_user_model().objects.get(id=self.staff_user) + + return request, view_class + @pytest.mark.usefixtures('base_data') class TestTotalCountsView(BaseTestViewMixin): @@ -284,6 +295,21 @@ def test_success(self): data = json.loads(response.content) self.assertDictEqual(data, serializers.LearnerDetailsExtendedSerializer(user).data) + @patch('futurex_openedx_extensions.dashboard.views.serializers.LearnerDetailsExtendedSerializer') + def test_request_in_context(self, mock_serializer): + """Verify that the view calls the serializer with the correct context""" + request, view_class = self._get_request_view_class() + mock_serializer.return_value = Mock(data={}) + + with patch('futurex_openedx_extensions.dashboard.views.get_learner_info_queryset') as mock_get_info: + mock_get_info.return_value = Mock() + view_class.get(self, request, 'user10') + + mock_serializer.assert_called_once_with( + mock_get_info.return_value.first(), + context={'request': request}, + ) + @patch.object( serializers.LearnerCoursesDetailsSerializer, @@ -316,3 +342,18 @@ def test_success(self): data = json.loads(response.content) self.assertEqual(len(data), 2) self.assertEqual(list(data), list(serializers.LearnerCoursesDetailsSerializer(courses, many=True).data)) + + @patch('futurex_openedx_extensions.dashboard.views.serializers.LearnerCoursesDetailsSerializer') + def test_request_in_context(self, mock_serializer): + """Verify that the view uses the correct serializer""" + request, view_class = self._get_request_view_class() + + with patch('futurex_openedx_extensions.dashboard.views.get_learner_courses_info_queryset') as mock_get_info: + mock_get_info.return_value = Mock() + view_class.get(self, request, 'user10') + + mock_serializer.assert_called_once_with( + mock_get_info.return_value, + context={'request': request}, + many=True, + )