From 068684d16b207c66097f7ce24d31d046416a4ea9 Mon Sep 17 00:00:00 2001 From: Hongbo Wu Date: Thu, 21 Jul 2022 14:55:11 +0800 Subject: [PATCH] add test for failing to send confirmation email when signup --- packages/api/src/generated/graphql.ts | 1 + packages/api/src/generated/schema.graphql | 1 + packages/api/src/schema.ts | 1 + packages/api/src/services/create_user.ts | 4 +- packages/api/test/resolvers/user.test.ts | 46 ++++-- .../api/test/services/create_user.test.ts | 140 ++++++++++-------- 6 files changed, 122 insertions(+), 71 deletions(-) diff --git a/packages/api/src/generated/graphql.ts b/packages/api/src/generated/graphql.ts index 2beab998c..142f114b9 100644 --- a/packages/api/src/generated/graphql.ts +++ b/packages/api/src/generated/graphql.ts @@ -1834,6 +1834,7 @@ export enum SignupErrorCode { AccessDenied = 'ACCESS_DENIED', ExpiredToken = 'EXPIRED_TOKEN', GoogleAuthError = 'GOOGLE_AUTH_ERROR', + InvalidEmail = 'INVALID_EMAIL', InvalidPassword = 'INVALID_PASSWORD', InvalidUsername = 'INVALID_USERNAME', Unknown = 'UNKNOWN', diff --git a/packages/api/src/generated/schema.graphql b/packages/api/src/generated/schema.graphql index 187b8d072..610450dfd 100644 --- a/packages/api/src/generated/schema.graphql +++ b/packages/api/src/generated/schema.graphql @@ -1355,6 +1355,7 @@ enum SignupErrorCode { ACCESS_DENIED EXPIRED_TOKEN GOOGLE_AUTH_ERROR + INVALID_EMAIL INVALID_PASSWORD INVALID_USERNAME UNKNOWN diff --git a/packages/api/src/schema.ts b/packages/api/src/schema.ts index 9dea9b1ec..259e66c9d 100755 --- a/packages/api/src/schema.ts +++ b/packages/api/src/schema.ts @@ -168,6 +168,7 @@ const schema = gql` USER_EXISTS EXPIRED_TOKEN INVALID_PASSWORD + INVALID_EMAIL } type GoogleSignupError { diff --git a/packages/api/src/services/create_user.ts b/packages/api/src/services/create_user.ts index 7b9f62986..ae944fcd8 100644 --- a/packages/api/src/services/create_user.ts +++ b/packages/api/src/services/create_user.ts @@ -110,9 +110,11 @@ export const createUser = async (input: { // delete user if email failed to send await AppDataSource.transaction(async (e) => { await setClaims(e, user.id) + return e.getRepository(User).delete(user.id) }) - return Promise.reject({ errorCode: SignupErrorCode.Unknown }) + + return Promise.reject({ errorCode: SignupErrorCode.InvalidEmail }) } } diff --git a/packages/api/test/resolvers/user.test.ts b/packages/api/test/resolvers/user.test.ts index e905264dd..78abb33df 100644 --- a/packages/api/test/resolvers/user.test.ts +++ b/packages/api/test/resolvers/user.test.ts @@ -333,6 +333,7 @@ describe('User API', () => { let email: string let password: string let username: string + let fake: (msg: MailDataRequired) => Promise beforeEach(() => { query = ` @@ -363,29 +364,52 @@ describe('User API', () => { }) context('when inputs are valid and user not exists', () => { - let fake: (msg: MailDataRequired) => Promise - beforeEach(() => { password = correctPassword username = 'Some_username' email = `${username}@fake.com` - fake = sinon.replace(util, 'sendEmail', sinon.fake.resolves(true)) }) afterEach(async () => { await deleteTestUser(username) - sinon.restore() }) - it('responds with 200', async () => { - return graphqlRequest(query).expect(200) + context('when confirmation email sent', () => { + beforeEach(() => { + fake = sinon.replace(util, 'sendEmail', sinon.fake.resolves(true)) + }) + + afterEach(() => { + sinon.restore() + }) + + it('responds with 200', async () => { + return graphqlRequest(query).expect(200) + }) + + it('returns the user with the lowercase username', async () => { + const res = await graphqlRequest(query).expect(200) + expect(res.body.data.signup.me.profile.username).to.eql( + username.toLowerCase() + ) + }) }) - it('returns the user with the lowercase username', async () => { - const res = await graphqlRequest(query).expect(200) - expect(res.body.data.signup.me.profile.username).to.eql( - username.toLowerCase() - ) + context('when confirmation email not sent', () => { + before(() => { + fake = sinon.replace(util, 'sendEmail', sinon.fake.resolves(false)) + }) + + after(() => { + sinon.restore() + }) + + it('responds with error code INVALID_EMAIL', async () => { + const res = await graphqlRequest(query).expect(200) + expect(res.body.data.signup.errorCodes).to.eql([ + SignupErrorCode.InvalidEmail, + ]) + }) }) }) diff --git a/packages/api/test/services/create_user.test.ts b/packages/api/test/services/create_user.test.ts index eb825c9a0..9615250db 100644 --- a/packages/api/test/services/create_user.test.ts +++ b/packages/api/test/services/create_user.test.ts @@ -17,71 +17,93 @@ import sinonChai from 'sinon-chai' import sinon from 'sinon' import * as util from '../../src/utils/sendEmail' import { MailDataRequired } from '@sendgrid/helpers/classes/mail' +import { getRepository } from '../../src/entity/utils' +import { User } from '../../src/entity/user' chai.use(sinonChai) -describe('create a user with an invite', () => { - it('follows the other user in the group', async () => { - after(async () => { - await deleteTestUser(testOwner) - await deleteTestUser(testUser) +describe('create user', () => { + context('create a user with an invite', () => { + it('follows the other user in the group', async () => { + after(async () => { + await deleteTestUser(testOwner) + await deleteTestUser(testUser) + }) + + const testOwner = 'testowner' + const testUser = 'testuser' + + const adminUser = await createTestUser(testOwner) + const [, invite] = await createGroup({ + admin: adminUser, + name: 'testgroup', + }) + const user = await createTestUser(testUser, invite.code) + + expect(await getUserFollowers(user)).to.eql([adminUser]) + expect(await getUserFollowing(user)).to.eql([adminUser]) + expect(await getUserFollowers(adminUser)).to.eql([user]) + expect(await getUserFollowing(adminUser)).to.eql([user]) + }).timeout(10000) + + it('creates profile when user exists but profile not', async () => { + after(async () => { + await deleteTestUser(name) + }) + + const name = 'userWithoutProfile' + const user = await createUserWithoutProfile(name) + + await createTestUser(user.name) + + const profile = await getProfile(user) + + expect(profile).to.exist + }) + }) + + context('create a user with pending confirmation', () => { + const name = 'pendingUser' + let fake: (msg: MailDataRequired) => Promise + + context('when email sends successfully', () => { + beforeEach(() => { + fake = sinon.replace(util, 'sendEmail', sinon.fake.resolves(true)) + }) + + afterEach(async () => { + sinon.restore() + await deleteTestUser(name) + }) + + it('creates the user with pending status and correct name', async () => { + const user = await createTestUser(name, undefined, undefined, true) + + expect(user.status).to.eql(StatusType.Pending) + expect(user.name).to.eql(name) + }) + + it('sends an email to the user', async () => { + await createTestUser(name, undefined, undefined, true) + + expect(fake).to.have.been.calledOnce + }) }) - const testOwner = 'testowner' - const testUser = 'testuser' + context('when failed to send email', () => { + before(() => { + fake = sinon.replace(util, 'sendEmail', sinon.fake.resolves(false)) + }) - const adminUser = await createTestUser(testOwner) - const [, invite] = await createGroup({ - admin: adminUser, - name: 'testgroup', + after(() => { + sinon.restore() + }) + + it('does not create the user', async () => { + await expect(createTestUser(name, undefined, undefined, true)).to.be + .rejected + expect(await getRepository(User).findOneBy({ name })).to.be.null + }) }) - const user = await createTestUser(testUser, invite.code) - - expect(await getUserFollowers(user)).to.eql([adminUser]) - expect(await getUserFollowing(user)).to.eql([adminUser]) - expect(await getUserFollowers(adminUser)).to.eql([user]) - expect(await getUserFollowing(adminUser)).to.eql([user]) - }).timeout(10000) - - it('creates profile when user exists but profile not', async () => { - after(async () => { - await deleteTestUser(name) - }) - - const name = 'userWithoutProfile' - const user = await createUserWithoutProfile(name) - - await createTestUser(user.name) - - const profile = await getProfile(user) - - expect(profile).to.exist - }) -}) - -describe('create a user with pending confirmation', () => { - const name = 'pendingUser' - let fake: (msg: MailDataRequired) => Promise - - beforeEach(() => { - fake = sinon.replace(util, 'sendEmail', sinon.fake.resolves(true)) - }) - - afterEach(async () => { - sinon.restore() - await deleteTestUser(name) - }) - - it('creates the user with pending status and correct name', async () => { - const user = await createTestUser(name, undefined, undefined, true) - - expect(user.status).to.eql(StatusType.Pending) - expect(user.name).to.eql(name) - }) - - it('sends an email to the user', async () => { - await createTestUser(name, undefined, undefined, true) - - expect(fake).to.have.been.calledOnce }) })