diff --git a/lms/djangoapps/survey/tests/test_signals.py b/lms/djangoapps/survey/tests/test_signals.py
index a78aefb0ae15da8d0bb471312eb8cbc033350be6..c22a258e639a7b89dcf4486cc52e8c3282990c2d 100644
--- a/lms/djangoapps/survey/tests/test_signals.py
+++ b/lms/djangoapps/survey/tests/test_signals.py
@@ -2,7 +2,7 @@
 Test signal handlers for the survey app
 """
 
-from openedx.core.djangoapps.user_api.accounts.tests.retirement_helpers import fake_retirement
+from openedx.core.djangoapps.user_api.accounts.tests.retirement_helpers import fake_completed_retirement
 from student.tests.factories import UserFactory
 from survey.models import SurveyAnswer
 from survey.tests.factories import SurveyAnswerFactory
@@ -43,7 +43,7 @@ class SurveyRetireSignalTests(ModuleStoreTestCase):
 
         # Run twice to make sure no errors are raised
         _listen_for_lms_retire(sender=self.__class__, user=answer.user)
-        fake_retirement(answer.user)
+        fake_completed_retirement(answer.user)
         _listen_for_lms_retire(sender=self.__class__, user=answer.user)
 
         # All values for this user should still be here and just be an empty string
diff --git a/lms/djangoapps/verify_student/tests/test_signals.py b/lms/djangoapps/verify_student/tests/test_signals.py
index 294cff801de2c2168a6fdbf0f2cc1785756fc5b6..deaa5b41fa8f8a9cc2f13226fdc23deb04a2b0e3 100644
--- a/lms/djangoapps/verify_student/tests/test_signals.py
+++ b/lms/djangoapps/verify_student/tests/test_signals.py
@@ -9,7 +9,7 @@ from pytz import UTC
 from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification, VerificationDeadline
 from lms.djangoapps.verify_student.signals import _listen_for_course_publish, _listen_for_lms_retire
 from lms.djangoapps.verify_student.tests.factories import SoftwareSecurePhotoVerificationFactory
-from openedx.core.djangoapps.user_api.accounts.tests.retirement_helpers import fake_retirement
+from openedx.core.djangoapps.user_api.accounts.tests.retirement_helpers import fake_completed_retirement
 from student.tests.factories import UserFactory
 from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
 from xmodule.modulestore.tests.factories import CourseFactory
@@ -95,7 +95,7 @@ class RetirementSignalTest(ModuleStoreTestCase):
 
         # Run this twice to make sure there are no errors raised 2nd time through
         _listen_for_lms_retire(sender=self.__class__, user=verification.user)
-        fake_retirement(verification.user)
+        fake_completed_retirement(verification.user)
         _listen_for_lms_retire(sender=self.__class__, user=verification.user)
 
         ver_obj = SoftwareSecurePhotoVerification.objects.get(user=verification.user)
diff --git a/openedx/core/djangoapps/credit/tests/test_models.py b/openedx/core/djangoapps/credit/tests/test_models.py
index da957c1041da376eadf155ea636271d4a6b7f02f..e844a424d1ad03010398a2349188f83c5a2c2c28 100644
--- a/openedx/core/djangoapps/credit/tests/test_models.py
+++ b/openedx/core/djangoapps/credit/tests/test_models.py
@@ -15,7 +15,10 @@ from openedx.core.djangoapps.credit.models import (
     CreditRequirement,
     CreditRequirementStatus
 )
-from openedx.core.djangoapps.user_api.accounts.tests.retirement_helpers import RetirementTestCase
+from openedx.core.djangoapps.user_api.accounts.tests.retirement_helpers import (  # pylint: disable=unused-import
+    RetirementTestCase,
+    setup_retirement_states
+)
 from openedx.core.djangoapps.user_api.models import UserRetirementStatus
 from student.tests.factories import UserFactory
 
@@ -97,7 +100,7 @@ class CreditEligibilityModelTests(TestCase):
         self.assertEqual(len(requirements), 1)
 
 
-class CreditRequirementStatusTests(TestCase):
+class CreditRequirementStatusTests(RetirementTestCase):
     """
     Tests for credit requirement status models.
     """
@@ -105,7 +108,6 @@ class CreditRequirementStatusTests(TestCase):
     def setUp(self):
         super(CreditRequirementStatusTests, self).setUp()
         self.course_key = CourseKey.from_string("edX/DemoX/Demo_Course")
-        RetirementTestCase.setup_states()
         self.old_username = "username"
         self.user = UserFactory(username=self.old_username)
         self.retirement = UserRetirementStatus.create_retirement(self.user)
@@ -170,14 +172,13 @@ class CreditRequirementStatusTests(TestCase):
         self.assertFalse(retirement_succeeded)
 
 
-class CreditRequestTest(TestCase):
+class CreditRequestTest(RetirementTestCase):
     """
     The CreditRequest model's test suite.
     """
 
     def setUp(self):
         super(CreditRequestTest, self).setUp()
-        RetirementTestCase.setup_states()
         self.user = UserFactory.create()
         self.retirement = UserRetirementStatus.create_retirement(self.user)
         self.credit_course = CreditCourse.objects.create()
diff --git a/openedx/core/djangoapps/user_api/accounts/tests/retirement_helpers.py b/openedx/core/djangoapps/user_api/accounts/tests/retirement_helpers.py
index 0953892ea635b7d343ec6388c3145a8a93cfd1b7..b2bf9d1004f9532f6ac8a529d8b7a4720c156257 100644
--- a/openedx/core/djangoapps/user_api/accounts/tests/retirement_helpers.py
+++ b/openedx/core/djangoapps/user_api/accounts/tests/retirement_helpers.py
@@ -3,6 +3,7 @@ Helpers for testing retirement functionality
 """
 import datetime
 
+import pytest
 import pytz
 from django.test import TestCase
 from social_django.models import UserSocialAuth
@@ -21,75 +22,92 @@ from student.tests.factories import UserFactory
 from ..views import AccountRetirementView
 
 
-class RetirementTestCase(TestCase):
+@pytest.fixture
+def setup_retirement_states(scope="module"):  # pylint: disable=unused-argument
     """
-    Test case with a helper methods for retirement
+    Create basic states that mimic the retirement process.
     """
-    @classmethod
-    def setUpClass(cls):
-        super(RetirementTestCase, cls).setUpClass()
-        cls.setup_states()
-
-    @staticmethod
-    def setup_states():
-        """
-        Create basic states that mimic our current understanding of the retirement process
-        """
-        default_states = [
-            ('PENDING', 1, False, True),
-            ('LOCKING_ACCOUNT', 20, False, False),
-            ('LOCKING_COMPLETE', 30, False, False),
-            ('RETIRING_CREDENTIALS', 40, False, False),
-            ('CREDENTIALS_COMPLETE', 50, False, False),
-            ('RETIRING_ECOM', 60, False, False),
-            ('ECOM_COMPLETE', 70, False, False),
-            ('RETIRING_FORUMS', 80, False, False),
-            ('FORUMS_COMPLETE', 90, False, False),
-            ('RETIRING_EMAIL_LISTS', 100, False, False),
-            ('EMAIL_LISTS_COMPLETE', 110, False, False),
-            ('RETIRING_ENROLLMENTS', 120, False, False),
-            ('ENROLLMENTS_COMPLETE', 130, False, False),
-            ('RETIRING_NOTES', 140, False, False),
-            ('NOTES_COMPLETE', 150, False, False),
-            ('RETIRING_LMS', 160, False, False),
-            ('LMS_COMPLETE', 170, False, False),
-            ('ADDING_TO_PARTNER_QUEUE', 180, False, False),
-            ('PARTNER_QUEUE_COMPLETE', 190, False, False),
-            ('ERRORED', 200, True, True),
-            ('ABORTED', 210, True, True),
-            ('COMPLETE', 220, True, True),
-        ]
-
-        for name, ex, dead, req in default_states:
-            RetirementState.objects.create(
-                state_name=name,
-                state_execution_order=ex,
-                is_dead_end_state=dead,
-                required=req
-            )
-
-    def _create_retirement(self, state, create_datetime=None):
-        """
-        Helper method to create a RetirementStatus with useful defaults
-        """
-        if create_datetime is None:
-            create_datetime = datetime.datetime.now(pytz.UTC) - datetime.timedelta(days=8)
-
-        user = UserFactory()
-        return UserRetirementStatus.objects.create(
-            user=user,
-            original_username=user.username,
-            original_email=user.email,
-            original_name=user.profile.name,
-            retired_username=get_retired_username_by_username(user.username),
-            retired_email=get_retired_email_by_email(user.email),
-            current_state=state,
-            last_state=state,
-            responses="",
-            created=create_datetime,
-            modified=create_datetime
+    default_states = [
+        ('PENDING', 1, False, True),
+        ('LOCKING_ACCOUNT', 20, False, False),
+        ('LOCKING_COMPLETE', 30, False, False),
+        ('RETIRING_CREDENTIALS', 40, False, False),
+        ('CREDENTIALS_COMPLETE', 50, False, False),
+        ('RETIRING_ECOM', 60, False, False),
+        ('ECOM_COMPLETE', 70, False, False),
+        ('RETIRING_FORUMS', 80, False, False),
+        ('FORUMS_COMPLETE', 90, False, False),
+        ('RETIRING_EMAIL_LISTS', 100, False, False),
+        ('EMAIL_LISTS_COMPLETE', 110, False, False),
+        ('RETIRING_ENROLLMENTS', 120, False, False),
+        ('ENROLLMENTS_COMPLETE', 130, False, False),
+        ('RETIRING_NOTES', 140, False, False),
+        ('NOTES_COMPLETE', 150, False, False),
+        ('RETIRING_LMS', 160, False, False),
+        ('LMS_COMPLETE', 170, False, False),
+        ('ADDING_TO_PARTNER_QUEUE', 180, False, False),
+        ('PARTNER_QUEUE_COMPLETE', 190, False, False),
+        ('ERRORED', 200, True, True),
+        ('ABORTED', 210, True, True),
+        ('COMPLETE', 220, True, True),
+    ]
+
+    for name, ex, dead, req in default_states:
+        RetirementState.objects.create(
+            state_name=name,
+            state_execution_order=ex,
+            is_dead_end_state=dead,
+            required=req
         )
 
+    yield
+
+    RetirementState.objects.all().delete()
+
+
+def create_retirement_status(state=None, create_datetime=None):
+    """
+    Helper method to create a RetirementStatus with useful defaults.
+    Assumes that retirement states have been setup before calling.
+    """
+    if create_datetime is None:
+        create_datetime = datetime.datetime.now(pytz.UTC) - datetime.timedelta(days=8)
+
+    user = UserFactory()
+    retirement = UserRetirementStatus.create_retirement(user)
+    if state:
+        retirement.current_state = state
+        retirement.last_state = state
+    retirement.created = create_datetime
+    retirement.modified = create_datetime
+    retirement.save()
+    return retirement
+
+
+def _fake_logged_out_user(user):
+    # Simulate the initial logout retirement endpoint.
+    user.username = get_retired_username_by_username(user.username)
+    user.email = get_retired_email_by_email(user.email)
+    user.set_unusable_password()
+    user.save()
+
+
+@pytest.fixture
+def logged_out_retirement_request():
+    """
+    Returns a UserRetirementStatus test fixture object that has been logged out and email-changed,
+    which is the first step which happens to a user being added to the retirement queue.
+    """
+    retirement = create_retirement_status()
+    _fake_logged_out_user(retirement.user)
+    return retirement
+
+
+@pytest.mark.usefixtures("setup_retirement_states")
+class RetirementTestCase(TestCase):
+    """
+    Test case with a helper methods for retirement
+    """
     def _retirement_to_dict(self, retirement, all_fields=False):
         """
         Return a dict format of this model to a consistent format for serialization, removing the long text field
@@ -131,7 +149,7 @@ class RetirementTestCase(TestCase):
         return retirement_dict
 
     def _create_users_all_states(self):
-        return [self._create_retirement(state) for state in RetirementState.objects.all()]
+        return [create_retirement_status(state) for state in RetirementState.objects.all()]
 
     def _get_non_dead_end_states(self):
         return [state for state in RetirementState.objects.filter(is_dead_end_state=False)]
@@ -140,7 +158,7 @@ class RetirementTestCase(TestCase):
         return [state for state in RetirementState.objects.filter(is_dead_end_state=True)]
 
 
-def fake_retirement(user):
+def fake_completed_retirement(user):
     """
     Makes an attempt to put user for the given user into a "COMPLETED"
     retirement state by faking important parts of retirement.
@@ -151,12 +169,10 @@ def fake_retirement(user):
     """
     # Deactivate / logout and hash username & email
     UserSocialAuth.objects.filter(user_id=user.id).delete()
+    _fake_logged_out_user(user)
     user.first_name = ''
     user.last_name = ''
     user.is_active = False
-    user.username = get_retired_username_by_username(user.username)
-    user.email = get_retired_email_by_email(user.email)
-    user.set_unusable_password()
     user.save()
 
     # Clear profile
diff --git a/openedx/core/djangoapps/user_api/accounts/tests/test_models.py b/openedx/core/djangoapps/user_api/accounts/tests/test_models.py
index 1f59f86c311341ec422877b0b88f8da76ad99a58..e903ed0bd647c0f1a133059ead0d0246c66efda6 100644
--- a/openedx/core/djangoapps/user_api/accounts/tests/test_models.py
+++ b/openedx/core/djangoapps/user_api/accounts/tests/test_models.py
@@ -11,38 +11,13 @@ from openedx.core.djangoapps.user_api.models import (
 )
 from student.models import get_retired_email_by_email, get_retired_username_by_username
 from student.tests.factories import UserFactory
+from .retirement_helpers import setup_retirement_states  # pylint: disable=unused-import
 
 
 # Tell pytest it's ok to use the database
 pytestmark = pytest.mark.django_db
 
 
-@pytest.fixture
-def setup_retirement_states():
-    """
-    Pytest fixture to create some basic states for testing. Duplicates functionality of the
-    Django test runner in test_views.py unfortunately, but they're not compatible.
-    """
-    default_states = [
-        ('PENDING', 1, False, True),
-        ('LOCKING_ACCOUNT', 20, False, False),
-        ('LOCKING_COMPLETE', 30, False, False),
-        ('RETIRING_LMS', 40, False, False),
-        ('LMS_COMPLETE', 50, False, False),
-        ('ERRORED', 60, True, True),
-        ('ABORTED', 70, True, True),
-        ('COMPLETE', 80, True, True),
-    ]
-
-    for name, ex, dead, req in default_states:
-        RetirementState.objects.create(
-            state_name=name,
-            state_execution_order=ex,
-            is_dead_end_state=dead,
-            required=req
-        )
-
-
 def _assert_retirementstatus_is_user(retirement, user):
     """
     Helper function to compare a newly created UserRetirementStatus object to expected values for
diff --git a/openedx/core/djangoapps/user_api/accounts/tests/test_retirement_views.py b/openedx/core/djangoapps/user_api/accounts/tests/test_retirement_views.py
index c3b8b718e48b218844879877fcb81017f6255c30..f820ca87e1643db50ed61cf9dcb6a28431ebe8cd 100644
--- a/openedx/core/djangoapps/user_api/accounts/tests/test_retirement_views.py
+++ b/openedx/core/djangoapps/user_api/accounts/tests/test_retirement_views.py
@@ -78,7 +78,12 @@ from student.tests.factories import (
 
 from ..views import AccountRetirementView, USER_PROFILE_PII
 from ...tests.factories import UserOrgTagFactory
-from .retirement_helpers import RetirementTestCase, fake_retirement
+from .retirement_helpers import (  # pylint: disable=unused-import
+    RetirementTestCase,
+    fake_completed_retirement,
+    create_retirement_status,
+    setup_retirement_states
+)
 
 
 def build_jwt_headers(user):
@@ -255,8 +260,7 @@ class TestAccountRetireMailings(RetirementTestCase):
 
         # Should be created in parent setUpClass
         retiring_email_lists = RetirementState.objects.get(state_name='RETIRING_EMAIL_LISTS')
-
-        self.retirement = self._create_retirement(retiring_email_lists)
+        self.retirement = create_retirement_status(retiring_email_lists)
         self.test_user = self.retirement.user
 
         self.url = reverse('accounts_retire_mailings')
@@ -456,7 +460,7 @@ class TestPartnerReportingPut(RetirementTestCase, ModuleStoreTestCase):
         Checks the simple success case of creating a user, enrolling in a course, and doing the partner
         report PUT. User should then have the appropriate row in UserRetirementPartnerReportingStatus
         """
-        retirement = self._create_retirement(self.partner_queue_state)
+        retirement = create_retirement_status(self.partner_queue_state)
         for course in self.courses:
             CourseEnrollment.enroll(user=retirement.user, course_key=course.id)
 
@@ -467,7 +471,7 @@ class TestPartnerReportingPut(RetirementTestCase, ModuleStoreTestCase):
         """
         Runs the success test twice to make sure that re-running the step still succeeds.
         """
-        retirement = self._create_retirement(self.partner_queue_state)
+        retirement = create_retirement_status(self.partner_queue_state)
         for course in self.courses:
             CourseEnrollment.enroll(user=retirement.user, course_key=course.id)
 
@@ -475,7 +479,7 @@ class TestPartnerReportingPut(RetirementTestCase, ModuleStoreTestCase):
         self.put_and_assert_status({'username': retirement.original_username})
 
         # Do our basic other retirement step fakery
-        fake_retirement(retirement.user)
+        fake_completed_retirement(retirement.user)
 
         # Try running our step again
         self.put_and_assert_status({'username': retirement.original_username})
@@ -500,7 +504,7 @@ class TestPartnerReportingPut(RetirementTestCase, ModuleStoreTestCase):
         the enrollment.course.org. We now just use the enrollment.course_id.org
         since for this purpose we don't care if the course exists.
         """
-        retirement = self._create_retirement(self.partner_queue_state)
+        retirement = create_retirement_status(self.partner_queue_state)
         user = retirement.user
         enrollment = CourseEnrollment.enroll(user=user, course_key=CourseKey.from_string('edX/Test201/2018_Fall'))
 
@@ -717,7 +721,7 @@ class TestAccountRetirementList(RetirementTestCase):
         Verify that users in dead end states are not returned
         """
         for state in self._get_dead_end_states():
-            self._create_retirement(state)
+            create_retirement_status(state)
         self.assert_status_and_user_list([], states_to_request=self._get_non_dead_end_states())
 
     def test_users_retrieved_in_multiple_states(self):
@@ -726,7 +730,7 @@ class TestAccountRetirementList(RetirementTestCase):
         """
         multiple_states = ['PENDING', 'FORUMS_COMPLETE']
         for state in multiple_states:
-            self._create_retirement(RetirementState.objects.get(state_name=state))
+            create_retirement_status(RetirementState.objects.get(state_name=state))
         data = {'cool_off_days': 0, 'states': multiple_states}
         response = self.client.get(self.url, data, **self.headers)
         self.assertEqual(response.status_code, status.HTTP_200_OK)
@@ -763,7 +767,7 @@ class TestAccountRetirementList(RetirementTestCase):
         pending_state = RetirementState.objects.get(state_name='PENDING')
         for days_back in range(1, days_back_to_test, -1):
             create_datetime = datetime.datetime.now(pytz.UTC) - datetime.timedelta(days=days_back)
-            retirements.append(self._create_retirement(state=pending_state, create_datetime=create_datetime))
+            retirements.append(create_retirement_status(state=pending_state, create_datetime=create_datetime))
 
         # Confirm we get the correct number and data back for each day we add to cool off days
         # For each day we add to `cool_off_days` we expect to get one fewer retirement.
@@ -886,7 +890,7 @@ class TestAccountRetirementsByStatusAndDate(RetirementTestCase):
         Verify that users in non-requested states are not returned
         """
         state = RetirementState.objects.get(state_name='PENDING')
-        self._create_retirement(state=state)
+        create_retirement_status(state=state)
         self.assert_status_and_user_list([])
 
     def test_users_exist(self):
@@ -917,7 +921,7 @@ class TestAccountRetirementsByStatusAndDate(RetirementTestCase):
         # Create retirements for the last 10 days
         for days_back in range(0, 10):
             create_datetime = datetime.datetime.now(pytz.UTC) - datetime.timedelta(days=days_back)
-            ret = self._create_retirement(state=complete_state, create_datetime=create_datetime)
+            ret = create_retirement_status(state=complete_state, create_datetime=create_datetime)
             retirements.append(self._retirement_to_dict(ret))
 
         # Go back in time adding days to the query, assert the correct retirements are present
@@ -1010,7 +1014,7 @@ class TestAccountRetirementRetrieve(RetirementTestCase):
         retirements = []
 
         for state in RetirementState.objects.all():
-            retirements.append(self._create_retirement(state))
+            retirements.append(create_retirement_status(state))
 
         for retirement in retirements:
             values = self._retirement_to_dict(retirement)
@@ -1021,7 +1025,7 @@ class TestAccountRetirementRetrieve(RetirementTestCase):
         Simulate retrieving a retirement by the old username, after the name has been changed to the hashed one
         """
         pending_state = RetirementState.objects.get(state_name='PENDING')
-        retirement = self._create_retirement(pending_state)
+        retirement = create_retirement_status(pending_state)
         original_username = retirement.user.username
 
         hashed_username = get_retired_username_by_username(original_username)
@@ -1044,7 +1048,7 @@ class TestAccountRetirementUpdate(RetirementTestCase):
         self.pending_state = RetirementState.objects.get(state_name='PENDING')
         self.locking_state = RetirementState.objects.get(state_name='LOCKING_ACCOUNT')
 
-        self.retirement = self._create_retirement(self.pending_state)
+        self.retirement = create_retirement_status(self.pending_state)
         self.test_user = self.retirement.user
         self.test_superuser = SuperuserFactory()
         self.headers = build_jwt_headers(self.test_superuser)
@@ -1354,7 +1358,7 @@ class TestAccountRetirementPost(RetirementTestCase):
     def test_retire_user_twice_idempotent(self):
         data = {'username': self.original_username}
         self.post_and_assert_status(data)
-        fake_retirement(self.test_user)
+        fake_completed_retirement(self.test_user)
         self.post_and_assert_status(data)
 
     def test_deletes_pii_from_user_profile(self):
@@ -1574,5 +1578,5 @@ class TestLMSAccountRetirementPost(RetirementTestCase, ModuleStoreTestCase):
         # check that a second call to the retire_misc endpoint will work
         data = {'username': self.original_username}
         self.post_and_assert_status(data)
-        fake_retirement(self.test_user)
+        fake_completed_retirement(self.test_user)
         self.post_and_assert_status(data)
diff --git a/openedx/core/djangoapps/user_api/management/commands/cancel_user_retirement_request.py b/openedx/core/djangoapps/user_api/management/commands/cancel_user_retirement_request.py
new file mode 100644
index 0000000000000000000000000000000000000000..0b7f8f85bcd7bdc7fbee2064d97e9c768225d2ee
--- /dev/null
+++ b/openedx/core/djangoapps/user_api/management/commands/cancel_user_retirement_request.py
@@ -0,0 +1,59 @@
+"""
+Use this mgmt command when a user requests retirement mistakenly, then requests
+for the retirement request to be cancelled. The command can't cancel a retirement
+that has already commenced - only pending retirements.
+"""
+from __future__ import print_function
+
+import logging
+
+from django.core.management.base import BaseCommand, CommandError
+from openedx.core.djangoapps.user_api.models import UserRetirementStatus
+
+
+LOGGER = logging.getLogger(__name__)
+
+
+class Command(BaseCommand):
+    """
+    Implementation of the cancel_user_retirement_request command.
+    """
+    help = 'Cancels the retirement of a user who has requested retirement - but has not yet been retired.'
+
+    def add_arguments(self, parser):
+        parser.add_argument('email_address',
+                            help='Email address of user whose retirement request will be cancelled.')
+
+    def handle(self, *args, **options):
+        """
+        Execute the command.
+        """
+        email_address = options['email_address'].lower()
+
+        try:
+            # Load the user retirement status.
+            retirement_status = UserRetirementStatus.objects.select_related('current_state').select_related('user').get(
+                original_email=email_address
+            )
+        except UserRetirementStatus.DoesNotExist:
+            raise CommandError("No retirement request with email address '{}' exists.".format(email_address))
+
+        # Check if the user has started the retirement process -or- not.
+        if retirement_status.current_state.state_name != 'PENDING':
+            raise CommandError(
+                "Retirement requests can only be cancelled for users in the PENDING state."
+                " Current request state for '{}': {}".format(
+                    email_address,
+                    retirement_status.current_state.state_name
+                )
+            )
+
+        # Load the user record using the retired email address -and- change the email address back.
+        retirement_status.user.email = email_address
+        retirement_status.user.save()
+
+        # Delete the user retirement status record.
+        # No need to delete the accompanying "permanent" retirement request record - it gets done via Django signal.
+        retirement_status.delete()
+
+        print("Successfully cancelled retirement request for user with email address '{}'.")
diff --git a/openedx/core/djangoapps/user_api/management/tests/test_cancel_retirement.py b/openedx/core/djangoapps/user_api/management/tests/test_cancel_retirement.py
new file mode 100644
index 0000000000000000000000000000000000000000..609c8356549a68967f6e01929e2661f977d368c6
--- /dev/null
+++ b/openedx/core/djangoapps/user_api/management/tests/test_cancel_retirement.py
@@ -0,0 +1,50 @@
+"""
+Test the cancel_user_retirement_request management command
+"""
+import pytest
+from django.contrib.auth.models import User
+from django.core.management import CommandError, call_command
+
+from openedx.core.djangoapps.user_api.accounts.tests.retirement_helpers import (  # pylint: disable=unused-import
+    logged_out_retirement_request,
+    setup_retirement_states
+)
+from openedx.core.djangoapps.user_api.models import RetirementState, UserRetirementRequest, UserRetirementStatus
+from student.tests.factories import UserFactory
+
+pytestmark = pytest.mark.django_db
+
+
+def test_successful_cancellation(setup_retirement_states, logged_out_retirement_request):  # pylint: disable=redefined-outer-name, unused-argument
+    """
+    Test a successfully cancelled retirement request.
+    """
+    call_command('cancel_user_retirement_request', logged_out_retirement_request.original_email)
+    # Confirm that no retirement status exists for the user.
+    with pytest.raises(UserRetirementStatus.DoesNotExist):
+        UserRetirementStatus.objects.get(original_email=logged_out_retirement_request.user.email)
+    # Confirm that no retirement request exists for the user.
+    with pytest.raises(UserRetirementRequest.DoesNotExist):
+        UserRetirementRequest.objects.get(user=logged_out_retirement_request.user)
+    # Ensure user can be retrieved using the original email address.
+    User.objects.get(email=logged_out_retirement_request.original_email)
+
+
+def test_cancellation_in_unrecoverable_state(setup_retirement_states, logged_out_retirement_request):  # pylint: disable=redefined-outer-name, unused-argument
+    """
+    Test a failed cancellation of a retirement request due to the retirement already beginning.
+    """
+    retiring_lms_state = RetirementState.objects.get(state_name='RETIRING_LMS')
+    logged_out_retirement_request.current_state = retiring_lms_state
+    logged_out_retirement_request.save()
+    with pytest.raises(CommandError, match=r'Retirement requests can only be cancelled for users in the PENDING state'):
+        call_command('cancel_user_retirement_request', logged_out_retirement_request.original_email)
+
+
+def test_cancellation_unknown_email_address(setup_retirement_states, logged_out_retirement_request):  # pylint: disable=redefined-outer-name, unused-argument
+    """
+    Test attempting to cancel a non-existent request of a user.
+    """
+    user = UserFactory()
+    with pytest.raises(CommandError, match=r'No retirement request with email address'):
+        call_command('cancel_user_retirement_request', user.email)
diff --git a/openedx/core/djangoapps/user_api/tests/test_views.py b/openedx/core/djangoapps/user_api/tests/test_views.py
index 0c2576a0e92e88481571b7e68cbcb5dad3fe6a32..d756eaa7f2951029273c79158d22c44f3c9f5ac9 100644
--- a/openedx/core/djangoapps/user_api/tests/test_views.py
+++ b/openedx/core/djangoapps/user_api/tests/test_views.py
@@ -41,6 +41,7 @@ from ..accounts import (
     NAME_MAX_LENGTH, EMAIL_MIN_LENGTH, EMAIL_MAX_LENGTH,
     USERNAME_MIN_LENGTH, USERNAME_MAX_LENGTH, USERNAME_BAD_LENGTH_MSG
 )
+from ..accounts.tests.retirement_helpers import setup_retirement_states  # pylint: disable=unused-import
 from ..accounts.api import get_account_settings
 from ..models import UserOrgTag
 from ..tests.factories import UserPreferenceFactory