From a537d9ac17adc3c7aa2147a5cc29247b4a1346cf Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Wed, 22 Nov 2017 14:08:18 -0700 Subject: [PATCH] added message to ban/suspend workflows --- graph/mutators/user.js | 12 +-- graph/resolvers/root_mutation.js | 14 ++- graph/typeDefs.graphql | 88 +++++++++---------- models/user.js | 6 ++ routes/api/users/index.js | 25 ------ services/actions.js | 4 +- services/comments.js | 6 +- services/email/notification.html.ejs | 1 - services/email/notification.txt.ejs | 1 - .../email/{banned.html.ejs => plain.html.ejs} | 0 .../email/{banned.txt.ejs => plain.txt.ejs} | 0 services/email/suspension.html.ejs | 1 - services/email/suspension.txt.ejs | 1 - services/users.js | 82 +++++++---------- .../graph/mutations/setUserBanStatus.js | 61 +++++++++---- .../mutations/setUserSuspensionStatus.js | 59 ++++++++++--- 16 files changed, 188 insertions(+), 173 deletions(-) delete mode 100644 services/email/notification.html.ejs delete mode 100644 services/email/notification.txt.ejs rename services/email/{banned.html.ejs => plain.html.ejs} (100%) rename services/email/{banned.txt.ejs => plain.txt.ejs} (100%) delete mode 100644 services/email/suspension.html.ejs delete mode 100644 services/email/suspension.txt.ejs diff --git a/graph/mutators/user.js b/graph/mutators/user.js index 1167372f7..e84dea302 100644 --- a/graph/mutators/user.js +++ b/graph/mutators/user.js @@ -17,15 +17,15 @@ const setUserUsernameStatus = async (ctx, id, status) => { } }; -const setUserBanStatus = async (ctx, id, status) => { - const user = await UsersService.setBanStatus(id, status, ctx.user.id); +const setUserBanStatus = async (ctx, id, status = false, message = null) => { + const user = await UsersService.setBanStatus(id, status, ctx.user.id, message); if (user.banned) { ctx.pubsub.publish('userBanned', user); } }; -const setUserSuspensionStatus = async (ctx, id, until) => { - const user = await UsersService.setSuspensionStatus(id, until, ctx.user.id); +const setUserSuspensionStatus = async (ctx, id, until = null, message = null) => { + const user = await UsersService.setSuspensionStatus(id, until, ctx.user.id, message); if (user.suspended) { ctx.pubsub.publish('userSuspended', user); } @@ -77,11 +77,11 @@ module.exports = (ctx) => { } if (ctx.user.can(SET_USER_BAN_STATUS)) { - mutators.User.setUserBanStatus = (id, status) => setUserBanStatus(ctx, id, status); + mutators.User.setUserBanStatus = (id, status, message) => setUserBanStatus(ctx, id, status, message); } if (ctx.user.can(SET_USER_SUSPENSION_STATUS)) { - mutators.User.setUserSuspensionStatus = (id, until) => setUserSuspensionStatus(ctx, id, until); + mutators.User.setUserSuspensionStatus = (id, until, message) => setUserSuspensionStatus(ctx, id, until, message); } } diff --git a/graph/resolvers/root_mutation.js b/graph/resolvers/root_mutation.js index 1d7cf0d2c..2b2592dc4 100644 --- a/graph/resolvers/root_mutation.js +++ b/graph/resolvers/root_mutation.js @@ -31,11 +31,17 @@ const RootMutation = { setUsername: async (_, {id, username}, {mutators: {User}}) => { await User.setUsername(id, username); }, - setUserSuspensionStatus: async (_, {input: {id, until}}, {mutators: {User}}) => { - await User.setUserSuspensionStatus(id, until); + suspendUser: async (obj, {input: {id, until, message}}, {mutators: {User}}) => { + await User.setUserSuspensionStatus(id, until, message); }, - setUserBanStatus: async (_, {input: {id, status}}, {mutators: {User}}) => { - await User.setUserBanStatus(id, status); + unSuspendUser: async (obj, {input: {id}}, {mutators: {User}}) => { + await User.setUserSuspensionStatus(id); + }, + banUser: async (obj, {input: {id, message}}, {mutators: {User}}) => { + await User.setUserBanStatus(id, true, message); + }, + unBanUser: async (obj, {input: {id}}, {mutators: {User}}) => { + await User.setUserBanStatus(id, false); }, ignoreUser: async (_, {id}, {mutators: {User}}) => { await User.ignoreUser({id}); diff --git a/graph/typeDefs.graphql b/graph/typeDefs.graphql index 38d67053d..d2d2943ce 100644 --- a/graph/typeDefs.graphql +++ b/graph/typeDefs.graphql @@ -1012,15 +1012,6 @@ enum ACTION_ITEM_TYPE { USERS } -input CreateLikeInput { - - # The item's id for which we are to create a like. - item_id: ID! - - # The type of the item for which we are to create the like. - item_type: ACTION_ITEM_TYPE! -} - # CreateCommentInput is the input content used to create a new comment. input CreateCommentInput { @@ -1035,7 +1026,6 @@ input CreateCommentInput { # Tags tags: [String] - } input CreateFlagInput { @@ -1090,16 +1080,6 @@ input CreateDontAgreeInput { message: String } -# Input for suspendUser mutation. -input SetUserSuspensionStatusInput { - - # id of target user. - id: ID! - - # target user will be suspended until this date. - until: Date -} - # Configurable settings that can be overridden for the Asset. You must specify # all fields that should be updated. input AssetSettingsInput { @@ -1158,22 +1138,6 @@ type DeleteActionResponse implements Response { errors: [UserError!] } -# SetUserSuspensionStatusResponse is the response returned with possibly some -# errors relating to the suspend action attempt. -type SetUserSuspensionStatusResponse implements Response { - - # An array of errors relating to the mutation that occurred. - errors: [UserError!] -} - -# RejectUsernameResponse is the response returned with possibly some errors -# relating to the reject username action attempt. -type RejectUsernameResponse implements Response { - - # An array of errors relating to the mutation that occurred. - errors: [UserError!] -} - # SetCommentStatusResponse is the response returned with possibly some errors # relating to the delete action attempt. type SetCommentStatusResponse implements Response { @@ -1356,18 +1320,40 @@ type RevokeTokenResponse implements Response { errors: [UserError!] } -# SetUserBanStatusInput contains the input to change the ban status of a given -# user. -input SetUserBanStatusInput { - - # id is the user to set the ban status on. +input BanUserInput { id: ID! - - # status is the ban status to set on the target user. - status: Boolean! + message: String! } -type SetUserBanStatusResponse implements Response { +input UnBanUserInput { + id: ID! +} + +type BanUsersResponse implements Response { + errors: [UserError!] +} + +type UnBanUserResponse implements Response { + errors: [UserError!] +} + +input SuspendUserInput { + id: ID! + message: String! + until: Date! +} + +type SuspendUserResponse implements Response { + errors: [UserError!] +} + +input UnSuspendUserInput { + id: ID! +} + +# UnSuspendUserResponse is the response returned with possibly some +# errors relating to the suspend action attempt. +type UnSuspendUserResponse implements Response { # An array of errors relating to the mutation that occurred. errors: [UserError!] @@ -1411,11 +1397,19 @@ type RootMutation { # Sets the suspension status on a given user. Requires the `MODERATOR` role. # Mutation is restricted. - setUserSuspensionStatus(input: SetUserSuspensionStatusInput!): SetUserSuspensionStatusResponse + suspendUser(input: SuspendUserInput!): SuspendUserResponse + + # Sets the suspension status on a given user. Requires the `MODERATOR` role. + # Mutation is restricted. + unSuspendUser(input: UnSuspendUserInput!): UnSuspendUserResponse # Sets the ban status on a given user. Requires the `MODERATOR` role. # Mutation is restricted. - setUserBanStatus(input: SetUserBanStatusInput!): SetUserBanStatusResponse + banUser(input: BanUserInput!): BanUsersResponse + + # Sets the ban status on a given user. Requires the `MODERATOR` role. + # Mutation is restricted. + unBanUser(input: UnBanUserInput!): UnBanUserResponse # Sets the username status on a given user to `APPROVED`. Requires the # `MODERATOR` role. Mutation is restricted. diff --git a/models/user.js b/models/user.js index a793ca9df..98643157c 100644 --- a/models/user.js +++ b/models/user.js @@ -144,6 +144,9 @@ const UserSchema = new Schema({ // assigned_by stores the user id of the user who assigned this status. assigned_by: {type: String, default: null}, + // message stores the email content sent to the user. + message: {type: String, default: null}, + // created_at stores the date when this status was assigned. created_at: {type: Date, default: Date.now} }], @@ -166,6 +169,9 @@ const UserSchema = new Schema({ // assigned_by stores the user id of the user who assigned this status. assigned_by: {type: String, default: null}, + // message stores the email content sent to the user. + message: {type: String, default: null}, + // created_at stores the date when this status was assigned. created_at: {type: Date, default: Date.now} }] diff --git a/routes/api/users/index.js b/routes/api/users/index.js index a8d55d7ae..24f5b65e0 100644 --- a/routes/api/users/index.js +++ b/routes/api/users/index.js @@ -1,7 +1,6 @@ const express = require('express'); const router = express.Router(); const UsersService = require('../../../services/users'); -const mailer = require('../../../services/mailer'); const errors = require('../../../errors'); const authorization = require('../../../middleware/authorization'); const Limit = require('../../../services/limit'); @@ -56,30 +55,6 @@ router.post('/:user_id/role', authorization.needed('ADMIN', 'MODERATOR'), async } }); -router.post('/:user_id/email', authorization.needed('ADMIN', 'MODERATOR'), async (req, res, next) => { - try { - let user = await UsersService.findById(req.params.user_id); - - let localProfile = user.profiles.find((profile) => profile.provider === 'local'); - if (!localProfile) { - return next(errors.ErrMissingEmail); - } - - await mailer.sendSimple({ - template: 'notification', // needed to know which template to render! - locals: { // specifies the template locals. - body: req.body.body - }, - subject: req.body.subject, - to: localProfile.id // This only works if the user has registered via e-mail. - }); - - res.status(204).end(); - } catch (e) { - next(e); - } -}); - // create a local user. router.post('/', async (req, res, next) => { const {email, password, username} = req.body; diff --git a/services/actions.js b/services/actions.js index d6f709632..adffdd134 100644 --- a/services/actions.js +++ b/services/actions.js @@ -78,7 +78,7 @@ module.exports = class ActionsService { }); // Emit that there was a new action created. - events.emit(ACTIONS_NEW, foundAction); + await events.emitAsync(ACTIONS_NEW, foundAction); return foundAction; } @@ -233,7 +233,7 @@ module.exports = class ActionsService { } // Emit that the action was deleted. - events.emit(ACTIONS_DELETE, action); + await events.emitAsync(ACTIONS_DELETE, action); return action; } diff --git a/services/comments.js b/services/comments.js index 495472a81..086c228aa 100644 --- a/services/comments.js +++ b/services/comments.js @@ -44,7 +44,7 @@ module.exports = class CommentsService { const savedCommentModel = await commentModel.save(); // Emit that the comment was created! - events.emit(COMMENTS_NEW, savedCommentModel); + await events.emitAsync(COMMENTS_NEW, savedCommentModel); return savedCommentModel; } @@ -193,7 +193,7 @@ module.exports = class CommentsService { } } - events.emit(COMMENTS_EDIT, originalComment, editedComment); + await events.emitAsync(COMMENTS_EDIT, originalComment, editedComment); return editedComment; } @@ -305,7 +305,7 @@ module.exports = class CommentsService { // Emit that the comment was edited, and pass the original comment and the // edited comment. - events.emit(COMMENTS_EDIT, originalComment, editedComment); + await events.emitAsync(COMMENTS_EDIT, originalComment, editedComment); return editedComment; } diff --git a/services/email/notification.html.ejs b/services/email/notification.html.ejs deleted file mode 100644 index b36560ec5..000000000 --- a/services/email/notification.html.ejs +++ /dev/null @@ -1 +0,0 @@ -<%= body %> diff --git a/services/email/notification.txt.ejs b/services/email/notification.txt.ejs deleted file mode 100644 index b36560ec5..000000000 --- a/services/email/notification.txt.ejs +++ /dev/null @@ -1 +0,0 @@ -<%= body %> diff --git a/services/email/banned.html.ejs b/services/email/plain.html.ejs similarity index 100% rename from services/email/banned.html.ejs rename to services/email/plain.html.ejs diff --git a/services/email/banned.txt.ejs b/services/email/plain.txt.ejs similarity index 100% rename from services/email/banned.txt.ejs rename to services/email/plain.txt.ejs diff --git a/services/email/suspension.html.ejs b/services/email/suspension.html.ejs deleted file mode 100644 index 4fd8f191c..000000000 --- a/services/email/suspension.html.ejs +++ /dev/null @@ -1 +0,0 @@ -<%= body.replace(/\n/g, '
') %> diff --git a/services/email/suspension.txt.ejs b/services/email/suspension.txt.ejs deleted file mode 100644 index b36560ec5..000000000 --- a/services/email/suspension.txt.ejs +++ /dev/null @@ -1 +0,0 @@ -<%= body %> diff --git a/services/users.js b/services/users.js index 6080a0369..faeb6b667 100644 --- a/services/users.js +++ b/services/users.js @@ -3,7 +3,6 @@ const bcrypt = require('bcryptjs'); const errors = require('../errors'); const some = require('lodash/some'); const merge = require('lodash/merge'); -const timeago = require('./timeago'); const { USERS_NEW, @@ -33,7 +32,6 @@ const MailerService = require('./mailer'); const i18n = require('./i18n'); const Wordlist = require('./wordlist'); const DomainList = require('./domain_list'); -const SettingsService = require('./settings'); const {escapeRegExp} = require('./regex'); const EMAIL_CONFIRM_JWT_SUBJECT = 'email_confirm'; @@ -90,7 +88,7 @@ class UsersService { } } - static async setSuspensionStatus(id, until, assignedBy = null) { + static async setSuspensionStatus(id, until, assignedBy = null, message) { let user = await UserModel.findOneAndUpdate({id}, { $set: { 'status.suspension.until': until @@ -99,11 +97,13 @@ class UsersService { 'status.suspension.history': { until, assigned_by: assignedBy, + message, created_at: Date.now() } } }, { - new: true + new: true, + runValidators: true }); if (user === null) { user = await UserModel.findOne({id}); @@ -125,12 +125,12 @@ class UsersService { } // Emit that the user username status was changed. - await events.emitAsync(USERS_SUSPENSION_CHANGE, user, until); + await await events.emitAsync(USERS_SUSPENSION_CHANGE, user, {until, message, assignedBy}); return user; } - static async setBanStatus(id, status, assignedBy = null) { + static async setBanStatus(id, status, assignedBy = null, message) { let user = await UserModel.findOneAndUpdate({ id, status: { @@ -144,6 +144,7 @@ class UsersService { 'status.banned.history': { status, assigned_by: assignedBy, + message, created_at: Date.now() } } @@ -164,7 +165,7 @@ class UsersService { } // Emit that the user ban status was changed. - await events.emitAsync(USERS_BAN_CHANGE, user, status); + await await events.emitAsync(USERS_BAN_CHANGE, user, {status, assignedBy, message}); return user; } @@ -203,7 +204,7 @@ class UsersService { } // Emit that the user username status was changed. - await events.emitAsync(USERS_USERNAME_STATUS_CHANGE, user, status); + await await events.emitAsync(USERS_USERNAME_STATUS_CHANGE, user, {status, assignedBy}); return user; } @@ -252,7 +253,7 @@ class UsersService { } // Emit that the user username status was changed. - await events.emitAsync(USERS_USERNAME_STATUS_CHANGE, user, toStatus); + await await events.emitAsync(USERS_USERNAME_STATUS_CHANGE, user, toStatus); return user; } catch (err) { @@ -385,7 +386,7 @@ class UsersService { await user.save(); // Emit that the user was created. - events.emit(USERS_NEW, user); + await events.emitAsync(USERS_NEW, user); return user; } @@ -550,7 +551,7 @@ class UsersService { } // Emit that the user was created. - events.emit(USERS_NEW, user); + await events.emitAsync(USERS_NEW, user); return user; } @@ -903,53 +904,30 @@ class UsersService { module.exports = UsersService; -events.on(USERS_BAN_CHANGE, async (user, status) => { +events.on(USERS_BAN_CHANGE, async (user, {status, message}) => { // Check to see if the user was banned now and is currently banned. - if (user.banned && status) { + if (user.banned && status && message && message.length > 0) { await UsersService.sendEmail(user, { - template: 'banned', - locals: { - body: 'In accordance with The Coral Project’s community guidelines, your account has been banned. You are now longer allowed to comment, flag or engage with our community.' - }, - subject: 'Your account has been banned', - }); - } -}); - -events.on(USERS_SUSPENSION_CHANGE, async (user, until) => { - - // Check to see if the user was suspended now and is currently suspended. - if (user.suspended && until !== null && until > Date.now()) { - const {organizationName} = await SettingsService.retrieve(); - - const message = i18n.t( - 'suspenduser.email_message_suspend', - user.username, - organizationName, - timeago(until), - ); - - await UsersService.sendEmail(user, { - template: 'suspension', - locals: { - body: message, - }, - subject: 'Your account has been banned', - }); - } -}); - -events.on(USERS_USERNAME_STATUS_CHANGE, async (user, status) => { - if (status === 'REJECTED') { - const message = i18n.t('reject_username.email_message_reject'); - - await UsersService.sendEmail(user, { - template: 'suspension', + template: 'plain', locals: { body: message }, - subject: 'Username Rejected' + subject: 'Your account has been banned', + }); + } +}); + +events.on(USERS_SUSPENSION_CHANGE, async (user, {until, message}) => { + + // Check to see if the user was suspended now and is currently suspended. + if (user.suspended && until !== null && until > Date.now() && message && message.length > 0) { + await UsersService.sendEmail(user, { + template: 'plain', + locals: { + body: message, + }, + subject: 'Your account has been suspended', }); } }); diff --git a/test/server/graph/mutations/setUserBanStatus.js b/test/server/graph/mutations/setUserBanStatus.js index a59476b9d..3ea9f68df 100644 --- a/test/server/graph/mutations/setUserBanStatus.js +++ b/test/server/graph/mutations/setUserBanStatus.js @@ -5,10 +5,14 @@ const Context = require('../../../../graph/context'); const SettingsService = require('../../../../services/settings'); const UserModel = require('../../../../models/user'); const UsersService = require('../../../../services/users'); +const MailerService = require('../../../../services/mailer'); -const {expect} = require('chai'); +const sinon = require('sinon'); +const chai = require('chai'); +chai.use(require('sinon-chai')); +const {expect} = chai; -describe('graph.mutations.setUserBanStatus', () => { +describe('graph.mutations.banUser', () => { let user; beforeEach(async () => { await SettingsService.init(); @@ -16,11 +20,34 @@ describe('graph.mutations.setUserBanStatus', () => { user = await UsersService.createLocalUser('usernameA@example.com', 'password', 'usernameA'); }); - const setUserBanStatusMutation = ` - mutation SetUserBanStatus($user_id: ID!, $status: Boolean!) { - setUserBanStatus(input: { + let spy; + before(() => { + spy = sinon.spy(MailerService, 'sendSimple'); + }); + + afterEach(() => { + spy.reset(); + }); + + after(() => { + spy.restore(); + }); + + const banUserMutation = ` + mutation BanUser($user_id: ID!, $message: String!) { + banUser(input: { id: $user_id, - status: $status + message: $message + }) { + errors { + translation_key + } + } + } + + mutation UnBanUser($user_id: ID!) { + unBanUser(input: { + id: $user_id }) { errors { translation_key @@ -51,40 +78,42 @@ describe('graph.mutations.setUserBanStatus', () => { const ctx = new Context({user: actor}); - const {data, errors} = await graphql(schema, setUserBanStatusMutation, {}, ctx, { + const {data, errors} = await graphql(schema, banUserMutation, {}, ctx, { user_id: user.id, - status: true - }); + message: 'This is a message' + }, 'BanUser'); if (errors && errors.length > 0) { console.error(errors); } expect(errors).to.be.undefined; if (error) { - expect(data.setUserBanStatus).to.have.property('errors').not.null; - expect(data.setUserBanStatus.errors[0]).to.have.property('translation_key', error); + expect(data.banUser).to.have.property('errors').not.null; + expect(data.banUser.errors[0]).to.have.property('translation_key', error); } else { - expect(data.setUserBanStatus).to.be.null; + expect(data.banUser).to.be.null; user = await UserModel.findOne({id: user.id}); expect(user.status.banned.status).to.be.true; expect(user.status.banned.history).to.have.length(1); expect(user.status.banned.history[0]).to.have.property('status', true); + expect(user.status.banned.history[0]).to.have.property('message', 'This is a message'); expect(user.status.banned.history[0]).to.have.property('assigned_by', actor.id); expect(user.status.banned.history[0]).to.have.property('created_at').not.null; expect(user.banned).to.be.true; - const res = await graphql(schema, setUserBanStatusMutation, {}, ctx, { + expect(spy).to.have.been.calledOnce; + + const res = await graphql(schema, banUserMutation, {}, ctx, { user_id: user.id, - status: false - }); + }, 'UnBanUser'); if (res.errors && res.errors.length > 0) { console.error(res.errors); } expect(res.errors).to.be.undefined; - expect(res.data.setUserBanStatus).to.be.null; + expect(res.data.unBanUser).to.be.null; user = await UserModel.findOne({id: user.id}); diff --git a/test/server/graph/mutations/setUserSuspensionStatus.js b/test/server/graph/mutations/setUserSuspensionStatus.js index 43d998d4e..84f24ceb7 100644 --- a/test/server/graph/mutations/setUserSuspensionStatus.js +++ b/test/server/graph/mutations/setUserSuspensionStatus.js @@ -6,12 +6,15 @@ const Context = require('../../../../graph/context'); const SettingsService = require('../../../../services/settings'); const UserModel = require('../../../../models/user'); const UsersService = require('../../../../services/users'); +const MailerService = require('../../../../services/mailer'); +const sinon = require('sinon'); const chai = require('chai'); chai.use(require('chai-datetime')); +chai.use(require('sinon-chai')); const {expect} = chai; -describe('graph.mutations.setUserSuspensionStatus', () => { +describe('graph.mutations.suspendUser', () => { let user; beforeEach(async () => { await SettingsService.init(); @@ -19,11 +22,35 @@ describe('graph.mutations.setUserSuspensionStatus', () => { user = await UsersService.createLocalUser('usernameA@example.com', 'password', 'usernameA'); }); - const setUserSuspensionStatusMutation = ` - mutation SetUserUsernameStatus($user_id: ID!, $until: Date) { - setUserSuspensionStatus(input: { + let spy; + before(() => { + spy = sinon.spy(MailerService, 'sendSimple'); + }); + + afterEach(() => { + spy.reset(); + }); + + after(() => { + spy.restore(); + }); + + const mutation = ` + mutation SuspendUser($user_id: ID!, $until: Date!, $message: String!) { + suspendUser(input: { + id: $user_id, + until: $until, + message: $message, + }) { + errors { + translation_key + } + } + } + + mutation UnSuspendUser($user_id: ID!) { + unSuspendUser(input: { id: $user_id, - until: $until }) { errors { translation_key @@ -57,20 +84,21 @@ describe('graph.mutations.setUserSuspensionStatus', () => { const now = new Date(); const oneHourFromNow = new Date(new Date(now).setHours(now.getHours() + 1)); - const {data, errors} = await graphql(schema, setUserSuspensionStatusMutation, {}, ctx, { + const {data, errors} = await graphql(schema, mutation, {}, ctx, { user_id: user.id, - until: oneHourFromNow - }); + until: oneHourFromNow, + message: 'This is a message' + }, 'SuspendUser'); if (errors && errors.length > 0) { console.error(errors); } expect(errors).to.be.undefined; if (error) { - expect(data.setUserSuspensionStatus).to.have.property('errors').not.null; - expect(data.setUserSuspensionStatus.errors[0]).to.have.property('translation_key', error); + expect(data.suspendUser).to.have.property('errors').not.null; + expect(data.suspendUser.errors[0]).to.have.property('translation_key', error); } else { - expect(data.setUserSuspensionStatus).to.be.null; + expect(data.suspendUser).to.be.null; user = await UserModel.findOne({id: user.id}); @@ -79,6 +107,7 @@ describe('graph.mutations.setUserSuspensionStatus', () => { expect(user.status.suspension.history).to.have.length(1); expect(user.status.suspension.history[0]).to.have.property('until').to.be.withinTime(new Date(oneHourFromNow.getTime() - 1000), new Date(oneHourFromNow.getTime() + 1000)); expect(user.status.suspension.history[0]).to.have.property('assigned_by', actor.id); + expect(user.status.suspension.history[0]).to.have.property('message', 'This is a message'); expect(user.status.suspension.history[0]).to.have.property('created_at').not.null; expect(user.suspended).to.be.true; @@ -86,15 +115,17 @@ describe('graph.mutations.setUserSuspensionStatus', () => { expect(user.suspended).to.be.false; timekeeper.reset(); - const res = await graphql(schema, setUserSuspensionStatusMutation, {}, ctx, { + expect(spy).to.have.been.calledOnce; + + const res = await graphql(schema, mutation, {}, ctx, { user_id: user.id, until: null - }); + }, 'UnSuspendUser'); if (res.errors && res.errors.length > 0) { console.error(res.errors); } expect(res.errors).to.be.undefined; - expect(res.data.setUserSuspensionStatus).to.be.null; + expect(res.data.unSuspendUser).to.be.null; user = await UserModel.findOne({id: user.id});