def test_multiple_courses(self): user = self.make_test_user(self.STUDENT_EMAIL) COURSE_TWO = 'course_two' COURSE_TWO_NS = 'ns_' + COURSE_TWO actions.simple_add_course( COURSE_TWO, self.ADMIN_EMAIL, 'Data Removal Test Two') actions.login(user.email()) actions.register(self, user.email(), course=self.COURSE) actions.register(self, user.email(), course=COURSE_TWO) actions.unregister(self, self.COURSE, do_data_removal=True) self.execute_all_deferred_tasks( models.StudentLifecycleObserver.QUEUE_NAME) self.get( data_removal.DataRemovalCronHandler.URL, headers={'X-AppEngine-Cron': 'True'}) self.execute_all_deferred_tasks() with common_utils.Namespace(self.NAMESPACE): self.assertIsNone(models.Student.get_by_user(user)) with common_utils.Namespace(COURSE_TWO_NS): self.assertIsNotNone( models.Student.get_by_user(user))
def test_notifications_succeed(self): actions.login(self.STUDENT_EMAIL) user_id = None actions.register(self, self.STUDENT_EMAIL) self.assertIsNone(self._user_id) self.execute_all_deferred_tasks( models.StudentLifecycleObserver.QUEUE_NAME) self.assertIsNotNone(self._user_id) user_id = self._user_id self.assertEquals(1, self._num_add_calls) self.assertEquals(0, self._num_unenroll_calls) self.assertEquals(0, self._num_reenroll_calls) actions.unregister(self) self.execute_all_deferred_tasks( models.StudentLifecycleObserver.QUEUE_NAME) self.assertEquals(1, self._num_add_calls) self.assertEquals(1, self._num_unenroll_calls) self.assertEquals(0, self._num_reenroll_calls) with common_utils.Namespace(self.NAMESPACE): models.StudentProfileDAO.update( user_id, self.STUDENT_EMAIL, is_enrolled=True) self.execute_all_deferred_tasks( models.StudentLifecycleObserver.QUEUE_NAME) self.assertEquals(1, self._num_add_calls) self.assertEquals(1, self._num_unenroll_calls) self.assertEquals(1, self._num_reenroll_calls)
def test_notifications_succeed(self): actions.login(self.STUDENT_EMAIL) user_id = None actions.register(self, self.STUDENT_EMAIL) self.assertIsNone(self._user_id) self.execute_all_deferred_tasks( models.StudentLifecycleObserver.QUEUE_NAME) self.assertIsNotNone(self._user_id) user_id = self._user_id self.assertEquals(1, self._num_add_calls) self.assertEquals(0, self._num_unenroll_calls) self.assertEquals(0, self._num_reenroll_calls) actions.unregister(self) self.execute_all_deferred_tasks( models.StudentLifecycleObserver.QUEUE_NAME) self.assertEquals(1, self._num_add_calls) self.assertEquals(1, self._num_unenroll_calls) self.assertEquals(0, self._num_reenroll_calls) with common_utils.Namespace(self.NAMESPACE): models.StudentProfileDAO.update(user_id, self.STUDENT_EMAIL, is_enrolled=True) self.execute_all_deferred_tasks( models.StudentLifecycleObserver.QUEUE_NAME) self.assertEquals(1, self._num_add_calls) self.assertEquals(1, self._num_unenroll_calls) self.assertEquals(1, self._num_reenroll_calls)
def test_end_to_end(self): """Actually enroll and unenroll students; verify reporting counts.""" COURSE_NAME_BASE = 'test' NUM_COURSES = 2 NUM_STUDENTS = 3 THE_TIMESTAMP = 1427245200 for course_num in range(NUM_COURSES): course_name = '%s_%d' % (COURSE_NAME_BASE, course_num) actions.simple_add_course(course_name, ADMIN_EMAIL, course_name) actions.update_course_config(course_name, { 'course': { 'now_available': True, 'browsable': True, }, }) for student_num in range(NUM_STUDENTS): name = '%s_%d_%d' % (COURSE_NAME_BASE, course_num, student_num) actions.login(name + '@foo.com') actions.register(self, name, course_name) if student_num == 0: actions.unregister(self, course_name) actions.logout() # Expect no messages yet; haven't run job. self.assertEquals([], MockSender.get_sent()) # Run all counting jobs. usage_reporting.StartReportingJobs._submit_jobs() self.execute_all_deferred_tasks() # Verify counts. (Ignore dates, these are fickle and subject to # weirdness on hour boundaries. Also ignore course/instance IDs; # they are non-random and thus all the same.) num_enrolled_msgs = 0 num_unenrolled_msgs = 0 num_student_count_msgs = 0 for message in MockSender.get_sent(): if (message[messaging.Message._METRIC] == messaging.Message.METRIC_STUDENT_COUNT): num_student_count_msgs += 1 self.assertEquals(NUM_STUDENTS, message[messaging.Message._VALUE]) elif (message[messaging.Message._METRIC] == messaging.Message.METRIC_ENROLLED): num_enrolled_msgs += 1 self.assertEquals(NUM_STUDENTS, message[messaging.Message._VALUE]) elif (message[messaging.Message._METRIC] == messaging.Message.METRIC_UNENROLLED): num_unenrolled_msgs += 1 self.assertEquals(1, message[messaging.Message._VALUE]) self.assertEquals(NUM_COURSES, num_enrolled_msgs) self.assertEquals(NUM_COURSES, num_unenrolled_msgs) self.assertEquals(NUM_COURSES, num_student_count_msgs) sites.reset_courses()
def test_delete_link_when_unregistered_then_proceed(self): user = actions.login(self.STUDENT_EMAIL) actions.register(self, self.STUDENT_EMAIL) actions.unregister(self) response = self.get('course') response = self.click(response, 'Delete My Data') self._deletion_flow_for_unregistered_student(response, cancel=False) response = self.get('course') self.assertNotIn('Delete My Data', response.body)
def test_student_property_removed(self): """Test a sampling of types whose index contains user ID. Here, indices start with the user ID, but are suffixed with the name of a specific property sub-type. Verify that these are removed. """ user = self.make_test_user(self.STUDENT_EMAIL) user_id = None actions.login(user.email()) actions.register(self, self.STUDENT_EMAIL, course=self.COURSE) # Get IDs of those students; make an event for each. with common_utils.Namespace(self.NAMESPACE): student = models.Student.get_by_user(user) user_id = student.user_id p = models.StudentPropertyEntity.create(student, 'foo') p.value = 'foo' p.put() invitation.InvitationStudentProperty.load_or_create(student) questionnaire.StudentFormEntity.load_or_create(student, 'a_form') cm = competency.BaseCompetencyMeasure(user_id) cm.load(123) cm.save() # Assure ourselves that we have exactly one of the items we just added. with common_utils.Namespace(self.NAMESPACE): l = list(models.StudentPropertyEntity.all().run()) self.assertEquals(2, len(l)) # 'foo', 'linear-course-completion' l = list(invitation.InvitationStudentProperty.all().run()) self.assertEquals(1, len(l)) l = list(questionnaire.StudentFormEntity.all().run()) self.assertEquals(1, len(l)) l = list(competency.CompetencyMeasureEntity.all().run()) self.assertEquals(1, len(l)) actions.unregister(self, self.COURSE, do_data_removal=True) self.execute_all_deferred_tasks( models.StudentLifecycleObserver.QUEUE_NAME) self.get( data_removal.DataRemovalCronHandler.URL, headers={'X-AppEngine-Cron': 'True'}) self.execute_all_deferred_tasks() # Assure ourselves that all added items are now gone. with common_utils.Namespace(self.NAMESPACE): l = list(models.StudentPropertyEntity.all().run()) self.assertEquals(0, len(l)) l = list(invitation.InvitationStudentProperty.all().run()) self.assertEquals(0, len(l)) l = list(questionnaire.StudentFormEntity.all().run()) self.assertEquals(0, len(l)) l = list(competency.CompetencyMeasureEntity.all().run()) self.assertEquals(0, len(l))
def test_student_property_removed(self): """Test a sampling of types whose index contains user ID. Here, indices start with the user ID, but are suffixed with the name of a specific property sub-type. Verify that these are removed. """ user = self.make_test_user(self.STUDENT_EMAIL) user_id = None actions.login(user.email()) actions.register(self, self.STUDENT_EMAIL, course=self.COURSE) # Get IDs of those students; make an event for each. with common_utils.Namespace(self.NAMESPACE): student = models.Student.get_by_user(user) user_id = student.user_id p = models.StudentPropertyEntity.create(student, 'foo') p.value = 'foo' p.put() invitation.InvitationStudentProperty.load_or_create(student) questionnaire.StudentFormEntity.load_or_create(student, 'a_form') cm = competency.BaseCompetencyMeasure(user_id) cm.load(123) cm.save() # Assure ourselves that we have exactly one of the items we just added. with common_utils.Namespace(self.NAMESPACE): l = list(models.StudentPropertyEntity.all().run()) self.assertEquals(2, len(l)) # 'foo', 'linear-course-completion' l = list(invitation.InvitationStudentProperty.all().run()) self.assertEquals(1, len(l)) l = list(questionnaire.StudentFormEntity.all().run()) self.assertEquals(1, len(l)) l = list(competency.CompetencyMeasureEntity.all().run()) self.assertEquals(1, len(l)) actions.unregister(self, self.COURSE, do_data_removal=True) self.execute_all_deferred_tasks( models.StudentLifecycleObserver.QUEUE_NAME) self.get(data_removal.DataRemovalCronHandler.URL, headers={'X-AppEngine-Cron': 'True'}) self.execute_all_deferred_tasks() # Assure ourselves that all added items are now gone. with common_utils.Namespace(self.NAMESPACE): l = list(models.StudentPropertyEntity.all().run()) self.assertEquals(0, len(l)) l = list(invitation.InvitationStudentProperty.all().run()) self.assertEquals(0, len(l)) l = list(questionnaire.StudentFormEntity.all().run()) self.assertEquals(0, len(l)) l = list(competency.CompetencyMeasureEntity.all().run()) self.assertEquals(0, len(l))
def test_non_removal_policy(self): with actions.OverriddenEnvironment({ data_removal.DATA_REMOVAL_SETTINGS_SECTION: { data_removal.REMOVAL_POLICY: data_removal.IndefiniteRetentionPolicy.get_name() } }): user = actions.login(self.STUDENT_EMAIL) actions.register(self, self.STUDENT_EMAIL, course=self.COURSE) self.execute_all_deferred_tasks( models.StudentLifecycleObserver.QUEUE_NAME) with common_utils.Namespace(self.NAMESPACE): # After registration, we should have a student object, and no # ImmediateRemovalState instance due to the don't-care policy. student = models.Student.get_by_user(user) self.assertIsNotNone(student) self.assertIsNone( removal_models.ImmediateRemovalState.get_by_user_id( student.user_id)) r = removal_models.BatchRemovalState.get_by_user_ids( [student.user_id]) self.assertEqual([None], r) actions.unregister(self, course=self.COURSE) # Expect to see unregister event on queue -- register event handled # as part of actions.register. task_count = self.execute_all_deferred_tasks( models.StudentLifecycleObserver.QUEUE_NAME) self.assertEquals(1, task_count) # Running deletion cycle should have no effect. Verify that. self._complete_removal() with common_utils.Namespace(self.NAMESPACE): # After unregister, we should still have a student object. student = models.Student.get_by_user(user) self.assertIsNotNone(student) self.assertIsNone( removal_models.ImmediateRemovalState.get_by_user_id( student.user_id)) r = removal_models.BatchRemovalState.get_by_user_ids( [student.user_id]) self.assertEqual([None], r)
def test_non_removal_policy(self): with actions.OverriddenEnvironment({ data_removal.DATA_REMOVAL_SETTINGS_SECTION: { data_removal.REMOVAL_POLICY: data_removal.IndefiniteRetentionPolicy.get_name()}}): user = actions.login(self.STUDENT_EMAIL) actions.register(self, self.STUDENT_EMAIL, course=self.COURSE) self.execute_all_deferred_tasks( models.StudentLifecycleObserver.QUEUE_NAME) with common_utils.Namespace(self.NAMESPACE): # After registration, we should have a student object, and no # ImmediateRemovalState instance due to the don't-care policy. student = models.Student.get_by_user(user) self.assertIsNotNone(student) self.assertIsNone( removal_models.ImmediateRemovalState.get_by_user_id( student.user_id)) r = removal_models.BatchRemovalState.get_by_user_ids( [student.user_id]) self.assertEqual([None], r) actions.unregister(self, course=self.COURSE) # Expect to see unregister event on queue -- register event handled # as part of actions.register. task_count = self.execute_all_deferred_tasks( models.StudentLifecycleObserver.QUEUE_NAME) self.assertEquals(1, task_count) # Running deletion cycle should have no effect. Verify that. self._complete_removal() with common_utils.Namespace(self.NAMESPACE): # After unregister, we should still have a student object. student = models.Student.get_by_user(user) self.assertIsNotNone(student) self.assertIsNone( removal_models.ImmediateRemovalState.get_by_user_id( student.user_id)) r = removal_models.BatchRemovalState.get_by_user_ids( [student.user_id]) self.assertEqual([None], r)
def test_reregistration_blocked_during_deletion(self): def assert_cannot_register(): response = self.get('register') self.assertIn('You cannot re-register for this course', response.body) self.assertNotIn('What is your name?', response.body) user = self.make_test_user(self.STUDENT_EMAIL) user_id = None actions.login(user.email()) actions.register(self, user.email()) with common_utils.Namespace(self.NAMESPACE): # After registration, we should have a student object, and # a ImmediateRemovalState instance. student = models.Student.get_by_user(user) self.assertIsNotNone(student) user_id = student.user_id actions.unregister(self, do_data_removal=True) assert_cannot_register() self.execute_all_deferred_tasks( models.StudentLifecycleObserver.QUEUE_NAME) assert_cannot_register() self.get( data_removal.DataRemovalCronHandler.URL, headers={'X-AppEngine-Cron': 'True'}) assert_cannot_register() # Can re-register after all items are cleaned. self.execute_all_deferred_tasks() with common_utils.Namespace(self.NAMESPACE): student = models.Student.get_by_user(user) self.assertIsNone(student) removal_state = removal_models.ImmediateRemovalState.get_by_user_id( user_id) self.assertIsNone(removal_state) actions.register(self, self.STUDENT_EMAIL)
def test_remove_by_email(self): user = self.make_test_user(self.STUDENT_EMAIL) actions.login(user.email()) actions.register(self, user.email(), course=self.COURSE) # Get IDs of those students; make an event for each. with common_utils.Namespace(self.NAMESPACE): sse = unsubscribe.SubscriptionStateEntity(key_name=user.email()) sse.save() actions.unregister(self, self.COURSE, do_data_removal=True) self.execute_all_deferred_tasks( models.StudentLifecycleObserver.QUEUE_NAME) self.get(data_removal.DataRemovalCronHandler.URL, headers={'X-AppEngine-Cron': 'True'}) self.execute_all_deferred_tasks() with common_utils.Namespace(self.NAMESPACE): l = list(unsubscribe.SubscriptionStateEntity.all().run()) self.assertEquals(0, len(l))
def test_reregistration_blocked_during_deletion(self): def assert_cannot_register(): response = self.get('register') self.assertIn('You cannot re-register for this course', response.body) self.assertNotIn('What is your name?', response.body) user = self.make_test_user(self.STUDENT_EMAIL) user_id = None actions.login(user.email()) actions.register(self, user.email()) with common_utils.Namespace(self.NAMESPACE): # After registration, we should have a student object, and # a ImmediateRemovalState instance. student = models.Student.get_by_user(user) self.assertIsNotNone(student) user_id = student.user_id actions.unregister(self, do_data_removal=True) assert_cannot_register() self.execute_all_deferred_tasks( models.StudentLifecycleObserver.QUEUE_NAME) assert_cannot_register() self.get(data_removal.DataRemovalCronHandler.URL, headers={'X-AppEngine-Cron': 'True'}) assert_cannot_register() # Can re-register after all items are cleaned. self.execute_all_deferred_tasks() with common_utils.Namespace(self.NAMESPACE): student = models.Student.get_by_user(user) self.assertIsNone(student) removal_state = removal_models.ImmediateRemovalState.get_by_user_id( user_id) self.assertIsNone(removal_state) actions.register(self, self.STUDENT_EMAIL)
def test_multiple_students(self): user = self.make_test_user(self.STUDENT_EMAIL) other_user = self.make_test_user('*****@*****.**') # Register two students actions.login(user.email()) actions.register(self, user.email(), course=self.COURSE) actions.login(other_user.email()) actions.register(self, other_user.email(), course=self.COURSE) # Get IDs of those students; make an event for each. with common_utils.Namespace(self.NAMESPACE): student1_id = ( models.Student.get_by_user(user).user_id) student2_id = ( models.Student.get_by_user(other_user).user_id) models.EventEntity(user_id=student1_id, source='test').put() models.EventEntity(user_id=student2_id, source='test').put() # Unregister one of them. actions.login(self.STUDENT_EMAIL) actions.unregister(self, self.COURSE, do_data_removal=True) # Complete all data removal tasks. self.execute_all_deferred_tasks( models.StudentLifecycleObserver.QUEUE_NAME) self.get( data_removal.DataRemovalCronHandler.URL, headers={'X-AppEngine-Cron': 'True'}) self.execute_all_deferred_tasks() # Unregistered student and his data are gone; still-registered # student's data is still present. with common_utils.Namespace(self.NAMESPACE): self.assertIsNone(models.Student.get_by_user(user)) self.assertIsNotNone(models.Student.get_by_user(other_user)) entities = list(models.EventEntity.all().run()) self.assertEquals(1, len(entities)) self.assertEquals(student2_id, entities[0].user_id)
def test_remove_by_email(self): user = self.make_test_user(self.STUDENT_EMAIL) actions.login(user.email()) actions.register(self, user.email(), course=self.COURSE) # Get IDs of those students; make an event for each. with common_utils.Namespace(self.NAMESPACE): sse = unsubscribe.SubscriptionStateEntity( key_name=user.email()) sse.save() actions.unregister(self, self.COURSE, do_data_removal=True) self.execute_all_deferred_tasks( models.StudentLifecycleObserver.QUEUE_NAME) self.get( data_removal.DataRemovalCronHandler.URL, headers={'X-AppEngine-Cron': 'True'}) self.execute_all_deferred_tasks() with common_utils.Namespace(self.NAMESPACE): l = list(unsubscribe.SubscriptionStateEntity.all().run()) self.assertEquals(0, len(l))
def test_multiple_students(self): user = self.make_test_user(self.STUDENT_EMAIL) other_user = self.make_test_user('*****@*****.**') # Register two students actions.login(user.email()) actions.register(self, user.email(), course=self.COURSE) actions.login(other_user.email()) actions.register(self, other_user.email(), course=self.COURSE) # Get IDs of those students; make an event for each. with common_utils.Namespace(self.NAMESPACE): student1_id = (models.Student.get_by_user(user).user_id) student2_id = (models.Student.get_by_user(other_user).user_id) models.EventEntity(user_id=student1_id, source='test').put() models.EventEntity(user_id=student2_id, source='test').put() # Unregister one of them. actions.login(self.STUDENT_EMAIL) actions.unregister(self, self.COURSE, do_data_removal=True) # Complete all data removal tasks. self.execute_all_deferred_tasks( models.StudentLifecycleObserver.QUEUE_NAME) self.get(data_removal.DataRemovalCronHandler.URL, headers={'X-AppEngine-Cron': 'True'}) self.execute_all_deferred_tasks() # Unregistered student and his data are gone; still-registered # student's data is still present. with common_utils.Namespace(self.NAMESPACE): self.assertIsNone(models.Student.get_by_user(user)) self.assertIsNotNone(models.Student.get_by_user(other_user)) entities = list(models.EventEntity.all().run()) self.assertEquals(1, len(entities)) self.assertEquals(student2_id, entities[0].user_id)
def test_multiple_courses(self): user = self.make_test_user(self.STUDENT_EMAIL) COURSE_TWO = 'course_two' COURSE_TWO_NS = 'ns_' + COURSE_TWO actions.simple_add_course(COURSE_TWO, self.ADMIN_EMAIL, 'Data Removal Test Two') actions.login(user.email()) actions.register(self, user.email(), course=self.COURSE) actions.register(self, user.email(), course=COURSE_TWO) actions.unregister(self, self.COURSE, do_data_removal=True) self.execute_all_deferred_tasks( models.StudentLifecycleObserver.QUEUE_NAME) self.get(data_removal.DataRemovalCronHandler.URL, headers={'X-AppEngine-Cron': 'True'}) self.execute_all_deferred_tasks() with common_utils.Namespace(self.NAMESPACE): self.assertIsNone(models.Student.get_by_user(user)) with common_utils.Namespace(COURSE_TWO_NS): self.assertIsNotNone(models.Student.get_by_user(user))
def unregister(self): self.base = '/test' actions.unregister(self) self.base = ''
def test_end_to_end(self): """Actually enroll and unenroll students; verify reporting counts.""" COURSE_NAME_BASE = 'test' NUM_COURSES = 2 NUM_STUDENTS = 3 THE_TIMESTAMP = 1427245200 for course_num in range(NUM_COURSES): course_name = '%s_%d' % (COURSE_NAME_BASE, course_num) actions.simple_add_course(course_name, ADMIN_EMAIL, course_name) actions.update_course_config( course_name, { 'course': { 'now_available': True, 'browsable': True, }, }) for student_num in range(NUM_STUDENTS): name = '%s_%d_%d' % (COURSE_NAME_BASE, course_num, student_num) actions.login(name + '@foo.com') actions.register(self, name, course_name) if student_num == 0: actions.unregister(self, course_name) actions.logout() # Expect no messages yet; haven't run job. self.assertEquals([], MockSender.get_sent()) # Run all counting jobs. with actions.OverriddenConfig(config.REPORT_ALLOWED.name, True): usage_reporting.StartReportingJobs._for_testing_only_get() self.execute_all_deferred_tasks( models.StudentLifecycleObserver.QUEUE_NAME) self.execute_all_deferred_tasks() # Verify counts. (Ignore dates, these are fickle and subject to # weirdness on hour boundaries. Also ignore course/instance IDs; # they are non-random and thus all the same.) num_enrolled_msgs = 0 num_unenrolled_msgs = 0 num_student_count_msgs = 0 for message in MockSender.get_sent(): if (message[messaging.Message._METRIC] == messaging.Message.METRIC_STUDENT_COUNT): num_student_count_msgs += 1 self.assertEquals( NUM_STUDENTS, message[messaging.Message._VALUE]) elif (message[messaging.Message._METRIC] == messaging.Message.METRIC_ENROLLED): num_enrolled_msgs += 1 self.assertEquals( NUM_STUDENTS, message[messaging.Message._VALUE]) elif (message[messaging.Message._METRIC] == messaging.Message.METRIC_UNENROLLED): num_unenrolled_msgs += 1 self.assertEquals( 1, message[messaging.Message._VALUE]) self.assertEquals(NUM_COURSES, num_enrolled_msgs) self.assertEquals(NUM_COURSES, num_unenrolled_msgs) self.assertEquals(NUM_COURSES, num_student_count_msgs) sites.reset_courses()
def test_immediate_removal_policy(self): user = self.make_test_user(self.STUDENT_EMAIL) actions.login(user.email()) actions.register(self, self.STUDENT_EMAIL, course=self.COURSE) task_count = self.execute_all_deferred_tasks( models.StudentLifecycleObserver.QUEUE_NAME) self.assertEquals(1, task_count) # registration. user_id = None with common_utils.Namespace(self.NAMESPACE): # After registration, we should have a student object, and # a ImmediateRemovalState instance. student = models.Student.get_by_user(user) self.assertIsNotNone(student) user_id = student.user_id removal_state = removal_models.ImmediateRemovalState.get_by_user_id( user_id) self.assertIsNotNone(removal_state) self.assertEquals( removal_models.ImmediateRemovalState.STATE_REGISTERED, removal_state.state) r = removal_models.BatchRemovalState.get_by_user_ids([user_id]) self.assertEqual([None], r) # Add an EventEntity record so we can see it being removed. event = models.EventEntity(user_id=user_id, source='test') event.put() actions.unregister(self, self.COURSE, do_data_removal=True) with common_utils.Namespace(self.NAMESPACE): # Immediately upon unregistration, we should still have the student # record, and removal state should be pending deletion. student = models.Student.get_by_user(user) self.assertIsNotNone(student) removal_state = removal_models.ImmediateRemovalState.get_by_user_id( user_id) self.assertIsNotNone(removal_state) self.assertEquals( removal_models.ImmediateRemovalState.STATE_DELETION_PENDING, removal_state.state) r = removal_models.BatchRemovalState.get_by_user_ids([user_id]) self.assertEqual([None], r) events = list(models.EventEntity.all().run()) self.assertEquals(1, len(events)) # We should have gotten a to-do item on the task queue for student # removal. task_count = self.execute_all_deferred_tasks( models.StudentLifecycleObserver.QUEUE_NAME) self.assertEquals(1, task_count) # unregistration. with common_utils.Namespace(self.NAMESPACE): # Having processed the queue item, the student record should now # be gone. students = list(models.Student.all().run()) student = models.Student.get_by_user(user) self.assertIsNone(student) # But the record tracking removal should not yet be gone. removal_state = removal_models.ImmediateRemovalState.get_by_user_id( user_id) self.assertIsNotNone(removal_state) self.assertEquals( removal_models.ImmediateRemovalState.STATE_DELETION_PENDING, removal_state.state) # And we should have a to-do item for the cron batch cleanup. r = removal_models.BatchRemovalState.get_by_user_ids([user_id]) self.assertEquals(1, len(r)) removal_record = r[0] self.assertEquals( models_data_removal.Registry.get_unindexed_class_names(), removal_record.resource_types) # Events won't have been cleaned up yet; need cron batch to run. events = list(models.EventEntity.all().run()) self.assertEquals(1, len(events)) # Call the cron handler to schedule batch removal tasks. This, in # turn, will schedule map/reduce jobs to remove records for that # student. response = self.get(data_removal.DataRemovalCronHandler.URL, headers={'X-AppEngine-Cron': 'True'}) self.assertEquals(200, response.status_int) self.assertEquals('OK.', response.body) # Run the map/reduce jobs to completion. self.execute_all_deferred_tasks() # We should now be completely clean; the M/R job that finishes last # should also clean up the to-do tracking item. with common_utils.Namespace(self.NAMESPACE): student = models.Student.get_by_user(user) self.assertIsNone(student) removal_state = removal_models.ImmediateRemovalState.get_by_user_id( user_id) self.assertIsNone(removal_state) # Events should now be gone. events = list(models.EventEntity.all().run()) self.assertEquals(0, len(events))
def test_unregister_without_deletion_permits_reregistration(self): actions.login(self.STUDENT_EMAIL) actions.register(self, self.STUDENT_EMAIL) actions.unregister(self) actions.register(self, self.STUDENT_EMAIL)
def test_immediate_removal_policy(self): user = self.make_test_user(self.STUDENT_EMAIL) actions.login(user.email()) actions.register(self, self.STUDENT_EMAIL, course=self.COURSE) task_count = self.execute_all_deferred_tasks( models.StudentLifecycleObserver.QUEUE_NAME) self.assertEquals(1, task_count) # registration. user_id = None with common_utils.Namespace(self.NAMESPACE): # After registration, we should have a student object, and # a ImmediateRemovalState instance. student = models.Student.get_by_user(user) self.assertIsNotNone(student) user_id = student.user_id removal_state = removal_models.ImmediateRemovalState.get_by_user_id( user_id) self.assertIsNotNone(removal_state) self.assertEquals( removal_models.ImmediateRemovalState.STATE_REGISTERED, removal_state.state) r = removal_models.BatchRemovalState.get_by_user_ids([user_id]) self.assertEqual([None], r) # Add an EventEntity record so we can see it being removed. event = models.EventEntity(user_id=user_id, source='test') event.put() actions.unregister(self, self.COURSE, do_data_removal=True) with common_utils.Namespace(self.NAMESPACE): # Immediately upon unregistration, we should still have the student # record, and removal state should be pending deletion. student = models.Student.get_by_user(user) self.assertIsNotNone(student) removal_state = removal_models.ImmediateRemovalState.get_by_user_id( user_id) self.assertIsNotNone(removal_state) self.assertEquals( removal_models.ImmediateRemovalState.STATE_DELETION_PENDING, removal_state.state) r = removal_models.BatchRemovalState.get_by_user_ids([user_id]) self.assertEqual([None], r) events = list(models.EventEntity.all().run()) self.assertEquals(1, len(events)) # We should have gotten a to-do item on the task queue for student # removal. task_count = self.execute_all_deferred_tasks( models.StudentLifecycleObserver.QUEUE_NAME) self.assertEquals(1, task_count) # unregistration. with common_utils.Namespace(self.NAMESPACE): # Having processed the queue item, the student record should now # be gone. students = list(models.Student.all().run()) student = models.Student.get_by_user(user) self.assertIsNone(student) # But the record tracking removal should not yet be gone. removal_state = removal_models.ImmediateRemovalState.get_by_user_id( user_id) self.assertIsNotNone(removal_state) self.assertEquals( removal_models.ImmediateRemovalState.STATE_DELETION_PENDING, removal_state.state) # And we should have a to-do item for the cron batch cleanup. r = removal_models.BatchRemovalState.get_by_user_ids([user_id]) self.assertEquals(1, len(r)) removal_record = r[0] self.assertEquals( models_data_removal.Registry.get_unindexed_class_names(), removal_record.resource_types) # Events won't have been cleaned up yet; need cron batch to run. events = list(models.EventEntity.all().run()) self.assertEquals(1, len(events)) # Call the cron handler to schedule batch removal tasks. This, in # turn, will schedule map/reduce jobs to remove records for that # student. response = self.get( data_removal.DataRemovalCronHandler.URL, headers={'X-AppEngine-Cron': 'True'}) self.assertEquals(200, response.status_int) self.assertEquals('OK.', response.body) # Run the map/reduce jobs to completion. self.execute_all_deferred_tasks() # We should now be completely clean; the M/R job that finishes last # should also clean up the to-do tracking item. with common_utils.Namespace(self.NAMESPACE): student = models.Student.get_by_user(user) self.assertIsNone(student) removal_state = removal_models.ImmediateRemovalState.get_by_user_id( user_id) self.assertIsNone(removal_state) # Events should now be gone. events = list(models.EventEntity.all().run()) self.assertEquals(0, len(events))