fix: fixed atmoic block error in course_status_info
* fix: fixed atmoic block error in course_status_info We are getting "An error occurred in the current transaction. You cant execute queries until the end of the atomic block." error in get course status info on both versions i.e. 0.5 and 1. I was unable to reproduce it locally. But by looking in django documentation and some other online helps I concluded that it might be because of some db intefrity error that we didnt handle properly and now the cursor is broken for this transaction. Therefore i am making it non atomic transaction. Since its a get request we can make transactions run independentantly. LEARNER-8267
This commit is contained in:
@@ -11,15 +11,17 @@ import ddt
|
||||
import pytz
|
||||
from completion.test_utils import CompletionWaffleTestMixin, submit_completions_for_testing
|
||||
from django.conf import settings
|
||||
from django.db import transaction
|
||||
from django.template import defaultfilters
|
||||
from django.test import RequestFactory, override_settings
|
||||
from django.utils import timezone
|
||||
from django.utils.timezone import now
|
||||
from milestones.tests.utils import MilestonesTestCaseMixin
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
|
||||
from common.djangoapps.course_modes.models import CourseMode
|
||||
from common.djangoapps.student.models import CourseEnrollment
|
||||
from common.djangoapps.student.tests.factories import CourseEnrollmentFactory
|
||||
from common.djangoapps.student.tests.factories import CourseEnrollmentFactory, UserFactory
|
||||
from common.djangoapps.util.milestones_helpers import set_prerequisite_courses
|
||||
from common.djangoapps.util.testing import UrlResetMixin
|
||||
from lms.djangoapps.certificates.data import CertificateStatuses
|
||||
@@ -439,6 +441,7 @@ class TestCourseStatusGET(CourseStatusAPITestCase, MobileAuthUserTestMixin,
|
||||
"""
|
||||
Tests for GET of /api/mobile/v<version_number>/users/<user_name>/course_status_info/{course_id}
|
||||
"""
|
||||
|
||||
def test_success_v0(self):
|
||||
self.login_and_enroll()
|
||||
|
||||
@@ -454,6 +457,53 @@ class TestCourseStatusGET(CourseStatusAPITestCase, MobileAuthUserTestMixin,
|
||||
response = self.api_response(api_version=API_V1)
|
||||
assert response.data['last_visited_block_id'] == str(self.unit.location)
|
||||
|
||||
# Since we are testing an non atomic view in atomic test case, therefore we are expecting error on failures
|
||||
def api_error_response(self, reverse_args=None, data=None, **kwargs):
|
||||
"""
|
||||
Same as api response from MobileAPITestCase but handle views which throw errors
|
||||
"""
|
||||
url = self.reverse_url(reverse_args, **kwargs)
|
||||
try:
|
||||
with transaction.atomic():
|
||||
self.url_method(url, data=data, **kwargs)
|
||||
assert False
|
||||
except transaction.TransactionManagementError:
|
||||
assert True
|
||||
|
||||
def test_invalid_user(self):
|
||||
self.login_and_enroll()
|
||||
self.api_error_response(username='no_user')
|
||||
|
||||
def test_other_user(self):
|
||||
# login and enroll as the test user
|
||||
self.login_and_enroll()
|
||||
self.logout()
|
||||
|
||||
# login and enroll as another user
|
||||
other = UserFactory.create()
|
||||
self.client.login(username=other.username, password='test')
|
||||
self.enroll()
|
||||
self.logout()
|
||||
|
||||
# now login and call the API as the test user
|
||||
self.login()
|
||||
self.api_error_response(username=other.username)
|
||||
|
||||
def test_course_not_found(self):
|
||||
non_existent_course_id = CourseKey.from_string('a/b/c')
|
||||
self.init_course_access(course_id=non_existent_course_id)
|
||||
|
||||
self.api_error_response(course_id=non_existent_course_id)
|
||||
|
||||
def test_unenrolled_user(self):
|
||||
self.login()
|
||||
self.unenroll()
|
||||
self.api_error_response(expected_response_code=None)
|
||||
|
||||
def test_no_auth(self):
|
||||
self.logout()
|
||||
self.api_error_response()
|
||||
|
||||
|
||||
class TestCourseStatusPATCH(CourseStatusAPITestCase, MobileAuthUserTestMixin,
|
||||
MobileCourseAccessTestMixin, MilestonesTestCaseMixin):
|
||||
|
||||
@@ -7,12 +7,15 @@ from completion.exceptions import UnavailableCompletionData
|
||||
from completion.utilities import get_key_to_last_completed_block
|
||||
from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user
|
||||
from django.contrib.auth.signals import user_logged_in
|
||||
from django.db import transaction
|
||||
from django.shortcuts import redirect
|
||||
from django.utils import dateparse
|
||||
from django.utils.decorators import method_decorator
|
||||
from opaque_keys import InvalidKeyError
|
||||
from opaque_keys.edx.keys import UsageKey
|
||||
from rest_framework import generics, views
|
||||
from rest_framework.decorators import api_view
|
||||
from rest_framework.permissions import SAFE_METHODS
|
||||
from rest_framework.response import Response
|
||||
from xblock.fields import Scope
|
||||
from xblock.runtime import KeyValueStore
|
||||
@@ -77,6 +80,7 @@ class UserDetail(generics.RetrieveAPIView):
|
||||
|
||||
|
||||
@mobile_view(is_user=True)
|
||||
@method_decorator(transaction.non_atomic_requests, name='dispatch')
|
||||
class UserCourseStatus(views.APIView):
|
||||
"""
|
||||
**Use Cases**
|
||||
@@ -122,6 +126,13 @@ class UserCourseStatus(views.APIView):
|
||||
|
||||
http_method_names = ["get", "patch"]
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
if request.method in SAFE_METHODS:
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
else:
|
||||
with transaction.atomic():
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
def _last_visited_module_path(self, request, course):
|
||||
"""
|
||||
Returns the path from the last module visited by the current user in the given course up to
|
||||
@@ -135,7 +146,7 @@ class UserCourseStatus(views.APIView):
|
||||
request.user, request, course, field_data_cache, course.id, course=course
|
||||
)
|
||||
|
||||
path = [course_module]
|
||||
path = [course_module] if course_module else []
|
||||
chapter = get_current_child(course_module, min_depth=2)
|
||||
if chapter is not None:
|
||||
path.append(chapter)
|
||||
|
||||
Reference in New Issue
Block a user