def test_send_email_as_user(describe, session, stubmailer): with describe('setup'): user = helpers.create_user_with_perms(session, [], []) task_result = m.TaskResult(user) session.add(task_result) session.commit() task_result2 = m.TaskResult(user) session.add(task_result2) session.commit() with describe('can send the first time'): psef.tasks._send_email_as_user_1([user.id], 'd', 'b', task_result.id.hex, user.id) assert stubmailer.was_called assert m.TaskResult.query.get( task_result.id).state == m.TaskResultState.finished with describe('Second call should not send'): psef.tasks._send_email_as_user_1([user.id], 'd', 'b', task_result.id.hex, user.id) assert not stubmailer.was_called assert m.TaskResult.query.get( task_result.id).state == m.TaskResultState.finished with describe('Should not crash for non existing task id'): psef.tasks._send_email_as_user_1([user.id], 'd', 'b', uuid.uuid4().hex, user.id) assert not stubmailer.was_called assert m.TaskResult.query.get( task_result.id).state == m.TaskResultState.finished with describe('task result should indicate failure when function crashes'): psef.tasks._send_email_as_user_1([user.id], 'd', 'b', task_result2.id.hex, -user.id) assert not stubmailer.was_called assert m.TaskResult.query.get( task_result2.id).state == m.TaskResultState.crashed
def send_students_an_email(course_id: int) -> JSONResponse[models.TaskResult]: """Sent the authors in this course an email. .. :quickref: Course; Send users in this course an email. :>json subject: The subject of the email to send, should not be empty. :>json body: The body of the email to send, should not be empty. :>json email_all_users: If true all users are emailed, except for those in ``usernames``. If ``false`` no users are emailed except for those in ``usernames``. :>jsonarr usernames: The usernames of the users to which you want to send an email (or not if ``email_all_users`` is ``true``). :returns: A task result that will send these emails. """ course = helpers.filter_single_or_404( models.Course, models.Course.id == course_id, also_error=lambda c: c.virtual, ) auth.ensure_permission(CPerm.can_email_students, course.id) with helpers.get_from_request_transaction() as [get, _]: subject = get('subject', str) body = get('body', str) email_all_users = get('email_all_users', bool) usernames: t.List[str] = get('usernames', list) if helpers.contains_duplicate(usernames): raise APIException('The given exceptions list contains duplicates', 'Each exception can only be mentioned once', APICodes.INVALID_PARAM, 400) exceptions = helpers.get_in_or_error( models.User, models.User.username, usernames, same_order_as_given=True, ) if any(course_id not in u.courses for u in exceptions): raise APIException( 'Not all given users are enrolled in this course', f'Some given users are not enrolled in course {course_id}', APICodes.INVALID_PARAM, 400) if not (subject and body): raise APIException( 'Both a subject and body should be given', (f'One or both of the given subject ({subject}) or body' f' ({body}) is empty'), APICodes.INVALID_PARAM, 400) if email_all_users: recipients = course.get_all_users_in_course( include_test_students=False).filter( models.User.id.notin_([e.id for e in exceptions ])).with_entities(models.User).all() else: recipients = exceptions # The test student cannot be a member of a group, so we do not need to # run this on the expanded group members, and we also do not want to # run it when `email_all_users` is true because in that case we let the # DB handle it for us. if any(r.is_test_student for r in recipients): raise APIException('Cannot send an email to the test student', 'Test student was selected', APICodes.INVALID_PARAM, 400) recipients = helpers.flatten(r.get_contained_users() for r in recipients) if not recipients: raise APIException( 'At least one recipient should be given as recipient', 'No recipients were selected', APICodes.INVALID_PARAM, 400) task_result = models.TaskResult(current_user) db.session.add(task_result) db.session.commit() psef.tasks.send_email_as_user( receiver_ids=[u.id for u in recipients], subject=subject, body=body, task_result_hex_id=task_result.id.hex, sender_id=current_user.id, ) return JSONResponse.make(task_result)
def test_send_login_links(describe, session, test_client, logged_in, admin_user, tomorrow, stub_function, yesterday): with describe('setup'), logged_in(admin_user): stub_apply = stub_function(psef.tasks._send_login_links_to_users_1, 'apply_async') stub_mail = stub_function(psef.mail, 'send_login_link_mail') assig = helpers.create_assignment(test_client) assig_id = helpers.get_id(assig) login_links_token = uuid.uuid4() m.Assignment.query.filter_by(id=assig_id).update({ '_available_at': tomorrow.isoformat(), '_deadline': (tomorrow + timedelta(minutes=1)).isoformat(), '_send_login_links_token': login_links_token, 'kind': 'exam', }) task_result = m.TaskResult(user=None) session.add(task_result) session.commit() student = helpers.create_user_with_role(session, 'Student', assig['course']) with describe('does not crash if task does not exist'): psef.tasks._send_login_links_to_users_1(assig_id, uuid.uuid4().hex, tomorrow.isoformat(), login_links_token, 1) assert task_result.state.is_not_started with describe('reschedules when called too early'): psef.tasks._send_login_links_to_users_1(assig_id, task_result.id.hex, tomorrow.isoformat(), login_links_token.hex, 1) assert stub_apply.called_amount == 1 assert task_result.state.is_not_started with describe('Does nothing when token is different'): psef.tasks._send_login_links_to_users_1(assig_id, task_result.id.hex, yesterday.isoformat(), uuid.uuid4().hex, 1) assert not stub_mail.called assert task_result.state.is_skipped task_result.state = m.TaskResultState.not_started session.commit() with describe('Does nothing when deadline expired'): del flask.g.request_start_time with freeze_time(tomorrow + timedelta(days=1)): psef.tasks._send_login_links_to_users_1(assig_id, task_result.id.hex, yesterday.isoformat(), login_links_token.hex, 1) assert not stub_mail.called assert task_result.state.is_skipped task_result.state = m.TaskResultState.not_started session.commit() with describe('Does nothing when task was already finished'): task_result.state = m.TaskResultState.finished session.commit() psef.tasks._send_login_links_to_users_1(assig_id, task_result.id.hex, yesterday.isoformat(), login_links_token.hex, 1) assert not stub_mail.called assert task_result.state.is_finished