def add_user_to_cohorts_race_condition(self, mock_tracker): """ Makes use of before_after to force a race condition, in order to confirm handling of such conditions is done correctly. """ course_user = UserFactory(username="******", email="*****@*****.**") course = modulestore().get_course(self.toy_course_key) CourseEnrollment.enroll(course_user, self.toy_course_key) first_cohort = CohortFactory(course_id=course.id, name="FirstCohort") second_cohort = CohortFactory(course_id=course.id, name="SecondCohort") # This before_after contextmanager allows for reliable reproduction of a race condition. # It will break before the first save() call creates an entry, and then run add_user_to_cohort again. # Because this second call will write before control is returned, the first call will be writing stale data. # This test confirms that the first add_user_to_cohort call can handle this stale read condition properly. # Proper handling is defined as treating calls as sequential, with write time deciding the order. with before_after.before_after('django.db.models.Model.save', after_ftn=cohorts.add_user_to_cohort( second_cohort, course_user.username), autospec=True): # This method will read, then break, then try to write stale data. # It should fail at that, then retry with refreshed data cohorts.add_user_to_cohort(first_cohort, course_user.username) mock_tracker.emit.assert_any_call( "edx.cohort.user_add_requested", { "user_id": course_user.id, "cohort_id": first_cohort.id, "cohort_name": first_cohort.name, "previous_cohort_id": second_cohort.id, "previous_cohort_name": second_cohort.name, }) # Note that the following get() will fail with MultipleObjectsReturned if race condition is not handled. self.assertEqual(first_cohort.users.get(), course_user)
def test_before_and_after(self): def before_fn(*a): test_functions.test_list.append(1) def after_fn(*a): test_functions.test_list.append(3) with before_after('before_after.tests.test_functions.sample_fn', before_fn=before_fn, after_fn=after_fn): test_functions.sample_fn(2) self.assertEqual(test_functions.test_list, [1, 2, 3])
def add_user_to_cohorts_race_condition(self, mock_tracker): """ Makes use of before_after to force a race condition, in order to confirm handling of such conditions is done correctly. """ course_user = UserFactory(username="******", email="*****@*****.**") course = modulestore().get_course(self.toy_course_key) CourseEnrollment.enroll(course_user, self.toy_course_key) first_cohort = CohortFactory(course_id=course.id, name="FirstCohort") second_cohort = CohortFactory(course_id=course.id, name="SecondCohort") # This before_after contextmanager allows for reliable reproduction of a race condition. # It will break before the first save() call creates an entry, and then run add_user_to_cohort again. # Because this second call will write before control is returned, the first call will be writing stale data. # This test confirms that the first add_user_to_cohort call can handle this stale read condition properly. # Proper handling is defined as treating calls as sequential, with write time deciding the order. with before_after.before_after( 'django.db.models.Model.save', after_ftn=cohorts.add_user_to_cohort(second_cohort, course_user.username), autospec=True ): # This method will read, then break, then try to write stale data. # It should fail at that, then retry with refreshed data cohorts.add_user_to_cohort(first_cohort, course_user.username) mock_tracker.emit.assert_any_call( "edx.cohort.user_add_requested", { "user_id": course_user.id, "cohort_id": first_cohort.id, "cohort_name": first_cohort.name, "previous_cohort_id": second_cohort.id, "previous_cohort_name": second_cohort.name, } ) # Note that the following get() will fail with MultipleObjectsReturned if race condition is not handled. self.assertEqual(first_cohort.users.get(), course_user)