Commit 445d0dab by jsa

Correct deactivation logic in enrollment api and test.

XCOM-396
parent 298ba727
......@@ -2,6 +2,7 @@
Tests for user enrollment.
"""
import json
import itertools
import unittest
import datetime
......@@ -91,11 +92,11 @@ class EnrollmentTestMixin(object):
return response
def assert_enrollment_activation(self, expected_activation, expected_mode=CourseMode.VERIFIED):
def assert_enrollment_activation(self, expected_activation, expected_mode):
"""Change an enrollment's activation and verify its activation and mode are as expected."""
self.assert_enrollment_status(
as_server=True,
mode=None,
mode=expected_mode,
is_active=expected_activation,
expected_status=status.HTTP_200_OK
)
......@@ -603,6 +604,58 @@ class EnrollmentTest(EnrollmentTestMixin, ModuleStoreTestCase, APITestCase):
self.assertTrue(is_active)
self.assertEqual(course_mode, CourseMode.HONOR)
@ddt.data(*itertools.product(
(CourseMode.HONOR, CourseMode.VERIFIED),
(CourseMode.HONOR, CourseMode.VERIFIED),
(True, False),
(True, False),
))
@ddt.unpack
def test_change_mode_from_server(self, old_mode, new_mode, old_is_active, new_is_active):
"""
Server-to-server calls should be allowed to change the mode of any
enrollment, as long as the enrollment is not being deactivated during
the same call (this is assumed to be an error on the client's side).
"""
for mode in [CourseMode.HONOR, CourseMode.VERIFIED]:
CourseModeFactory.create(
course_id=self.course.id,
mode_slug=mode,
mode_display_name=mode,
)
# Set up the initial enrollment
self.assert_enrollment_status(as_server=True, mode=old_mode, is_active=old_is_active)
course_mode, is_active = CourseEnrollment.enrollment_mode_for_user(self.user, self.course.id)
self.assertEqual(is_active, old_is_active)
self.assertEqual(course_mode, old_mode)
expected_status = status.HTTP_400_BAD_REQUEST if (
old_mode != new_mode and
old_is_active != new_is_active and
not new_is_active
) else status.HTTP_200_OK
# simulate the server-server api call under test
response = self.assert_enrollment_status(
as_server=True,
mode=new_mode,
is_active=new_is_active,
expected_status=expected_status,
)
course_mode, is_active = CourseEnrollment.enrollment_mode_for_user(self.user, self.course.id)
if expected_status == status.HTTP_400_BAD_REQUEST:
# nothing should have changed
self.assertEqual(is_active, old_is_active)
self.assertEqual(course_mode, old_mode)
# error message should contain specific text. Otto checks for this text in the message.
self.assertRegexpMatches(json.loads(response.content)['message'], 'Enrollment mode mismatch')
else:
# call should have succeeded
self.assertEqual(is_active, new_is_active)
self.assertEqual(course_mode, new_mode)
def test_change_mode_invalid_user(self):
"""
Attempts to change an enrollment for a non-existent user should result in an HTTP 404 for non-server users,
......
......@@ -3,6 +3,8 @@ The Enrollment API Views should be simple, lean HTTP endpoints for API access. T
consist primarily of authentication, request validation, and serialization.
"""
import logging
from ipware.ip import get_ip
from django.core.exceptions import ObjectDoesNotExist
from django.utils.decorators import method_decorator
......@@ -31,6 +33,9 @@ from enrollment.errors import (
from student.models import User
log = logging.getLogger(__name__)
class EnrollmentCrossDomainSessionAuth(SessionAuthenticationAllowInactiveUser, SessionAuthenticationCrossDomainCsrf):
"""Session authentication that allows inactive users and cross-domain requests. """
pass
......@@ -421,7 +426,18 @@ class EnrollmentListView(APIView, ApiKeyPermissionMixIn):
)
enrollment = api.get_enrollment(username, unicode(course_id))
if has_api_key_permissions and enrollment and enrollment['mode'] != mode:
mode_changed = enrollment and mode is not None and enrollment['mode'] != mode
active_changed = enrollment and is_active is not None and enrollment['is_active'] != is_active
if has_api_key_permissions and (mode_changed or active_changed):
if mode_changed and active_changed and not is_active:
# if the requester wanted to deactivate but specified the wrong mode, fail
# the request (on the assumption that the requester had outdated information
# about the currently active enrollment).
msg = u"Enrollment mode mismatch: active mode={}, requested mode={}. Won't deactivate.".format(
enrollment["mode"], mode
)
log.warning(msg)
return Response(status=status.HTTP_400_BAD_REQUEST, data={"message": msg})
response = api.update_enrollment(username, unicode(course_id), mode=mode, is_active=is_active)
else:
# Will reactivate inactive enrollments.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment