def test_notify_status_change(self): project = self.projects['empty_project'] internal_name = settings.SLACK_INTERNAL_NOTIFICATION_CHANNEL.strip('#') internal_groups = [ group for group in self.slack.groups.list().body['groups'] if group['name'] == internal_name ] internal_group_id = internal_groups[0]['id'] internal_slack_messages = self.slack.get_messages(internal_group_id) experts_slack_messages = self.slack.get_messages( project.slack_group_id) def _validate_slack_messages(message_stub): """ Check that correct slack message was sent if API key present. """ self.assertIn(message_stub, internal_slack_messages.pop()) self.assertIn(message_stub, experts_slack_messages.pop()) task = TaskFactory(project=project, step=self.test_step, status=Task.Status.AWAITING_PROCESSING) # Entry-level worker picks up task self.assertEqual(task.status, Task.Status.AWAITING_PROCESSING) task = assign_task(self.workers[0].id, task.id) self.assertTrue(task.is_worker_assigned(self.workers[0])) # Notification should be sent to entry-level worker self.assertEqual(len(self.mail.inbox), 1) notification = self.mail.inbox.pop() self.assertEqual(notification['recipient'], self.workers[0].user.email) self.assertEqual(notification['subject'], "You've been assigned to a new task!") _validate_slack_messages('Task has been picked up by a worker.') self.assertEqual(len(internal_slack_messages), 0) self.assertEqual(len(experts_slack_messages), 0) with patch('orchestra.utils.task_lifecycle._is_review_needed', return_value=True): # Entry-level worker submits task task = submit_task(task.id, {}, Iteration.Status.REQUESTED_REVIEW, self.workers[0]) self.assertEqual(task.status, Task.Status.PENDING_REVIEW) # Notification should be sent to entry-level worker self.assertEqual(len(self.mail.inbox), 1) notification = self.mail.inbox.pop() self.assertEqual(notification['recipient'], self.workers[0].user.email) self.assertEqual(notification['subject'], 'Your task is under review!') _validate_slack_messages('Task is awaiting review.') self.assertEqual(len(internal_slack_messages), 0) self.assertEqual(len(experts_slack_messages), 0) # Reviewer picks up task task = assign_task(self.workers[1].id, task.id) self.assertEqual(task.status, Task.Status.REVIEWING) # No notification should be sent self.assertEqual(len(self.mail.inbox), 0) _validate_slack_messages('Task is under review.') self.assertEqual(len(internal_slack_messages), 0) self.assertEqual(len(experts_slack_messages), 0) # Reviewer rejects task task = submit_task(task.id, {}, Iteration.Status.PROVIDED_REVIEW, self.workers[1]) self.assertEqual(task.status, Task.Status.POST_REVIEW_PROCESSING) # Notification should be sent to original worker self.assertEqual(len(self.mail.inbox), 1) notification = self.mail.inbox.pop() self.assertEqual(notification['recipient'], self.workers[0].user.email) self.assertEqual(notification['subject'], 'Your task has been returned') _validate_slack_messages('Task was returned by reviewer.') self.assertEqual(len(internal_slack_messages), 0) self.assertEqual(len(experts_slack_messages), 0) # Entry-level worker resubmits task task = submit_task(task.id, {}, Iteration.Status.REQUESTED_REVIEW, self.workers[0]) self.assertEqual(task.status, Task.Status.REVIEWING) # Notification should be sent to reviewer self.assertEqual(len(self.mail.inbox), 1) notification = self.mail.inbox.pop() self.assertEqual(notification['recipient'], self.workers[1].user.email) self.assertEqual(notification['subject'], 'A task is ready for re-review!') _validate_slack_messages('Task is under review.') self.assertEqual(len(internal_slack_messages), 0) self.assertEqual(len(experts_slack_messages), 0) # First reviewer accepts task with patch('orchestra.utils.task_lifecycle._is_review_needed', return_value=True): task = submit_task(task.id, {}, Iteration.Status.REQUESTED_REVIEW, self.workers[1]) self.assertEqual(task.status, Task.Status.PENDING_REVIEW) # Notification should be sent to first reviewer self.assertEqual(len(self.mail.inbox), 1) notification = self.mail.inbox.pop() self.assertEqual(notification['recipient'], self.workers[1].user.email) self.assertEqual(notification['subject'], 'Your task is under review!') _validate_slack_messages('Task is awaiting review.') self.assertEqual(len(internal_slack_messages), 0) self.assertEqual(len(experts_slack_messages), 0) # Second reviewer picks up task task = assign_task(self.workers[3].id, task.id) self.assertEqual(task.status, Task.Status.REVIEWING) # No notification should be sent self.assertEqual(len(self.mail.inbox), 0) _validate_slack_messages('Task is under review.') self.assertEqual(len(internal_slack_messages), 0) self.assertEqual(len(experts_slack_messages), 0) # Second reviewer rejects task task = submit_task(task.id, {}, Iteration.Status.PROVIDED_REVIEW, self.workers[3]) self.assertEqual(task.status, Task.Status.POST_REVIEW_PROCESSING) # Notification should be sent to first reviewer self.assertEqual(len(self.mail.inbox), 1) notification = self.mail.inbox.pop() self.assertEqual(notification['recipient'], self.workers[1].user.email) self.assertEqual(notification['subject'], 'Your task has been returned') _validate_slack_messages('Task was returned by reviewer.') self.assertEqual(len(internal_slack_messages), 0) self.assertEqual(len(experts_slack_messages), 0) # First reviewer resubmits task task = submit_task(task.id, {}, Iteration.Status.REQUESTED_REVIEW, self.workers[1]) self.assertEqual(task.status, Task.Status.REVIEWING) # Notification should be sent to second reviewer self.assertEqual(len(self.mail.inbox), 1) notification = self.mail.inbox.pop() self.assertEqual(notification['recipient'], self.workers[3].user.email) self.assertEqual(notification['subject'], 'A task is ready for re-review!') _validate_slack_messages('Task is under review.') self.assertEqual(len(internal_slack_messages), 0) self.assertEqual(len(experts_slack_messages), 0) # Second reviewer accepts task; task is complete with patch('orchestra.utils.task_lifecycle._is_review_needed', return_value=False): task = submit_task(task.id, {}, Iteration.Status.REQUESTED_REVIEW, self.workers[3]) self.assertEqual(task.status, Task.Status.COMPLETE) # Notification should be sent to all workers on task self.assertEqual(len(self.mail.inbox), 3) recipients = {mail['recipient'] for mail in self.mail.inbox} subjects = {mail['subject'] for mail in self.mail.inbox} self.assertEqual(recipients, {self.workers[uid].user.email for uid in (0, 1, 3)}) self.assertEqual(subjects, {'Task complete!'}) self.mail.clear() _validate_slack_messages('Task has been completed.') self.assertEqual(len(internal_slack_messages), 0) self.assertEqual(len(experts_slack_messages), 0) # End project end_project(task.project.id) task = Task.objects.get(id=task.id) self.assertEqual(task.status, Task.Status.ABORTED) # Notification should be sent to all workers on task self.assertEqual(len(self.mail.inbox), 3) recipients = {mail['recipient'] for mail in self.mail.inbox} subjects = {mail['subject'] for mail in self.mail.inbox} self.assertEqual(recipients, {self.workers[uid].user.email for uid in (0, 1, 3)}) self.assertEqual(subjects, {'A task you were working on has been ended'}) self.mail.clear() for task in project.tasks.all(): _validate_slack_messages('Task has been aborted.') self.assertEqual(len(internal_slack_messages), 0) self.assertEqual(len(experts_slack_messages), 0)
def test_assign_task(self): entry_task = TaskFactory( project=self.projects['base_test_project'], status=Task.Status.AWAITING_PROCESSING, step=self.test_step) # No iterations should be present for task self.assertEqual( Iteration.objects.filter(assignment__task=entry_task).count(), 0) # Assign entry-level task to entry-level worker entry_task = assign_task(self.workers[0].id, entry_task.id) self.assertTrue(entry_task.is_worker_assigned(self.workers[0])) self.assertEqual(entry_task.status, Task.Status.PROCESSING) self.assertEqual(entry_task.assignments.count(), 1) entry_assignment = entry_task.assignments.first() # A single iteration was created for the assignment self.assertEqual(entry_assignment.iterations.count(), 1) self.assertEqual( Iteration.objects.filter(assignment__task=entry_task).count(), 1) self.assertEqual( entry_assignment.iterations.first().start_datetime, entry_assignment.start_datetime) # Attempt to assign task which isn't awaiting a new assignment invalid = (Task.Status.PROCESSING, Task.Status.ABORTED, Task.Status.REVIEWING, Task.Status.COMPLETE, Task.Status.POST_REVIEW_PROCESSING) for status in invalid: invalid_status_task = Task.objects.create( project=self.projects['base_test_project'], status=status, step=self.test_step) with self.assertRaises(TaskAssignmentError): invalid_status_task = assign_task( self.workers[0].id, invalid_status_task.id) # Attempt to assign review task to worker already in review hierarchy review_task = Task.objects.create( project=self.projects['base_test_project'], status=Task.Status.PENDING_REVIEW, step=self.test_step) test_data = {'test_assign': True} TaskAssignmentFactory( worker=self.workers[1], task=review_task, status=TaskAssignment.Status.SUBMITTED, in_progress_task_data=test_data) with self.assertRaises(TaskAssignmentError): assign_task(self.workers[1].id, review_task.id) self.assertEqual( current_assignment(review_task).in_progress_task_data, test_data) # Attempt to assign review task to worker not certified for task with self.assertRaises(WorkerCertificationError): assign_task(self.workers[2].id, review_task.id) self.assertEqual( current_assignment(review_task).in_progress_task_data, test_data) # Assign review task to review worker self.assertEquals(review_task.assignments.count(), 1) review_task = assign_task(self.workers[3].id, review_task.id) self.assertEquals(review_task.assignments.count(), 2) reviewer_assignment = current_assignment(review_task) self.assertEqual( reviewer_assignment.worker, self.workers[3]) self.assertEqual( reviewer_assignment.in_progress_task_data, test_data) self.assertEquals( reviewer_assignment.iterations.count(), 1) self.assertEqual( reviewer_assignment.iterations.first().start_datetime, reviewer_assignment.start_datetime) self.assertEquals( review_task.status, Task.Status.REVIEWING)
def test_assign_task(self): entry_task = TaskFactory(project=self.projects['base_test_project'], status=Task.Status.AWAITING_PROCESSING, step=self.test_step) # No iterations should be present for task self.assertEqual( Iteration.objects.filter(assignment__task=entry_task).count(), 0) # Assign entry-level task to entry-level worker entry_task = assign_task(self.workers[0].id, entry_task.id) self.assertTrue(entry_task.is_worker_assigned(self.workers[0])) self.assertEqual(entry_task.status, Task.Status.PROCESSING) self.assertEqual(entry_task.assignments.count(), 1) entry_assignment = entry_task.assignments.first() # A single iteration was created for the assignment self.assertEqual(entry_assignment.iterations.count(), 1) self.assertEqual( Iteration.objects.filter(assignment__task=entry_task).count(), 1) self.assertEqual(entry_assignment.iterations.first().start_datetime, entry_assignment.start_datetime) # Attempt to assign task which isn't awaiting a new assignment invalid = (Task.Status.PROCESSING, Task.Status.ABORTED, Task.Status.REVIEWING, Task.Status.COMPLETE, Task.Status.POST_REVIEW_PROCESSING) for status in invalid: invalid_status_task = Task.objects.create( project=self.projects['base_test_project'], status=status, step=self.test_step) with self.assertRaises(TaskAssignmentError): invalid_status_task = assign_task(self.workers[0].id, invalid_status_task.id) # Attempt to assign review task to worker already in review hierarchy review_task = Task.objects.create( project=self.projects['base_test_project'], status=Task.Status.PENDING_REVIEW, step=self.test_step) test_data = {'test_assign': True} TaskAssignmentFactory(worker=self.workers[1], task=review_task, status=TaskAssignment.Status.SUBMITTED, in_progress_task_data=test_data) with self.assertRaises(TaskAssignmentError): assign_task(self.workers[1].id, review_task.id) self.assertEqual( current_assignment(review_task).in_progress_task_data, test_data) # Attempt to assign review task to worker not certified for task with self.assertRaises(WorkerCertificationError): assign_task(self.workers[2].id, review_task.id) self.assertEqual( current_assignment(review_task).in_progress_task_data, test_data) # Assign review task to review worker self.assertEquals(review_task.assignments.count(), 1) review_task = assign_task(self.workers[3].id, review_task.id) self.assertEquals(review_task.assignments.count(), 2) reviewer_assignment = current_assignment(review_task) self.assertEqual(reviewer_assignment.worker, self.workers[3]) self.assertEqual(reviewer_assignment.in_progress_task_data, test_data) self.assertEquals(reviewer_assignment.iterations.count(), 1) self.assertEqual(reviewer_assignment.iterations.first().start_datetime, reviewer_assignment.start_datetime) self.assertEquals(review_task.status, Task.Status.REVIEWING)
def test_notify_status_change(self): project = self.projects['empty_project'] internal_name = settings.SLACK_INTERNAL_NOTIFICATION_CHANNEL.strip('#') internal_groups = [ group for group in self.slack.groups.list().body['groups'] if group['name'] == internal_name] internal_group_id = internal_groups[0]['id'] internal_slack_messages = self.slack.get_messages(internal_group_id) experts_slack_messages = self.slack.get_messages( project.slack_group_id) def _validate_slack_messages(message_stub): """ Check that correct slack message was sent if API key present. """ self.assertIn(message_stub, internal_slack_messages.pop()) self.assertIn(message_stub, experts_slack_messages.pop()) task = TaskFactory(project=project, step=self.test_step, status=Task.Status.AWAITING_PROCESSING) # Entry-level worker picks up task self.assertEquals(task.status, Task.Status.AWAITING_PROCESSING) task = assign_task(self.workers[0].id, task.id) self.assertTrue(task.is_worker_assigned(self.workers[0])) # Notification should be sent to entry-level worker self.assertEquals(len(self.mail.inbox), 1) notification = self.mail.inbox.pop() self.assertEquals(notification['recipient'], self.workers[0].user.email) self.assertEquals(notification['subject'], "You've been assigned to a new task!") _validate_slack_messages('Task has been picked up by a worker.') self.assertEquals(len(internal_slack_messages), 0) self.assertEquals(len(experts_slack_messages), 0) with patch('orchestra.utils.task_lifecycle._is_review_needed', return_value=True): # Entry-level worker submits task task = submit_task(task.id, {}, Iteration.Status.REQUESTED_REVIEW, self.workers[0]) self.assertEquals(task.status, Task.Status.PENDING_REVIEW) # Notification should be sent to entry-level worker self.assertEquals(len(self.mail.inbox), 1) notification = self.mail.inbox.pop() self.assertEquals(notification['recipient'], self.workers[0].user.email) self.assertEquals(notification['subject'], 'Your task is under review!') _validate_slack_messages('Task is awaiting review.') self.assertEquals(len(internal_slack_messages), 0) self.assertEquals(len(experts_slack_messages), 0) # Reviewer picks up task task = assign_task(self.workers[1].id, task.id) self.assertEquals(task.status, Task.Status.REVIEWING) # No notification should be sent self.assertEquals(len(self.mail.inbox), 0) _validate_slack_messages('Task is under review.') self.assertEquals(len(internal_slack_messages), 0) self.assertEquals(len(experts_slack_messages), 0) # Reviewer rejects task task = submit_task(task.id, {}, Iteration.Status.PROVIDED_REVIEW, self.workers[1]) self.assertEquals(task.status, Task.Status.POST_REVIEW_PROCESSING) # Notification should be sent to original worker self.assertEquals(len(self.mail.inbox), 1) notification = self.mail.inbox.pop() self.assertEquals(notification['recipient'], self.workers[0].user.email) self.assertEquals(notification['subject'], 'Your task has been returned') _validate_slack_messages('Task was returned by reviewer.') self.assertEquals(len(internal_slack_messages), 0) self.assertEquals(len(experts_slack_messages), 0) # Entry-level worker resubmits task task = submit_task(task.id, {}, Iteration.Status.REQUESTED_REVIEW, self.workers[0]) self.assertEquals(task.status, Task.Status.REVIEWING) # Notification should be sent to reviewer self.assertEquals(len(self.mail.inbox), 1) notification = self.mail.inbox.pop() self.assertEquals(notification['recipient'], self.workers[1].user.email) self.assertEquals(notification['subject'], 'A task is ready for re-review!') _validate_slack_messages('Task is under review.') self.assertEquals(len(internal_slack_messages), 0) self.assertEquals(len(experts_slack_messages), 0) # First reviewer accepts task with patch('orchestra.utils.task_lifecycle._is_review_needed', return_value=True): task = submit_task(task.id, {}, Iteration.Status.REQUESTED_REVIEW, self.workers[1]) self.assertEquals(task.status, Task.Status.PENDING_REVIEW) # Notification should be sent to first reviewer self.assertEquals(len(self.mail.inbox), 1) notification = self.mail.inbox.pop() self.assertEquals(notification['recipient'], self.workers[1].user.email) self.assertEquals(notification['subject'], 'Your task is under review!') _validate_slack_messages('Task is awaiting review.') self.assertEquals(len(internal_slack_messages), 0) self.assertEquals(len(experts_slack_messages), 0) # Second reviewer picks up task task = assign_task(self.workers[3].id, task.id) self.assertEquals(task.status, Task.Status.REVIEWING) # No notification should be sent self.assertEquals(len(self.mail.inbox), 0) _validate_slack_messages('Task is under review.') self.assertEquals(len(internal_slack_messages), 0) self.assertEquals(len(experts_slack_messages), 0) # Second reviewer rejects task task = submit_task(task.id, {}, Iteration.Status.PROVIDED_REVIEW, self.workers[3]) self.assertEquals(task.status, Task.Status.POST_REVIEW_PROCESSING) # Notification should be sent to first reviewer self.assertEquals(len(self.mail.inbox), 1) notification = self.mail.inbox.pop() self.assertEquals(notification['recipient'], self.workers[1].user.email) self.assertEquals(notification['subject'], 'Your task has been returned') _validate_slack_messages('Task was returned by reviewer.') self.assertEquals(len(internal_slack_messages), 0) self.assertEquals(len(experts_slack_messages), 0) # First reviewer resubmits task task = submit_task(task.id, {}, Iteration.Status.REQUESTED_REVIEW, self.workers[1]) self.assertEquals(task.status, Task.Status.REVIEWING) # Notification should be sent to second reviewer self.assertEquals(len(self.mail.inbox), 1) notification = self.mail.inbox.pop() self.assertEquals(notification['recipient'], self.workers[3].user.email) self.assertEquals(notification['subject'], 'A task is ready for re-review!') _validate_slack_messages('Task is under review.') self.assertEquals(len(internal_slack_messages), 0) self.assertEquals(len(experts_slack_messages), 0) # Second reviewer accepts task; task is complete with patch('orchestra.utils.task_lifecycle._is_review_needed', return_value=False): task = submit_task(task.id, {}, Iteration.Status.REQUESTED_REVIEW, self.workers[3]) self.assertEquals(task.status, Task.Status.COMPLETE) # Notification should be sent to all workers on task self.assertEquals(len(self.mail.inbox), 3) recipients = {mail['recipient'] for mail in self.mail.inbox} subjects = {mail['subject'] for mail in self.mail.inbox} self.assertEquals(recipients, {self.workers[uid].user.email for uid in (0, 1, 3)}) self.assertEquals(subjects, {'Task complete!'}) self.mail.clear() _validate_slack_messages('Task has been completed.') self.assertEquals(len(internal_slack_messages), 0) self.assertEquals(len(experts_slack_messages), 0) # End project end_project(task.project.id) task = Task.objects.get(id=task.id) self.assertEquals(task.status, Task.Status.ABORTED) # Notification should be sent to all workers on task self.assertEquals(len(self.mail.inbox), 3) recipients = {mail['recipient'] for mail in self.mail.inbox} subjects = {mail['subject'] for mail in self.mail.inbox} self.assertEquals(recipients, {self.workers[uid].user.email for uid in (0, 1, 3)}) self.assertEquals(subjects, {'A task you were working on has been ended'}) self.mail.clear() for task in project.tasks.all(): _validate_slack_messages('Task has been aborted.') self.assertEquals(len(internal_slack_messages), 0) self.assertEquals(len(experts_slack_messages), 0)