mirror of
https://github.com/Permissionless-Software-Foundation/ipfs-bch-wallet-consumer.git
synced 2026-09-21 16:52:03 -07:00
Porting code from chris-troutner to PSF GitHub Repo
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
# Automated End-to-end Tests
|
||||
|
||||
This contains the original boilerplate tests, which are end-to-end tests. These tests are fully automated and test the system directly by making REST API calls with axios.
|
||||
|
||||
These tests function exactly the same as a normal user would, by making real REST API calls to the software. As a result, they are fine for testing internal system components like authorization and user handling. However, they are inappropriate for testing sophisticated endpoints that involve complex operations. For example, interacting with a blockchain, pinging other network systems, or writing data to a secondary database.
|
||||
|
||||
There is some redundancy between these tests and the unit tests. The focus is on *how* the tests are executed. The unit tests call the libraries directly (internally). These e2e tests use the REST API (externally).
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
End-to-end tests for /auth endpoints.
|
||||
|
||||
This test sets up the environment for other e2e tests.
|
||||
*/
|
||||
|
||||
// Public npm libraries
|
||||
const assert = require('chai').assert
|
||||
const axios = require('axios').default
|
||||
|
||||
// Local support libraries
|
||||
const config = require('../../../config')
|
||||
const app = require('../../../bin/server')
|
||||
const testUtils = require('../../utils/test-utils')
|
||||
const AdminLib = require('../../../src/lib/admin')
|
||||
const adminLib = new AdminLib()
|
||||
|
||||
// const request = supertest.agent(app.listen())
|
||||
const context = {}
|
||||
|
||||
const LOCALHOST = `http://localhost:${config.port}`
|
||||
|
||||
describe('Auth', () => {
|
||||
before(async () => {
|
||||
// This should be the first instruction. It starts the REST API server.
|
||||
await app.startServer()
|
||||
|
||||
// Delete all previous users in the database.
|
||||
await testUtils.deleteAllUsers()
|
||||
|
||||
// Create a new admin user.
|
||||
await adminLib.createSystemUser()
|
||||
|
||||
const userObj = {
|
||||
email: 'test@test.com',
|
||||
password: 'pass',
|
||||
name: 'test'
|
||||
}
|
||||
const testUser = await testUtils.createUser(userObj)
|
||||
// console.log('TestUser: ', testUser)
|
||||
|
||||
context.user = testUser.user
|
||||
context.token = testUser.token
|
||||
})
|
||||
|
||||
describe('POST /auth', () => {
|
||||
it('should throw 401 if credentials are incorrect', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'post',
|
||||
url: `${LOCALHOST}/auth`,
|
||||
data: {
|
||||
email: 'test@test.com',
|
||||
password: 'wrongpassword'
|
||||
}
|
||||
}
|
||||
|
||||
const result = await axios(options)
|
||||
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
console.log(
|
||||
`result stringified: ${JSON.stringify(result.data, null, 2)}`
|
||||
)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert(err.response.status === 401, 'Error code 401 expected.')
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw 401 if email is wrong format', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'post',
|
||||
url: `${LOCALHOST}/auth`,
|
||||
data: {
|
||||
email: 'wrongEmail',
|
||||
password: 'wrongpassword'
|
||||
}
|
||||
}
|
||||
|
||||
await axios(options)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert(err.response.status === 401, 'Error code 401 expected.')
|
||||
}
|
||||
})
|
||||
|
||||
it('should auth user', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'post',
|
||||
url: `${LOCALHOST}/auth`,
|
||||
data: {
|
||||
email: 'test@test.com',
|
||||
password: 'pass'
|
||||
}
|
||||
}
|
||||
const result = await axios(options)
|
||||
// console.log(`result: ${JSON.stringify(result.data, null, 2)}`)
|
||||
|
||||
assert(result.status === 200, 'Status Code 200 expected.')
|
||||
assert(
|
||||
result.data.user.email === 'test@test.com',
|
||||
'Email of test expected'
|
||||
)
|
||||
assert(
|
||||
result.data.user.password === undefined,
|
||||
'Password expected to be omited'
|
||||
)
|
||||
} catch (err) {
|
||||
console.log(
|
||||
'Error authenticating test user: ' + JSON.stringify(err, null, 2)
|
||||
)
|
||||
throw err
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,786 @@
|
||||
const testUtils = require('../../utils/test-utils')
|
||||
const assert = require('chai').assert
|
||||
const config = require('../../../config')
|
||||
const axios = require('axios').default
|
||||
const sinon = require('sinon')
|
||||
|
||||
const util = require('util')
|
||||
util.inspect.defaultOptions = { depth: 1 }
|
||||
|
||||
const LOCALHOST = `http://localhost:${config.port}`
|
||||
|
||||
const context = {}
|
||||
|
||||
const UserController = require('../../../src/modules/users/controller')
|
||||
let uut
|
||||
let sandbox
|
||||
|
||||
// const mockContext = require('../../unit/mocks/ctx-mock').context
|
||||
|
||||
describe('Users', () => {
|
||||
before(async () => {
|
||||
// console.log(`config: ${JSON.stringify(config, null, 2)}`)
|
||||
|
||||
// Create a second test user.
|
||||
const userObj = {
|
||||
email: 'test2@test.com',
|
||||
password: 'pass2',
|
||||
name: 'test2'
|
||||
}
|
||||
const testUser = await testUtils.createUser(userObj)
|
||||
// console.log(`testUser2: ${JSON.stringify(testUser, null, 2)}`)
|
||||
|
||||
context.user2 = testUser.user
|
||||
context.token2 = testUser.token
|
||||
context.id2 = testUser.user._id
|
||||
|
||||
// Get the JWT used to log in as the admin 'system' user.
|
||||
const adminJWT = await testUtils.getAdminJWT()
|
||||
// console.log(`adminJWT: ${admi nJWT}`)
|
||||
context.adminJWT = adminJWT
|
||||
|
||||
// const admin = await testUtils.loginAdminUser()
|
||||
// context.adminJWT = admin.token
|
||||
|
||||
// const admin = await adminLib.loginAdmin()
|
||||
// console.log(`admin: ${JSON.stringify(admin, null, 2)}`)
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
uut = new UserController()
|
||||
|
||||
sandbox = sinon.createSandbox()
|
||||
})
|
||||
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
describe('POST /users - Create User', () => {
|
||||
it('should reject signup when data is incomplete', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'POST',
|
||||
url: `${LOCALHOST}/users`,
|
||||
data: {
|
||||
email: 'test2@test.com'
|
||||
}
|
||||
}
|
||||
|
||||
await axios(options)
|
||||
|
||||
/* console.log(
|
||||
`result stringified: ${JSON.stringify(result.data, null, 2)}`
|
||||
) */
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert(err.response.status === 422, 'Error code 422 expected.')
|
||||
}
|
||||
})
|
||||
|
||||
it('should reject signup if no email property is provided', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'POST',
|
||||
url: `${LOCALHOST}/users`,
|
||||
data: {
|
||||
user: {
|
||||
password: 'pass2'
|
||||
}
|
||||
}
|
||||
}
|
||||
await axios(options)
|
||||
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
// console.log('err', err)
|
||||
assert.equal(err.response.status, 422)
|
||||
assert.include(err.response.data, "Property 'email' must be a string")
|
||||
}
|
||||
})
|
||||
|
||||
it('should reject signup if no password property is provided', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'POST',
|
||||
url: `${LOCALHOST}/users`,
|
||||
data: {
|
||||
user: {
|
||||
email: 'test2@test.com'
|
||||
}
|
||||
}
|
||||
}
|
||||
await axios(options)
|
||||
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 422)
|
||||
assert.include(
|
||||
err.response.data,
|
||||
"Property 'password' must be a string"
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it('should reject if name property property is not string', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'POST',
|
||||
url: `${LOCALHOST}/users`,
|
||||
data: {
|
||||
user: {
|
||||
email: 'test322@test.com',
|
||||
password: 'supersecretpassword',
|
||||
name: 1234
|
||||
}
|
||||
}
|
||||
}
|
||||
await axios(options)
|
||||
|
||||
assert.fail('Unexpected result')
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
assert.equal(err.response.status, 422)
|
||||
assert.include(err.response.data, "Property 'name' must be a string")
|
||||
}
|
||||
})
|
||||
|
||||
it("should signup of type 'user' by default", async () => {
|
||||
const options = {
|
||||
method: 'post',
|
||||
url: `${LOCALHOST}/users`,
|
||||
data: {
|
||||
user: {
|
||||
email: 'test3@test.com',
|
||||
password: 'supersecretpassword',
|
||||
name: 'test3'
|
||||
}
|
||||
}
|
||||
}
|
||||
const result = await axios(options)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
context.user = result.data.user
|
||||
context.token = result.data.token
|
||||
|
||||
assert(result.status === 200, 'Status Code 200 expected.')
|
||||
assert(
|
||||
result.data.user.email === 'test3@test.com',
|
||||
'Email of test expected'
|
||||
)
|
||||
assert(
|
||||
result.data.user.password === undefined,
|
||||
'Password expected to be omited'
|
||||
)
|
||||
assert.property(result.data, 'token', 'Token property exists.')
|
||||
assert.equal(result.data.user.type, 'user')
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /users', () => {
|
||||
it('should not fetch users if the authorization header is missing', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'GET',
|
||||
url: `${LOCALHOST}/users`,
|
||||
headers: {
|
||||
Accept: 'application/json'
|
||||
}
|
||||
}
|
||||
await axios(options)
|
||||
|
||||
assert.equal(true, false, 'Unexpected behavior')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 401)
|
||||
}
|
||||
})
|
||||
|
||||
it('should not fetch users if the authorization header is missing the scheme', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'GET',
|
||||
url: `${LOCALHOST}/users`,
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: '1'
|
||||
}
|
||||
}
|
||||
await axios(options)
|
||||
|
||||
assert.equal(true, false, 'Unexpected behavior')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 401)
|
||||
}
|
||||
})
|
||||
|
||||
it('should not fetch users if the authorization header has invalid scheme', async () => {
|
||||
const { token } = context
|
||||
try {
|
||||
const options = {
|
||||
method: 'GET',
|
||||
url: `${LOCALHOST}/users`,
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Unknown ${token}`
|
||||
}
|
||||
}
|
||||
await axios(options)
|
||||
|
||||
assert.equal(true, false, 'Unexpected behavior')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 401)
|
||||
}
|
||||
})
|
||||
|
||||
it('should not fetch users if token is invalid', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'GET',
|
||||
url: `${LOCALHOST}/users`,
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: 'Bearer 1'
|
||||
}
|
||||
}
|
||||
await axios(options)
|
||||
|
||||
assert.equal(true, false, 'Unexpected behavior')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 401)
|
||||
}
|
||||
})
|
||||
|
||||
it('should fetch all users', async () => {
|
||||
const { token } = context
|
||||
|
||||
const options = {
|
||||
method: 'GET',
|
||||
url: `${LOCALHOST}/users`,
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
}
|
||||
}
|
||||
const result = await axios(options)
|
||||
|
||||
const users = result.data.users
|
||||
// console.log(`users: ${util.inspect(users)}`)
|
||||
|
||||
assert.hasAnyKeys(users[0], ['type', '_id', 'email'])
|
||||
assert.isNumber(users.length)
|
||||
})
|
||||
|
||||
it('should return a 422 http status if biz-logic throws an error', async () => {
|
||||
try {
|
||||
const { token } = context
|
||||
|
||||
// Force an error
|
||||
sandbox
|
||||
.stub(uut.userLib, 'getAllUsers')
|
||||
.rejects(new Error('test error'))
|
||||
|
||||
const options = {
|
||||
method: 'GET',
|
||||
url: `${LOCALHOST}/users`,
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
}
|
||||
}
|
||||
await axios(options)
|
||||
|
||||
assert.fail('Unexpected code path!')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 422)
|
||||
assert.equal(err.response.data, 'test error')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /users/:id', () => {
|
||||
it('should not fetch user if token is invalid', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'GET',
|
||||
url: `${LOCALHOST}/users/1`,
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: 'Bearer 1'
|
||||
}
|
||||
}
|
||||
await axios(options)
|
||||
|
||||
assert.equal(true, false, 'Unexpected behavior')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 401)
|
||||
}
|
||||
})
|
||||
|
||||
it("should throw 404 if user doesn't exist", async () => {
|
||||
const { token } = context
|
||||
|
||||
try {
|
||||
const options = {
|
||||
method: 'GET',
|
||||
url: `${LOCALHOST}/users/5fa4bd7ee1828f5f4d8ed004`,
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
}
|
||||
}
|
||||
await axios(options)
|
||||
|
||||
assert.equal(true, false, 'Unexpected behavior')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 404)
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw 422 for invalid input', async () => {
|
||||
const { token } = context
|
||||
|
||||
try {
|
||||
const options = {
|
||||
method: 'GET',
|
||||
url: `${LOCALHOST}/users/1`,
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
}
|
||||
}
|
||||
await axios(options)
|
||||
|
||||
assert.equal(true, false, 'Unexpected behavior')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 422)
|
||||
}
|
||||
})
|
||||
|
||||
it('should fetch own user', async () => {
|
||||
const _id = context.user._id
|
||||
const token = context.token
|
||||
|
||||
const options = {
|
||||
method: 'GET',
|
||||
url: `${LOCALHOST}/users/${_id}`,
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
}
|
||||
}
|
||||
const result = await axios(options)
|
||||
|
||||
const user = result.data.user
|
||||
// console.log(`user: ${util.inspect(user)}`)
|
||||
|
||||
assert.property(user, 'type')
|
||||
assert.property(user, 'email')
|
||||
|
||||
assert.property(user, '_id')
|
||||
assert.equal(user._id, _id)
|
||||
|
||||
assert.notProperty(
|
||||
user,
|
||||
'password',
|
||||
'Password property should not be returned'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('PUT /users/:id', () => {
|
||||
it('should not update user if token is invalid', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'PUT',
|
||||
url: `${LOCALHOST}/users/1`,
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: 'Bearer 1'
|
||||
}
|
||||
}
|
||||
await axios(options)
|
||||
|
||||
assert.equal(true, false, 'Unexpected behavior')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 401)
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw 401 if non-admin updating other user', async () => {
|
||||
const { token } = context
|
||||
|
||||
try {
|
||||
const options = {
|
||||
method: 'PUT',
|
||||
url: `${LOCALHOST}/users/1`,
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
}
|
||||
}
|
||||
await axios(options)
|
||||
|
||||
assert.equal(true, false, 'Unexpected behavior')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 401)
|
||||
}
|
||||
})
|
||||
|
||||
it('should not be able to update user type', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'PUT',
|
||||
url: `${LOCALHOST}/users/${context.user._id.toString()}`,
|
||||
headers: {
|
||||
Authorization: `Bearer ${context.token}`
|
||||
},
|
||||
data: {
|
||||
user: {
|
||||
email: 'test@test.com',
|
||||
password: 'password',
|
||||
name: 'new name',
|
||||
type: 'test'
|
||||
}
|
||||
}
|
||||
}
|
||||
await axios(options)
|
||||
|
||||
// console.log(`Users: ${JSON.stringify(result.data, null, 2)}`)
|
||||
|
||||
// assert(result.status === 200, 'Status Code 200 expected.')
|
||||
// assert(result.data.user.type === 'user', 'Type should be unchanged.')
|
||||
assert.equal(true, false, 'Unexpected behavior')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 422)
|
||||
assert.include(
|
||||
err.response.data,
|
||||
"Property 'type' can only be changed by Admin user"
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it('should not be able to update other user when not admin', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'PUT',
|
||||
url: `${LOCALHOST}/users/${context.user2._id.toString()}`,
|
||||
headers: {
|
||||
Authorization: `Bearer ${context.token}`
|
||||
},
|
||||
data: {
|
||||
user: {
|
||||
name: 'This should not work'
|
||||
}
|
||||
}
|
||||
}
|
||||
await axios(options)
|
||||
|
||||
// console.log(`result: ${JSON.stringify(result.data, null, 2)}`)
|
||||
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 401)
|
||||
}
|
||||
})
|
||||
|
||||
it('should not be able to update if name property is wrong', async () => {
|
||||
try {
|
||||
const _id = context.user._id
|
||||
const token = context.token
|
||||
|
||||
const options = {
|
||||
method: 'PUT',
|
||||
url: `${LOCALHOST}/users/${_id}`,
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
},
|
||||
data: {
|
||||
user: {
|
||||
email: 'testToUpdate@test.com',
|
||||
name: {},
|
||||
password: 'password'
|
||||
}
|
||||
}
|
||||
}
|
||||
await axios(options)
|
||||
} catch (error) {
|
||||
assert.equal(error.response.status, 422)
|
||||
assert.include(error.response.data, "Property 'name' must be a string!")
|
||||
}
|
||||
})
|
||||
|
||||
it('should not be able to update if password property is not string', async () => {
|
||||
const { token } = context
|
||||
const _id = context.user._id
|
||||
try {
|
||||
const options = {
|
||||
method: 'PUT',
|
||||
url: `${LOCALHOST}/users/${_id}`,
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
},
|
||||
data: {
|
||||
user: {
|
||||
password: 1234,
|
||||
email: 'test@test.com',
|
||||
name: 'test'
|
||||
}
|
||||
}
|
||||
}
|
||||
await axios(options)
|
||||
|
||||
assert.equal(true, false, 'Unexpected behavior')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 422)
|
||||
assert.include(
|
||||
err.response.data,
|
||||
"Property 'password' must be a string!"
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it('should not be able to update if email is not string', async () => {
|
||||
const { token } = context
|
||||
const _id = context.user._id
|
||||
try {
|
||||
const options = {
|
||||
method: 'PUT',
|
||||
url: `${LOCALHOST}/users/${_id}`,
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
},
|
||||
data: {
|
||||
user: {
|
||||
email: 1234
|
||||
}
|
||||
}
|
||||
}
|
||||
await axios(options)
|
||||
|
||||
assert.equal(true, false, 'Unexpected behavior')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 422)
|
||||
assert.include(err.response.data, "Property 'email' must be a string!")
|
||||
}
|
||||
})
|
||||
|
||||
it('should not be able to update type property if is not string', async () => {
|
||||
try {
|
||||
const _id = context.user._id
|
||||
const token = context.token
|
||||
|
||||
const options = {
|
||||
method: 'PUT',
|
||||
url: `${LOCALHOST}/users/${_id}`,
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
},
|
||||
data: {
|
||||
user: {
|
||||
type: 1,
|
||||
email: 'test@test.com',
|
||||
name: 'test',
|
||||
password: 'password'
|
||||
}
|
||||
}
|
||||
}
|
||||
await axios(options)
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 422)
|
||||
assert.include(err.response.data, "Property 'type' must be a string!")
|
||||
}
|
||||
})
|
||||
|
||||
it('should be able to update other user when admin', async () => {
|
||||
const adminJWT = context.adminJWT
|
||||
|
||||
const options = {
|
||||
method: 'PUT',
|
||||
url: `${LOCALHOST}/users/${context.user2._id.toString()}`,
|
||||
headers: {
|
||||
Authorization: `Bearer ${adminJWT}`
|
||||
},
|
||||
data: {
|
||||
user: {
|
||||
name: 'This should work',
|
||||
email: 'test4@test.com',
|
||||
password: 'password'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const result = await axios(options)
|
||||
// console.log(`result stringified: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
const userName = result.data.user.name
|
||||
assert.equal(userName, 'This should work')
|
||||
})
|
||||
|
||||
it('should update user with minimum inputs', async () => {
|
||||
const _id = context.user._id
|
||||
const token = context.token
|
||||
|
||||
const options = {
|
||||
method: 'PUT',
|
||||
url: `${LOCALHOST}/users/${_id}`,
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
},
|
||||
data: {
|
||||
user: { email: 'testToUpdate@test.com' }
|
||||
}
|
||||
}
|
||||
|
||||
const result = await axios(options)
|
||||
const user = result.data.user
|
||||
// console.log(`user: ${util.inspect(user)}`)
|
||||
|
||||
assert.property(user, 'type')
|
||||
assert.property(user, 'email')
|
||||
|
||||
assert.property(user, '_id')
|
||||
assert.equal(user._id, _id)
|
||||
|
||||
assert.notProperty(
|
||||
user,
|
||||
'password',
|
||||
'Password property should not be returned'
|
||||
)
|
||||
assert.equal(user.email, 'testToUpdate@test.com')
|
||||
})
|
||||
|
||||
it('should update user with all inputs', async () => {
|
||||
const _id = context.user._id
|
||||
const token = context.token
|
||||
|
||||
const options = {
|
||||
method: 'PUT',
|
||||
url: `${LOCALHOST}/users/${_id}`,
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
},
|
||||
data: {
|
||||
user: {
|
||||
email: 'testToUpdate@test.com',
|
||||
name: 'my name',
|
||||
username: 'myUsername'
|
||||
}
|
||||
}
|
||||
}
|
||||
const result = await axios(options)
|
||||
|
||||
const user = result.data.user
|
||||
// console.log(`user: ${util.inspect(user)}`)
|
||||
|
||||
assert.property(user, 'type')
|
||||
assert.property(user, 'email')
|
||||
assert.property(user, 'name')
|
||||
|
||||
assert.property(user, '_id')
|
||||
assert.equal(user._id, _id)
|
||||
assert.notProperty(
|
||||
user,
|
||||
'password',
|
||||
'Password property should not be returned'
|
||||
)
|
||||
assert.equal(user.name, 'my name')
|
||||
assert.equal(user.email, 'testToUpdate@test.com')
|
||||
assert.equal(user.username, 'myUsername')
|
||||
})
|
||||
})
|
||||
|
||||
describe('DELETE /users/:id', () => {
|
||||
it('should not delete user if token is invalid', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'DELETE',
|
||||
url: `${LOCALHOST}/users/1`,
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: 'Bearer 1'
|
||||
}
|
||||
}
|
||||
await axios(options)
|
||||
|
||||
assert.equal(true, false, 'Unexpected behavior')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 401)
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw 401 if deleting invalid user', async () => {
|
||||
const { token } = context
|
||||
|
||||
try {
|
||||
const options = {
|
||||
method: 'DELETE',
|
||||
url: `${LOCALHOST}/users/1`,
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
}
|
||||
}
|
||||
await axios(options)
|
||||
|
||||
assert.equal(true, false, 'Unexpected behavior')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 401)
|
||||
}
|
||||
})
|
||||
|
||||
it('should not be able to delete other users unless admin', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'DELETE',
|
||||
url: `${LOCALHOST}/users/${context.user2._id.toString()}`,
|
||||
headers: {
|
||||
Authorization: `Bearer ${context.token}`
|
||||
}
|
||||
}
|
||||
await axios(options)
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 401)
|
||||
}
|
||||
})
|
||||
|
||||
it('should delete own user', async () => {
|
||||
const _id = context.user._id
|
||||
const token = context.token
|
||||
|
||||
const options = {
|
||||
method: 'DELETE',
|
||||
url: `${LOCALHOST}/users/${_id}`,
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
}
|
||||
}
|
||||
const result = await axios(options)
|
||||
// console.log(`result: ${util.inspect(result.data.success)}`)
|
||||
|
||||
assert.equal(result.data.success, true)
|
||||
})
|
||||
|
||||
it('should be able to delete other users when admin', async () => {
|
||||
const id = context.id2
|
||||
const adminJWT = context.adminJWT
|
||||
|
||||
const options = {
|
||||
method: 'DELETE',
|
||||
url: `${LOCALHOST}/users/${id}`,
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${adminJWT}`
|
||||
}
|
||||
}
|
||||
const result = await axios(options)
|
||||
// console.log(`result: ${util.inspect(result.data)}`)
|
||||
|
||||
assert.equal(result.data.success, true)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,178 @@
|
||||
const config = require('../../../config')
|
||||
const axios = require('axios').default
|
||||
const assert = require('chai').assert
|
||||
const sinon = require('sinon')
|
||||
|
||||
// Mock data
|
||||
// const mockData = require('./mocks/contact-mocks')
|
||||
|
||||
const LOCALHOST = `http://localhost:${config.port}`
|
||||
|
||||
const mockContext = require('../../unit/mocks/ctx-mock').context
|
||||
const ContactController = require('../../../src/modules/contact/controller')
|
||||
let uut
|
||||
let sandbox
|
||||
|
||||
describe('Contact', () => {
|
||||
beforeEach(() => {
|
||||
uut = new ContactController()
|
||||
|
||||
sandbox = sinon.createSandbox()
|
||||
})
|
||||
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
describe('POST /contact/email', () => {
|
||||
it('should throw error if email property is not provided', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'POST',
|
||||
url: `${LOCALHOST}/contact/email`,
|
||||
data: {
|
||||
obj: {
|
||||
formMessage: 'message'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await axios(options)
|
||||
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
// console.log(`result stringified: ${JSON.stringify(result, null, 2)}`)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 422)
|
||||
assert.include(err.response.data, "Property 'email' must be a string!")
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw error if formMessage property is not provided', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'POST',
|
||||
url: `${LOCALHOST}/contact/email`,
|
||||
data: {
|
||||
obj: {
|
||||
email: 'email@email.com'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await axios(options)
|
||||
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
// console.log(`result stringified: ${JSON.stringify(result, null, 2)}`)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 422)
|
||||
assert.include(
|
||||
err.response.data,
|
||||
"Property 'formMessage' must be a string!"
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw error if email list provided is not a array', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'POST',
|
||||
url: `${LOCALHOST}/contact/email`,
|
||||
data: {
|
||||
obj: {
|
||||
email: 'email@email.com',
|
||||
formMessage: 'test message',
|
||||
emailList: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await axios(options)
|
||||
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
// console.log(`result stringified: ${JSON.stringify(result, null, 2)}`)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 422)
|
||||
assert.include(
|
||||
err.response.data,
|
||||
"Property 'emailList' must be a array of emails!"
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw error if email list provided is a empty array', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'POST',
|
||||
url: `${LOCALHOST}/contact/email`,
|
||||
data: {
|
||||
obj: {
|
||||
email: 'email@email.com',
|
||||
formMessage: 'test message',
|
||||
emailList: []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await axios(options)
|
||||
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
// console.log(`result stringified: ${JSON.stringify(result, null, 2)}`)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 422)
|
||||
assert.include(
|
||||
err.response.data,
|
||||
"Property 'emailList' must be a array of emails!"
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it('should send email with minimun input', async () => {
|
||||
try {
|
||||
// Mock live network calls.
|
||||
sandbox.stub(uut.contactLib, 'sendEmail').resolves(true)
|
||||
|
||||
// Mock the context object.
|
||||
const ctx = mockContext()
|
||||
ctx.request = {
|
||||
body: {
|
||||
obj: {
|
||||
email: 'email@email.com',
|
||||
formMessage: 'test message'
|
||||
}
|
||||
}
|
||||
}
|
||||
await uut.email(ctx)
|
||||
} catch (err) {
|
||||
assert(false, 'Unexpected result')
|
||||
}
|
||||
})
|
||||
|
||||
it('should send email with all inputs', async () => {
|
||||
try {
|
||||
// Mock live network calls.
|
||||
sandbox.stub(uut.contactLib, 'sendEmail').resolves(true)
|
||||
|
||||
// Mock the context object.
|
||||
const ctx = mockContext()
|
||||
ctx.request = {
|
||||
body: {
|
||||
obj: {
|
||||
email: 'email@email.com',
|
||||
formMessage: 'test message',
|
||||
emailList: ['email@email.com']
|
||||
}
|
||||
}
|
||||
}
|
||||
await uut.email(ctx)
|
||||
} catch (err) {
|
||||
assert(false, 'Unexpected result')
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,126 @@
|
||||
const config = require('../../../config')
|
||||
const assert = require('chai').assert
|
||||
|
||||
const axios = require('axios').default
|
||||
const sinon = require('sinon')
|
||||
|
||||
const util = require('util')
|
||||
util.inspect.defaultOptions = { depth: 1 }
|
||||
|
||||
const LOCALHOST = `http://localhost:${config.port}`
|
||||
|
||||
const LogsController = require('../../../src/modules/logapi/controller')
|
||||
const mockContext = require('../../unit/mocks/ctx-mock').context
|
||||
|
||||
let sandbox
|
||||
let uut
|
||||
describe('LogsApi', () => {
|
||||
beforeEach(() => {
|
||||
uut = new LogsController()
|
||||
|
||||
sandbox = sinon.createSandbox()
|
||||
})
|
||||
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
describe('POST /logapi', () => {
|
||||
it('should return false if password is not provided', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'post',
|
||||
url: `${LOCALHOST}/logapi`,
|
||||
data: {}
|
||||
}
|
||||
|
||||
const result = await axios(options)
|
||||
assert.isFalse(result.data.success)
|
||||
} catch (err) {
|
||||
assert(false, 'Unexpected result')
|
||||
}
|
||||
})
|
||||
it('should return log', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'post',
|
||||
url: `${LOCALHOST}/logapi`,
|
||||
data: {
|
||||
password: 'test'
|
||||
}
|
||||
}
|
||||
|
||||
const result = await axios(options)
|
||||
|
||||
assert.isTrue(result.data.success)
|
||||
assert.isArray(result.data.data)
|
||||
assert.property(result.data.data[0], 'message')
|
||||
assert.property(result.data.data[0], 'level')
|
||||
assert.property(result.data.data[0], 'timestamp')
|
||||
} catch (err) {
|
||||
assert(false, 'Unexpected result')
|
||||
}
|
||||
})
|
||||
it('should return false if files are not found!', async () => {
|
||||
try {
|
||||
sandbox.stub(uut.logsApiLib, 'getLogs').resolves({
|
||||
success: false,
|
||||
data: 'file does not exist'
|
||||
})
|
||||
|
||||
const ctx = mockContext()
|
||||
ctx.request = {
|
||||
body: {
|
||||
password: 'test'
|
||||
}
|
||||
}
|
||||
await uut.getLogs(ctx)
|
||||
|
||||
assert.isFalse(ctx.body.success)
|
||||
assert.include(ctx.body.data, 'file does not exist')
|
||||
} catch (err) {
|
||||
assert.fail('Unexpected result')
|
||||
}
|
||||
})
|
||||
it('should catch and handle errors', async () => {
|
||||
try {
|
||||
// Force an error
|
||||
sandbox.stub(uut.logsApiLib.fs, 'existsSync').throws(new Error('test error'))
|
||||
|
||||
// Mock the context object.
|
||||
const ctx = mockContext()
|
||||
|
||||
ctx.request = {
|
||||
body: {
|
||||
password: 'test'
|
||||
}
|
||||
}
|
||||
|
||||
await uut.getLogs(ctx)
|
||||
|
||||
assert.fail('Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
it('should throw unhandled error', async () => {
|
||||
try {
|
||||
// Force an error
|
||||
sandbox.stub(uut.logsApiLib.fs, 'existsSync').throws(new Error())
|
||||
|
||||
// Mock the context object.
|
||||
const ctx = mockContext()
|
||||
|
||||
ctx.request = {
|
||||
body: {
|
||||
password: 'test'
|
||||
}
|
||||
}
|
||||
|
||||
await uut.getLogs(ctx)
|
||||
|
||||
assert.fail('Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'Unhandled error')
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,8 @@
|
||||
# Unit Tests
|
||||
Unit tests are defined as testing the smallest possible unit of a function. They also do not make any live network calls.
|
||||
|
||||
Unit tests are broken up by directory:
|
||||
|
||||
- [biz-logic](./biz-logic) tests the business logic libraries.
|
||||
- [rest-api](./rest-api) tests the REST API specific handling of the router.
|
||||
- json-rpc (coming soon) tests the JSON-RPC routing using ipfs-coord library.
|
||||
@@ -0,0 +1,3 @@
|
||||
# Business Logic Unit Tests
|
||||
|
||||
The unit tests in this directly are concerned with business logic libraries in the /src/lib folder. These are the methods that should be triggered by REST API endpoints. These tests are not concerned with the handling of the REST API request/response, but by the code that is triggered by those endpoints. It also tests any business logic that is not directly associated with a REST API endpoint.
|
||||
@@ -0,0 +1,396 @@
|
||||
/*
|
||||
Unit tests for the src/lib/users.js business logic library.
|
||||
|
||||
TODO: verify that an admin can change the type of a user
|
||||
*/
|
||||
|
||||
// Public npm libraries
|
||||
const mongoose = require('mongoose')
|
||||
const assert = require('chai').assert
|
||||
const sinon = require('sinon')
|
||||
|
||||
// Local support libraries
|
||||
const config = require('../../../config')
|
||||
const testUtils = require('../../utils/test-utils')
|
||||
|
||||
// Unit under test (uut)
|
||||
const UserLib = require('../../../src/lib/users')
|
||||
|
||||
describe('#users', () => {
|
||||
let uut
|
||||
let sandbox
|
||||
let testUser = {}
|
||||
|
||||
before(async () => {
|
||||
// Connect to the Mongo Database.
|
||||
console.log(`Connecting to database: ${config.database}`)
|
||||
mongoose.Promise = global.Promise
|
||||
mongoose.set('useCreateIndex', true) // Stop deprecation warning.
|
||||
await mongoose.connect(
|
||||
config.database,
|
||||
{
|
||||
useUnifiedTopology: true,
|
||||
useNewUrlParser: true
|
||||
}
|
||||
)
|
||||
|
||||
// Delete all previous users in the database.
|
||||
await testUtils.deleteAllUsers()
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.createSandbox()
|
||||
|
||||
uut = new UserLib()
|
||||
})
|
||||
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
after(() => {
|
||||
mongoose.connection.close()
|
||||
})
|
||||
|
||||
describe('#createUser', () => {
|
||||
it('should throw an error if no input is given', async () => {
|
||||
try {
|
||||
await uut.createUser()
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
// assert.equal(err.status, 422)
|
||||
assert.include(err.message, 'Cannot read property')
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw an error if email is not provided', async () => {
|
||||
try {
|
||||
await uut.createUser({})
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(err.message, "Property 'email' must be a string!")
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw an error if password is not provided', async () => {
|
||||
try {
|
||||
const usrObj = {
|
||||
email: 'test@test.com'
|
||||
}
|
||||
|
||||
await uut.createUser(usrObj)
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(err.message, "Property 'password' must be a string!")
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw an error if name is not provided', async () => {
|
||||
try {
|
||||
const usrObj = {
|
||||
email: 'test@test.com',
|
||||
password: 'password'
|
||||
}
|
||||
|
||||
await uut.createUser(usrObj)
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(err.message, "Property 'name' must be a string!")
|
||||
}
|
||||
})
|
||||
|
||||
it('should catch and throw DB errors', async () => {
|
||||
try {
|
||||
// Force an error with the database.
|
||||
sandbox.stub(uut, 'UserModel').throws(new Error('test error'))
|
||||
|
||||
const usrObj = {
|
||||
email: 'test@test.com',
|
||||
password: 'password',
|
||||
name: 'test'
|
||||
}
|
||||
|
||||
await uut.createUser(usrObj)
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
|
||||
it('should create a new user in the DB', async () => {
|
||||
// Note: The user created in this test is used by the getUser, update,
|
||||
// and delete tests.
|
||||
|
||||
const usrObj = {
|
||||
email: 'test01@test.com',
|
||||
password: 'test',
|
||||
name: 'test01'
|
||||
}
|
||||
|
||||
const { userData, token } = await uut.createUser(usrObj)
|
||||
|
||||
testUser = userData
|
||||
|
||||
// Assert that the user model has the expected properties with expected values.
|
||||
assert.property(userData, 'type')
|
||||
assert.equal(userData.type, 'user')
|
||||
assert.property(userData, '_id')
|
||||
assert.property(userData, 'email')
|
||||
assert.property(userData, 'name')
|
||||
|
||||
// Assert that the JWT token was generated for this user.
|
||||
assert.isString(token)
|
||||
assert.include(token, 'eyJ')
|
||||
})
|
||||
})
|
||||
|
||||
describe('#getAllUsers', () => {
|
||||
it('should return all users from the database', async () => {
|
||||
const users = await uut.getAllUsers()
|
||||
// console.log(`users: ${JSON.stringify(users, null, 2)}`)
|
||||
|
||||
assert.isArray(users)
|
||||
})
|
||||
|
||||
it('should catch and throw an error', async () => {
|
||||
try {
|
||||
// Force an error.
|
||||
sandbox.stub(uut.UserModel, 'find').rejects(new Error('test error'))
|
||||
|
||||
await uut.getAllUsers()
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('#getUser', () => {
|
||||
it('should throw 422 if no id given.', async () => {
|
||||
try {
|
||||
await uut.getUser()
|
||||
|
||||
assert.fail('Unexpected code path.')
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
assert.equal(err.status, 422)
|
||||
assert.include(err.message, 'Unprocessable Entity')
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw 422 for malformed id', async () => {
|
||||
try {
|
||||
const params = { id: 1 }
|
||||
await uut.getUser(params)
|
||||
|
||||
assert.fail('Unexpected code path.')
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
assert.equal(err.status, 422)
|
||||
assert.include(err.message, 'Unprocessable Entity')
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw 404 if user is not found', async () => {
|
||||
try {
|
||||
const params = { id: '5fa4bd7ee1828f5f4d3ed004' }
|
||||
await uut.getUser(params)
|
||||
|
||||
assert.fail('Unexpected code path.')
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
assert.equal(err.status, 404)
|
||||
assert.include(err.message, 'User not found')
|
||||
}
|
||||
})
|
||||
|
||||
it('should return the user model', async () => {
|
||||
const params = { id: testUser._id }
|
||||
const result = await uut.getUser(params)
|
||||
// console.log('result: ', result)
|
||||
|
||||
// Replace the JSON model with an actual Mongoos model. Used by later
|
||||
// test cases.
|
||||
testUser = result
|
||||
|
||||
// Assert that the expected properties for the user model exist.
|
||||
assert.property(result, 'type')
|
||||
assert.property(result, '_id')
|
||||
assert.property(result, 'email')
|
||||
assert.property(result, 'name')
|
||||
})
|
||||
})
|
||||
|
||||
describe('#updateUser', () => {
|
||||
it('should throw an error if no input is given', async () => {
|
||||
try {
|
||||
await uut.updateUser()
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
assert.include(err.message, 'Cannot read property')
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw an error if email is not a string', async () => {
|
||||
try {
|
||||
await uut.updateUser(testUser, {
|
||||
email: 1234
|
||||
})
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
assert.include(err.message, "Property 'email' must be a string!")
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw an error if name is not a string', async () => {
|
||||
try {
|
||||
const newData = {
|
||||
name: 1234
|
||||
}
|
||||
|
||||
await uut.updateUser(testUser, newData)
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
assert.include(err.message, "Property 'name' must be a string!")
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw an error if non-string password given', async () => {
|
||||
try {
|
||||
const newData = {
|
||||
email: 'test@test.com',
|
||||
name: 'test',
|
||||
password: 1234
|
||||
}
|
||||
|
||||
await uut.updateUser(testUser, newData)
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
assert.include(err.message, "Property 'password' must be a string!")
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw an error for malformed type given', async () => {
|
||||
try {
|
||||
const newData = {
|
||||
email: 'test@test.com',
|
||||
password: 'password',
|
||||
name: 'test',
|
||||
type: 1234
|
||||
}
|
||||
|
||||
await uut.updateUser(testUser, newData)
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
assert.include(err.message, "Property 'type' must be a string!")
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw an error if normal user tries to change themselves into an admin', async () => {
|
||||
try {
|
||||
const newData = {
|
||||
email: 'test@test.com',
|
||||
password: 'password',
|
||||
name: 'test',
|
||||
type: 'admin'
|
||||
}
|
||||
|
||||
await uut.updateUser(testUser, newData)
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
assert.include(
|
||||
err.message,
|
||||
"Property 'type' can only be changed by Admin user"
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it('should update the user model', async () => {
|
||||
const newData = {
|
||||
email: 'test@test.com',
|
||||
password: 'password',
|
||||
name: 'testy tester'
|
||||
}
|
||||
|
||||
const result = await uut.updateUser(testUser, newData)
|
||||
|
||||
// Assert that expected properties and values exist.
|
||||
assert.property(result, '_id')
|
||||
assert.property(result, 'email')
|
||||
assert.equal(result.email, 'test@test.com')
|
||||
assert.property(result, 'name')
|
||||
assert.equal(result.name, 'testy tester')
|
||||
})
|
||||
|
||||
// TODO: verify that an admin can change the type of a user
|
||||
})
|
||||
|
||||
describe('#authUser', () => {
|
||||
it('should return a user db model after successful authentication', async () => {
|
||||
const user = await uut.authUser('test@test.com', 'password')
|
||||
// console.log('user: ', user)
|
||||
|
||||
assert.property(user, '_id')
|
||||
assert.property(user, 'email')
|
||||
assert.property(user, 'name')
|
||||
})
|
||||
|
||||
it('should throw an error if no user matches the login', async () => {
|
||||
try {
|
||||
await uut.authUser('noone@nowhere.com', 'password')
|
||||
// console.log('user: ', user)
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'User not found')
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw an error if password does not match', async () => {
|
||||
try {
|
||||
await uut.authUser('test@test.com', 'badpassword')
|
||||
// console.log('user: ', user)
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'Login credential do not match')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('#deleteUser', () => {
|
||||
it('should throw error if no user provided', async () => {
|
||||
try {
|
||||
await uut.deleteUser()
|
||||
|
||||
assert.fail('Unexpected code path.')
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
assert.include(err.message, 'Cannot read property')
|
||||
}
|
||||
})
|
||||
|
||||
it('should delete the user from the database', async () => {
|
||||
await uut.deleteUser(testUser)
|
||||
|
||||
assert.isOk('Not throwing an error is a pass!')
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,230 @@
|
||||
/*
|
||||
Unit tests for the nodemailer.js library.
|
||||
*/
|
||||
|
||||
// Public npm libraries
|
||||
const assert = require('chai').assert
|
||||
const sinon = require('sinon')
|
||||
|
||||
const NodeMailer = require('../../../src/lib/nodemailer')
|
||||
|
||||
let sandbox
|
||||
let uut
|
||||
|
||||
describe('NodeMailer', () => {
|
||||
beforeEach(() => {
|
||||
uut = new NodeMailer()
|
||||
|
||||
sandbox = sinon.createSandbox()
|
||||
})
|
||||
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
describe('sendEmail()', () => {
|
||||
it('should throw error if email property is not provided', async () => {
|
||||
try {
|
||||
const data = {
|
||||
formMessage: 'test msg',
|
||||
name: 'test name',
|
||||
subject: 'test subject',
|
||||
to: ['test2@email.com']
|
||||
}
|
||||
await uut.sendEmail(data)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, "Property 'email' must be a string!")
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw error if formMessage property is not provided', async () => {
|
||||
try {
|
||||
const data = {
|
||||
email: 'test@email.com',
|
||||
name: 'test name',
|
||||
subject: 'test subject',
|
||||
to: ['test2@email.com']
|
||||
}
|
||||
await uut.sendEmail(data)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, "Property 'formMessage' must be a string!")
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw error if <to> property is not provided', async () => {
|
||||
try {
|
||||
const data = {
|
||||
email: 'test@email.com',
|
||||
name: 'test name',
|
||||
subject: 'test subject'
|
||||
}
|
||||
await uut.sendEmail(data)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, "Property 'to' must be a array!")
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw error if <to> is wrong type', async () => {
|
||||
try {
|
||||
const data = {
|
||||
email: 'test@email.com',
|
||||
formMessage: 'test msg',
|
||||
name: 'test name',
|
||||
subject: 'test subject',
|
||||
to: 'test'
|
||||
}
|
||||
await uut.sendEmail(data)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, "Property 'to' must be a array!")
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw error if subject Property is not provided', async () => {
|
||||
try {
|
||||
const data = {
|
||||
email: 'test@email.com',
|
||||
formMessage: 'test msg',
|
||||
name: 'test name',
|
||||
to: ['test2@email.com']
|
||||
}
|
||||
await uut.sendEmail(data)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, "Property 'subject' must be a string!")
|
||||
}
|
||||
})
|
||||
|
||||
it('should send email with default html data', async () => {
|
||||
try {
|
||||
sandbox
|
||||
.stub(uut.transporter, 'sendMail')
|
||||
.resolves({ messageId: 'messageId' })
|
||||
|
||||
const data = {
|
||||
email: 'test@email.com',
|
||||
formMessage: 'test msg',
|
||||
to: ['test2@email.com'],
|
||||
subject: 'test subject'
|
||||
}
|
||||
|
||||
const info = await uut.sendEmail(data)
|
||||
|
||||
assert.isObject(info)
|
||||
assert.isString(info.messageId)
|
||||
} catch (err) {
|
||||
assert(false, 'Unexpected result')
|
||||
}
|
||||
})
|
||||
|
||||
it('should send email if htmlData is provided', async () => {
|
||||
try {
|
||||
sandbox
|
||||
.stub(uut.transporter, 'sendMail')
|
||||
.resolves({ messageId: 'messageId' })
|
||||
const data = {
|
||||
email: 'test@email.com',
|
||||
formMessage: 'test msg',
|
||||
name: 'test name',
|
||||
to: ['test2@email.com'],
|
||||
subject: 'test subject',
|
||||
htmlData: '<p> Unit test </p>'
|
||||
}
|
||||
const info = await uut.sendEmail(data)
|
||||
assert.isObject(info)
|
||||
assert.isString(info.messageId)
|
||||
} catch (err) {
|
||||
assert(false, 'Unexpected result')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('validateEmailArray()', () => {
|
||||
it('should throw error if email list is not provided ', async () => {
|
||||
try {
|
||||
await uut.validateEmailArray()
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, "Property 'emailList' must be a array!")
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw error if email list is empty', async () => {
|
||||
try {
|
||||
const emailList = []
|
||||
await uut.validateEmailArray(emailList)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, "Property 'emailList' cant be empty!")
|
||||
}
|
||||
})
|
||||
|
||||
it('should return true ', async () => {
|
||||
try {
|
||||
const emailList = ['test@email.com', 'simple@email.com']
|
||||
const result = await uut.validateEmailArray(emailList)
|
||||
assert.isTrue(result)
|
||||
} catch (err) {
|
||||
assert(false, 'Unexpected result')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('getHtmlFromObject()', () => {
|
||||
it('should throw error if the input is not provided ', async () => {
|
||||
try {
|
||||
await uut.getHtmlFromObject()
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, "Property 'objectData' must be a object!")
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw error if the object is empty', async () => {
|
||||
try {
|
||||
await uut.getHtmlFromObject({})
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, "Property 'subject' must be a string!")
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw error if "formMessage" property is not provided', async () => {
|
||||
try {
|
||||
const obj = {
|
||||
subject: 'unit'
|
||||
}
|
||||
await uut.getHtmlFromObject(obj)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, "Property 'formMessage' must be a string!")
|
||||
}
|
||||
})
|
||||
|
||||
it('should return the html', async () => {
|
||||
try {
|
||||
const obj = {
|
||||
subject: 'unit ',
|
||||
formMessage: 'test',
|
||||
value1: 'value1',
|
||||
value2: 'value2',
|
||||
value3: 'value3'
|
||||
}
|
||||
const result = await uut.getHtmlFromObject(obj)
|
||||
assert.isString(result)
|
||||
assert.include(result, '<p>', 'expect html tag')
|
||||
assert.include(result, '</p>', 'expect html tag')
|
||||
assert.include(
|
||||
result,
|
||||
'value1',
|
||||
'Expect value 1 is included in the html'
|
||||
)
|
||||
assert.include(result, 'value2', 'expect is included in the html')
|
||||
assert.include(result, 'value3', 'expect is included in the html')
|
||||
} catch (err) {
|
||||
assert(false, 'Unexpected result')
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,121 @@
|
||||
const assert = require('chai').assert
|
||||
const sinon = require('sinon')
|
||||
|
||||
const ContactLib = require('../../../src/lib/contact')
|
||||
let uut
|
||||
let sandbox
|
||||
|
||||
describe('Contact', () => {
|
||||
beforeEach(() => {
|
||||
uut = new ContactLib()
|
||||
|
||||
sandbox = sinon.createSandbox()
|
||||
})
|
||||
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
describe('sendEmail()', () => {
|
||||
it('should throw error if email property is not provided', async () => {
|
||||
try {
|
||||
const data = {
|
||||
formMessage: 'test msg'
|
||||
}
|
||||
await uut.sendEmail(data)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, "Property 'email' must be a string!")
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw error if formMessage property is not provided', async () => {
|
||||
try {
|
||||
const data = {
|
||||
email: 'test@email.com'
|
||||
}
|
||||
await uut.sendEmail(data)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, "Property 'formMessage' must be a string!")
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw error if email list provided is not a array', async () => {
|
||||
try {
|
||||
sandbox.stub(uut.nodemailer, 'sendEmail').resolves(true)
|
||||
|
||||
const data = {
|
||||
formMessage: 'test msg',
|
||||
email: 'test@email.com',
|
||||
emailList: 'test@email.com'
|
||||
}
|
||||
await uut.sendEmail(data)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, "Property 'emailList' must be a array of emails!")
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw error if email list provided is a empty array', async () => {
|
||||
try {
|
||||
sandbox.stub(uut.nodemailer, 'sendEmail').resolves(true)
|
||||
|
||||
const data = {
|
||||
formMessage: 'test msg',
|
||||
email: 'test@email.com',
|
||||
emailList: []
|
||||
}
|
||||
await uut.sendEmail(data)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, "Property 'emailList' must be a array of emails!")
|
||||
}
|
||||
})
|
||||
|
||||
it('should send email to default server email', async () => {
|
||||
try {
|
||||
sandbox.stub(uut.nodemailer, 'sendEmail').resolves(true)
|
||||
|
||||
const data = {
|
||||
formMessage: 'test msg',
|
||||
email: 'test@email.com'
|
||||
}
|
||||
const result = await uut.sendEmail(data)
|
||||
assert.isTrue(result)
|
||||
} catch (err) {
|
||||
assert(false, 'Unexpected result')
|
||||
}
|
||||
})
|
||||
|
||||
it('should catch and throw nodemailer lib error', async () => {
|
||||
try {
|
||||
// Force an error with the database.
|
||||
sandbox.stub(uut.nodemailer, 'sendEmail').throws(new Error('test error'))
|
||||
|
||||
const data = {
|
||||
formMessage: 'test msg',
|
||||
email: 'test@email.com'
|
||||
}
|
||||
await uut.sendEmail(data)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
|
||||
it('should send email to specifics email list', async () => {
|
||||
try {
|
||||
sandbox.stub(uut.nodemailer, 'sendEmail').resolves(true)
|
||||
|
||||
const data = {
|
||||
formMessage: 'test msg',
|
||||
email: 'test@email.com',
|
||||
emailList: ['testcontact@email.com']
|
||||
}
|
||||
const result = await uut.sendEmail(data)
|
||||
assert.isTrue(result)
|
||||
} catch (err) {
|
||||
assert(false, 'Unexpected result')
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,46 @@
|
||||
const assert = require('chai').assert
|
||||
const PassportLib = require('../../../src/lib/passport')
|
||||
|
||||
const sinon = require('sinon')
|
||||
|
||||
let uut
|
||||
let sandbox
|
||||
|
||||
describe('#passport.js', () => {
|
||||
beforeEach(() => {
|
||||
uut = new PassportLib()
|
||||
|
||||
sandbox = sinon.createSandbox()
|
||||
})
|
||||
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
describe('authUser()', () => {
|
||||
it('should throw error if ctx is not provided', async () => {
|
||||
try {
|
||||
await uut.authUser()
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'ctx is required')
|
||||
}
|
||||
})
|
||||
|
||||
it('Should throw error if the passport library fails', async () => {
|
||||
try {
|
||||
const error = new Error('cant auth user')
|
||||
const user = null
|
||||
|
||||
// Mock calls
|
||||
// https://sinonjs.org/releases/latest/stubs/
|
||||
// About yields
|
||||
sandbox.stub(uut.passport, 'authenticate').yields(error, user)
|
||||
|
||||
const ctx = {}
|
||||
await uut.authUser(ctx)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'cant auth user')
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,231 @@
|
||||
const assert = require('chai').assert
|
||||
|
||||
const sinon = require('sinon')
|
||||
|
||||
const util = require('util')
|
||||
util.inspect.defaultOptions = { depth: 1 }
|
||||
|
||||
const LogsApiLib = require('../../../src/lib/logapi')
|
||||
const mockData = require('../mocks/log-api-mock')
|
||||
|
||||
const context = {}
|
||||
let sandbox
|
||||
let uut
|
||||
describe('#LogsApiLib', () => {
|
||||
beforeEach(() => {
|
||||
uut = new LogsApiLib()
|
||||
|
||||
sandbox = sinon.createSandbox()
|
||||
})
|
||||
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
describe('#getLogs()', () => {
|
||||
it('should return false if password is not provided', async () => {
|
||||
try {
|
||||
const result = await uut.getLogs()
|
||||
assert.property(result, 'success')
|
||||
assert.isFalse(result.success)
|
||||
} catch (err) {
|
||||
assert(false, 'Unexpected result')
|
||||
}
|
||||
})
|
||||
|
||||
it('should return log', async () => {
|
||||
try {
|
||||
const pass = 'test'
|
||||
const result = await uut.getLogs(pass)
|
||||
// console.log('result', result)
|
||||
|
||||
assert.isTrue(result.success)
|
||||
assert.isArray(result.data)
|
||||
assert.property(result.data[0], 'message')
|
||||
assert.property(result.data[0], 'level')
|
||||
assert.property(result.data[0], 'timestamp')
|
||||
} catch (err) {
|
||||
assert(false, 'Unexpected result')
|
||||
}
|
||||
})
|
||||
|
||||
it('should return false if files are not found!', async () => {
|
||||
try {
|
||||
sandbox.stub(uut, 'generateFileName').resolves('bad router')
|
||||
|
||||
const password = 'test'
|
||||
|
||||
const result = await uut.getLogs(password)
|
||||
// console.log(result)
|
||||
|
||||
assert.isFalse(result.success)
|
||||
assert.include(result.data, 'file does not exist')
|
||||
} catch (err) {
|
||||
console.log('ERRROR', err)
|
||||
assert.fail('Unexpected result')
|
||||
}
|
||||
})
|
||||
|
||||
it('should catch and handle errors', async () => {
|
||||
try {
|
||||
// Force an error
|
||||
sandbox.stub(uut.fs, 'existsSync').throws(new Error('test error'))
|
||||
const password = 'test'
|
||||
|
||||
await uut.getLogs(password)
|
||||
|
||||
assert.fail('Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw unhandled error', async () => {
|
||||
try {
|
||||
// Force an error
|
||||
sandbox.stub(uut.fs, 'existsSync').throws(new Error('Unhandled error'))
|
||||
const password = 'test'
|
||||
|
||||
await uut.getLogs(password)
|
||||
|
||||
assert.fail('Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'Unhandled error')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('#filterLogs()', () => {
|
||||
it('should throw error if data is not provided', async () => {
|
||||
try {
|
||||
await uut.filterLogs()
|
||||
|
||||
assert.fail('Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'Data must be array')
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw error if data provided is not an array', async () => {
|
||||
try {
|
||||
const data = 'data'
|
||||
await uut.filterLogs(data)
|
||||
|
||||
assert.fail('Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'Data must be array')
|
||||
}
|
||||
})
|
||||
|
||||
it('should sort the log data', async () => {
|
||||
try {
|
||||
const data = mockData.data
|
||||
const result = await uut.filterLogs(data)
|
||||
assert.isArray(result)
|
||||
assert.property(result[1], 'message')
|
||||
assert.property(result[1], 'level')
|
||||
assert.property(result[1], 'timestamp')
|
||||
} catch (err) {
|
||||
assert.fail('Unexpected result')
|
||||
}
|
||||
})
|
||||
|
||||
it('should sort the log data with a limit', async () => {
|
||||
try {
|
||||
const data = mockData.data
|
||||
const limit = 1
|
||||
const result = await uut.filterLogs(data, limit)
|
||||
assert.isArray(result)
|
||||
assert.equal(result.length, limit)
|
||||
assert.property(result[0], 'message')
|
||||
assert.property(result[0], 'level')
|
||||
assert.property(result[0], 'timestamp')
|
||||
} catch (err) {
|
||||
assert.fail('Unexpected result')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('#generateFileName()', () => {
|
||||
it('should return file name', async () => {
|
||||
try {
|
||||
const fileName = await uut.generateFileName()
|
||||
assert.isString(fileName)
|
||||
context.fileName = fileName
|
||||
} catch (err) {
|
||||
assert.fail('Unexpected result')
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw error if something fails', async () => {
|
||||
try {
|
||||
uut.config = null
|
||||
await uut.generateFileName()
|
||||
assert.fail('Unexpected result')
|
||||
} catch (err) {
|
||||
assert.exists(err)
|
||||
assert.isString(err.message)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('#readLines()', () => {
|
||||
it('should throw error if fileName is not provided', async () => {
|
||||
try {
|
||||
await uut.readLines()
|
||||
|
||||
assert.fail('Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'filename must be a string')
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw error if fileName provided is not string', async () => {
|
||||
try {
|
||||
const fileName = true
|
||||
await uut.readLines(fileName)
|
||||
|
||||
assert.fail('Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'filename must be a string')
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw error if the file does not exist', async () => {
|
||||
try {
|
||||
const fileName = 'test/logs/'
|
||||
await uut.readLines(fileName)
|
||||
|
||||
assert.fail('Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'file does not exist')
|
||||
}
|
||||
})
|
||||
|
||||
it('should ignore fileReader callback errors', async () => {
|
||||
try {
|
||||
// https://sinonjs.org/releases/latest/stubs/
|
||||
// About yields
|
||||
sandbox.stub(uut.lineReader, 'eachLine').yieldsRight({}, true)
|
||||
|
||||
const fileName = context.fileName
|
||||
const result = await uut.readLines(fileName)
|
||||
assert.isArray(result)
|
||||
} catch (err) {
|
||||
assert.fail('Unexpected result')
|
||||
}
|
||||
})
|
||||
|
||||
it('should return data', async () => {
|
||||
try {
|
||||
const fileName = context.fileName
|
||||
const result = await uut.readLines(fileName)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.property(result[1], 'message')
|
||||
assert.property(result[1], 'level')
|
||||
assert.property(result[1], 'timestamp')
|
||||
} catch (err) {
|
||||
assert.fail('Unexpected result')
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
Unit tests for the rpc/index.js library.
|
||||
*/
|
||||
|
||||
// Public npm libraries
|
||||
const jsonrpc = require('jsonrpc-lite')
|
||||
|
||||
const JSONRPC = require('../../../src/rpc')
|
||||
|
||||
describe('#JSON RPC', () => {
|
||||
let uut
|
||||
|
||||
beforeEach(() => {
|
||||
uut = new JSONRPC()
|
||||
})
|
||||
|
||||
describe('#router', () => {
|
||||
it('should do something', async () => {
|
||||
// const request = {
|
||||
// 'users', // id
|
||||
// 'getAll', // method
|
||||
// {}
|
||||
// }
|
||||
const json = jsonrpc.request('users', 'getAll', {})
|
||||
|
||||
const str = JSON.stringify(json)
|
||||
|
||||
await uut.router(str)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
Unit tests for the rpc/index.js library.
|
||||
*/
|
||||
|
||||
// Public npm libraries
|
||||
const assert = require('chai').assert
|
||||
const jsonrpc = require('jsonrpc-lite')
|
||||
const sinon = require('sinon')
|
||||
const { v4: uid } = require('uuid')
|
||||
|
||||
// Set the environment variable to signal this is a test.
|
||||
process.env.SVC_ENV = 'test'
|
||||
|
||||
// Local libraries.
|
||||
const JSONRPC = require('../../../src/rpc')
|
||||
|
||||
describe('#JSON RPC', () => {
|
||||
let uut
|
||||
let sandbox
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.createSandbox()
|
||||
|
||||
uut = new JSONRPC()
|
||||
})
|
||||
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
describe('#router', () => {
|
||||
it('should exit quietly if given a random string', async () => {
|
||||
const str = 'random string message'
|
||||
await uut.router(str)
|
||||
|
||||
assert.isOk('Not throwing an error is a pass.')
|
||||
})
|
||||
|
||||
it('should exit quietly if invalid JSON RPC message received', async () => {
|
||||
const malformedRpc = '{"jsonrpc":"2.0"}'
|
||||
|
||||
await uut.router(malformedRpc, 'peerA')
|
||||
|
||||
assert.isOk('Not throwing an error is a pass.')
|
||||
})
|
||||
|
||||
it('should return default response if routing is not possible', async () => {
|
||||
const id = uid()
|
||||
const json = jsonrpc.request(id, 'unknownMethod', {})
|
||||
|
||||
const str = JSON.stringify(json)
|
||||
|
||||
const result = await uut.router(str, 'peerA')
|
||||
// console.log('result: ', result)
|
||||
|
||||
const jsonObj = jsonrpc.parse(result.retStr)
|
||||
// console.log(`jsonObj: ${JSON.stringify(jsonObj, null, 2)}`)
|
||||
|
||||
// Assert the expected properties exist on the returned object.
|
||||
assert.property(jsonObj, 'payload')
|
||||
assert.property(jsonObj, 'type')
|
||||
assert.property(jsonObj.payload, 'jsonrpc')
|
||||
assert.property(jsonObj.payload, 'id')
|
||||
assert.property(jsonObj.payload, 'result')
|
||||
assert.property(jsonObj.payload.result, 'reciever')
|
||||
assert.property(jsonObj.payload.result.value, 'success')
|
||||
assert.property(jsonObj.payload.result.value, 'message')
|
||||
|
||||
// Assert the expected values exist.
|
||||
assert.equal(jsonObj.payload.id, id)
|
||||
assert.equal(jsonObj.payload.result.value.success, false)
|
||||
assert.equal(jsonObj.payload.result.value.status, 422)
|
||||
assert.equal(
|
||||
jsonObj.payload.result.value.message,
|
||||
'Input does not match routing rules.'
|
||||
)
|
||||
})
|
||||
|
||||
it('should catch and handle errors', async () => {
|
||||
// Force an error
|
||||
sandbox.stub(uut.jsonrpc, 'parse').throws(new Error('test error'))
|
||||
|
||||
const malformedRpc = '{"jsonrpc":"2.0"}'
|
||||
|
||||
await uut.router(malformedRpc, 'peerA')
|
||||
|
||||
assert.isOk('Not throwing an error is a pass.')
|
||||
})
|
||||
|
||||
it('should route to users handler', async () => {
|
||||
const id = uid()
|
||||
const userCall = jsonrpc.request(id, 'users', { endpoint: 'getAll' })
|
||||
const jsonStr = JSON.stringify(userCall, null, 2)
|
||||
|
||||
// Mock the users controller.
|
||||
sandbox.stub(uut.userController, 'userRouter').resolves('true')
|
||||
|
||||
const result = await uut.router(jsonStr, 'peerA')
|
||||
// console.log(result)
|
||||
|
||||
const obj = JSON.parse(result.retStr)
|
||||
// console.log('obj: ', obj)
|
||||
|
||||
assert.equal(obj.result.value, 'true')
|
||||
assert.equal(obj.result.method, 'users')
|
||||
assert.equal(obj.id, id)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
Unit tests for the rpc/auth/index.js file.
|
||||
*/
|
||||
|
||||
// Public npm libraries
|
||||
const jsonrpc = require('jsonrpc-lite')
|
||||
const mongoose = require('mongoose')
|
||||
const sinon = require('sinon')
|
||||
const assert = require('chai').assert
|
||||
const { v4: uid } = require('uuid')
|
||||
|
||||
// Set the environment variable to signal this is a test.
|
||||
process.env.SVC_ENV = 'test'
|
||||
|
||||
// Local libraries
|
||||
const config = require('../../../config')
|
||||
const AuthRPC = require('../../../src/rpc/auth')
|
||||
const UserLib = require('../../../src/lib/users')
|
||||
const userLib = new UserLib()
|
||||
|
||||
describe('#AuthRPC', () => {
|
||||
let uut
|
||||
let sandbox
|
||||
let testUser
|
||||
|
||||
before(async () => {
|
||||
// Connect to the Mongo Database.
|
||||
console.log(`Connecting to database: ${config.database}`)
|
||||
mongoose.Promise = global.Promise
|
||||
mongoose.set('useCreateIndex', true) // Stop deprecation warning.
|
||||
await mongoose.connect(
|
||||
config.database,
|
||||
{
|
||||
useUnifiedTopology: true,
|
||||
useNewUrlParser: true
|
||||
}
|
||||
)
|
||||
|
||||
// Create a test user.
|
||||
testUser = await userLib.createUser({
|
||||
email: 'test543@test.com',
|
||||
name: 'tester543',
|
||||
password: 'password'
|
||||
})
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.createSandbox()
|
||||
|
||||
uut = new AuthRPC()
|
||||
})
|
||||
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
after(async () => {
|
||||
// Delete the test user.
|
||||
testUser = await userLib.getUser({ id: testUser.userData._id })
|
||||
await userLib.deleteUser(testUser)
|
||||
|
||||
mongoose.connection.close()
|
||||
})
|
||||
|
||||
describe('#authRouter', () => {
|
||||
it('should route to the authUser method', async () => {
|
||||
// Mock dependencies
|
||||
sandbox.stub(uut, 'authUser').resolves(true)
|
||||
|
||||
// Generate the parsed data that the main router would pass to this
|
||||
// endpoint.
|
||||
const id = uid()
|
||||
const authCall = jsonrpc.request(id, 'auth', { endpoint: 'authUser' })
|
||||
const jsonStr = JSON.stringify(authCall, null, 2)
|
||||
const rpcData = jsonrpc.parse(jsonStr)
|
||||
|
||||
const result = await uut.authRouter(rpcData)
|
||||
|
||||
assert.equal(result, true)
|
||||
})
|
||||
|
||||
it('should return 500 status on routing issue', async () => {
|
||||
// Mock dependencies
|
||||
sandbox.stub(uut, 'authUser').rejects(new Error('test error'))
|
||||
|
||||
// Generate the parsed data that the main router would pass to this
|
||||
// endpoint.
|
||||
const id = uid()
|
||||
const authCall = jsonrpc.request(id, 'auth', { endpoint: 'authUser' })
|
||||
const jsonStr = JSON.stringify(authCall, null, 2)
|
||||
const rpcData = jsonrpc.parse(jsonStr)
|
||||
|
||||
const result = await uut.authRouter(rpcData)
|
||||
|
||||
assert.equal(result.success, false)
|
||||
assert.equal(result.status, 500)
|
||||
assert.equal(result.message, 'test error')
|
||||
assert.equal(result.endpoint, 'authUser')
|
||||
})
|
||||
})
|
||||
|
||||
describe('#authUser', () => {
|
||||
it('should return a JWT token if user successfully authenticates', async () => {
|
||||
// Generate the parsed data that the main router would pass to this
|
||||
// endpoint.
|
||||
const id = uid()
|
||||
const authCall = jsonrpc.request(id, 'auth', {
|
||||
endpoint: 'authUser',
|
||||
login: 'test543@test.com',
|
||||
password: 'password'
|
||||
})
|
||||
const jsonStr = JSON.stringify(authCall, null, 2)
|
||||
const rpcData = jsonrpc.parse(jsonStr)
|
||||
|
||||
const response = await uut.authUser(rpcData)
|
||||
// console.log('response: ', response)
|
||||
|
||||
assert.equal(response.endpoint, 'authUser')
|
||||
assert.property(response, 'userId')
|
||||
assert.equal(response.userType, 'user')
|
||||
assert.property(response, 'userName')
|
||||
assert.property(response, 'userEmail')
|
||||
assert.property(response, 'apiToken')
|
||||
assert.equal(response.status, 200)
|
||||
assert.equal(response.success, true)
|
||||
assert.property(response, 'message')
|
||||
})
|
||||
|
||||
it('should return an error for invalid credentials', async () => {
|
||||
// Generate the parsed data that the main router would pass to this
|
||||
// endpoint.
|
||||
const id = uid()
|
||||
const authCall = jsonrpc.request(id, 'auth', {
|
||||
endpoint: 'authUser',
|
||||
login: 'test543@test.com',
|
||||
password: 'badpassword'
|
||||
})
|
||||
const jsonStr = JSON.stringify(authCall, null, 2)
|
||||
const rpcData = jsonrpc.parse(jsonStr)
|
||||
|
||||
const response = await uut.authUser(rpcData)
|
||||
// console.log('response: ', response)
|
||||
|
||||
assert.equal(response.success, false)
|
||||
assert.equal(response.status, 422)
|
||||
assert.equal(response.message, 'Login credential do not match')
|
||||
assert.equal(response.endpoint, 'authUser')
|
||||
})
|
||||
|
||||
it('should throw an error if login is not provided', async () => {
|
||||
// Generate the parsed data that the main router would pass to this
|
||||
// endpoint.
|
||||
const id = uid()
|
||||
const authCall = jsonrpc.request(id, 'auth', {
|
||||
endpoint: 'authUser'
|
||||
})
|
||||
const jsonStr = JSON.stringify(authCall, null, 2)
|
||||
const rpcData = jsonrpc.parse(jsonStr)
|
||||
|
||||
const response = await uut.authUser(rpcData)
|
||||
// console.log('response: ', response)
|
||||
|
||||
assert.equal(response.success, false)
|
||||
assert.equal(response.status, 422)
|
||||
assert.equal(response.message, 'login must be specified')
|
||||
assert.equal(response.endpoint, 'authUser')
|
||||
})
|
||||
|
||||
it('should throw an error if password is not provided', async () => {
|
||||
// Generate the parsed data that the main router would pass to this
|
||||
// endpoint.
|
||||
const id = uid()
|
||||
const authCall = jsonrpc.request(id, 'auth', {
|
||||
endpoint: 'authUser',
|
||||
login: 'test543@test.com'
|
||||
})
|
||||
const jsonStr = JSON.stringify(authCall, null, 2)
|
||||
const rpcData = jsonrpc.parse(jsonStr)
|
||||
|
||||
const response = await uut.authUser(rpcData)
|
||||
// console.log('response: ', response)
|
||||
|
||||
assert.equal(response.success, false)
|
||||
assert.equal(response.status, 422)
|
||||
assert.equal(response.message, 'password must be specified')
|
||||
assert.equal(response.endpoint, 'authUser')
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,270 @@
|
||||
/*
|
||||
Unit tests for the JSON RPC validator middleware.
|
||||
|
||||
TODO: ensureTargetUserOrAdmin: it should exit quietly if user is an admin.
|
||||
*/
|
||||
|
||||
// Public npm libraries
|
||||
const jsonrpc = require('jsonrpc-lite')
|
||||
const mongoose = require('mongoose')
|
||||
const sinon = require('sinon')
|
||||
const assert = require('chai').assert
|
||||
const { v4: uid } = require('uuid')
|
||||
|
||||
// Set the environment variable to signal this is a test.
|
||||
process.env.SVC_ENV = 'test'
|
||||
|
||||
// Local libraries
|
||||
const config = require('../../../config')
|
||||
const Validators = require('../../../src/rpc/validators')
|
||||
const UserLib = require('../../../src/lib/users')
|
||||
const userLib = new UserLib()
|
||||
|
||||
describe('#validators', () => {
|
||||
let testUser
|
||||
let uut
|
||||
let sandbox
|
||||
|
||||
before(async () => {
|
||||
// Connect to the Mongo Database.
|
||||
console.log(`Connecting to database: ${config.database}`)
|
||||
mongoose.Promise = global.Promise
|
||||
mongoose.set('useCreateIndex', true) // Stop deprecation warning.
|
||||
await mongoose.connect(
|
||||
config.database,
|
||||
{
|
||||
useUnifiedTopology: true,
|
||||
useNewUrlParser: true
|
||||
}
|
||||
)
|
||||
|
||||
// Create a test user.
|
||||
testUser = await userLib.createUser({
|
||||
email: 'test544@test.com',
|
||||
name: 'tester544',
|
||||
password: 'password'
|
||||
})
|
||||
// console.log('testUser: ', testUser)
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.createSandbox()
|
||||
|
||||
uut = new Validators()
|
||||
})
|
||||
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
after(async () => {
|
||||
// Delete the test user.
|
||||
testUser = await userLib.getUser({ id: testUser.userData._id })
|
||||
await userLib.deleteUser(testUser)
|
||||
|
||||
mongoose.connection.close()
|
||||
})
|
||||
|
||||
describe('#ensureUser', () => {
|
||||
it('should return user model for valid JWT token', async () => {
|
||||
// Generate the parsed data that the main router would pass to this
|
||||
// endpoint.
|
||||
const id = uid()
|
||||
const userCall = jsonrpc.request(id, 'users', {
|
||||
endpoint: 'getAll',
|
||||
apiToken: testUser.token
|
||||
})
|
||||
const jsonStr = JSON.stringify(userCall, null, 2)
|
||||
const rpcData = jsonrpc.parse(jsonStr)
|
||||
|
||||
const user = await uut.ensureUser(rpcData)
|
||||
// console.log('user: ', user)
|
||||
|
||||
assert.property(user, 'type')
|
||||
assert.property(user, '_id')
|
||||
assert.property(user, 'email')
|
||||
assert.property(user, 'name')
|
||||
})
|
||||
|
||||
it('should throw an error if JWT token is not included', async () => {
|
||||
try {
|
||||
// Generate the parsed data that the main router would pass to this
|
||||
// endpoint.
|
||||
const id = uid()
|
||||
const userCall = jsonrpc.request(id, 'users', {
|
||||
endpoint: 'getAll'
|
||||
})
|
||||
const jsonStr = JSON.stringify(userCall, null, 2)
|
||||
const rpcData = jsonrpc.parse(jsonStr)
|
||||
|
||||
await uut.ensureUser(rpcData)
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
assert.include(err.message, 'apiToken JWT required as a parameter')
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw an error if JWT token can not be decoded', async () => {
|
||||
try {
|
||||
const token =
|
||||
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjYwNmQxYTlkNTgxNTVjMjIzNWFmMTNhMSIsImlhdCI6MTYxNzc2Mjk3M30.6JkM1v0n71Mzsd3qzClzlMKtq6HlD0umoauG23N9FFF'
|
||||
|
||||
// Generate the parsed data that the main router would pass to this
|
||||
// endpoint.
|
||||
const id = uid()
|
||||
const userCall = jsonrpc.request(id, 'users', {
|
||||
endpoint: 'getAll',
|
||||
apiToken: token
|
||||
})
|
||||
const jsonStr = JSON.stringify(userCall, null, 2)
|
||||
const rpcData = jsonrpc.parse(jsonStr)
|
||||
|
||||
await uut.ensureUser(rpcData)
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
assert.include(err.message, 'invalid signature')
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw an error if the user can not be found', async () => {
|
||||
try {
|
||||
// Force 'error not found' error
|
||||
sandbox.stub(uut.UserModel, 'findById').resolves(null)
|
||||
|
||||
// Generate the parsed data that the main router would pass to this
|
||||
// endpoint.
|
||||
const id = uid()
|
||||
const userCall = jsonrpc.request(id, 'users', {
|
||||
endpoint: 'getAll',
|
||||
apiToken: testUser.token
|
||||
})
|
||||
const jsonStr = JSON.stringify(userCall, null, 2)
|
||||
const rpcData = jsonrpc.parse(jsonStr)
|
||||
|
||||
await uut.ensureUser(rpcData)
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
assert.include(err.message, 'User not found!')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('#ensureTargetUserOrAdmin', () => {
|
||||
it('should return user model for valid JWT token', async () => {
|
||||
// Generate the parsed data that the main router would pass to this
|
||||
// endpoint.
|
||||
const id = uid()
|
||||
const userCall = jsonrpc.request(id, 'users', {
|
||||
endpoint: 'deleteUser',
|
||||
apiToken: testUser.token,
|
||||
userId: testUser.userData._id.toString()
|
||||
})
|
||||
const jsonStr = JSON.stringify(userCall, null, 2)
|
||||
const rpcData = jsonrpc.parse(jsonStr)
|
||||
|
||||
const user = await uut.ensureTargetUserOrAdmin(rpcData)
|
||||
|
||||
assert.property(user, 'type')
|
||||
assert.property(user, '_id')
|
||||
assert.property(user, 'email')
|
||||
assert.property(user, 'name')
|
||||
})
|
||||
|
||||
it('should throw error if JWT token is not provided', async () => {
|
||||
try {
|
||||
// Generate the parsed data that the main router would pass to this
|
||||
// endpoint.
|
||||
const id = uid()
|
||||
const userCall = jsonrpc.request(id, 'users', {
|
||||
endpoint: 'deleteUser'
|
||||
})
|
||||
const jsonStr = JSON.stringify(userCall, null, 2)
|
||||
const rpcData = jsonrpc.parse(jsonStr)
|
||||
|
||||
await uut.ensureTargetUserOrAdmin(rpcData)
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
assert.include(err.message, 'apiToken JWT required as a parameter')
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw error if user ID is not specified', async () => {
|
||||
try {
|
||||
// Generate the parsed data that the main router would pass to this
|
||||
// endpoint.
|
||||
const id = uid()
|
||||
const userCall = jsonrpc.request(id, 'users', {
|
||||
endpoint: 'deleteUser',
|
||||
apiToken: testUser.token
|
||||
})
|
||||
const jsonStr = JSON.stringify(userCall, null, 2)
|
||||
const rpcData = jsonrpc.parse(jsonStr)
|
||||
|
||||
await uut.ensureTargetUserOrAdmin(rpcData)
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
assert.include(err.message, 'userId must be specified')
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw error if JWT token can not be decoded', async () => {
|
||||
try {
|
||||
const token =
|
||||
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjYwNmU0YzkxYzdlYWNjN2Q4NWJjOGI0NCIsImlhdCI6MTYxNzg0MTI5N30.n1sas7YlqtmhBlNDBY_IXxQCrIQTiE8UITqy0PJAFFF'
|
||||
|
||||
// Generate the parsed data that the main router would pass to this
|
||||
// endpoint.
|
||||
const id = uid()
|
||||
const userCall = jsonrpc.request(id, 'users', {
|
||||
endpoint: 'deleteUser',
|
||||
apiToken: token,
|
||||
userId: testUser.userData._id.toString()
|
||||
})
|
||||
const jsonStr = JSON.stringify(userCall, null, 2)
|
||||
const rpcData = jsonrpc.parse(jsonStr)
|
||||
|
||||
await uut.ensureTargetUserOrAdmin(rpcData)
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
assert.include(err.message, 'invalid signature')
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw an error if user can not be found', async () => {
|
||||
try {
|
||||
// Force an error
|
||||
sandbox.stub(uut.UserModel, 'findById').rejects(new Error('test error'))
|
||||
|
||||
// Generate the parsed data that the main router would pass to this
|
||||
// endpoint.
|
||||
const id = uid()
|
||||
const userCall = jsonrpc.request(id, 'users', {
|
||||
endpoint: 'deleteUser',
|
||||
apiToken: testUser.token,
|
||||
userId: testUser.userData._id.toString()
|
||||
})
|
||||
const jsonStr = JSON.stringify(userCall, null, 2)
|
||||
const rpcData = jsonrpc.parse(jsonStr)
|
||||
|
||||
await uut.ensureTargetUserOrAdmin(rpcData)
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
|
||||
// TODO: it should exit quietly if user is an admin.
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,378 @@
|
||||
/*
|
||||
Unit tests for the rpc/users/index.js file.
|
||||
*/
|
||||
|
||||
// Public npm libraries
|
||||
const jsonrpc = require('jsonrpc-lite')
|
||||
const mongoose = require('mongoose')
|
||||
const sinon = require('sinon')
|
||||
const assert = require('chai').assert
|
||||
const { v4: uid } = require('uuid')
|
||||
|
||||
// Set the environment variable to signal this is a test.
|
||||
process.env.SVC_ENV = 'test'
|
||||
|
||||
// Local libraries
|
||||
const config = require('../../../config')
|
||||
const UserRPC = require('../../../src/rpc/users')
|
||||
const UserModel = require('../../../src/models/users')
|
||||
|
||||
describe('#UserRPC', () => {
|
||||
let uut
|
||||
let sandbox
|
||||
let testUser
|
||||
|
||||
before(async () => {
|
||||
// Connect to the Mongo Database.
|
||||
console.log(`Connecting to database: ${config.database}`)
|
||||
mongoose.Promise = global.Promise
|
||||
mongoose.set('useCreateIndex', true) // Stop deprecation warning.
|
||||
await mongoose.connect(
|
||||
config.database,
|
||||
{
|
||||
useUnifiedTopology: true,
|
||||
useNewUrlParser: true
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.createSandbox()
|
||||
|
||||
uut = new UserRPC()
|
||||
})
|
||||
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
after(() => {
|
||||
mongoose.connection.close()
|
||||
})
|
||||
|
||||
describe('#createUser', () => {
|
||||
it('should create a new user', async () => {
|
||||
// Generate the parsed data that the main router would pass to this
|
||||
// endpoint.
|
||||
const id = uid()
|
||||
const userCall = jsonrpc.request(id, 'users', {
|
||||
endpoint: 'createUser',
|
||||
email: 'test973@test.com',
|
||||
name: 'test973',
|
||||
password: 'password'
|
||||
})
|
||||
const jsonStr = JSON.stringify(userCall, null, 2)
|
||||
const rpcData = jsonrpc.parse(jsonStr)
|
||||
|
||||
const result = await uut.createUser(rpcData)
|
||||
// console.log('result: ', result)
|
||||
|
||||
// CreateUser() specific return values.
|
||||
assert.equal(result.userData.type, 'user')
|
||||
assert.equal(result.userData.email, 'test973@test.com')
|
||||
assert.equal(result.userData.name, 'test973')
|
||||
assert.property(result.userData, '_id')
|
||||
assert.property(result, 'token')
|
||||
|
||||
// Generic JSON RPC return values
|
||||
assert.equal(result.endpoint, 'createUser')
|
||||
assert.equal(result.success, true)
|
||||
assert.equal(result.status, 200)
|
||||
assert.equal(result.message, '')
|
||||
|
||||
// Save the user ID for future tests.
|
||||
testUser = result
|
||||
})
|
||||
|
||||
it('should return error data if biz logic throws an error', async () => {
|
||||
// Force an error
|
||||
sandbox.stub(uut.userLib, 'createUser').rejects(new Error('test error'))
|
||||
|
||||
// Generate the parsed data that the main router would pass to this
|
||||
// endpoint.
|
||||
const id = uid()
|
||||
const userCall = jsonrpc.request(id, 'users', {
|
||||
endpoint: 'createUser',
|
||||
email: 'test973@test.com',
|
||||
name: 'test973',
|
||||
password: 'password'
|
||||
})
|
||||
const jsonStr = JSON.stringify(userCall, null, 2)
|
||||
const rpcData = jsonrpc.parse(jsonStr)
|
||||
|
||||
const result = await uut.createUser(rpcData)
|
||||
// console.log('result: ', result)
|
||||
|
||||
// Generic JSON RPC return values
|
||||
assert.equal(result.endpoint, 'createUser')
|
||||
assert.equal(result.success, false)
|
||||
assert.equal(result.status, 422)
|
||||
assert.equal(result.message, 'test error')
|
||||
})
|
||||
})
|
||||
|
||||
describe('#userRouter', () => {
|
||||
it('should route to the createUser method', async () => {
|
||||
// Mock dependencies
|
||||
sandbox.stub(uut, 'createUser').resolves(true)
|
||||
|
||||
// Generate the parsed data that the main router would pass to this
|
||||
// endpoint.
|
||||
const id = uid()
|
||||
const userCall = jsonrpc.request(id, 'users', { endpoint: 'createUser' })
|
||||
const jsonStr = JSON.stringify(userCall, null, 2)
|
||||
const rpcData = jsonrpc.parse(jsonStr)
|
||||
|
||||
const result = await uut.userRouter(rpcData)
|
||||
|
||||
assert.equal(result, true)
|
||||
})
|
||||
|
||||
it('should route to the getAllUsers method', async () => {
|
||||
// Mock dependencies
|
||||
sandbox.stub(uut, 'getAll').resolves(true)
|
||||
|
||||
// Generate the parsed data that the main router would pass to this
|
||||
// endpoint.
|
||||
const id = uid()
|
||||
const userCall = jsonrpc.request(id, 'users', {
|
||||
endpoint: 'getAllUsers',
|
||||
apiToken: testUser.token
|
||||
})
|
||||
const jsonStr = JSON.stringify(userCall, null, 2)
|
||||
const rpcData = jsonrpc.parse(jsonStr)
|
||||
|
||||
const result = await uut.userRouter(rpcData)
|
||||
|
||||
assert.equal(result, true)
|
||||
})
|
||||
|
||||
it('should route to the updateUser method', async () => {
|
||||
// Mock dependencies
|
||||
sandbox.stub(uut, 'updateUser').resolves(true)
|
||||
|
||||
// Generate the parsed data that the main router would pass to this
|
||||
// endpoint.
|
||||
const id = uid()
|
||||
const userCall = jsonrpc.request(id, 'users', {
|
||||
endpoint: 'updateUser',
|
||||
apiToken: testUser.token,
|
||||
userId: testUser.userData._id
|
||||
})
|
||||
const jsonStr = JSON.stringify(userCall, null, 2)
|
||||
const rpcData = jsonrpc.parse(jsonStr)
|
||||
|
||||
const result = await uut.userRouter(rpcData)
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.equal(result, true)
|
||||
})
|
||||
|
||||
it('should route to the getUser method', async () => {
|
||||
// Mock dependencies
|
||||
sandbox.stub(uut, 'getUser').resolves(true)
|
||||
|
||||
// Generate the parsed data that the main router would pass to this
|
||||
// endpoint.
|
||||
const id = uid()
|
||||
const userCall = jsonrpc.request(id, 'users', {
|
||||
endpoint: 'getUser',
|
||||
apiToken: testUser.token
|
||||
})
|
||||
const jsonStr = JSON.stringify(userCall, null, 2)
|
||||
const rpcData = jsonrpc.parse(jsonStr)
|
||||
|
||||
const result = await uut.userRouter(rpcData)
|
||||
|
||||
assert.equal(result, true)
|
||||
})
|
||||
|
||||
it('should route to the deleteUsers method', async () => {
|
||||
// Mock dependencies
|
||||
sandbox.stub(uut, 'deleteUser').resolves(true)
|
||||
|
||||
// Generate the parsed data that the main router would pass to this
|
||||
// endpoint.
|
||||
const id = uid()
|
||||
const userCall = jsonrpc.request(id, 'users', {
|
||||
endpoint: 'deleteUser',
|
||||
apiToken: testUser.token,
|
||||
userId: testUser.userData._id
|
||||
})
|
||||
const jsonStr = JSON.stringify(userCall, null, 2)
|
||||
const rpcData = jsonrpc.parse(jsonStr)
|
||||
|
||||
const result = await uut.userRouter(rpcData)
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.equal(result, true)
|
||||
})
|
||||
|
||||
it('should return 500 status on routing issue', async () => {
|
||||
// Force an error
|
||||
sandbox.stub(uut, 'createUser').rejects(new Error('test error'))
|
||||
|
||||
// Generate the parsed data that the main router would pass to this
|
||||
// endpoint.
|
||||
const id = uid()
|
||||
const userCall = jsonrpc.request(id, 'users', { endpoint: 'createUser' })
|
||||
const jsonStr = JSON.stringify(userCall, null, 2)
|
||||
const rpcData = jsonrpc.parse(jsonStr)
|
||||
|
||||
const result = await uut.userRouter(rpcData)
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.equal(result.success, false)
|
||||
assert.equal(result.status, 500)
|
||||
assert.equal(result.message, 'test error')
|
||||
assert.equal(result.endpoint, 'createUser')
|
||||
})
|
||||
})
|
||||
|
||||
describe('#getAllUsers', () => {
|
||||
it('should return all users', async () => {
|
||||
const result = await uut.getAll()
|
||||
// console.log('getAll result: ', result)
|
||||
|
||||
// Endpoint specific properties
|
||||
assert.property(result, 'users')
|
||||
assert.isArray(result.users)
|
||||
|
||||
// Generic JSON RPC return values
|
||||
assert.equal(result.endpoint, 'getAllUsers')
|
||||
assert.equal(result.success, true)
|
||||
assert.equal(result.status, 200)
|
||||
assert.equal(result.message, '')
|
||||
})
|
||||
|
||||
it('should return error data if biz logic throws an error', async () => {
|
||||
// Force an error
|
||||
sandbox.stub(uut.userLib, 'getAllUsers').rejects(new Error('test error'))
|
||||
|
||||
const result = await uut.getAll()
|
||||
// console.log('result: ', result)
|
||||
|
||||
// Generic JSON RPC return values
|
||||
assert.equal(result.endpoint, 'getAllUsers')
|
||||
assert.equal(result.success, false)
|
||||
assert.equal(result.status, 422)
|
||||
assert.equal(result.message, 'test error')
|
||||
})
|
||||
})
|
||||
|
||||
describe('#updateUser', () => {
|
||||
it('should update a user', async () => {
|
||||
// Get the user model for the test user.
|
||||
const testUserModel = await UserModel.findById(
|
||||
testUser.userData._id,
|
||||
'-password'
|
||||
)
|
||||
|
||||
// Generate the parsed data that the main router would pass to this
|
||||
// endpoint.
|
||||
const id = uid()
|
||||
const userCall = jsonrpc.request(id, 'users', {
|
||||
endpoint: 'updateUser',
|
||||
userId: testUser.userData._id.toString(),
|
||||
name: 'test777'
|
||||
})
|
||||
const jsonStr = JSON.stringify(userCall, null, 2)
|
||||
const rpcData = jsonrpc.parse(jsonStr)
|
||||
|
||||
const result = await uut.updateUser(rpcData, testUserModel)
|
||||
// console.log('updateUser result: ', result)
|
||||
|
||||
// Endpoint specific properties
|
||||
assert.property(result, 'user')
|
||||
assert.property(result.user, 'type')
|
||||
assert.property(result.user, '_id')
|
||||
assert.property(result.user, 'email')
|
||||
assert.property(result.user, 'name')
|
||||
|
||||
// Generic JSON RPC return values
|
||||
assert.equal(result.endpoint, 'updateUser')
|
||||
assert.equal(result.success, true)
|
||||
assert.equal(result.status, 200)
|
||||
assert.equal(result.message, '')
|
||||
})
|
||||
|
||||
it('should return error data if biz logic throws an error', async () => {
|
||||
// Force an error by not specifying an user ID.
|
||||
const result = await uut.updateUser()
|
||||
// console.log('result: ', result)
|
||||
|
||||
// Generic JSON RPC return values
|
||||
assert.equal(result.endpoint, 'updateUser')
|
||||
assert.equal(result.success, false)
|
||||
assert.equal(result.status, 422)
|
||||
assert.include(result.message, 'Cannot read property')
|
||||
})
|
||||
})
|
||||
|
||||
describe('#getUser', () => {
|
||||
it('should return a specific user', async () => {
|
||||
// Generate the parsed data that the main router would pass to this
|
||||
// endpoint.
|
||||
const id = uid()
|
||||
const userCall = jsonrpc.request(id, 'users', {
|
||||
endpoint: 'getUser',
|
||||
userId: testUser.userData._id.toString()
|
||||
})
|
||||
const jsonStr = JSON.stringify(userCall, null, 2)
|
||||
const rpcData = jsonrpc.parse(jsonStr)
|
||||
|
||||
const result = await uut.getUser(rpcData)
|
||||
// console.log('getUser result: ', result)
|
||||
|
||||
// Endpoint specific properties
|
||||
assert.property(result, 'user')
|
||||
assert.property(result.user, 'type')
|
||||
assert.property(result.user, '_id')
|
||||
assert.property(result.user, 'email')
|
||||
assert.property(result.user, 'name')
|
||||
|
||||
// Generic JSON RPC return values
|
||||
assert.equal(result.endpoint, 'getUser')
|
||||
assert.equal(result.success, true)
|
||||
assert.equal(result.status, 200)
|
||||
assert.equal(result.message, '')
|
||||
})
|
||||
|
||||
it('should return error data if biz logic throws an error', async () => {
|
||||
// Force an error by not specifying an user ID.
|
||||
const result = await uut.getUser()
|
||||
// console.log('result: ', result)
|
||||
|
||||
// Generic JSON RPC return values
|
||||
assert.equal(result.endpoint, 'getUser')
|
||||
assert.equal(result.success, false)
|
||||
assert.equal(result.status, 422)
|
||||
assert.include(result.message, 'Cannot read property')
|
||||
})
|
||||
})
|
||||
|
||||
describe('#deleteUser', () => {
|
||||
it('should delete a user', async () => {
|
||||
// Get the user model for the test user.
|
||||
const testUserModel = await UserModel.findById(
|
||||
testUser.userData._id,
|
||||
'-password'
|
||||
)
|
||||
|
||||
await uut.deleteUser({}, testUserModel)
|
||||
// console.log(result)
|
||||
|
||||
assert.isOk('Not throwing an error is a success')
|
||||
})
|
||||
|
||||
it('should return error data if biz logic throws an error', async () => {
|
||||
// Force an error by not specifying an user ID.
|
||||
const result = await uut.deleteUser()
|
||||
// console.log('result: ', result)
|
||||
|
||||
// Generic JSON RPC return values
|
||||
assert.equal(result.endpoint, 'deleteUser')
|
||||
assert.equal(result.success, false)
|
||||
assert.equal(result.status, 422)
|
||||
assert.include(result.message, 'Cannot read property')
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,50 @@
|
||||
// Ripped from https://github.com/koajs/koa/blob/master/test/helpers/context.js
|
||||
// Solution courtesy of user @fl0w. See: https://github.com/koajs/koa/issues/999#issuecomment-309270599
|
||||
// Take from this gist: https://gist.github.com/emmanuelnk/f1254eed8f947a81e8d715476d9cc92c
|
||||
|
||||
// if you want more comprehensive Koa Context object to test stuff like Cookies etc
|
||||
// then use https://www.npmjs.com/package/@shopify/jest-koa-mocks (requires Jest)
|
||||
|
||||
// INSTRUCTIONS:
|
||||
// Import in test file as below:
|
||||
//
|
||||
// const mockContext = require('./mocks/ctx-mock').context
|
||||
// const ctx = mockContext()
|
||||
// ...
|
||||
|
||||
const Stream = require('stream')
|
||||
const Koa = require('koa')
|
||||
|
||||
const context = (req, res, app) => {
|
||||
const socket = new Stream.Duplex()
|
||||
|
||||
req = Object.assign(
|
||||
{ headers: {}, socket },
|
||||
Stream.Readable.prototype,
|
||||
req || {}
|
||||
)
|
||||
res = Object.assign(
|
||||
{ _headers: {}, socket },
|
||||
Stream.Writable.prototype,
|
||||
res || {}
|
||||
)
|
||||
req.socket.remoteAddress = req.socket.remoteAddress || '127.0.0.1'
|
||||
app = app || new Koa()
|
||||
res.getHeader = k => res._headers[k.toLowerCase()]
|
||||
res.setHeader = (k, v) => (res._headers[k.toLowerCase()] = v)
|
||||
res.removeHeader = (k, v) => delete res._headers[k.toLowerCase()]
|
||||
|
||||
const retApp = app.createContext(req, res)
|
||||
|
||||
return retApp
|
||||
}
|
||||
|
||||
const request = (req, res, app) => context(req, res, app).request
|
||||
|
||||
const response = (req, res, app) => context(req, res, app).response
|
||||
|
||||
module.exports = {
|
||||
context,
|
||||
request,
|
||||
response
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// Mocks representing an array of logs for the
|
||||
// Unit tests of logapi
|
||||
|
||||
const data = [
|
||||
{
|
||||
message: 'Error in lib/nodemailer.js/validateEmailArray()',
|
||||
level: 'error',
|
||||
timestamp: '2020-11-14T12:15:55.230Z'
|
||||
},
|
||||
{
|
||||
message: 'Error in lib/nodemailer.js/validateEmailArray()',
|
||||
level: 'error',
|
||||
timestamp: '2020-11-14T12:15:55.231Z'
|
||||
},
|
||||
{
|
||||
message: 'Error in lib/nodemailer.js/validateEmailArray()',
|
||||
level: 'error',
|
||||
timestamp: '2020-11-14T12:15:55.230Z'
|
||||
},
|
||||
{
|
||||
message: 'Error in lib/nodemailer.js/validateEmailArray()',
|
||||
level: 'error',
|
||||
timestamp: '2020-11-14T12:15:55.231Z'
|
||||
}
|
||||
]
|
||||
module.exports = {
|
||||
data
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
const app = require('../../bin/server')
|
||||
const utils = require('./utils')
|
||||
const config = require('../../config')
|
||||
const assert = require('chai').assert
|
||||
|
||||
const axios = require('axios').default
|
||||
|
||||
// const request = supertest.agent(app.listen())
|
||||
const context = {}
|
||||
|
||||
const LOCALHOST = `http://localhost:${config.port}`
|
||||
|
||||
describe('Auth', () => {
|
||||
before(async () => {
|
||||
// await utils.cleanDb() // This should be first instruction.
|
||||
|
||||
await app.startServer() // This should be second instruction.
|
||||
|
||||
const userObj = {
|
||||
email: 'test@test.com',
|
||||
password: 'pass'
|
||||
}
|
||||
const testUser = await utils.createUser(userObj)
|
||||
console.log(`TestUser : ${testUser}`)
|
||||
|
||||
context.user = testUser.user
|
||||
context.token = testUser.token
|
||||
})
|
||||
|
||||
describe('POST /auth', () => {
|
||||
it('should throw 401 if credentials are incorrect', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'post',
|
||||
url: `${LOCALHOST}/auth`,
|
||||
data: {
|
||||
email: 'test@test.com',
|
||||
password: 'wrongpassword'
|
||||
}
|
||||
}
|
||||
|
||||
const result = await axios(options)
|
||||
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
console.log(
|
||||
`result stringified: ${JSON.stringify(result.data, null, 2)}`
|
||||
)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert(err.response.status === 401, 'Error code 401 expected.')
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw 401 if email is wrong format', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'post',
|
||||
url: `${LOCALHOST}/auth`,
|
||||
data: {
|
||||
email: 'wrongEmail',
|
||||
password: 'wrongpassword'
|
||||
}
|
||||
}
|
||||
|
||||
await axios(options)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert(err.response.status === 401, 'Error code 401 expected.')
|
||||
}
|
||||
})
|
||||
|
||||
it('should auth user', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'post',
|
||||
url: `${LOCALHOST}/auth`,
|
||||
data: {
|
||||
email: 'test@test.com',
|
||||
password: 'pass'
|
||||
}
|
||||
}
|
||||
const result = await axios(options)
|
||||
// console.log(`result: ${JSON.stringify(result.data, null, 2)}`)
|
||||
|
||||
assert(result.status === 200, 'Status Code 200 expected.')
|
||||
assert(
|
||||
result.data.user.email === 'test@test.com',
|
||||
'Email of test expected'
|
||||
)
|
||||
assert(
|
||||
result.data.user.password === undefined,
|
||||
'Password expected to be omited'
|
||||
)
|
||||
} catch (err) {
|
||||
console.log(
|
||||
'Error authenticating test user: ' + JSON.stringify(err, null, 2)
|
||||
)
|
||||
throw err
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,851 @@
|
||||
const testUtils = require('./utils')
|
||||
const assert = require('chai').assert
|
||||
const config = require('../../config')
|
||||
const axios = require('axios').default
|
||||
const sinon = require('sinon')
|
||||
|
||||
const util = require('util')
|
||||
util.inspect.defaultOptions = { depth: 1 }
|
||||
|
||||
const LOCALHOST = `http://localhost:${config.port}`
|
||||
|
||||
const context = {}
|
||||
|
||||
const UserController = require('../../src/modules/users/controller')
|
||||
let uut
|
||||
let sandbox
|
||||
|
||||
const mockContext = require('./mocks/ctx-mock').context
|
||||
|
||||
describe('Users', () => {
|
||||
before(async () => {
|
||||
// console.log(`config: ${JSON.stringify(config, null, 2)}`)
|
||||
|
||||
// Create a second test user.
|
||||
const userObj = {
|
||||
email: 'test2@test.com',
|
||||
password: 'pass2'
|
||||
}
|
||||
const testUser = await testUtils.createUser(userObj)
|
||||
// console.log(`testUser2: ${JSON.stringify(testUser, null, 2)}`)
|
||||
|
||||
context.user2 = testUser.user
|
||||
context.token2 = testUser.token
|
||||
context.id2 = testUser.user._id
|
||||
|
||||
// Get the JWT used to log in as the admin 'system' user.
|
||||
const adminJWT = await testUtils.getAdminJWT()
|
||||
// console.log(`adminJWT: ${adminJWT}`)
|
||||
context.adminJWT = adminJWT
|
||||
|
||||
// const admin = await testUtils.loginAdminUser()
|
||||
// context.adminJWT = admin.token
|
||||
|
||||
// const admin = await adminLib.loginAdmin()
|
||||
// console.log(`admin: ${JSON.stringify(admin, null, 2)}`)
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
uut = new UserController()
|
||||
|
||||
sandbox = sinon.createSandbox()
|
||||
})
|
||||
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
describe('POST /users', () => {
|
||||
it('should reject signup when data is incomplete', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'POST',
|
||||
url: `${LOCALHOST}/users`,
|
||||
data: {
|
||||
email: 'test2@test.com'
|
||||
}
|
||||
}
|
||||
|
||||
await axios(options)
|
||||
|
||||
/* console.log(
|
||||
`result stringified: ${JSON.stringify(result.data, null, 2)}`
|
||||
) */
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert(err.response.status === 422, 'Error code 422 expected.')
|
||||
}
|
||||
})
|
||||
|
||||
it('should reject signup if no email property is provided', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'POST',
|
||||
url: `${LOCALHOST}/users`,
|
||||
data: {
|
||||
user: {
|
||||
password: 'pass2'
|
||||
}
|
||||
}
|
||||
}
|
||||
await axios(options)
|
||||
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
// console.log('err', err)
|
||||
assert.equal(err.response.status, 422)
|
||||
assert.include(err.response.data, "Property 'email' must be a string")
|
||||
}
|
||||
})
|
||||
|
||||
// it('should reject signup if email property provided in wrong format', async () => {
|
||||
// try {
|
||||
// const options = {
|
||||
// method: 'POST',
|
||||
// url: `${LOCALHOST}/users`,
|
||||
// data: {
|
||||
// user: {
|
||||
// email: 'badEmailFormat',
|
||||
// password: 'test'
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// await axios(options)
|
||||
//
|
||||
// assert(false, 'Unexpected result')
|
||||
// } catch (err) {
|
||||
// assert.equal(err.response.status, 422)
|
||||
// assert.include(
|
||||
// err.response.data,
|
||||
// "Property 'email' must be email format"
|
||||
// )
|
||||
// }
|
||||
// })
|
||||
|
||||
it('should reject signup if no password property is provided', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'POST',
|
||||
url: `${LOCALHOST}/users`,
|
||||
data: {
|
||||
user: {
|
||||
email: 'test2@test.com'
|
||||
}
|
||||
}
|
||||
}
|
||||
await axios(options)
|
||||
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 422)
|
||||
assert.include(
|
||||
err.response.data,
|
||||
"Property 'password' must be a string"
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it('should reject if name property property is not string', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'POST',
|
||||
url: `${LOCALHOST}/users`,
|
||||
data: {
|
||||
user: {
|
||||
email: 'test322@test.com',
|
||||
password: 'supersecretpassword',
|
||||
name: 1234
|
||||
}
|
||||
}
|
||||
}
|
||||
await axios(options)
|
||||
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 422)
|
||||
assert.include(err.response.data, "Property 'name' must be a string")
|
||||
}
|
||||
})
|
||||
|
||||
it("should signup of type 'user' by default", async () => {
|
||||
const options = {
|
||||
method: 'post',
|
||||
url: `${LOCALHOST}/users`,
|
||||
data: {
|
||||
user: {
|
||||
email: 'test3@test.com',
|
||||
password: 'supersecretpassword'
|
||||
}
|
||||
}
|
||||
}
|
||||
const result = await axios(options)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
context.user = result.data.user
|
||||
context.token = result.data.token
|
||||
|
||||
assert(result.status === 200, 'Status Code 200 expected.')
|
||||
assert(
|
||||
result.data.user.email === 'test3@test.com',
|
||||
'Email of test expected'
|
||||
)
|
||||
assert(
|
||||
result.data.user.password === undefined,
|
||||
'Password expected to be omited'
|
||||
)
|
||||
assert.property(result.data, 'token', 'Token property exists.')
|
||||
assert.equal(result.data.user.type, 'user')
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /users', () => {
|
||||
it('should not fetch users if the authorization header is missing', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'GET',
|
||||
url: `${LOCALHOST}/users`,
|
||||
headers: {
|
||||
Accept: 'application/json'
|
||||
}
|
||||
}
|
||||
await axios(options)
|
||||
|
||||
assert.equal(true, false, 'Unexpected behavior')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 401)
|
||||
}
|
||||
})
|
||||
|
||||
it('should not fetch users if the authorization header is missing the scheme', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'GET',
|
||||
url: `${LOCALHOST}/users`,
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: '1'
|
||||
}
|
||||
}
|
||||
await axios(options)
|
||||
|
||||
assert.equal(true, false, 'Unexpected behavior')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 401)
|
||||
}
|
||||
})
|
||||
|
||||
it('should not fetch users if the authorization header has invalid scheme', async () => {
|
||||
const { token } = context
|
||||
try {
|
||||
const options = {
|
||||
method: 'GET',
|
||||
url: `${LOCALHOST}/users`,
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Unknown ${token}`
|
||||
}
|
||||
}
|
||||
await axios(options)
|
||||
|
||||
assert.equal(true, false, 'Unexpected behavior')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 401)
|
||||
}
|
||||
})
|
||||
|
||||
it('should not fetch users if token is invalid', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'GET',
|
||||
url: `${LOCALHOST}/users`,
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: 'Bearer 1'
|
||||
}
|
||||
}
|
||||
await axios(options)
|
||||
|
||||
assert.equal(true, false, 'Unexpected behavior')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 401)
|
||||
}
|
||||
})
|
||||
|
||||
it('should fetch all users', async () => {
|
||||
const { token } = context
|
||||
|
||||
const options = {
|
||||
method: 'GET',
|
||||
url: `${LOCALHOST}/users`,
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
}
|
||||
}
|
||||
const result = await axios(options)
|
||||
|
||||
const users = result.data.users
|
||||
// console.log(`users: ${util.inspect(users)}`)
|
||||
|
||||
assert.hasAnyKeys(users[0], ['type', '_id', 'email'])
|
||||
assert.isNumber(users.length)
|
||||
})
|
||||
|
||||
it('should catch and handle errors', async () => {
|
||||
try {
|
||||
// Force an error
|
||||
sandbox.stub(uut.User, 'find').rejects(new Error('test error'))
|
||||
|
||||
// Mock the context object.
|
||||
const ctx = mockContext()
|
||||
|
||||
await uut.getUsers(ctx)
|
||||
|
||||
assert.fail('Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'Not Found')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /users/:id', () => {
|
||||
it('should not fetch user if token is invalid', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'GET',
|
||||
url: `${LOCALHOST}/users/1`,
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: 'Bearer 1'
|
||||
}
|
||||
}
|
||||
await axios(options)
|
||||
|
||||
assert.equal(true, false, 'Unexpected behavior')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 401)
|
||||
}
|
||||
})
|
||||
|
||||
it("should throw 404 if user doesn't exist", async () => {
|
||||
const { token } = context
|
||||
|
||||
try {
|
||||
const options = {
|
||||
method: 'GET',
|
||||
url: `${LOCALHOST}/users/1`,
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
}
|
||||
}
|
||||
await axios(options)
|
||||
|
||||
assert.equal(true, false, 'Unexpected behavior')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 404)
|
||||
}
|
||||
})
|
||||
|
||||
it('should fetch own user', async () => {
|
||||
const _id = context.user._id
|
||||
const token = context.token
|
||||
|
||||
const options = {
|
||||
method: 'GET',
|
||||
url: `${LOCALHOST}/users/${_id}`,
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
}
|
||||
}
|
||||
const result = await axios(options)
|
||||
|
||||
const user = result.data.user
|
||||
// console.log(`user: ${util.inspect(user)}`)
|
||||
|
||||
assert.property(user, 'type')
|
||||
assert.property(user, 'email')
|
||||
|
||||
assert.property(user, '_id')
|
||||
assert.equal(user._id, _id)
|
||||
|
||||
assert.notProperty(
|
||||
user,
|
||||
'password',
|
||||
'Password property should not be returned'
|
||||
)
|
||||
})
|
||||
|
||||
it('should catch and handle errors', async () => {
|
||||
try {
|
||||
// Force an error
|
||||
sandbox.stub(uut.User, 'findById').rejects(new Error('test error'))
|
||||
|
||||
// Mock the context object.
|
||||
const ctx = mockContext()
|
||||
|
||||
await uut.getUser(ctx)
|
||||
|
||||
assert.fail('Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'Internal Server Error')
|
||||
}
|
||||
})
|
||||
|
||||
it('should handle user not found', async () => {
|
||||
try {
|
||||
// Force an error
|
||||
sandbox.stub(uut.User, 'findById').resolves(false)
|
||||
|
||||
// Mock the context object.
|
||||
const ctx = mockContext()
|
||||
ctx.params = { id: 1 }
|
||||
|
||||
await uut.getUser(ctx)
|
||||
|
||||
assert.fail('Unexpected result')
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
assert.include(err.message, 'Not Found')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('PUT /users/:id', () => {
|
||||
it('should not update user if token is invalid', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'PUT',
|
||||
url: `${LOCALHOST}/users/1`,
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: 'Bearer 1'
|
||||
}
|
||||
}
|
||||
await axios(options)
|
||||
|
||||
assert.equal(true, false, 'Unexpected behavior')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 401)
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw 401 if non-admin updating other user', async () => {
|
||||
const { token } = context
|
||||
|
||||
try {
|
||||
const options = {
|
||||
method: 'PUT',
|
||||
url: `${LOCALHOST}/users/1`,
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
}
|
||||
}
|
||||
await axios(options)
|
||||
|
||||
assert.equal(true, false, 'Unexpected behavior')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 401)
|
||||
}
|
||||
})
|
||||
|
||||
it('should not be able to update user type', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'PUT',
|
||||
url: `${LOCALHOST}/users/${context.user._id.toString()}`,
|
||||
headers: {
|
||||
Authorization: `Bearer ${context.token}`
|
||||
},
|
||||
data: {
|
||||
user: {
|
||||
name: 'new name',
|
||||
type: 'test'
|
||||
}
|
||||
}
|
||||
}
|
||||
await axios(options)
|
||||
|
||||
// console.log(`Users: ${JSON.stringify(result.data, null, 2)}`)
|
||||
|
||||
// assert(result.status === 200, 'Status Code 200 expected.')
|
||||
// assert(result.data.user.type === 'user', 'Type should be unchanged.')
|
||||
assert.equal(true, false, 'Unexpected behavior')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 422)
|
||||
assert.include(
|
||||
err.response.data,
|
||||
"Property 'type' can only be changed by Admin user"
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it('should not be able to update other user when not admin', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'PUT',
|
||||
url: `${LOCALHOST}/users/${context.user2._id.toString()}`,
|
||||
headers: {
|
||||
Authorization: `Bearer ${context.token}`
|
||||
},
|
||||
data: {
|
||||
user: {
|
||||
name: 'This should not work'
|
||||
}
|
||||
}
|
||||
}
|
||||
await axios(options)
|
||||
|
||||
// console.log(`result: ${JSON.stringify(result.data, null, 2)}`)
|
||||
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 401)
|
||||
}
|
||||
})
|
||||
|
||||
it('should not be able to update if name property is wrong', async () => {
|
||||
try {
|
||||
const _id = context.user._id
|
||||
const token = context.token
|
||||
|
||||
const options = {
|
||||
method: 'PUT',
|
||||
url: `${LOCALHOST}/users/${_id}`,
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
},
|
||||
data: {
|
||||
user: {
|
||||
email: 'testToUpdate@test.com',
|
||||
name: {}
|
||||
}
|
||||
}
|
||||
}
|
||||
await axios(options)
|
||||
} catch (error) {
|
||||
assert.equal(error.response.status, 422)
|
||||
assert.include(error.response.data, "Property 'name' must be a string!")
|
||||
}
|
||||
})
|
||||
it('should not be able to update if password property is not string', async () => {
|
||||
const { token } = context
|
||||
const _id = context.user._id
|
||||
try {
|
||||
const options = {
|
||||
method: 'PUT',
|
||||
url: `${LOCALHOST}/users/${_id}`,
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
},
|
||||
data: {
|
||||
user: {
|
||||
password: 1234
|
||||
}
|
||||
}
|
||||
}
|
||||
await axios(options)
|
||||
|
||||
assert.equal(true, false, 'Unexpected behavior')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 422)
|
||||
assert.include(
|
||||
err.response.data,
|
||||
"Property 'password' must be a string!"
|
||||
)
|
||||
}
|
||||
})
|
||||
it('should not be able to update if project property is not array', async () => {
|
||||
const { token } = context
|
||||
const _id = context.user._id
|
||||
try {
|
||||
const options = {
|
||||
method: 'PUT',
|
||||
url: `${LOCALHOST}/users/${_id}`,
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
},
|
||||
data: {
|
||||
user: {
|
||||
projects: 'projects'
|
||||
}
|
||||
}
|
||||
}
|
||||
await axios(options)
|
||||
|
||||
assert.equal(true, false, 'Unexpected behavior')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 422)
|
||||
assert.include(
|
||||
err.response.data,
|
||||
"Property 'projects' must be a Array!"
|
||||
)
|
||||
}
|
||||
})
|
||||
it('should not be able to update if email is not string', async () => {
|
||||
const { token } = context
|
||||
const _id = context.user._id
|
||||
try {
|
||||
const options = {
|
||||
method: 'PUT',
|
||||
url: `${LOCALHOST}/users/${_id}`,
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
},
|
||||
data: {
|
||||
user: {
|
||||
email: 1234
|
||||
}
|
||||
}
|
||||
}
|
||||
await axios(options)
|
||||
|
||||
assert.equal(true, false, 'Unexpected behavior')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 422)
|
||||
assert.include(err.response.data, "Property 'email' must be a string!")
|
||||
}
|
||||
})
|
||||
it('should not be able to update if email is wrong format', async () => {
|
||||
try {
|
||||
const _id = context.user._id
|
||||
const token = context.token
|
||||
|
||||
const options = {
|
||||
method: 'PUT',
|
||||
url: `${LOCALHOST}/users/${_id}`,
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
},
|
||||
data: {
|
||||
user: {
|
||||
email: 'badEmailFormat'
|
||||
}
|
||||
}
|
||||
}
|
||||
await axios(options)
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 422)
|
||||
assert.include(
|
||||
err.response.data,
|
||||
"Property 'email' must be email format!"
|
||||
)
|
||||
}
|
||||
})
|
||||
it('should not be able to update type property if is not string', async () => {
|
||||
try {
|
||||
const _id = context.user._id
|
||||
const token = context.token
|
||||
|
||||
const options = {
|
||||
method: 'PUT',
|
||||
url: `${LOCALHOST}/users/${_id}`,
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
},
|
||||
data: {
|
||||
user: {
|
||||
type: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
await axios(options)
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 422)
|
||||
assert.include(err.response.data, "Property 'type' must be a string!")
|
||||
}
|
||||
})
|
||||
|
||||
it('should be able to update other user when admin', async () => {
|
||||
const adminJWT = context.adminJWT
|
||||
|
||||
const options = {
|
||||
method: 'PUT',
|
||||
url: `${LOCALHOST}/users/${context.user2._id.toString()}`,
|
||||
headers: {
|
||||
Authorization: `Bearer ${adminJWT}`
|
||||
},
|
||||
data: {
|
||||
user: {
|
||||
name: 'This should work'
|
||||
}
|
||||
}
|
||||
}
|
||||
const result = await axios(options)
|
||||
// console.log(`result stringified: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
const userName = result.data.user.name
|
||||
assert.equal(userName, 'This should work')
|
||||
})
|
||||
it('should update user with minimum inputs', async () => {
|
||||
const _id = context.user._id
|
||||
const token = context.token
|
||||
|
||||
const options = {
|
||||
method: 'PUT',
|
||||
url: `${LOCALHOST}/users/${_id}`,
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
},
|
||||
data: {
|
||||
user: { email: 'testToUpdate@test.com' }
|
||||
}
|
||||
}
|
||||
|
||||
const result = await axios(options)
|
||||
const user = result.data.user
|
||||
// console.log(`user: ${util.inspect(user)}`)
|
||||
|
||||
assert.property(user, 'type')
|
||||
assert.property(user, 'email')
|
||||
|
||||
assert.property(user, '_id')
|
||||
assert.equal(user._id, _id)
|
||||
|
||||
assert.notProperty(
|
||||
user,
|
||||
'password',
|
||||
'Password property should not be returned'
|
||||
)
|
||||
assert.equal(user.email, 'testToUpdate@test.com')
|
||||
})
|
||||
|
||||
it('should update user with all inputs', async () => {
|
||||
const _id = context.user._id
|
||||
const token = context.token
|
||||
|
||||
const options = {
|
||||
method: 'PUT',
|
||||
url: `${LOCALHOST}/users/${_id}`,
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
},
|
||||
data: {
|
||||
user: {
|
||||
email: 'testToUpdate@test.com',
|
||||
name: 'my name',
|
||||
username: 'myUsername'
|
||||
}
|
||||
}
|
||||
}
|
||||
const result = await axios(options)
|
||||
|
||||
const user = result.data.user
|
||||
// console.log(`user: ${util.inspect(user)}`)
|
||||
|
||||
assert.property(user, 'type')
|
||||
assert.property(user, 'email')
|
||||
assert.property(user, 'name')
|
||||
|
||||
assert.property(user, '_id')
|
||||
assert.equal(user._id, _id)
|
||||
assert.notProperty(
|
||||
user,
|
||||
'password',
|
||||
'Password property should not be returned'
|
||||
)
|
||||
assert.equal(user.name, 'my name')
|
||||
assert.equal(user.email, 'testToUpdate@test.com')
|
||||
assert.equal(user.username, 'myUsername')
|
||||
})
|
||||
})
|
||||
|
||||
describe('DELETE /users/:id', () => {
|
||||
it('should not delete user if token is invalid', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'DELETE',
|
||||
url: `${LOCALHOST}/users/1`,
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: 'Bearer 1'
|
||||
}
|
||||
}
|
||||
await axios(options)
|
||||
|
||||
assert.equal(true, false, 'Unexpected behavior')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 401)
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw 401 if deleting invalid user', async () => {
|
||||
const { token } = context
|
||||
|
||||
try {
|
||||
const options = {
|
||||
method: 'DELETE',
|
||||
url: `${LOCALHOST}/users/1`,
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
}
|
||||
}
|
||||
await axios(options)
|
||||
|
||||
assert.equal(true, false, 'Unexpected behavior')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 401)
|
||||
}
|
||||
})
|
||||
|
||||
it('should not be able to delete other users unless admin', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'DELETE',
|
||||
url: `${LOCALHOST}/users/${context.user2._id.toString()}`,
|
||||
headers: {
|
||||
Authorization: `Bearer ${context.token}`
|
||||
}
|
||||
}
|
||||
await axios(options)
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 401)
|
||||
}
|
||||
})
|
||||
|
||||
it('should delete own user', async () => {
|
||||
const _id = context.user._id
|
||||
const token = context.token
|
||||
|
||||
const options = {
|
||||
method: 'DELETE',
|
||||
url: `${LOCALHOST}/users/${_id}`,
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
}
|
||||
}
|
||||
const result = await axios(options)
|
||||
// console.log(`result: ${util.inspect(result.data.success)}`)
|
||||
|
||||
assert.equal(result.data.success, true)
|
||||
})
|
||||
|
||||
it('should be able to delete other users when admin', async () => {
|
||||
const id = context.id2
|
||||
const adminJWT = context.adminJWT
|
||||
|
||||
const options = {
|
||||
method: 'DELETE',
|
||||
url: `${LOCALHOST}/users/${id}`,
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${adminJWT}`
|
||||
}
|
||||
}
|
||||
const result = await axios(options)
|
||||
// console.log(`result: ${util.inspect(result.data)}`)
|
||||
|
||||
assert.equal(result.data.success, true)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,204 @@
|
||||
const assert = require('chai').assert
|
||||
|
||||
const NodeMailer = require('../../src/lib/nodemailer')
|
||||
|
||||
const sinon = require('sinon')
|
||||
|
||||
const util = require('util')
|
||||
util.inspect.defaultOptions = { depth: 1 }
|
||||
|
||||
let sandbox
|
||||
let uut
|
||||
describe('NodeMailer', () => {
|
||||
beforeEach(() => {
|
||||
uut = new NodeMailer()
|
||||
|
||||
sandbox = sinon.createSandbox()
|
||||
})
|
||||
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
describe('sendEmail()', () => {
|
||||
it('should throw error if email property is not provided', async () => {
|
||||
try {
|
||||
const data = {
|
||||
formMessage: 'test msg',
|
||||
name: 'test name',
|
||||
subject: 'test subject',
|
||||
to: ['test2@email.com']
|
||||
}
|
||||
await uut.sendEmail(data)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'Property \'email\' must be a string!')
|
||||
}
|
||||
})
|
||||
it('should throw error if email property is wrong format', async () => {
|
||||
try {
|
||||
const data = {
|
||||
email: 'test',
|
||||
formMessage: 'test msg',
|
||||
name: 'test name',
|
||||
subject: 'test subject',
|
||||
to: ['test2@email.com']
|
||||
}
|
||||
await uut.sendEmail(data)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'Property \'email\' must be email format!')
|
||||
}
|
||||
})
|
||||
it('should throw error if formMessage property is not provided', async () => {
|
||||
try {
|
||||
const data = {
|
||||
email: 'test@email.com',
|
||||
name: 'test name',
|
||||
subject: 'test subject',
|
||||
to: ['test2@email.com']
|
||||
}
|
||||
await uut.sendEmail(data)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'Property \'message\' must be a string!')
|
||||
}
|
||||
})
|
||||
it('should throw error if <to> property is not provided', async () => {
|
||||
try {
|
||||
const data = {
|
||||
email: 'test@email.com',
|
||||
name: 'test name',
|
||||
subject: 'test subject'
|
||||
}
|
||||
await uut.sendEmail(data)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'Property \'to\' must be a array!')
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw error if <to> is wrong format', async () => {
|
||||
try {
|
||||
const data = {
|
||||
email: 'test@email.com',
|
||||
formMessage: 'test msg',
|
||||
name: 'test name',
|
||||
subject: 'test subject',
|
||||
to: ['test']
|
||||
}
|
||||
await uut.sendEmail(data)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'Array must contain emails format!')
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw error if subject Property is not provided', async () => {
|
||||
try {
|
||||
const data = {
|
||||
email: 'test@email.com',
|
||||
formMessage: 'test msg',
|
||||
name: 'test name',
|
||||
to: ['test2@email.com']
|
||||
}
|
||||
await uut.sendEmail(data)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'Property \'subject\' must be a string!')
|
||||
}
|
||||
})
|
||||
it('should throw error if payloadTitle property is not provided', async () => {
|
||||
try {
|
||||
const data = {
|
||||
email: 'test@email.com',
|
||||
formMessage: 'test msg',
|
||||
name: 'test name',
|
||||
subject: 'test subject',
|
||||
to: ['test2@email.com']
|
||||
}
|
||||
await uut.sendEmail(data)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'Property \'payloadTitle\' must be a string!')
|
||||
}
|
||||
})
|
||||
it('should throw error if payloadTitle property is not string', async () => {
|
||||
try {
|
||||
const data = {
|
||||
email: 'test@email.com',
|
||||
formMessage: 'test msg',
|
||||
name: 'test name',
|
||||
subject: 'test subject',
|
||||
to: ['test2@email.com'],
|
||||
payloadTitle: true
|
||||
|
||||
}
|
||||
await uut.sendEmail(data)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'Property \'payloadTitle\' must be a string!')
|
||||
}
|
||||
})
|
||||
|
||||
it('should send email', async () => {
|
||||
try {
|
||||
sandbox.stub(uut.transporter, 'sendMail').resolves({ messageId: 'messageId' })
|
||||
const data = {
|
||||
email: 'test@email.com',
|
||||
formMessage: 'test msg',
|
||||
name: 'test name',
|
||||
to: ['test2@email.com'],
|
||||
subject: 'test subject',
|
||||
payloadTitle: 'test title'
|
||||
}
|
||||
const info = await uut.sendEmail(data)
|
||||
assert.isObject(info)
|
||||
assert.isString(info.messageId)
|
||||
} catch (err) {
|
||||
assert(false, 'Unexpected result')
|
||||
}
|
||||
})
|
||||
})
|
||||
describe('validateEmailArray()', () => {
|
||||
it('should throw error if email list is not provided ', async () => {
|
||||
try {
|
||||
await uut.validateEmailArray()
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'Property \'emailList\' must be a array!')
|
||||
}
|
||||
})
|
||||
it('should throw error if email list is empty', async () => {
|
||||
try {
|
||||
const emailList = []
|
||||
await uut.validateEmailArray(emailList)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'Property \'emailList\' cant be empty!')
|
||||
}
|
||||
})
|
||||
it('should throw error if email list contain wrong format', async () => {
|
||||
try {
|
||||
const emailList = [
|
||||
'wrongEmail',
|
||||
'bad format'
|
||||
]
|
||||
await uut.validateEmailArray(emailList)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'Array must contain emails format!')
|
||||
}
|
||||
})
|
||||
it('should return true if email list contain email format', async () => {
|
||||
try {
|
||||
const emailList = [
|
||||
'test@email.com',
|
||||
'simple@email.com'
|
||||
]
|
||||
const result = await uut.validateEmailArray(emailList)
|
||||
assert.isTrue(result)
|
||||
} catch (err) {
|
||||
assert(false, 'Unexpected result')
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,267 @@
|
||||
const config = require('../../config')
|
||||
const axios = require('axios').default
|
||||
const assert = require('chai').assert
|
||||
const sinon = require('sinon')
|
||||
|
||||
// Mock data
|
||||
// const mockData = require('./mocks/contact-mocks')
|
||||
|
||||
const LOCALHOST = `http://localhost:${config.port}`
|
||||
|
||||
const mockContext = require('./mocks/ctx-mock').context
|
||||
const ContactController = require('../../src/modules/contact/controller')
|
||||
let uut
|
||||
let sandbox
|
||||
|
||||
describe('Contact', () => {
|
||||
beforeEach(() => {
|
||||
uut = new ContactController()
|
||||
|
||||
sandbox = sinon.createSandbox()
|
||||
})
|
||||
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
describe('POST /contact/email', () => {
|
||||
it('should throw error if email property is not provided', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'POST',
|
||||
url: `${LOCALHOST}/contact/email`,
|
||||
data: {
|
||||
obj: {
|
||||
formMessage: 'message'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await axios(options)
|
||||
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
// console.log(`result stringified: ${JSON.stringify(result, null, 2)}`)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 422)
|
||||
assert.include(err.response.data, "Property 'email' must be a string!")
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw error if email property is wrong format', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'POST',
|
||||
url: `${LOCALHOST}/contact/email`,
|
||||
data: {
|
||||
obj: {
|
||||
email: 'email',
|
||||
formMessage: 'test message'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await axios(options)
|
||||
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
// console.log(`result stringified: ${JSON.stringify(result, null, 2)}`)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 422)
|
||||
assert.include(
|
||||
err.response.data,
|
||||
"Property 'email' must be email format!"
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw error if formMessage property is not provided', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'POST',
|
||||
url: `${LOCALHOST}/contact/email`,
|
||||
data: {
|
||||
obj: {
|
||||
email: 'email@email.com'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await axios(options)
|
||||
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
// console.log(`result stringified: ${JSON.stringify(result, null, 2)}`)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 422)
|
||||
assert.include(
|
||||
err.response.data,
|
||||
"Property 'message' must be a string!"
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw error if payloadTitle property is not provided', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'POST',
|
||||
url: `${LOCALHOST}/contact/email`,
|
||||
data: {
|
||||
obj: {
|
||||
email: 'email@email.com',
|
||||
formMessage: 'test message'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await axios(options)
|
||||
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
// console.log(`result stringified: ${JSON.stringify(result, null, 2)}`)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 422)
|
||||
assert.include(
|
||||
err.response.data,
|
||||
"Property 'payloadTitle' must be a string!"
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw error if payloadTitle property is not string', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'POST',
|
||||
url: `${LOCALHOST}/contact/email`,
|
||||
data: {
|
||||
obj: {
|
||||
email: 'email@email.com',
|
||||
formMessage: 'test message',
|
||||
payloadTitle: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await axios(options)
|
||||
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
// console.log(`result stringified: ${JSON.stringify(result, null, 2)}`)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 422)
|
||||
assert.include(
|
||||
err.response.data,
|
||||
"Property 'payloadTitle' must be a string!"
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw error if email list provided is not a array', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'POST',
|
||||
url: `${LOCALHOST}/contact/email`,
|
||||
data: {
|
||||
obj: {
|
||||
email: 'email@email.com',
|
||||
formMessage: 'test message',
|
||||
payloadTitle: 'title',
|
||||
emailList: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await axios(options)
|
||||
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
// console.log(`result stringified: ${JSON.stringify(result, null, 2)}`)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 422)
|
||||
assert.include(
|
||||
err.response.data,
|
||||
"Property 'emailList' must be a array of emails!"
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw error if email list provided is a empty array', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'POST',
|
||||
url: `${LOCALHOST}/contact/email`,
|
||||
data: {
|
||||
obj: {
|
||||
email: 'email@email.com',
|
||||
formMessage: 'test message',
|
||||
payloadTitle: 'title',
|
||||
emailList: []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await axios(options)
|
||||
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
// console.log(`result stringified: ${JSON.stringify(result, null, 2)}`)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 422)
|
||||
assert.include(
|
||||
err.response.data,
|
||||
"Property 'emailList' must be a array of emails!"
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it('should send email with minimun input', async () => {
|
||||
try {
|
||||
// Mock live network calls.
|
||||
sandbox.stub(uut.nodemailer, 'sendEmail').resolves(true)
|
||||
|
||||
// Mock the context object.
|
||||
const ctx = mockContext()
|
||||
ctx.request = {
|
||||
body: {
|
||||
obj: {
|
||||
email: 'email@email.com',
|
||||
formMessage: 'test message',
|
||||
payloadTitle: 'title'
|
||||
}
|
||||
}
|
||||
}
|
||||
await uut.email(ctx)
|
||||
} catch (err) {
|
||||
assert(false, 'Unexpected result')
|
||||
}
|
||||
})
|
||||
|
||||
it('should send email with all input', async () => {
|
||||
try {
|
||||
// Mock live network calls.
|
||||
sandbox.stub(uut.nodemailer, 'sendEmail').resolves(true)
|
||||
|
||||
// Mock the context object.
|
||||
const ctx = mockContext()
|
||||
ctx.request = {
|
||||
body: {
|
||||
obj: {
|
||||
email: 'email@email.com',
|
||||
formMessage: 'test message',
|
||||
payloadTitle: 'title',
|
||||
emailList: ['email@email.com']
|
||||
}
|
||||
}
|
||||
}
|
||||
await uut.email(ctx)
|
||||
} catch (err) {
|
||||
assert(false, 'Unexpected result')
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,46 @@
|
||||
const assert = require('chai').assert
|
||||
const PassportLib = require('../../src/lib/passport')
|
||||
|
||||
const sinon = require('sinon')
|
||||
|
||||
let uut
|
||||
let sandbox
|
||||
|
||||
describe('#passport.js', () => {
|
||||
beforeEach(() => {
|
||||
uut = new PassportLib()
|
||||
|
||||
sandbox = sinon.createSandbox()
|
||||
})
|
||||
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
describe('authUser()', () => {
|
||||
it('should throw error if ctx is not provided', async () => {
|
||||
try {
|
||||
await uut.authUser()
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'ctx is required')
|
||||
}
|
||||
})
|
||||
|
||||
it('Should throw error if the passport library fails', async () => {
|
||||
try {
|
||||
const error = new Error('cant auth user')
|
||||
const user = null
|
||||
|
||||
// Mock calls
|
||||
// https://sinonjs.org/releases/latest/stubs/
|
||||
// About yields
|
||||
sandbox.stub(uut.passport, 'authenticate').yields(error, user)
|
||||
|
||||
const ctx = {}
|
||||
await uut.authUser(ctx)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'cant auth user')
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,251 @@
|
||||
const config = require('../../config')
|
||||
const assert = require('chai').assert
|
||||
|
||||
const axios = require('axios').default
|
||||
const sinon = require('sinon')
|
||||
|
||||
const util = require('util')
|
||||
util.inspect.defaultOptions = { depth: 1 }
|
||||
|
||||
const LOCALHOST = `http://localhost:${config.port}`
|
||||
|
||||
const LogsController = require('../../src/modules/logapi/controller')
|
||||
const mockContext = require('./mocks/ctx-mock').context
|
||||
const mockData = require('./mocks/log-api-mock')
|
||||
|
||||
const context = {}
|
||||
let sandbox
|
||||
let uut
|
||||
describe('LogsApi', () => {
|
||||
beforeEach(() => {
|
||||
uut = new LogsController()
|
||||
|
||||
sandbox = sinon.createSandbox()
|
||||
})
|
||||
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
describe('POST /logapi', () => {
|
||||
it('should return false if password is not provided', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'post',
|
||||
url: `${LOCALHOST}/logapi`,
|
||||
data: {}
|
||||
}
|
||||
|
||||
const result = await axios(options)
|
||||
assert.isFalse(result.data.success)
|
||||
} catch (err) {
|
||||
assert(false, 'Unexpected result')
|
||||
}
|
||||
})
|
||||
it('should return log', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'post',
|
||||
url: `${LOCALHOST}/logapi`,
|
||||
data: {
|
||||
password: 'test'
|
||||
}
|
||||
}
|
||||
|
||||
const result = await axios(options)
|
||||
|
||||
assert.isTrue(result.data.success)
|
||||
assert.isArray(result.data.data)
|
||||
assert.property(result.data.data[0], 'message')
|
||||
assert.property(result.data.data[0], 'level')
|
||||
assert.property(result.data.data[0], 'timestamp')
|
||||
} catch (err) {
|
||||
assert(false, 'Unexpected result')
|
||||
}
|
||||
})
|
||||
it('should return false if files are not found!', async () => {
|
||||
try {
|
||||
sandbox.stub(uut, 'generateFileName').resolves('bad router')
|
||||
|
||||
const ctx = mockContext()
|
||||
ctx.request = {
|
||||
body: {
|
||||
password: 'test'
|
||||
}
|
||||
}
|
||||
await uut.getLogs(ctx)
|
||||
|
||||
assert.isFalse(ctx.body.success)
|
||||
assert.include(ctx.body.data, 'file does not exist')
|
||||
} catch (err) {
|
||||
assert.fail('Unexpected result')
|
||||
}
|
||||
})
|
||||
it('should catch and handle errors', async () => {
|
||||
try {
|
||||
// Force an error
|
||||
sandbox.stub(uut.fs, 'existsSync').throws(new Error('test error'))
|
||||
|
||||
// Mock the context object.
|
||||
const ctx = mockContext()
|
||||
|
||||
ctx.request = {
|
||||
body: {
|
||||
password: 'test'
|
||||
}
|
||||
}
|
||||
|
||||
await uut.getLogs(ctx)
|
||||
|
||||
assert.fail('Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
it('should throw unhandled error', async () => {
|
||||
try {
|
||||
// Force an error
|
||||
sandbox.stub(uut.fs, 'existsSync').throws(new Error())
|
||||
|
||||
// Mock the context object.
|
||||
const ctx = mockContext()
|
||||
|
||||
ctx.request = {
|
||||
body: {
|
||||
password: 'test'
|
||||
}
|
||||
}
|
||||
|
||||
await uut.getLogs(ctx)
|
||||
|
||||
assert.fail('Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'Unhandled error')
|
||||
}
|
||||
})
|
||||
})
|
||||
describe('#filterLogs()', () => {
|
||||
it('should throw error if data is not provided', async () => {
|
||||
try {
|
||||
await uut.filterLogs()
|
||||
|
||||
assert.fail('Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'Data must be array')
|
||||
}
|
||||
})
|
||||
it('should throw error if data provided is not an array', async () => {
|
||||
try {
|
||||
const data = 'data'
|
||||
await uut.filterLogs(data)
|
||||
|
||||
assert.fail('Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'Data must be array')
|
||||
}
|
||||
})
|
||||
it('should sort the log data', async () => {
|
||||
try {
|
||||
const data = mockData.data
|
||||
const result = await uut.filterLogs(data)
|
||||
assert.isArray(result)
|
||||
assert.property(result[1], 'message')
|
||||
assert.property(result[1], 'level')
|
||||
assert.property(result[1], 'timestamp')
|
||||
} catch (err) {
|
||||
assert.fail('Unexpected result')
|
||||
}
|
||||
})
|
||||
it('should sort the log data with a limit', async () => {
|
||||
try {
|
||||
const data = mockData.data
|
||||
const limit = 1
|
||||
const result = await uut.filterLogs(data, limit)
|
||||
assert.isArray(result)
|
||||
assert.equal(result.length, limit)
|
||||
assert.property(result[0], 'message')
|
||||
assert.property(result[0], 'level')
|
||||
assert.property(result[0], 'timestamp')
|
||||
} catch (err) {
|
||||
assert.fail('Unexpected result')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('#generateFileName()', () => {
|
||||
it('should return file name', async () => {
|
||||
try {
|
||||
const fileName = await uut.generateFileName()
|
||||
assert.isString(fileName)
|
||||
context.fileName = fileName
|
||||
} catch (err) {
|
||||
assert.fail('Unexpected result')
|
||||
}
|
||||
})
|
||||
it('should throw error if something fails', async () => {
|
||||
try {
|
||||
uut.config = null
|
||||
await uut.generateFileName()
|
||||
assert.fail('Unexpected result')
|
||||
} catch (err) {
|
||||
assert.exists(err)
|
||||
assert.isString(err.message)
|
||||
}
|
||||
})
|
||||
})
|
||||
describe('#readLines()', () => {
|
||||
it('should throw error if fileName is not provided', async () => {
|
||||
try {
|
||||
await uut.readLines()
|
||||
|
||||
assert.fail('Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'filename must be a string')
|
||||
}
|
||||
})
|
||||
it('should throw error if fileName provided is not string', async () => {
|
||||
try {
|
||||
const fileName = true
|
||||
await uut.readLines(fileName)
|
||||
|
||||
assert.fail('Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'filename must be a string')
|
||||
}
|
||||
})
|
||||
it('should throw error if the file does not exist', async () => {
|
||||
try {
|
||||
const fileName = 'test/logs/'
|
||||
await uut.readLines(fileName)
|
||||
|
||||
assert.fail('Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'file does not exist')
|
||||
}
|
||||
})
|
||||
it('should ignore fileReader callback errors', async () => {
|
||||
try {
|
||||
// https://sinonjs.org/releases/latest/stubs/
|
||||
// About yields
|
||||
sandbox.stub(uut.lineReader, 'eachLine').yieldsRight({}, true)
|
||||
|
||||
const fileName = context.fileName
|
||||
const result = await uut.readLines(fileName)
|
||||
assert.isArray(result)
|
||||
} catch (err) {
|
||||
assert.fail('Unexpected result')
|
||||
}
|
||||
})
|
||||
it('should return data', async () => {
|
||||
try {
|
||||
const fileName = context.fileName
|
||||
const result = await uut.readLines(fileName)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.property(result[1], 'message')
|
||||
assert.property(result[1], 'level')
|
||||
assert.property(result[1], 'timestamp')
|
||||
} catch (err) {
|
||||
assert.fail('Unexpected result')
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,139 @@
|
||||
const assert = require('chai').assert
|
||||
const fs = require('fs')
|
||||
const sinon = require('sinon')
|
||||
|
||||
const util = require('util')
|
||||
util.inspect.defaultOptions = { depth: 1 }
|
||||
|
||||
const JsonFiles = require('../../src/lib/utils/json-files')
|
||||
|
||||
const JSON_FILE = 'test-json-file.json'
|
||||
const JSON_PATH = `${__dirname.toString()}/${JSON_FILE}`
|
||||
|
||||
const deleteFile = filepath => {
|
||||
try {
|
||||
// Delete state if exist
|
||||
fs.unlinkSync(filepath)
|
||||
} catch (error) {}
|
||||
}
|
||||
let sandbox
|
||||
let uut
|
||||
describe('JsonFiles', () => {
|
||||
const obj = {
|
||||
json: 'file'
|
||||
}
|
||||
beforeEach(() => {
|
||||
uut = new JsonFiles()
|
||||
sandbox = sinon.createSandbox()
|
||||
})
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
after(() => {
|
||||
deleteFile(JSON_PATH)
|
||||
})
|
||||
describe('writeJSON()', () => {
|
||||
it('should throw error if inputs is not provided', async () => {
|
||||
try {
|
||||
await uut.writeJSON()
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'obj property is required')
|
||||
}
|
||||
})
|
||||
it('should throw error if filename property is not provided', async () => {
|
||||
try {
|
||||
await uut.writeJSON(obj)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'fileName property must be a string')
|
||||
}
|
||||
})
|
||||
it('should throw error if filename property is not string', async () => {
|
||||
try {
|
||||
await uut.writeJSON(obj, 1)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'fileName property must be a string')
|
||||
}
|
||||
})
|
||||
it('should throw error if fs library return an error', async () => {
|
||||
try {
|
||||
// https://sinonjs.org/releases/latest/stubs/
|
||||
// About yields
|
||||
sandbox.stub(uut.fs, 'writeFile').yields(new Error('test error'))
|
||||
|
||||
await uut.writeJSON(obj, JSON_PATH)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
it('should write a json file', async () => {
|
||||
try {
|
||||
await uut.writeJSON(obj, JSON_PATH)
|
||||
|
||||
assert.isTrue(fs.existsSync(JSON_PATH))
|
||||
} catch (err) {
|
||||
assert(false, 'Unexpected result')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('readJSON()', () => {
|
||||
it('should throw error if filename property is not provided', async () => {
|
||||
try {
|
||||
await uut.readJSON(obj)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'fileName property must be a string')
|
||||
}
|
||||
})
|
||||
it('should throw error if filename property is not string', async () => {
|
||||
try {
|
||||
await uut.readJSON(obj, 1)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'fileName property must be a string')
|
||||
}
|
||||
})
|
||||
it('should throw error if fs library return an error', async () => {
|
||||
try {
|
||||
// https://sinonjs.org/releases/latest/stubs/
|
||||
// About yields
|
||||
sandbox.stub(uut.fs, 'readFile').yields(new Error('test error'))
|
||||
|
||||
await uut.readJSON(JSON_PATH)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
it('should throw error if file not found', async () => {
|
||||
try {
|
||||
const testError = new Error('test error')
|
||||
testError.code = 'ENOENT'
|
||||
|
||||
sandbox.stub(uut.fs, 'readFile').yields(testError)
|
||||
|
||||
await uut.readJSON(JSON_PATH)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
|
||||
it('should read a json file', async () => {
|
||||
try {
|
||||
const result = await uut.readJSON(JSON_PATH)
|
||||
|
||||
const objKeys = Object.keys(obj)
|
||||
const resultKeys = Object.keys(result)
|
||||
|
||||
assert.isObject(result)
|
||||
assert.equal(objKeys.length, resultKeys.length)
|
||||
} catch (err) {
|
||||
assert(false, 'Unexpected result')
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,317 @@
|
||||
const assert = require('chai').assert
|
||||
const testUtils = require('./utils')
|
||||
|
||||
const Validators = require('../../src/middleware/validators')
|
||||
|
||||
const sinon = require('sinon')
|
||||
const mockContext = require('./mocks/ctx-mock').context
|
||||
|
||||
const util = require('util')
|
||||
util.inspect.defaultOptions = { depth: 1 }
|
||||
|
||||
const context = {}
|
||||
|
||||
let sandbox
|
||||
let uut
|
||||
describe('Validators', () => {
|
||||
before(async () => {
|
||||
// console.log(`config: ${JSON.stringify(config, null, 2)}`)
|
||||
|
||||
// Create a second test user.
|
||||
const userObj = {
|
||||
email: 'test2@test.com',
|
||||
password: 'pass2'
|
||||
}
|
||||
const testUser = await testUtils.createUser(userObj)
|
||||
// console.log(`testUser2: ${JSON.stringify(testUser, null, 2)}`)
|
||||
|
||||
context.user = testUser.user
|
||||
context.token = testUser.token
|
||||
context.id = testUser.user._id
|
||||
|
||||
// Get the JWT used to log in as the admin 'system' user.
|
||||
const adminJWT = await testUtils.getAdminJWT()
|
||||
// console.log(`adminJWT: ${adminJWT}`)
|
||||
context.adminJWT = adminJWT
|
||||
|
||||
// const admin = await testUtils.loginAdminUser()
|
||||
// context.adminJWT = admin.token
|
||||
|
||||
// const admin = await adminLib.loginAdmin()
|
||||
// console.log(`admin: ${JSON.stringify(admin, null, 2)}`)
|
||||
})
|
||||
beforeEach(() => {
|
||||
uut = new Validators()
|
||||
|
||||
sandbox = sinon.createSandbox()
|
||||
})
|
||||
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
describe('ensureUser()', () => {
|
||||
it('should throw 401 if user cant be found', async () => {
|
||||
try {
|
||||
// Force an error
|
||||
sandbox.stub(uut.User, 'findById').resolves(false)
|
||||
|
||||
// Mock the context object.
|
||||
const ctx = mockContext()
|
||||
ctx.request = {
|
||||
header: {
|
||||
authorization: `Bearer ${context.token}`
|
||||
}
|
||||
}
|
||||
|
||||
await uut.ensureUser(ctx)
|
||||
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.equal(err.status, 401)
|
||||
assert.include(err.message, 'Unauthorized')
|
||||
}
|
||||
})
|
||||
it('should throw 401 if token not found', async () => {
|
||||
try {
|
||||
// Mock the context object.
|
||||
const ctx = mockContext()
|
||||
|
||||
await uut.ensureUser(ctx)
|
||||
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.equal(err.status, 401)
|
||||
assert.include(err.message, 'Unauthorized')
|
||||
}
|
||||
})
|
||||
it('should throw 401 if token is invalid', async () => {
|
||||
try {
|
||||
// Mock the context object.
|
||||
const ctx = mockContext()
|
||||
ctx.request = {
|
||||
header: {
|
||||
authorization: 'Bearer 1'
|
||||
}
|
||||
}
|
||||
await uut.ensureUser(ctx)
|
||||
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.equal(err.status, 401)
|
||||
assert.include(err.message, 'Unauthorized')
|
||||
}
|
||||
})
|
||||
it('should trigger the "next" function if user is admin', async () => {
|
||||
try {
|
||||
// Mock the context object.
|
||||
const ctx = mockContext()
|
||||
ctx.params = { id: context.id }
|
||||
|
||||
ctx.request = {
|
||||
header: {
|
||||
authorization: `Bearer ${context.adminJWT}`
|
||||
}
|
||||
}
|
||||
// Function that execute if the validations
|
||||
// are successful
|
||||
const next = () => { return 'next function' }
|
||||
|
||||
const result = await uut.ensureUser(ctx, next)
|
||||
|
||||
assert.isString(result)
|
||||
assert.equal(result, 'next function')
|
||||
} catch (err) {
|
||||
assert(false, 'Unexpected result')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('ensureAdmin()', () => {
|
||||
it('should throw 401 if token not found', async () => {
|
||||
try {
|
||||
// Mock the context object.
|
||||
const ctx = mockContext()
|
||||
|
||||
await uut.ensureAdmin(ctx)
|
||||
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.equal(err.status, 401)
|
||||
assert.include(err.message, 'Unauthorized')
|
||||
}
|
||||
})
|
||||
it('should throw 401 if token is invalid', async () => {
|
||||
try {
|
||||
// Mock the context object.
|
||||
const ctx = mockContext()
|
||||
ctx.request = {
|
||||
header: {
|
||||
authorization: 'Bearer 1'
|
||||
}
|
||||
}
|
||||
await uut.ensureAdmin(ctx)
|
||||
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.equal(err.status, 401)
|
||||
assert.include(err.message, 'Unauthorized')
|
||||
}
|
||||
})
|
||||
it('should throw 401 if user cant be found', async () => {
|
||||
try {
|
||||
// Force an error
|
||||
sandbox.stub(uut.User, 'findById').resolves(false)
|
||||
|
||||
// Mock the context object.
|
||||
const ctx = mockContext()
|
||||
ctx.request = {
|
||||
header: {
|
||||
authorization: `Bearer ${context.token}`
|
||||
}
|
||||
}
|
||||
await uut.ensureAdmin(ctx)
|
||||
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.equal(err.status, 401)
|
||||
assert.include(err.message, 'Unauthorized')
|
||||
}
|
||||
})
|
||||
it('should throw 401 if user is not admin type', async () => {
|
||||
try {
|
||||
// Mock the context object.
|
||||
const ctx = mockContext()
|
||||
ctx.request = {
|
||||
header: {
|
||||
authorization: `Bearer ${context.token}`
|
||||
}
|
||||
}
|
||||
await uut.ensureAdmin(ctx)
|
||||
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.equal(err.status, 401)
|
||||
assert.include(err.message, 'not admin')
|
||||
}
|
||||
})
|
||||
it('should trigger the "next" function if user is admin', async () => {
|
||||
try {
|
||||
// Mock the context object.
|
||||
const ctx = mockContext()
|
||||
ctx.request = {
|
||||
header: {
|
||||
authorization: `Bearer ${context.adminJWT}`
|
||||
}
|
||||
}
|
||||
// Function that execute if the validations
|
||||
// are successful
|
||||
const next = () => { return 'next function' }
|
||||
|
||||
const result = await uut.ensureAdmin(ctx, next)
|
||||
|
||||
assert.isString(result)
|
||||
assert.equal(result, 'next function')
|
||||
} catch (err) {
|
||||
assert(false, 'Unexpected result')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('ensureTargetUserOrAdmin()', () => {
|
||||
it('should throw 401 if token not found', async () => {
|
||||
try {
|
||||
// Mock the context object.
|
||||
const ctx = mockContext()
|
||||
ctx.params = { id: context.id }
|
||||
await uut.ensureTargetUserOrAdmin(ctx)
|
||||
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.equal(err.status, 401)
|
||||
assert.include(err.message, 'Unauthorized')
|
||||
}
|
||||
})
|
||||
it('should throw 401 if token is invalid', async () => {
|
||||
try {
|
||||
// Mock the context object.
|
||||
const ctx = mockContext()
|
||||
ctx.params = { id: context.id }
|
||||
|
||||
ctx.request = {
|
||||
header: {
|
||||
authorization: 'Bearer 1'
|
||||
}
|
||||
}
|
||||
await uut.ensureTargetUserOrAdmin(ctx)
|
||||
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.equal(err.status, 401)
|
||||
assert.include(err.message, 'Unauthorized')
|
||||
}
|
||||
})
|
||||
it('should throw 401 if user cant be found', async () => {
|
||||
try {
|
||||
// Force an error
|
||||
sandbox.stub(uut.User, 'findById').resolves(false)
|
||||
|
||||
// Mock the context object.
|
||||
const ctx = mockContext()
|
||||
ctx.params = { id: context.id }
|
||||
|
||||
ctx.request = {
|
||||
header: {
|
||||
authorization: `Bearer ${context.token}`
|
||||
}
|
||||
}
|
||||
await uut.ensureTargetUserOrAdmin(ctx)
|
||||
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.equal(err.status, 401)
|
||||
assert.include(err.message, 'Unauthorized')
|
||||
}
|
||||
})
|
||||
it('should throw 401 if user is not admin type', async () => {
|
||||
try {
|
||||
// Mock the context object.
|
||||
const ctx = mockContext()
|
||||
ctx.params = { id: 'Target Id' }
|
||||
|
||||
ctx.request = {
|
||||
header: {
|
||||
authorization: `Bearer ${context.token}`
|
||||
}
|
||||
}
|
||||
await uut.ensureTargetUserOrAdmin(ctx)
|
||||
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.equal(err.status, 401)
|
||||
assert.include(err.message, 'not admin')
|
||||
}
|
||||
})
|
||||
it('should trigger the "next" function if user is admin', async () => {
|
||||
try {
|
||||
// Mock the context object.
|
||||
const ctx = mockContext()
|
||||
ctx.params = { id: context.id }
|
||||
|
||||
ctx.request = {
|
||||
header: {
|
||||
authorization: `Bearer ${context.adminJWT}`
|
||||
}
|
||||
}
|
||||
// Function that execute if the validations
|
||||
// are successful
|
||||
const next = () => { return 'next function' }
|
||||
|
||||
const result = await uut.ensureTargetUserOrAdmin(ctx, next)
|
||||
|
||||
assert.isString(result)
|
||||
assert.equal(result, 'next function')
|
||||
} catch (err) {
|
||||
assert(false, 'Unexpected result')
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,116 @@
|
||||
const assert = require('chai').assert
|
||||
|
||||
const Admin = require('../../src/lib/admin')
|
||||
|
||||
const sinon = require('sinon')
|
||||
|
||||
const util = require('util')
|
||||
util.inspect.defaultOptions = { depth: 1 }
|
||||
|
||||
let sandbox
|
||||
let uut
|
||||
describe('Admin', () => {
|
||||
beforeEach(() => {
|
||||
uut = new Admin()
|
||||
|
||||
sandbox = sinon.createSandbox()
|
||||
})
|
||||
|
||||
afterEach(() => sandbox.restore())
|
||||
describe('loginAdmin()', () => {
|
||||
it('should logind admin', async () => {
|
||||
try {
|
||||
const error = new Error('test error')
|
||||
error.response = {
|
||||
status: 422
|
||||
}
|
||||
// sandbox.stub(uut.axios, 'request').onFirstCall().throws(error)
|
||||
|
||||
const result = await uut.loginAdmin()
|
||||
const user = result.data.user
|
||||
|
||||
assert.property(user, '_id')
|
||||
assert.property(user, 'email')
|
||||
assert.property(user, 'type')
|
||||
|
||||
assert.isString(user._id)
|
||||
assert.isString(user.email)
|
||||
assert.isString(user.type)
|
||||
|
||||
assert.equal(user.type, 'admin')
|
||||
} catch (err) {
|
||||
assert(false, 'Unexpected result')
|
||||
}
|
||||
})
|
||||
it('should handle axios error', async () => {
|
||||
try {
|
||||
// Returns an erroneous password to force
|
||||
// an auth error
|
||||
sandbox
|
||||
.stub(uut.jsonFiles, 'readJSON').resolves({ password: 'wrong' })
|
||||
|
||||
await uut.loginAdmin()
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 401)
|
||||
assert.include(err.response.data, 'Unauthorized')
|
||||
}
|
||||
})
|
||||
})
|
||||
describe('createSystemUser()', () => {
|
||||
it('should create admin', async () => {
|
||||
try {
|
||||
const result = await uut.createSystemUser()
|
||||
|
||||
assert.property(result, 'email')
|
||||
assert.property(result, 'password')
|
||||
assert.property(result, 'id')
|
||||
assert.property(result, 'token')
|
||||
} catch (err) {
|
||||
assert(false, 'Unexpected result')
|
||||
}
|
||||
})
|
||||
it('should handle axios error', async () => {
|
||||
try {
|
||||
const error1 = new Error('test error')
|
||||
error1.response = {
|
||||
status: 422
|
||||
}
|
||||
const error2 = new Error('test error')
|
||||
error1.response = {
|
||||
status: 500
|
||||
}
|
||||
// The loginAdmin() function in some use cases is recursive
|
||||
// after handling the 422 error, it gets called again
|
||||
sandbox
|
||||
.stub(uut.axios, 'request')
|
||||
.onFirstCall()
|
||||
.throws(error1)
|
||||
.onSecondCall()
|
||||
.throws(error2)
|
||||
|
||||
await uut.createSystemUser()
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
it('should handle errors when remove user', async () => {
|
||||
try {
|
||||
const error1 = new Error('test error')
|
||||
error1.response = {
|
||||
status: 422
|
||||
}
|
||||
sandbox
|
||||
.stub(uut.axios, 'request').throws(error1)
|
||||
sandbox
|
||||
.stub(uut.User, 'deleteOne').throws(new Error('test error'))
|
||||
|
||||
await uut.createSystemUser()
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,135 @@
|
||||
const mongoose = require('mongoose')
|
||||
const config = require('../../config')
|
||||
const axios = require('axios').default
|
||||
|
||||
const LOCALHOST = `http://localhost:${config.port}`
|
||||
|
||||
// Remove all collections from the DB.
|
||||
async function cleanDb () {
|
||||
for (const collection in mongoose.connection.collections) {
|
||||
const collections = mongoose.connection.collections
|
||||
if (collections.collection) {
|
||||
// const thisCollection = mongoose.connection.collections[collection]
|
||||
// console.log(`thisCollection: ${JSON.stringify(thisCollection, null, 2)}`)
|
||||
|
||||
await collection.deleteMany()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This function is used to create new users.
|
||||
// userObj = {
|
||||
// username,
|
||||
// password
|
||||
// }
|
||||
async function createUser (userObj) {
|
||||
try {
|
||||
const options = {
|
||||
method: 'POST',
|
||||
url: `${LOCALHOST}/users`,
|
||||
data: {
|
||||
user: {
|
||||
email: userObj.email,
|
||||
password: userObj.password
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const result = await axios(options)
|
||||
|
||||
const retObj = {
|
||||
user: result.data.user,
|
||||
token: result.data.token
|
||||
}
|
||||
|
||||
return retObj
|
||||
} catch (err) {
|
||||
console.log('Error in utils.js/createUser(): ' + JSON.stringify(err, null, 2))
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
async function loginTestUser () {
|
||||
try {
|
||||
const options = {
|
||||
method: 'POST',
|
||||
url: `${LOCALHOST}/auth`,
|
||||
data: {
|
||||
email: 'test@test.com',
|
||||
password: 'pass'
|
||||
}
|
||||
}
|
||||
|
||||
const result = await axios(options)
|
||||
|
||||
// console.log(`result: ${JSON.stringify(result.data, null, 2)}`)
|
||||
|
||||
const retObj = {
|
||||
token: result.data.token,
|
||||
user: result.data.user.username,
|
||||
id: result.data.user._id.toString()
|
||||
}
|
||||
|
||||
return retObj
|
||||
} catch (err) {
|
||||
console.log('Error authenticating test user: ' + JSON.stringify(err, null, 2))
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
async function loginAdminUser () {
|
||||
try {
|
||||
const FILENAME = `../../config/system-user-${config.env}.json`
|
||||
const adminUserData = require(FILENAME)
|
||||
console.log(`adminUserData: ${JSON.stringify(adminUserData, null, 2)}`)
|
||||
|
||||
const options = {
|
||||
method: 'POST',
|
||||
url: `${LOCALHOST}/auth`,
|
||||
data: {
|
||||
email: adminUserData.email,
|
||||
password: adminUserData.password
|
||||
}
|
||||
}
|
||||
|
||||
const result = await axios(options)
|
||||
|
||||
// console.log(`result: ${JSON.stringify(result.data, null, 2)}`)
|
||||
|
||||
const retObj = {
|
||||
token: result.data.token,
|
||||
user: result.data.user.username,
|
||||
id: result.data.user._id.toString()
|
||||
}
|
||||
|
||||
return retObj
|
||||
} catch (err) {
|
||||
console.log('Error authenticating test admin user: ' + JSON.stringify(err, null, 2))
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieve the admin user JWT token from the JSON file it's saved at.
|
||||
async function getAdminJWT () {
|
||||
try {
|
||||
// process.env.KOA_ENV = process.env.KOA_ENV || 'dev'
|
||||
// console.log(`env: ${process.env.KOA_ENV}`)
|
||||
|
||||
const FILENAME = `../../config/system-user-${config.env}.json`
|
||||
const adminUserData = require(FILENAME)
|
||||
// console.log(`adminUserData: ${JSON.stringify(adminUserData, null, 2)}`)
|
||||
|
||||
return adminUserData.token
|
||||
} catch (err) {
|
||||
console.error('Error in test/utils.js/getAdminJWT()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
cleanDb,
|
||||
createUser,
|
||||
loginTestUser,
|
||||
loginAdminUser,
|
||||
getAdminJWT
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
# REST API Unit Tests
|
||||
|
||||
The tests in this directory are unit tests of REST API. These tests are not
|
||||
concerned with the business logic behind the endpoints. They are only concerned
|
||||
with the handling of the REST API endpoint. These tests answer questions like:
|
||||
|
||||
- Is the endpoint responding properly when the business logic throws an error?
|
||||
- When returning an error, is it returning the proper HTTP response?
|
||||
- When returning success, is it returning the correct payload?
|
||||
@@ -0,0 +1,254 @@
|
||||
/*
|
||||
Unit tests for the REST API handler for the /users endpoints.
|
||||
*/
|
||||
|
||||
// Public npm libraries
|
||||
const assert = require('chai').assert
|
||||
const sinon = require('sinon')
|
||||
const mongoose = require('mongoose')
|
||||
|
||||
// Local support libraries
|
||||
const config = require('../../../config')
|
||||
const testUtils = require('../../utils/test-utils')
|
||||
const User = require('../../../src/models/users')
|
||||
|
||||
const UserController = require('../../../src/modules/users/controller')
|
||||
let uut
|
||||
let sandbox
|
||||
let ctx
|
||||
|
||||
const mockContext = require('../../unit/mocks/ctx-mock').context
|
||||
|
||||
describe('Users', () => {
|
||||
let testUser = {}
|
||||
|
||||
before(async () => {
|
||||
// Connect to the Mongo Database.
|
||||
mongoose.Promise = global.Promise
|
||||
mongoose.set('useCreateIndex', true) // Stop deprecation warning.
|
||||
await mongoose.connect(config.database, {
|
||||
useUnifiedTopology: true,
|
||||
useNewUrlParser: true
|
||||
})
|
||||
|
||||
// Delete all previous users in the database.
|
||||
await testUtils.deleteAllUsers()
|
||||
|
||||
// console.log(`config: ${JSON.stringify(config, null, 2)}`)
|
||||
|
||||
// Create a second test user.
|
||||
// const userObj = {
|
||||
// email: 'test2@test.com',
|
||||
// password: 'pass2'
|
||||
// }
|
||||
// const testUser = await testUtils.createUser(userObj)
|
||||
// console.log(`testUser2: ${JSON.stringify(testUser, null, 2)}`)
|
||||
|
||||
// context.user2 = testUser.user
|
||||
// context.token2 = testUser.token
|
||||
// context.id2 = testUser.user._id
|
||||
|
||||
// Get the JWT used to log in as the admin 'system' user.
|
||||
// const adminJWT = await testUtils.getAdminJWT()
|
||||
// // console.log(`adminJWT: ${adminJWT}`)
|
||||
// context.adminJWT = adminJWT
|
||||
|
||||
// const admin = await testUtils.loginAdminUser()
|
||||
// context.adminJWT = admin.token
|
||||
|
||||
// const admin = await adminLib.loginAdmin()
|
||||
// console.log(`admin: ${JSON.stringify(admin, null, 2)}`)
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
uut = new UserController()
|
||||
|
||||
sandbox = sinon.createSandbox()
|
||||
|
||||
// Mock the context object.
|
||||
ctx = mockContext()
|
||||
})
|
||||
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
after(() => {
|
||||
mongoose.connection.close()
|
||||
})
|
||||
|
||||
describe('#POST /users', () => {
|
||||
it('should return 422 status on biz logic error', async () => {
|
||||
try {
|
||||
await uut.createUser(ctx)
|
||||
|
||||
assert.fail('Unexpected result')
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
assert.equal(err.status, 422)
|
||||
assert.include(err.message, 'Cannot read property')
|
||||
}
|
||||
})
|
||||
|
||||
it('should return 200 status on success', async () => {
|
||||
ctx.request.body = {
|
||||
user: {
|
||||
email: 'test02@test.com',
|
||||
password: 'test',
|
||||
name: 'test02'
|
||||
}
|
||||
}
|
||||
|
||||
await uut.createUser(ctx)
|
||||
|
||||
// Assert the expected HTTP response
|
||||
assert.equal(ctx.status, 200)
|
||||
|
||||
// Assert that expected properties exist in the returned data.
|
||||
assert.property(ctx.response.body, 'user')
|
||||
assert.property(ctx.response.body, 'token')
|
||||
|
||||
// Used by downstream tests.
|
||||
testUser = ctx.response.body.user
|
||||
// console.log('testUser: ', testUser)
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /users', () => {
|
||||
it('should return 422 status on arbitrary biz logic error', async () => {
|
||||
try {
|
||||
// Force an error
|
||||
sandbox
|
||||
.stub(uut.userLib, 'getAllUsers')
|
||||
.rejects(new Error('test error'))
|
||||
|
||||
await uut.getUsers(ctx)
|
||||
|
||||
assert.fail('Unexpected result')
|
||||
} catch (err) {
|
||||
assert.equal(err.status, 422)
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
|
||||
it('should return 200 status on success', async () => {
|
||||
await uut.getUsers(ctx)
|
||||
|
||||
// Assert the expected HTTP response
|
||||
assert.equal(ctx.status, 200)
|
||||
|
||||
// Assert that expected properties exist in the returned data.
|
||||
assert.property(ctx.response.body, 'users')
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /users/:id', () => {
|
||||
it('should return 422 status on arbitrary biz logic error', async () => {
|
||||
try {
|
||||
// Force an error
|
||||
sandbox.stub(uut.userLib, 'getUser').rejects(new Error('test error'))
|
||||
|
||||
await uut.getUser(ctx)
|
||||
|
||||
assert.fail('Unexpected result')
|
||||
} catch (err) {
|
||||
assert.equal(err.status, 422)
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
|
||||
it('should return 200 status on success', async () => {
|
||||
// Mock dependencies
|
||||
sandbox.stub(uut.userLib, 'getUser').resolves({ _id: '123' })
|
||||
|
||||
await uut.getUser(ctx)
|
||||
|
||||
// Assert the expected HTTP response
|
||||
assert.equal(ctx.status, 200)
|
||||
|
||||
// Assert that expected properties exist in the returned data.
|
||||
assert.property(ctx.response.body, 'user')
|
||||
})
|
||||
|
||||
it('should return other error status passed by biz logic', async () => {
|
||||
try {
|
||||
// Mock dependencies
|
||||
const testErr = new Error('test error')
|
||||
testErr.status = 404
|
||||
sandbox.stub(uut.userLib, 'getUser').rejects(testErr)
|
||||
|
||||
await uut.getUser(ctx)
|
||||
|
||||
assert.fail('Unexpected result')
|
||||
} catch (err) {
|
||||
assert.equal(err.status, 404)
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('PUT /users/:id', () => {
|
||||
it('should return 422 if no input data given', async () => {
|
||||
try {
|
||||
await uut.updateUser(ctx)
|
||||
|
||||
assert.fail('Unexpected result')
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
assert.equal(err.status, 422)
|
||||
assert.include(err.message, 'Cannot read property')
|
||||
}
|
||||
})
|
||||
|
||||
it('should return 200 on success', async () => {
|
||||
// Prep the testUser data.
|
||||
// console.log('testUser: ', testUser)
|
||||
testUser.password = 'password'
|
||||
delete testUser.type
|
||||
|
||||
// Replace the testUser variable with an actual model from the DB.
|
||||
const existingUser = await User.findById(testUser._id)
|
||||
|
||||
ctx.body = {
|
||||
user: existingUser
|
||||
}
|
||||
ctx.request.body = {
|
||||
user: testUser
|
||||
}
|
||||
|
||||
await uut.updateUser(ctx)
|
||||
|
||||
// Assert the expected HTTP response
|
||||
assert.equal(ctx.status, 200)
|
||||
|
||||
// Assert that expected properties exist in the returned data.
|
||||
assert.property(ctx.response.body, 'user')
|
||||
})
|
||||
})
|
||||
|
||||
describe('DELETE /users/:id', () => {
|
||||
it('should return 422 if no input data given', async () => {
|
||||
try {
|
||||
await uut.deleteUser(ctx)
|
||||
|
||||
assert.fail('Unexpected result')
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
assert.equal(err.status, 422)
|
||||
assert.include(err.message, 'Cannot read property')
|
||||
}
|
||||
})
|
||||
|
||||
it('should return 200 status on success', async () => {
|
||||
// Replace the testUser variable with an actual model from the DB.
|
||||
const existingUser = await User.findById(testUser._id)
|
||||
|
||||
ctx.body = {
|
||||
user: existingUser
|
||||
}
|
||||
|
||||
await uut.deleteUser(ctx)
|
||||
|
||||
// Assert the expected HTTP response
|
||||
assert.equal(ctx.status, 200)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
Unit tests for the REST API handler for the /users endpoints.
|
||||
*/
|
||||
|
||||
// Public npm libraries
|
||||
const assert = require('chai').assert
|
||||
const sinon = require('sinon')
|
||||
|
||||
const ContactController = require('../../../src/modules/contact/controller')
|
||||
let uut
|
||||
let sandbox
|
||||
let ctx
|
||||
|
||||
const mockContext = require('../../unit/mocks/ctx-mock').context
|
||||
|
||||
describe('Contact', () => {
|
||||
before(async () => {
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
uut = new ContactController()
|
||||
|
||||
sandbox = sinon.createSandbox()
|
||||
|
||||
// Mock the context object.
|
||||
ctx = mockContext()
|
||||
})
|
||||
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
describe('#POST /contact', () => {
|
||||
it('should return 422 status on biz logic error', async () => {
|
||||
try {
|
||||
await uut.email(ctx)
|
||||
|
||||
assert.fail('Unexpected result')
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
assert.equal(err.status, 422)
|
||||
assert.include(err.message, 'Cannot read property')
|
||||
}
|
||||
})
|
||||
|
||||
it('should return 200 status on success', async () => {
|
||||
sandbox.stub(uut.contactLib, 'sendEmail').resolves(true)
|
||||
|
||||
ctx.request.body = {
|
||||
email: 'test02@test.com',
|
||||
formMessage: 'test'
|
||||
}
|
||||
|
||||
await uut.email(ctx)
|
||||
|
||||
// Assert the expected HTTP response
|
||||
assert.equal(ctx.status, 200)
|
||||
|
||||
// Assert that expected properties exist in the returned data.
|
||||
assert.property(ctx.response.body, 'success')
|
||||
assert.isTrue(ctx.response.body.success)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
Unit tests for the REST API handler for the /users endpoints.
|
||||
*/
|
||||
|
||||
// Public npm libraries
|
||||
const assert = require('chai').assert
|
||||
const sinon = require('sinon')
|
||||
|
||||
const LogsApiController = require('../../../src/modules/logapi/controller')
|
||||
let uut
|
||||
let sandbox
|
||||
let ctx
|
||||
|
||||
const mockContext = require('../../unit/mocks/ctx-mock').context
|
||||
|
||||
describe('Logapi', () => {
|
||||
before(async () => {
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
uut = new LogsApiController()
|
||||
|
||||
sandbox = sinon.createSandbox()
|
||||
|
||||
// Mock the context object.
|
||||
ctx = mockContext()
|
||||
})
|
||||
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
describe('#POST /logapi', () => {
|
||||
it('should return 422 status on biz logic error', async () => {
|
||||
try {
|
||||
await uut.getLogs(ctx)
|
||||
|
||||
assert.fail('Unexpected result')
|
||||
} catch (err) {
|
||||
assert.equal(err.status, 422)
|
||||
assert.include(err.message, 'Cannot read property')
|
||||
}
|
||||
})
|
||||
it('should return 500 status on biz logic Unhandled error', async () => {
|
||||
try {
|
||||
// eslint-disable
|
||||
sandbox.stub(uut.logsApiLib, 'getLogs').returns(Promise.reject(new Error()))
|
||||
|
||||
ctx.request.body = {
|
||||
password: 'test'
|
||||
}
|
||||
|
||||
await uut.getLogs(ctx)
|
||||
|
||||
assert.fail('Unexpected result')
|
||||
} catch (err) {
|
||||
assert.equal(err.status, 500)
|
||||
assert.include(err.message, 'Unhandled error')
|
||||
}
|
||||
})
|
||||
|
||||
it('should return 200 status on success', async () => {
|
||||
ctx.request.body = {
|
||||
password: 'test'
|
||||
}
|
||||
|
||||
await uut.getLogs(ctx)
|
||||
|
||||
// Assert the expected HTTP response
|
||||
assert.equal(ctx.status, 200)
|
||||
|
||||
// Assert that expected properties exist in the returned data.
|
||||
assert.property(ctx.response.body, 'success')
|
||||
assert.isTrue(ctx.response.body.success)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
Utility functions used to prepare the environment for tests.
|
||||
*/
|
||||
|
||||
// Public NPM libraries
|
||||
const mongoose = require('mongoose')
|
||||
const axios = require('axios').default
|
||||
|
||||
// Local libraries
|
||||
const config = require('../../config')
|
||||
const User = require('../../src/models/users')
|
||||
|
||||
const LOCALHOST = `http://localhost:${config.port}`
|
||||
|
||||
// Remove all collections from the DB.
|
||||
async function cleanDb () {
|
||||
for (const collection in mongoose.connection.collections) {
|
||||
const collections = mongoose.connection.collections
|
||||
if (collections.collection) {
|
||||
// const thisCollection = mongoose.connection.collections[collection]
|
||||
// console.log(`thisCollection: ${JSON.stringify(thisCollection, null, 2)}`)
|
||||
|
||||
await collection.deleteMany()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Delete all users in the database. This ensures there is no previous state
|
||||
// to confuse tests.
|
||||
async function deleteAllUsers () {
|
||||
try {
|
||||
// Get all the users in the DB.
|
||||
const users = await User.find({}, '-password')
|
||||
// console.log(`users: ${JSON.stringify(users, null, 2)}`)
|
||||
|
||||
// Delete each user.
|
||||
for (let i = 0; i < users.length; i++) {
|
||||
const thisUser = users[i]
|
||||
await thisUser.remove()
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error in test-utils.js/deleteAllUsers()')
|
||||
}
|
||||
}
|
||||
|
||||
// This function is used to create new users.
|
||||
// userObj = {
|
||||
// username,
|
||||
// password
|
||||
// }
|
||||
async function createUser (userObj) {
|
||||
try {
|
||||
const options = {
|
||||
method: 'POST',
|
||||
url: `${LOCALHOST}/users`,
|
||||
data: {
|
||||
user: {
|
||||
email: userObj.email,
|
||||
password: userObj.password,
|
||||
name: userObj.name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const result = await axios(options)
|
||||
|
||||
const retObj = {
|
||||
user: result.data.user,
|
||||
token: result.data.token
|
||||
}
|
||||
|
||||
return retObj
|
||||
} catch (err) {
|
||||
console.log(
|
||||
'Error in utils.js/createUser(): ' + JSON.stringify(err, null, 2)
|
||||
)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
async function loginTestUser () {
|
||||
try {
|
||||
const options = {
|
||||
method: 'POST',
|
||||
url: `${LOCALHOST}/auth`,
|
||||
data: {
|
||||
email: 'test@test.com',
|
||||
password: 'pass'
|
||||
}
|
||||
}
|
||||
|
||||
const result = await axios(options)
|
||||
|
||||
// console.log(`result: ${JSON.stringify(result.data, null, 2)}`)
|
||||
|
||||
const retObj = {
|
||||
token: result.data.token,
|
||||
user: result.data.user.username,
|
||||
id: result.data.user._id.toString()
|
||||
}
|
||||
|
||||
return retObj
|
||||
} catch (err) {
|
||||
console.log(
|
||||
'Error authenticating test user: ' + JSON.stringify(err, null, 2)
|
||||
)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
async function loginAdminUser () {
|
||||
try {
|
||||
const FILENAME = `../../config/system-user-${config.env}.json`
|
||||
const adminUserData = require(FILENAME)
|
||||
console.log(`adminUserData: ${JSON.stringify(adminUserData, null, 2)}`)
|
||||
|
||||
const options = {
|
||||
method: 'POST',
|
||||
url: `${LOCALHOST}/auth`,
|
||||
data: {
|
||||
email: adminUserData.email,
|
||||
password: adminUserData.password,
|
||||
name: 'admin'
|
||||
}
|
||||
}
|
||||
|
||||
const result = await axios(options)
|
||||
|
||||
// console.log(`result: ${JSON.stringify(result.data, null, 2)}`)
|
||||
|
||||
const retObj = {
|
||||
token: result.data.token,
|
||||
user: result.data.user.username,
|
||||
id: result.data.user._id.toString()
|
||||
}
|
||||
|
||||
return retObj
|
||||
} catch (err) {
|
||||
console.log(
|
||||
'Error authenticating test admin user: ' + JSON.stringify(err, null, 2)
|
||||
)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieve the admin user JWT token from the JSON file it's saved at.
|
||||
async function getAdminJWT () {
|
||||
try {
|
||||
// process.env.KOA_ENV = process.env.KOA_ENV || 'dev'
|
||||
// console.log(`env: ${process.env.KOA_ENV}`)
|
||||
|
||||
const FILENAME = `../../config/system-user-${config.env}.json`
|
||||
const adminUserData = require(FILENAME)
|
||||
// console.log(`adminUserData: ${JSON.stringify(adminUserData, null, 2)}`)
|
||||
|
||||
return adminUserData.token
|
||||
} catch (err) {
|
||||
console.error('Error in test/utils.js/getAdminJWT()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
cleanDb,
|
||||
createUser,
|
||||
loginTestUser,
|
||||
loginAdminUser,
|
||||
getAdminJWT,
|
||||
deleteAllUsers
|
||||
}
|
||||
Reference in New Issue
Block a user