PLAT-2186 - allow retirement states to be forced by driver script

When the optional "force" param is sent in this skips the checks for moving from dead end state and moving to earlier state, allowing us to bulk reset errored users or run users through new states.
This commit is contained in:
bmedx
2018-07-13 15:08:31 -04:00
parent 628954d5d7
commit 74b07c3ff8
2 changed files with 31 additions and 29 deletions

View File

@@ -8,6 +8,9 @@ import datetime
import json
import unittest
import ddt
import pytz
import mock
from consent.models import DataSharingConsent
from django.conf import settings
from django.contrib.auth.models import User
@@ -25,9 +28,7 @@ from enterprise.models import (
from integrated_channels.sap_success_factors.models import (
SapSuccessFactorsLearnerDataTransmissionAudit
)
import mock
from opaque_keys.edx.keys import CourseKey
import pytz
from rest_framework import status
from six import iteritems, text_type
from social_django.models import UserSocialAuth
@@ -51,7 +52,6 @@ from openedx.core.djangoapps.user_api.models import (
UserRetirementPartnerReportingStatus,
UserOrgTag
)
from openedx.core.djangoapps.user_api.accounts.tests.retirement_helpers import fake_retirement
from openedx.core.djangoapps.user_api.accounts.views import AccountRetirementPartnerReportView
from openedx.core.lib.token_utils import JwtBuilder
from student.models import (
@@ -881,6 +881,7 @@ class TestAccountRetirementRetrieve(RetirementTestCase):
self.assert_status_and_user_data(values, username_to_find=original_username)
@ddt.ddt
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Account APIs are only supported in LMS')
class TestAccountRetirementUpdate(RetirementTestCase):
"""
@@ -994,35 +995,31 @@ class TestAccountRetirementUpdate(RetirementTestCase):
data = {'new_state': 'LOCKING_ACCOUNT', 'response': 'this should fail', 'username': 'does not exist'}
self.update_and_assert_status(data, status.HTTP_404_NOT_FOUND)
def test_move_from_dead_end(self):
"""
Confirm that trying to move from a dead end state to any other state fails
"""
@ddt.data(
# Test moving backward from intermediate state
('LOCKING_ACCOUNT', 'PENDING', False, status.HTTP_400_BAD_REQUEST),
('LOCKING_ACCOUNT', 'PENDING', True, status.HTTP_204_NO_CONTENT),
# Test moving backward from dead end state
('COMPLETE', 'PENDING', False, status.HTTP_400_BAD_REQUEST),
('COMPLETE', 'PENDING', True, status.HTTP_204_NO_CONTENT),
# Test moving to the same state
('LOCKING_ACCOUNT', 'LOCKING_ACCOUNT', False, status.HTTP_400_BAD_REQUEST),
('LOCKING_ACCOUNT', 'LOCKING_ACCOUNT', True, status.HTTP_204_NO_CONTENT),
)
@ddt.unpack
def test_moves(self, start_state, move_to_state, force, expected_response_code):
retirement = UserRetirementStatus.objects.get(id=self.retirement.id)
retirement.current_state = RetirementState.objects.filter(is_dead_end_state=True)[0]
retirement.current_state = RetirementState.objects.get(state_name=start_state)
retirement.save()
data = {'new_state': 'LOCKING_ACCOUNT', 'response': 'this should fail'}
self.update_and_assert_status(data, status.HTTP_400_BAD_REQUEST)
data = {'new_state': move_to_state, 'response': 'foo'}
def test_move_backward(self):
"""
Confirm that trying to move to an earlier step in the process fails
"""
retirement = UserRetirementStatus.objects.get(id=self.retirement.id)
retirement.current_state = RetirementState.objects.get(state_name='COMPLETE')
retirement.save()
if force:
data['force'] = True
data = {'new_state': 'PENDING', 'response': 'this should fail'}
self.update_and_assert_status(data, status.HTTP_400_BAD_REQUEST)
def test_move_same(self):
"""
Confirm that trying to move to the same step in the process fails
"""
# Should already be in 'PENDING'
data = {'new_state': 'PENDING', 'response': 'this should fail'}
self.update_and_assert_status(data, status.HTTP_400_BAD_REQUEST)
self.update_and_assert_status(data, expected_response_code)
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Account APIs are only supported in LMS')

View File

@@ -264,13 +264,15 @@ class UserRetirementStatus(TimeStampedModel):
Confirm that the data passed in is properly formatted
"""
required_keys = ('username', 'new_state', 'response')
optional_keys = ('force', )
known_keys = required_keys + optional_keys
for required_key in required_keys:
if required_key not in data:
raise RetirementStateError('RetirementStatus: Required key {} missing from update'.format(required_key))
for key in data:
if key not in required_keys:
if key not in known_keys:
raise RetirementStateError('RetirementStatus: Unknown key {} in update'.format(key))
@classmethod
@@ -310,7 +312,10 @@ class UserRetirementStatus(TimeStampedModel):
or throw a RetirementStateError with a useful error message
"""
self._validate_update_data(update)
self._validate_state_update(update['new_state'])
force = update.get('force', False)
if not force:
self._validate_state_update(update['new_state'])
old_state = self.current_state
self.current_state = RetirementState.objects.get(state_name=update['new_state'])