Files
edx-platform/lms/djangoapps/program_enrollments/api/tests/test_linking.py
Feanil Patel 9cf2f9f298 Run 2to3 -f future . -w
This will remove imports from __future__ that are no longer needed.

https://docs.python.org/3.5/library/2to3.html#2to3fixer-future
2019-12-30 10:35:30 -05:00

345 lines
14 KiB
Python

"""
Tests for account linking Python API.
"""
from uuid import uuid4
from django.test import TestCase
from edx_django_utils.cache import RequestCache
from opaque_keys.edx.keys import CourseKey
from testfixtures import LogCapture
from lms.djangoapps.program_enrollments.tests.factories import ProgramCourseEnrollmentFactory, ProgramEnrollmentFactory
from openedx.core.djangoapps.content.course_overviews.tests.factories import CourseOverviewFactory
from student.tests.factories import UserFactory
from ..linking import (
NO_LMS_USER_TEMPLATE,
NO_PROGRAM_ENROLLMENT_TEMPLATE,
_user_already_linked_message,
link_program_enrollments
)
LOG_PATH = 'lms.djangoapps.program_enrollments.api.linking'
class TestLinkProgramEnrollmentsMixin(object):
""" Utility methods and test data for testing linking """
@classmethod
def setUpTestData(cls): # pylint: disable=missing-docstring
cls.program = uuid4()
cls.curriculum = uuid4()
cls.other_program = uuid4()
cls.fruit_course = CourseKey.from_string('course-v1:edX+Oranges+Apples')
cls.animal_course = CourseKey.from_string('course-v1:edX+Cats+Dogs')
CourseOverviewFactory.create(id=cls.fruit_course)
CourseOverviewFactory.create(id=cls.animal_course)
def setUp(self):
self.user_1 = UserFactory.create()
self.user_2 = UserFactory.create()
def tearDown(self):
RequestCache.clear_all_namespaces()
def _create_waiting_enrollment(self, program_uuid, external_user_key):
"""
Create a waiting program enrollment for the given program and external user key.
"""
return ProgramEnrollmentFactory.create(
user=None,
program_uuid=program_uuid,
curriculum_uuid=self.curriculum,
external_user_key=external_user_key,
)
def _create_waiting_course_enrollment(self, program_enrollment, course_key, status='active'):
"""
Create a waiting program course enrollment for the given program enrollment,
course key, and optionally status.
"""
return ProgramCourseEnrollmentFactory.create(
program_enrollment=program_enrollment,
course_key=course_key,
course_enrollment=None,
status=status,
)
def _assert_no_user(self, program_enrollment, refresh=True):
"""
Assert that the given program enrollment has no LMS user associated with it
"""
if refresh:
program_enrollment.refresh_from_db()
self.assertIsNone(program_enrollment.user)
def _assert_no_program_enrollment(self, user, program_uuid, refresh=True):
"""
Assert that the given user is not enrolled in the given program
"""
if refresh:
user.refresh_from_db()
self.assertFalse(user.programenrollment_set.filter(program_uuid=program_uuid).exists())
def _assert_program_enrollment(self, user, program_uuid, external_user_key, refresh=True):
"""
Assert that the given user is enrolled in the given program with the
given external user key.
"""
if refresh:
user.refresh_from_db()
enrollment = user.programenrollment_set.get(
program_uuid=program_uuid, external_user_key=external_user_key
)
self.assertIsNotNone(enrollment)
def _assert_user_enrolled_in_program_courses(self, user, program_uuid, *course_keys):
"""
Assert that the given user is has active enrollments in the given courses
through the given program.
"""
user.refresh_from_db()
program_enrollment = user.programenrollment_set.get(
user=user, program_uuid=program_uuid
)
all_course_enrollments = program_enrollment.program_course_enrollments
program_course_enrollments = all_course_enrollments.select_related(
'course_enrollment__course'
).filter(
course_enrollment__isnull=False
)
course_enrollments = [
program_course_enrollment.course_enrollment
for program_course_enrollment in program_course_enrollments
]
self.assertTrue(
all(course_enrollment.is_active for course_enrollment in course_enrollments)
)
self.assertCountEqual(
course_keys,
[course_enrollment.course.id for course_enrollment in course_enrollments]
)
class TestLinkProgramEnrollments(TestLinkProgramEnrollmentsMixin, TestCase):
""" Tests for linking behavior """
def test_link_only_specified_program(self):
"""
Test that when there are two waiting program enrollments with the same external user key,
only the specified program's program enrollment will be linked
"""
program_enrollment = self._create_waiting_enrollment(self.program, '0001')
self._create_waiting_course_enrollment(program_enrollment, self.fruit_course)
self._create_waiting_course_enrollment(program_enrollment, self.animal_course)
another_program_enrollment = self._create_waiting_enrollment(self.other_program, '0001')
self._create_waiting_course_enrollment(another_program_enrollment, self.fruit_course)
self._create_waiting_course_enrollment(another_program_enrollment, self.animal_course)
link_program_enrollments(self.program, {'0001': self.user_1.username})
self._assert_program_enrollment(self.user_1, self.program, '0001')
self._assert_user_enrolled_in_program_courses(
self.user_1, self.program, self.fruit_course, self.animal_course
)
self._assert_no_user(another_program_enrollment)
def test_inactive_waiting_course_enrollment(self):
"""
Test that when a waiting program enrollment has waiting program course enrollments with a
status of 'inactive' the course enrollment created after calling link_program_enrollments
will be inactive.
"""
program_enrollment = self._create_waiting_enrollment(self.program, '0001')
active_enrollment = self._create_waiting_course_enrollment(
program_enrollment,
self.fruit_course
)
inactive_enrollment = self._create_waiting_course_enrollment(
program_enrollment,
self.animal_course,
status='inactive'
)
link_program_enrollments(self.program, {'0001': self.user_1.username})
self._assert_program_enrollment(self.user_1, self.program, '0001')
active_enrollment.refresh_from_db()
self.assertIsNotNone(active_enrollment.course_enrollment)
self.assertEqual(active_enrollment.course_enrollment.course.id, self.fruit_course)
self.assertTrue(active_enrollment.course_enrollment.is_active)
inactive_enrollment.refresh_from_db()
self.assertIsNotNone(inactive_enrollment.course_enrollment)
self.assertEqual(inactive_enrollment.course_enrollment.course.id, self.animal_course)
self.assertFalse(inactive_enrollment.course_enrollment.is_active)
class TestLinkProgramEnrollmentsErrors(TestLinkProgramEnrollmentsMixin, TestCase):
""" Tests for linking error behavior """
def test_program_enrollment_not_found__nonexistant(self):
self._create_waiting_enrollment(self.program, '0001')
self._program_enrollment_not_found()
def test_program_enrollment_not_found__different_program(self):
self._create_waiting_enrollment(self.program, '0001')
self._create_waiting_enrollment(self.other_program, '0002')
self._program_enrollment_not_found()
def _program_enrollment_not_found(self):
"""
Helper for test_program_not_found_* tests.
tries to link user_1 to '0001' and user_2 to '0002' in program
asserts that user_2 was not linked because the enrollment was not found
"""
with LogCapture() as logger:
errors = link_program_enrollments(
self.program,
{
'0001': self.user_1.username,
'0002': self.user_2.username,
}
)
expected_error_msg = NO_PROGRAM_ENROLLMENT_TEMPLATE.format(
program_uuid=self.program,
external_student_key='0002'
)
logger.check_present((LOG_PATH, 'WARNING', expected_error_msg))
self.assertDictEqual(errors, {'0002': expected_error_msg})
self._assert_program_enrollment(self.user_1, self.program, '0001')
self._assert_no_program_enrollment(self.user_2, self.program)
def test_user_not_found(self):
self._create_waiting_enrollment(self.program, '0001')
enrollment_2 = self._create_waiting_enrollment(self.program, '0002')
with LogCapture() as logger:
errors = link_program_enrollments(
self.program,
{
'0001': self.user_1.username,
'0002': 'nonexistant-user',
}
)
expected_error_msg = NO_LMS_USER_TEMPLATE.format('nonexistant-user')
logger.check_present((LOG_PATH, 'WARNING', expected_error_msg))
self.assertDictEqual(errors, {'0002': expected_error_msg})
self._assert_program_enrollment(self.user_1, self.program, '0001')
self._assert_no_user(enrollment_2)
def test_enrollment_already_linked_to_target_user(self):
self._create_waiting_enrollment(self.program, '0001')
program_enrollment = ProgramEnrollmentFactory.create(
user=self.user_2,
program_uuid=self.program,
external_user_key='0002',
)
self._assert_no_program_enrollment(self.user_1, self.program, refresh=False)
self._assert_program_enrollment(self.user_2, self.program, '0002', refresh=False)
with LogCapture() as logger:
errors = link_program_enrollments(
self.program,
{
'0001': self.user_1.username,
'0002': self.user_2.username
}
)
expected_error_msg = _user_already_linked_message(program_enrollment, self.user_2)
logger.check_present((LOG_PATH, 'WARNING', expected_error_msg))
self.assertDictEqual(errors, {'0002': expected_error_msg})
self._assert_program_enrollment(self.user_1, self.program, '0001')
self._assert_program_enrollment(self.user_2, self.program, '0002')
def test_enrollment_already_linked_to_different_user(self):
self._create_waiting_enrollment(self.program, '0001')
enrollment = ProgramEnrollmentFactory.create(
program_uuid=self.program,
external_user_key='0003',
)
user_3 = enrollment.user
self._assert_no_program_enrollment(self.user_1, self.program, refresh=False)
self._assert_no_program_enrollment(self.user_2, self.program, refresh=False)
self._assert_program_enrollment(user_3, self.program, '0003', refresh=False)
with LogCapture() as logger:
errors = link_program_enrollments(
self.program,
{
'0001': self.user_1.username,
'0003': self.user_2.username,
}
)
expected_error_msg = _user_already_linked_message(enrollment, self.user_2)
logger.check_present((LOG_PATH, 'WARNING', expected_error_msg))
self.assertDictEqual(errors, {'0003': expected_error_msg})
self._assert_program_enrollment(self.user_1, self.program, '0001')
self._assert_no_program_enrollment(self.user_2, self.program)
self._assert_program_enrollment(user_3, self.program, '0003')
def test_error_enrolling_in_course(self):
nonexistant_course = CourseKey.from_string('course-v1:edX+Zilch+Bupkis')
program_enrollment_1 = self._create_waiting_enrollment(self.program, '0001')
course_enrollment_1 = self._create_waiting_course_enrollment(
program_enrollment_1, nonexistant_course
)
course_enrollment_2 = self._create_waiting_course_enrollment(
program_enrollment_1, self.animal_course
)
program_enrollment_2 = self._create_waiting_enrollment(self.program, '0002')
self._create_waiting_course_enrollment(program_enrollment_2, self.fruit_course)
self._create_waiting_course_enrollment(program_enrollment_2, self.animal_course)
errors = link_program_enrollments(
self.program,
{
'0001': self.user_1.username,
'0002': self.user_2.username
}
)
self.assertIn(errors['0001'], 'NonExistentCourseError: ')
self._assert_no_program_enrollment(self.user_1, self.program)
self._assert_no_user(program_enrollment_1)
course_enrollment_1.refresh_from_db()
self.assertIsNone(course_enrollment_1.course_enrollment)
course_enrollment_2.refresh_from_db()
self.assertIsNone(course_enrollment_2.course_enrollment)
self._assert_user_enrolled_in_program_courses(
self.user_2, self.program, self.animal_course, self.fruit_course
)
def test_integrity_error(self):
existing_program_enrollment = self._create_waiting_enrollment(self.program, 'learner-0')
existing_program_enrollment.user = self.user_1
existing_program_enrollment.save()
program_enrollment_1 = self._create_waiting_enrollment(self.program, '0001')
self._create_waiting_enrollment(self.program, '0002')
errors = link_program_enrollments(
self.program,
{
'0001': self.user_1.username,
'0002': self.user_2.username,
}
)
self.assertEqual(len(errors), 1)
self.assertIn('UNIQUE constraint failed', errors['0001'])
self._assert_no_user(program_enrollment_1)
self._assert_program_enrollment(self.user_2, self.program, '0002')