435 lines
11 KiB
TypeScript
435 lines
11 KiB
TypeScript
import { createTestUser, deleteTestUser, getProfile, getUser } from '../db'
|
|
import { graphqlRequest, request } from '../util'
|
|
import { expect } from 'chai'
|
|
import {
|
|
LoginErrorCode,
|
|
SignupErrorCode,
|
|
UpdateUserErrorCode,
|
|
UpdateUserProfileErrorCode,
|
|
} from '../../src/generated/graphql'
|
|
import { User } from '../../src/entity/user'
|
|
import { hashPassword } from '../../src/utils/auth'
|
|
import 'mocha'
|
|
import { MailDataRequired } from '@sendgrid/helpers/classes/mail'
|
|
import sinon from 'sinon'
|
|
import * as util from '../../src/utils/sendEmail'
|
|
|
|
describe('User API', () => {
|
|
const username = 'fake_user'
|
|
const correctPassword = 'fakePassword'
|
|
const anotherUsername = 'newFakeUser'
|
|
|
|
let authToken: string
|
|
let user: User
|
|
let anotherUser: User
|
|
|
|
before(async () => {
|
|
const hashedPassword = await hashPassword(correctPassword)
|
|
// create test user and login
|
|
user = await createTestUser(username, '', hashedPassword)
|
|
const res = await request
|
|
.post('/local/debug/fake-user-login')
|
|
.send({ fakeEmail: user.email })
|
|
|
|
authToken = res.body.authToken
|
|
|
|
// create new fake user
|
|
anotherUser = await createTestUser(anotherUsername)
|
|
})
|
|
|
|
after(async () => {
|
|
// clean up
|
|
await deleteTestUser(username)
|
|
await deleteTestUser(anotherUsername)
|
|
})
|
|
|
|
describe('Update user', () => {
|
|
let name = 'Some name'
|
|
let bio = 'Some bio'
|
|
let query: string
|
|
|
|
beforeEach(() => {
|
|
query = `
|
|
mutation {
|
|
updateUser(
|
|
input: {
|
|
name: "${name}"
|
|
bio: "${bio}"
|
|
}
|
|
) {
|
|
... on UpdateUserSuccess {
|
|
user {
|
|
id
|
|
name
|
|
isFullUser
|
|
viewerIsFollowing
|
|
isFriend
|
|
picture
|
|
profile {
|
|
id
|
|
username
|
|
private
|
|
bio
|
|
pictureUrl
|
|
}
|
|
}
|
|
}
|
|
... on UpdateUserError {
|
|
errorCodes
|
|
}
|
|
}
|
|
}
|
|
`
|
|
})
|
|
|
|
context('when name in input is empty', () => {
|
|
before(() => {
|
|
name = ''
|
|
})
|
|
|
|
it('responds with error code EMPTY_NAME', async () => {
|
|
const response = await graphqlRequest(query, authToken).expect(200)
|
|
expect(response.body.data.updateUser.errorCodes).to.eql([
|
|
UpdateUserErrorCode.EmptyName,
|
|
])
|
|
})
|
|
})
|
|
|
|
context('when name is not empty', () => {
|
|
before(() => {
|
|
name = 'Some new name'
|
|
})
|
|
|
|
it('updates user and responds with status code 200', async () => {
|
|
const response = await graphqlRequest(query, authToken).expect(200)
|
|
const user = await getUser(response.body.data.updateUser.user.id)
|
|
expect(user?.name).to.eql(name)
|
|
})
|
|
})
|
|
|
|
it('responds status code 400 when invalid query', async () => {
|
|
const invalidQuery = `
|
|
mutation {
|
|
updateUser()
|
|
}
|
|
`
|
|
return graphqlRequest(invalidQuery, authToken).expect(400)
|
|
})
|
|
|
|
it('responds status code 500 when invalid user', async () => {
|
|
const invalidAuthToken = 'Fake token'
|
|
return graphqlRequest(query, invalidAuthToken).expect(500)
|
|
})
|
|
})
|
|
|
|
describe('Update user profile', () => {
|
|
let query: string
|
|
let userId = 'Some user id'
|
|
let newUsername = 'Some username'
|
|
let pictureUrl = 'Some picture url'
|
|
|
|
beforeEach(() => {
|
|
query = `
|
|
mutation {
|
|
updateUserProfile(
|
|
input: {
|
|
userId: "${userId}"
|
|
username: "${newUsername}"
|
|
pictureUrl: "${pictureUrl}"
|
|
}
|
|
) {
|
|
... on UpdateUserProfileSuccess {
|
|
user {
|
|
id
|
|
profile {
|
|
id
|
|
username
|
|
private
|
|
bio
|
|
pictureUrl
|
|
}
|
|
}
|
|
}
|
|
... on UpdateUserProfileError {
|
|
errorCodes
|
|
}
|
|
}
|
|
}
|
|
`
|
|
})
|
|
|
|
context('when username is new and valid', () => {
|
|
before(() => {
|
|
userId = user.id
|
|
newUsername = 'new_username'
|
|
})
|
|
|
|
it('updates user profile and responds with 200', async () => {
|
|
await graphqlRequest(query, authToken).expect(200)
|
|
const profile = await getProfile(user)
|
|
expect(profile?.username).to.eql(newUsername)
|
|
})
|
|
})
|
|
|
|
context('when userId not match', () => {
|
|
before(() => {
|
|
userId = anotherUser.id
|
|
})
|
|
|
|
it('responds with error code FORBIDDEN', async () => {
|
|
const response = await graphqlRequest(query, authToken).expect(200)
|
|
expect(response.body.data.updateUserProfile.errorCodes).to.eql([
|
|
UpdateUserProfileErrorCode.Forbidden,
|
|
])
|
|
})
|
|
})
|
|
|
|
context('when username and pictureUrl are null', () => {
|
|
before(() => {
|
|
userId = user.id
|
|
newUsername = ''
|
|
pictureUrl = ''
|
|
})
|
|
|
|
it('responds with error code BadData', async () => {
|
|
const response = await graphqlRequest(query, authToken).expect(200)
|
|
expect(response.body.data.updateUserProfile.errorCodes).to.eql([
|
|
UpdateUserProfileErrorCode.BadData,
|
|
])
|
|
})
|
|
})
|
|
|
|
context('when username exists', () => {
|
|
before(async () => {
|
|
userId = user.id
|
|
const profile = await getProfile(user)
|
|
newUsername = profile?.username || 'new_username'
|
|
})
|
|
|
|
it('responds with error code UsernameExists', async () => {
|
|
const response = await graphqlRequest(query, authToken).expect(200)
|
|
expect(response.body.data.updateUserProfile.errorCodes).to.eql([
|
|
UpdateUserProfileErrorCode.UsernameExists,
|
|
])
|
|
})
|
|
})
|
|
|
|
context('when username is invalid', () => {
|
|
before(() => {
|
|
userId = user.id
|
|
newUsername = 'omnivore'
|
|
})
|
|
|
|
it('responds with error code BadUsername', async () => {
|
|
const response = await graphqlRequest(query, authToken).expect(200)
|
|
expect(response.body.data.updateUserProfile.errorCodes).to.eql([
|
|
UpdateUserProfileErrorCode.BadUsername,
|
|
])
|
|
})
|
|
})
|
|
|
|
it('responds status code 400 when invalid query', async () => {
|
|
const invalidQuery = `
|
|
mutation {
|
|
updateUserProfile()
|
|
}
|
|
`
|
|
return graphqlRequest(invalidQuery, authToken).expect(400)
|
|
})
|
|
|
|
it('responds status code 500 when invalid user', async () => {
|
|
const invalidAuthToken = 'Fake token'
|
|
return graphqlRequest(query, invalidAuthToken).expect(500)
|
|
})
|
|
})
|
|
|
|
describe('login', () => {
|
|
let query: string
|
|
let email: string
|
|
let password: string
|
|
|
|
beforeEach(() => {
|
|
query = `
|
|
mutation {
|
|
login(
|
|
input: {
|
|
email: "${email}"
|
|
password: "${password}"
|
|
}
|
|
) {
|
|
... on LoginSuccess {
|
|
me {
|
|
id
|
|
name
|
|
profile {
|
|
username
|
|
}
|
|
}
|
|
}
|
|
... on LoginError {
|
|
errorCodes
|
|
}
|
|
}
|
|
}
|
|
`
|
|
})
|
|
|
|
context('when email and password are valid', () => {
|
|
before(() => {
|
|
email = user.email
|
|
password = correctPassword
|
|
})
|
|
|
|
it('responds with 200', async () => {
|
|
const res = await graphqlRequest(query).expect(200)
|
|
expect(res.body.data.login.me.id).to.eql(user.id)
|
|
})
|
|
})
|
|
|
|
context('when user not exists', () => {
|
|
before(() => {
|
|
email = 'Some email'
|
|
})
|
|
|
|
it('responds with error code UserNotFound', async () => {
|
|
const response = await graphqlRequest(query).expect(200)
|
|
expect(response.body.data.login.errorCodes).to.eql([
|
|
LoginErrorCode.UserNotFound,
|
|
])
|
|
})
|
|
})
|
|
|
|
context('when user has no password stored in db', () => {
|
|
before(() => {
|
|
email = anotherUser.email
|
|
password = 'Some password'
|
|
})
|
|
|
|
it('responds with error code WrongSource', async () => {
|
|
const response = await graphqlRequest(query).expect(200)
|
|
expect(response.body.data.login.errorCodes).to.eql([
|
|
LoginErrorCode.WrongSource,
|
|
])
|
|
})
|
|
})
|
|
|
|
context('when password is wrong', () => {
|
|
before(() => {
|
|
email = user.email
|
|
password = 'Some password'
|
|
})
|
|
|
|
it('responds with error code UserNotFound', async () => {
|
|
const response = await graphqlRequest(query).expect(200)
|
|
expect(response.body.data.login.errorCodes).to.eql([
|
|
LoginErrorCode.InvalidCredentials,
|
|
])
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('signup', () => {
|
|
let query: string
|
|
let email: string
|
|
let password: string
|
|
let username: string
|
|
|
|
beforeEach(() => {
|
|
query = `
|
|
mutation {
|
|
signup(
|
|
input: {
|
|
email: "${email}"
|
|
password: "${password}"
|
|
name: "Some name"
|
|
username: "${username}"
|
|
}
|
|
) {
|
|
... on SignupSuccess {
|
|
me {
|
|
id
|
|
name
|
|
profile {
|
|
username
|
|
}
|
|
}
|
|
}
|
|
... on SignupError {
|
|
errorCodes
|
|
}
|
|
}
|
|
}
|
|
`
|
|
})
|
|
|
|
context('when inputs are valid and user not exists', () => {
|
|
let fake: (msg: MailDataRequired) => Promise<boolean>
|
|
|
|
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)
|
|
})
|
|
|
|
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 password is too long', () => {
|
|
before(() => {
|
|
email = 'Some_email'
|
|
password = 'Some_password_that_is_too_long_for_database'
|
|
username = 'Some_username'
|
|
})
|
|
|
|
it('responds with status code 400', async () => {
|
|
return graphqlRequest(query).expect(400)
|
|
})
|
|
})
|
|
|
|
context('when user exists', () => {
|
|
before(() => {
|
|
email = user.email
|
|
password = 'Some password'
|
|
username = 'Some username'
|
|
})
|
|
|
|
it('responds with error code UserExists', async () => {
|
|
const response = await graphqlRequest(query).expect(200)
|
|
expect(response.body.data.signup.errorCodes).to.eql([
|
|
SignupErrorCode.UserExists,
|
|
])
|
|
})
|
|
})
|
|
|
|
context('when username is invalid', () => {
|
|
before(() => {
|
|
email = 'Some_email'
|
|
password = correctPassword
|
|
username = 'omnivore_admin'
|
|
})
|
|
|
|
it('responds with error code InvalidUsername', async () => {
|
|
const response = await graphqlRequest(query).expect(200)
|
|
expect(response.body.data.signup.errorCodes).to.eql([
|
|
SignupErrorCode.InvalidUsername,
|
|
])
|
|
})
|
|
})
|
|
})
|
|
})
|