import ava, { TestInterface } from 'ava'; import { Mongoose, Types } from 'mongoose'; import { UserInputError, ForbiddenError, AuthenticationError, } from 'apollo-server-micro'; import { Factory } from '../../../database/factory'; import * as testUtils from '../../../test/utils'; import { User } from '../user-model'; import { createContext } from '../../../test/utils/create-mock-context'; import { gqlCall } from '../../../test/utils/gqlCall'; import { defineUserAbility } from '../../../server/authorization/user-authorization'; // // Setup // const test = ava as TestInterface<{ db: Mongoose }>; test.before(async t => { await testUtils.setupDB(t); }); test.afterEach.always(async t => { await testUtils.cleanupDB(t); }); test.after.always(async t => { await testUtils.tearDownDB(t); }); // // Constants // const updateUserMutation = ` mutation Update($input: UpdateUserInput!) { updateUser(input: $input) { id firstName } } `; // // Tests // // ** update recipe test.serial('Should save user updates to DB', async t => { const user = await Factory.create('user', { firstName: 'Old', }); const context = createContext({ state: { user, abilities: defineUserAbility(user) }, }); const response = await gqlCall({ source: updateUserMutation, contextValue: context, variableValues: { input: { id: user.id, firstName: 'New', }, }, }); const updatedUser = await User.findById(user.id); t.truthy(updatedUser.firstName, 'New'); t.is(updatedUser.firstName, 'New'); }); test.serial('Should return the updated user', async t => { const user = await Factory.create('user', { firstName: 'Old', }); const context = createContext({ state: { user, abilities: defineUserAbility(user) }, }); const response = await gqlCall({ source: updateUserMutation, contextValue: context, variableValues: { input: { id: user.id, firstName: 'New', }, }, }); t.is(response.data.updateUser.id, user.id); t.is(response.data.updateUser.firstName, 'New'); }); test.serial( 'updateRecipe: Should return an error when the user is not logged in', async t => { const context = createContext({ state: { user: null } }); const response = await gqlCall({ source: updateUserMutation, contextValue: context, variableValues: { input: { id: Types.ObjectId().toHexString(), }, }, }); t.truthy(response.errors.length > 0); t.true(response.errors[0].originalError instanceof AuthenticationError); } ); test.serial( 'Should return an error when the user is trying to update a different user', async t => { const user = await Factory.create('user'); const otherUser = await Factory.create('user'); const context = createContext({ state: { user, abilities: defineUserAbility(user) }, }); const response = await gqlCall({ source: updateUserMutation, contextValue: context, variableValues: { input: { id: otherUser.id, firstName: 'New', }, }, }); t.truthy(response.errors.length > 0); t.true(response.errors[0].originalError instanceof ForbiddenError); } ); test.serial('Should return an error when the user doesnt exist', async t => { const user = await Factory.create('user'); const context = createContext({ state: { user, abilities: defineUserAbility(user) }, }); const fakeId = Types.ObjectId.createFromTime(Date.now()).toHexString(); const response = await gqlCall({ source: updateUserMutation, contextValue: context, variableValues: { input: { id: fakeId, }, }, }); t.truthy(response.errors.length > 0); t.true(response.errors[0].originalError instanceof ForbiddenError); }); test.serial('Should return an error when id is malformed', async t => { const user = await Factory.create('user'); const context = createContext({ state: { user, abilities: defineUserAbility(user) }, }); const response = await gqlCall({ source: updateUserMutation, contextValue: context, variableValues: { input: { id: 'malformed', }, }, }); t.truthy(response.errors.length > 0); t.true(response.errors[0].originalError instanceof UserInputError); });