mirror of
https://github.com/Permissionless-Software-Foundation/x402-base-facilitator.git
synced 2026-09-21 16:52:01 -07:00
Fixing package.json
This commit is contained in:
+4
-1
@@ -12,7 +12,10 @@ const cors = require('kcors')
|
||||
|
||||
// Local libraries
|
||||
const config = require('../config') // this first.
|
||||
const adminLib = require('../src/lib/admin')
|
||||
|
||||
const AdminLib = require('../src/lib/admin')
|
||||
const adminLib = new AdminLib()
|
||||
|
||||
const errorMiddleware = require('../src/middleware')
|
||||
const wlogger = require('../src/lib/wlogger')
|
||||
|
||||
|
||||
Generated
+852
-1896
File diff suppressed because it is too large
Load Diff
+4
-3
@@ -8,7 +8,7 @@
|
||||
"test": "export KOA_ENV=test && npm run prep-test && nyc --reporter=text --timeout 15000 ./node_modules/.bin/mocha --exit",
|
||||
"lint": "standard --env mocha --fix",
|
||||
"docs": "./node_modules/.bin/apidoc -i src/ -o docs",
|
||||
"coverage": "nyc report --reporter=text-lcov | coveralls",
|
||||
"coverage": "export KOA_ENV=test && npm run prep-test && nyc report --reporter=text-lcov | coveralls",
|
||||
"coverage:report": "export KOA_ENV=test && npm run prep-test && nyc --reporter=html mocha --exit",
|
||||
"prep-test": "node util/users/delete-all-test-users.js"
|
||||
},
|
||||
@@ -47,7 +47,7 @@
|
||||
"koa-router": "^9.0.1",
|
||||
"koa-static": "^5.0.0",
|
||||
"line-reader": "^0.4.0",
|
||||
"mongoose": "^5.7.5",
|
||||
"mongoose": "^5.10.15",
|
||||
"nodemailer": "^6.4.10",
|
||||
"passport-local": "^1.0.0",
|
||||
"winston": "^3.2.1",
|
||||
@@ -65,7 +65,8 @@
|
||||
"husky": "^4.2.5",
|
||||
"mocha": "^7.0.1",
|
||||
"nyc": "^15.0.0",
|
||||
"semantic-release": "^17.0.0",
|
||||
"semantic-release": "^17.2.3",
|
||||
"sinon": "^9.2.1",
|
||||
"standard": "^14.3.1"
|
||||
},
|
||||
"release": {
|
||||
|
||||
+34
-21
@@ -13,8 +13,9 @@
|
||||
const axios = require('axios').default
|
||||
const mongoose = require('mongoose')
|
||||
const User = require('../models/users')
|
||||
const jsonFiles = require('./utils/json-files')
|
||||
const config = require('../../config')
|
||||
const JsonFiles = require('./utils/json-files')
|
||||
const jsonFiles = new JsonFiles()
|
||||
|
||||
const JSON_FILE = `system-user-${config.env}.json`
|
||||
const JSON_PATH = `${__dirname}/../../config/${JSON_FILE}`
|
||||
@@ -22,13 +23,25 @@ const JSON_PATH = `${__dirname}/../../config/${JSON_FILE}`
|
||||
const LOCALHOST = `http://localhost:${config.port}`
|
||||
const context = {}
|
||||
|
||||
let _this
|
||||
class Admin {
|
||||
constructor () {
|
||||
this.axios = axios
|
||||
this.User = User
|
||||
this.config = config
|
||||
this.jsonFiles = jsonFiles
|
||||
this.context = context
|
||||
|
||||
_this = this
|
||||
}
|
||||
|
||||
// Create the first user in the system. A 'admin' level system user that is
|
||||
// used by the Listing Manager and test scripts, in order access private API
|
||||
// functions.
|
||||
async function createSystemUser () {
|
||||
async createSystemUser () {
|
||||
// Create the system user.
|
||||
try {
|
||||
context.password = _randomString(20)
|
||||
context.password = _this._randomString(20)
|
||||
|
||||
const options = {
|
||||
method: 'POST',
|
||||
@@ -40,18 +53,17 @@ async function createSystemUser () {
|
||||
}
|
||||
}
|
||||
}
|
||||
const result = await axios(options)
|
||||
|
||||
const result = await _this.axios.request(options)
|
||||
context.email = result.data.user.email
|
||||
context.id = result.data.user._id
|
||||
context.token = result.data.token
|
||||
|
||||
// Get the mongoDB entry
|
||||
const user = await User.findById(context.id)
|
||||
const user = await _this.User.findById(context.id)
|
||||
|
||||
// Change the user type to admin
|
||||
user.type = 'admin'
|
||||
// console.log(`user: ${JSON.stringify(user, null, 2)}`)
|
||||
// console.log(`user created: ${JSON.stringify(user, null, 2)}`)
|
||||
|
||||
// Save the user model.
|
||||
await user.save()
|
||||
@@ -61,6 +73,7 @@ async function createSystemUser () {
|
||||
|
||||
// Write out the system user information to a JSON file that external
|
||||
// applications like the Task Manager and the test scripts can access.
|
||||
|
||||
await jsonFiles.writeJSON(context, JSON_PATH)
|
||||
|
||||
return context
|
||||
@@ -69,58 +82,60 @@ async function createSystemUser () {
|
||||
if (err.response.status === 422) {
|
||||
try {
|
||||
// Delete the existing user
|
||||
await deleteExistingSystemUser()
|
||||
await _this.deleteExistingSystemUser()
|
||||
|
||||
// Call this function again.
|
||||
return createSystemUser()
|
||||
return _this.createSystemUser()
|
||||
} catch (err2) {
|
||||
console.error('Error in admin.js/createSystemUser() while trying generate new system user.')
|
||||
// process.end(1)
|
||||
throw err2
|
||||
}
|
||||
} else {
|
||||
console.log('Error in admin.js/createSystemUser: ' + JSON.stringify(err, null, 2))
|
||||
console.log('Error in admin.js/createSystemUser: ')
|
||||
// process.end(1)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteExistingSystemUser () {
|
||||
async deleteExistingSystemUser () {
|
||||
try {
|
||||
mongoose.Promise = global.Promise
|
||||
mongoose.set('useCreateIndex', true) // Stop deprecation warning.
|
||||
|
||||
await mongoose.connect(config.database, { useNewUrlParser: true, useUnifiedTopology: true })
|
||||
|
||||
await User.deleteOne({ email: 'system@system.com' })
|
||||
await _this.User.deleteOne({ email: 'system@system.com' })
|
||||
} catch (err) {
|
||||
console.log('Error in admin.js/deleteExistingSystemUser()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
async function loginAdmin () {
|
||||
async loginAdmin () {
|
||||
// console.log(`loginAdmin() running.`)
|
||||
let existingUser
|
||||
|
||||
try {
|
||||
// Read the exising file
|
||||
existingUser = await jsonFiles.readJSON(JSON_PATH)
|
||||
existingUser = await _this.jsonFiles.readJSON(JSON_PATH)
|
||||
// console.log(`existingUser: ${JSON.stringify(existingUser, null, 2)}`)
|
||||
|
||||
// Log in as the user.
|
||||
const options = {
|
||||
method: 'POST',
|
||||
url: `${LOCALHOST}/auth`,
|
||||
headers: {
|
||||
Accept: 'application/json'
|
||||
},
|
||||
data: {
|
||||
email: 'system@system.com',
|
||||
password: existingUser.password
|
||||
}
|
||||
}
|
||||
const result = await axios(options)
|
||||
const result = await _this.axios.request(options)
|
||||
// console.log(`result1: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
return result
|
||||
} catch (err) {
|
||||
console.error('Error in admin.js/loginAdmin().')
|
||||
@@ -131,7 +146,7 @@ async function loginAdmin () {
|
||||
}
|
||||
}
|
||||
|
||||
function _randomString (length) {
|
||||
_randomString (length) {
|
||||
var text = ''
|
||||
var possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
|
||||
for (var i = 0; i < length; i++) {
|
||||
@@ -139,8 +154,6 @@ function _randomString (length) {
|
||||
}
|
||||
return text
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
createSystemUser,
|
||||
loginAdmin
|
||||
}
|
||||
|
||||
module.exports = Admin
|
||||
|
||||
+18
-18
@@ -17,6 +17,20 @@ class NodeMailer {
|
||||
this.config = config
|
||||
|
||||
_this = this
|
||||
_this.transporter = _this.createTransporter()
|
||||
}
|
||||
|
||||
createTransporter () {
|
||||
const transporter = _this.nodemailer.createTransport({
|
||||
host: _this.config.emailServer,
|
||||
port: 587,
|
||||
secure: false, // true for 465, false for other ports
|
||||
auth: {
|
||||
user: _this.config.emailUser, // generated ethereal user
|
||||
pass: _this.config.emailPassword // generated ethereal password
|
||||
}
|
||||
})
|
||||
return transporter
|
||||
}
|
||||
|
||||
// Validate email
|
||||
@@ -44,10 +58,7 @@ class NodeMailer {
|
||||
throw new Error("Property 'to' must be a array!")
|
||||
}
|
||||
|
||||
const isEmailList = await _this.validateEmailArray(data.to)
|
||||
if (!isEmailList) {
|
||||
throw new Error("Property 'to' must be array of email format!")
|
||||
}
|
||||
await _this.validateEmailArray(data.to)
|
||||
|
||||
if (!data.formMessage || typeof data.formMessage !== 'string') {
|
||||
throw new Error("Property 'message' must be a string!")
|
||||
@@ -61,18 +72,6 @@ class NodeMailer {
|
||||
throw new Error("Property 'payloadTitle' must be a string!")
|
||||
}
|
||||
|
||||
// create reusable transporter object using the default SMTP transport
|
||||
const transporter = await _this.nodemailer.createTransport({
|
||||
host: _this.config.emailServer,
|
||||
port: 587,
|
||||
secure: false, // true for 465, false for other ports
|
||||
auth: {
|
||||
user: _this.config.emailUser, // generated ethereal user
|
||||
pass: _this.config.emailPassword // generated ethereal password
|
||||
}
|
||||
})
|
||||
// console.log(`transporter: ${JSON.stringify(transporter)}`)
|
||||
|
||||
const msg = data.formMessage.replace(/(\r\n|\n|\r)/g, '<br />')
|
||||
|
||||
const now = new Date()
|
||||
@@ -115,7 +114,7 @@ class NodeMailer {
|
||||
${htmlData}
|
||||
</p>`
|
||||
// send mail with defined transport object
|
||||
const info = await transporter.sendMail({
|
||||
const info = await _this.transporter.sendMail({
|
||||
// from: `${data.email}`, // sender address
|
||||
from: 'noreply@launchpadip.net',
|
||||
to: `${to}`, // list of receivers
|
||||
@@ -125,8 +124,9 @@ class NodeMailer {
|
||||
html: htmlMsg
|
||||
})
|
||||
console.log('Message sent: %s', info.messageId)
|
||||
return info
|
||||
} catch (err) {
|
||||
console.log('Error in sendEmail()')
|
||||
wlogger.error('Error in lib/nodemailer.js/sendEmail()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
+30
-16
@@ -1,39 +1,55 @@
|
||||
/*
|
||||
A utility file for reading and writing JSON files.
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
const fs = require('fs')
|
||||
|
||||
let _this
|
||||
|
||||
class JsonFiles {
|
||||
constructor () {
|
||||
this.fs = fs
|
||||
|
||||
_this = this
|
||||
}
|
||||
|
||||
// Writes out a JSON file of any object passed to the function.
|
||||
// This is used for testing.
|
||||
function writeJSON (obj, fileName) {
|
||||
writeJSON (obj, fileName) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
try {
|
||||
if (!obj) {
|
||||
throw new Error('obj property is required')
|
||||
}
|
||||
if (!fileName || typeof fileName !== 'string') {
|
||||
throw new Error('fileName property must be a string')
|
||||
}
|
||||
const fileStr = JSON.stringify(obj, null, 2)
|
||||
|
||||
fs.writeFile(fileName, fileStr, function (err) {
|
||||
_this.fs.writeFile(fileName, fileStr, function (err) {
|
||||
if (err) {
|
||||
console.error('Error while trying to write file: ', err)
|
||||
return reject(err)
|
||||
console.error('Error while trying to write file: ')
|
||||
throw err
|
||||
} else {
|
||||
// console.log(`${fileName} written successfully!`)
|
||||
return resolve()
|
||||
}
|
||||
})
|
||||
} catch (err) {
|
||||
console.error('Error trying to write out object in util.js/_writeJSON().', err)
|
||||
console.error('Error trying to write out object in util.js/_writeJSON().')
|
||||
return reject(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Read and parse a JSON file.
|
||||
function readJSON (fileName) {
|
||||
readJSON (fileName) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
try {
|
||||
fs.readFile(fileName, (err, data) => {
|
||||
if (!fileName || typeof fileName !== 'string') {
|
||||
throw new Error('fileName property must be a string')
|
||||
}
|
||||
|
||||
_this.fs.readFile(fileName, (err, data) => {
|
||||
if (err) {
|
||||
if (err.code === 'ENOENT') {
|
||||
console.log('Admin .json file not found!')
|
||||
@@ -41,7 +57,7 @@ function readJSON (fileName) {
|
||||
console.log(`err: ${JSON.stringify(err, null, 2)}`)
|
||||
}
|
||||
|
||||
return reject(err)
|
||||
throw err
|
||||
}
|
||||
|
||||
const obj = JSON.parse(data)
|
||||
@@ -49,13 +65,11 @@ function readJSON (fileName) {
|
||||
return resolve(obj)
|
||||
})
|
||||
} catch (err) {
|
||||
console.error('Error trying to read JSON file in util.js/_readJSON().', err)
|
||||
console.error('Error trying to read JSON file in util.js/_readJSON().')
|
||||
return reject(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
writeJSON,
|
||||
readJSON
|
||||
}
|
||||
|
||||
module.exports = JsonFiles
|
||||
|
||||
@@ -4,9 +4,22 @@ const getToken = require('../lib/auth')
|
||||
const jwt = require('jsonwebtoken')
|
||||
const wlogger = require('../lib/wlogger')
|
||||
|
||||
async function ensureUser (ctx, next) {
|
||||
let _this
|
||||
|
||||
class Validators {
|
||||
constructor () {
|
||||
this.User = User
|
||||
this.getToken = getToken
|
||||
this.jwt = jwt
|
||||
this.config = config
|
||||
|
||||
_this = this
|
||||
}
|
||||
|
||||
async ensureUser (ctx, next) {
|
||||
try {
|
||||
// console.log(`getToken: ${typeof (getToken)}`)
|
||||
const token = getToken(ctx)
|
||||
const token = _this.getToken(ctx)
|
||||
|
||||
if (!token) {
|
||||
// console.log(`Err: Token not provided.`)
|
||||
@@ -17,26 +30,30 @@ async function ensureUser (ctx, next) {
|
||||
try {
|
||||
// console.log(`token: ${JSON.stringify(token, null, 2)}`)
|
||||
// console.log(`config: ${JSON.stringify(config, null, 2)}`)
|
||||
decoded = jwt.verify(token, config.token)
|
||||
decoded = _this.jwt.verify(token, config.token)
|
||||
} catch (err) {
|
||||
// console.log(`Err: Token could not be decoded: ${err}`)
|
||||
ctx.throw(401)
|
||||
}
|
||||
|
||||
ctx.state.user = await User.findById(decoded.id, '-password')
|
||||
ctx.state.user = await _this.User.findById(decoded.id, '-password')
|
||||
if (!ctx.state.user) {
|
||||
// console.log(`Err: Could not find user.`)
|
||||
ctx.throw(401)
|
||||
}
|
||||
|
||||
return next()
|
||||
} catch (error) {
|
||||
ctx.throw(401)
|
||||
}
|
||||
}
|
||||
|
||||
// This funciton is almost identical to ensureUser, except at the end, it verifies
|
||||
// that the 'type' associated with the user equals 'admin'.
|
||||
async function ensureAdmin (ctx, next) {
|
||||
async ensureAdmin (ctx, next) {
|
||||
try {
|
||||
// console.log(`getToken: ${typeof (getToken)}`)
|
||||
const token = getToken(ctx)
|
||||
const token = _this.getToken(ctx)
|
||||
|
||||
if (!token) {
|
||||
// console.log(`Err: Token not provided.`)
|
||||
@@ -47,13 +64,13 @@ async function ensureAdmin (ctx, next) {
|
||||
try {
|
||||
// console.log(`token: ${JSON.stringify(token, null, 2)}`)
|
||||
// console.log(`config: ${JSON.stringify(config, null, 2)}`)
|
||||
decoded = jwt.verify(token, config.token)
|
||||
decoded = _this.jwt.verify(token, config.token)
|
||||
} catch (err) {
|
||||
// console.log(`Err: Token could not be decoded: ${err}`)
|
||||
ctx.throw(401)
|
||||
}
|
||||
|
||||
ctx.state.user = await User.findById(decoded.id, '-password')
|
||||
ctx.state.user = await _this.User.findById(decoded.id, '-password')
|
||||
if (!ctx.state.user) {
|
||||
// console.log(`Err: Could not find user.`)
|
||||
ctx.throw(401)
|
||||
@@ -64,6 +81,9 @@ async function ensureAdmin (ctx, next) {
|
||||
}
|
||||
|
||||
return next()
|
||||
} catch (error) {
|
||||
ctx.throw(401, error.message)
|
||||
}
|
||||
}
|
||||
|
||||
// This middleware ensures that the :id used in the API endpoint matches the
|
||||
@@ -71,9 +91,10 @@ async function ensureAdmin (ctx, next) {
|
||||
// an Admin user. This prevents situations like users updating other users
|
||||
// profiles or non-admins deleting users.
|
||||
// TODO Tests must be developed before developing this function.
|
||||
async function ensureTargetUserOrAdmin (ctx, next) {
|
||||
async ensureTargetUserOrAdmin (ctx, next) {
|
||||
try {
|
||||
// console.log(`getToken: ${typeof (getToken)}`)
|
||||
const token = getToken(ctx)
|
||||
const token = _this.getToken(ctx)
|
||||
|
||||
if (!token) {
|
||||
// console.log(`Err: Token not provided.`)
|
||||
@@ -88,13 +109,13 @@ async function ensureTargetUserOrAdmin (ctx, next) {
|
||||
try {
|
||||
// console.log(`token: ${JSON.stringify(token, null, 2)}`)
|
||||
// console.log(`config: ${JSON.stringify(config, null, 2)}`)
|
||||
decoded = jwt.verify(token, config.token)
|
||||
decoded = _this.jwt.verify(token, config.token)
|
||||
} catch (err) {
|
||||
// console.log(`Err: Token could not be decoded: ${err}`)
|
||||
ctx.throw(401)
|
||||
}
|
||||
|
||||
ctx.state.user = await User.findById(decoded.id, '-password')
|
||||
ctx.state.user = await _this.User.findById(decoded.id, '-password')
|
||||
if (!ctx.state.user) {
|
||||
// console.log(`Err: Could not find user.`)
|
||||
ctx.throw(401)
|
||||
@@ -102,6 +123,7 @@ async function ensureTargetUserOrAdmin (ctx, next) {
|
||||
|
||||
// console.log(`ctx.state.user: ${JSON.stringify(ctx.state.user, null, 2)}`)
|
||||
// Ensure the calling user and the target user are the same.
|
||||
|
||||
if (ctx.state.user._id.toString() !== targetId.toString()) {
|
||||
wlogger.verbose(
|
||||
`Calling user and target user do not match! Calling user: ${
|
||||
@@ -118,10 +140,10 @@ async function ensureTargetUserOrAdmin (ctx, next) {
|
||||
}
|
||||
|
||||
return next()
|
||||
} catch (error) {
|
||||
ctx.throw(401, error.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
ensureUser,
|
||||
ensureAdmin,
|
||||
ensureTargetUserOrAdmin
|
||||
}
|
||||
module.exports = Validators
|
||||
|
||||
@@ -65,11 +65,13 @@ class Contact {
|
||||
success: true
|
||||
}
|
||||
} catch (err) {
|
||||
ctx.body = {
|
||||
success: false
|
||||
}
|
||||
// ctx.body = {
|
||||
// success: false
|
||||
// }
|
||||
// console.error(`Error: `, err)
|
||||
throw err
|
||||
// throw err
|
||||
|
||||
ctx.throw(422, err.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,9 @@ let _this
|
||||
class LogsApi {
|
||||
constructor () {
|
||||
_this = this
|
||||
_this.fs = fs
|
||||
_this.lineReader = lineReader
|
||||
_this.config = config
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -56,20 +59,18 @@ class LogsApi {
|
||||
// console.log(`password: ${password}`)
|
||||
|
||||
// Password matches the password set in the config file.
|
||||
if (password === config.logPass) {
|
||||
if (password === _this.config.logPass) {
|
||||
// Generate the full path and file name for the current log file.
|
||||
const fullPath = _this.generateFileName()
|
||||
// console.log(`fullPath: ${JSON.stringify(fullPath, null, 2)}`)
|
||||
|
||||
// Throw an error if the file does not exist.
|
||||
fs.stat(fullPath, (err, stat) => {
|
||||
if (err) {
|
||||
if (!_this.fs.existsSync(fullPath)) {
|
||||
ctx.body = {
|
||||
success: false,
|
||||
data: 'file does not exist'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
} else {
|
||||
// Read in the data from the log file.
|
||||
const data = await _this.readLines(fullPath)
|
||||
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
|
||||
@@ -81,6 +82,7 @@ class LogsApi {
|
||||
success: true,
|
||||
data: filteredData
|
||||
}
|
||||
}
|
||||
|
||||
// Password does not match password in config file.
|
||||
} else {
|
||||
@@ -89,21 +91,23 @@ class LogsApi {
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
if (err) {
|
||||
if (err && err.message) {
|
||||
ctx.throw(500, err.message)
|
||||
} else {
|
||||
ctx.throw(500, 'Unhandled error')
|
||||
console.log('unhandled error: ', err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sorts the log data by their timestamp. Returns the LIMIT or less elements.
|
||||
filterLogs (data) {
|
||||
filterLogs (data, LIMIT = 100) {
|
||||
try {
|
||||
if (!Array.isArray(data)) {
|
||||
throw new Error('Data must be array')
|
||||
}
|
||||
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
|
||||
|
||||
const LIMIT = 100 // Max number of entries to return.
|
||||
// const LIMIT = 100 // Max number of entries to return.
|
||||
|
||||
// Sort the elements by date.
|
||||
data.sort(function (a, b) {
|
||||
@@ -142,7 +146,7 @@ class LogsApi {
|
||||
const thisYear = now.getFullYear()
|
||||
|
||||
const filename = `koa-${
|
||||
config.env
|
||||
_this.config.env
|
||||
}-${thisYear}-${thisMonth}-${thisDate}.log`
|
||||
// console.log(`filename: ${filename}`)
|
||||
const logDir = `${__dirname}/../../../logs/`
|
||||
@@ -157,23 +161,33 @@ class LogsApi {
|
||||
}
|
||||
|
||||
// Promise based read-file
|
||||
readFile (path, opts = 'utf8') {
|
||||
/* readFile (path, opts = 'utf8') {
|
||||
return new Promise((resolve, reject) => {
|
||||
fs.readFile(path, opts, (err, data) => {
|
||||
_this.fs.readFile(path, opts, (err, data) => {
|
||||
if (err) reject(err)
|
||||
else resolve(data)
|
||||
})
|
||||
})
|
||||
}
|
||||
} */
|
||||
|
||||
// Returns an array with each element containing a line of the file.
|
||||
readLines (filename) {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
if (!filename || typeof filename !== 'string') {
|
||||
throw new Error('filename must be a string')
|
||||
}
|
||||
// Throw an error if the file does not exist.
|
||||
|
||||
if (!_this.fs.existsSync(filename)) {
|
||||
throw new Error('file does not exist')
|
||||
}
|
||||
|
||||
const data = []
|
||||
|
||||
// let i = 0
|
||||
|
||||
lineReader.eachLine(filename, function (line, last) {
|
||||
_this.lineReader.eachLine(filename, function (line, last) {
|
||||
try {
|
||||
data.push(JSON.parse(line))
|
||||
|
||||
@@ -183,7 +197,8 @@ class LogsApi {
|
||||
|
||||
if (last) return resolve(data)
|
||||
} catch (err) {
|
||||
console.log('err: ', err)
|
||||
// console.log('err: ', err)
|
||||
if (last) return resolve(data)
|
||||
}
|
||||
})
|
||||
} catch (err) {
|
||||
|
||||
@@ -47,15 +47,14 @@ class UserController {
|
||||
* }
|
||||
*/
|
||||
async createUser (ctx) {
|
||||
const user = new _this.User(ctx.request.body.user)
|
||||
|
||||
const userObj = ctx.request.body.user
|
||||
try {
|
||||
/*
|
||||
* ERROR HANDLERS
|
||||
*
|
||||
*/
|
||||
// Required property
|
||||
if (!user.email || typeof user.email !== 'string') {
|
||||
if (!userObj.email || typeof userObj.email !== 'string') {
|
||||
throw new Error("Property 'email' must be a string!")
|
||||
}
|
||||
|
||||
@@ -66,14 +65,15 @@ class UserController {
|
||||
// throw new Error("Property 'email' must be email format!")
|
||||
// }
|
||||
|
||||
if (!user.password || typeof user.password !== 'string') {
|
||||
if (!userObj.password || typeof userObj.password !== 'string') {
|
||||
throw new Error("Property 'password' must be a string!")
|
||||
}
|
||||
|
||||
if (user.name && typeof user.name !== 'string') {
|
||||
if (userObj.name && typeof userObj.name !== 'string') {
|
||||
throw new Error("Property 'name' must be a string!")
|
||||
}
|
||||
|
||||
const user = new _this.User(userObj)
|
||||
// Enforce default value of 'user'
|
||||
user.type = 'user'
|
||||
|
||||
@@ -160,6 +160,7 @@ class UserController {
|
||||
*
|
||||
* @apiUse TokenError
|
||||
*/
|
||||
|
||||
async getUser (ctx, next) {
|
||||
try {
|
||||
const user = await _this.User.findById(ctx.params.id, '-password')
|
||||
@@ -171,12 +172,18 @@ class UserController {
|
||||
user
|
||||
}
|
||||
} catch (err) {
|
||||
if (err === 404 || err.name === 'CastError') {
|
||||
// Handle different error types.
|
||||
if (
|
||||
err === 404 ||
|
||||
err.name === 'CastError' ||
|
||||
err.message.toString().includes('Not Found')
|
||||
) {
|
||||
ctx.throw(404)
|
||||
}
|
||||
|
||||
ctx.throw(500)
|
||||
}
|
||||
|
||||
if (next) {
|
||||
return next()
|
||||
}
|
||||
@@ -239,7 +246,8 @@ class UserController {
|
||||
if (userObj.email && typeof userObj.email !== 'string') {
|
||||
throw new Error("Property 'email' must be a string!")
|
||||
}
|
||||
const isEmail = await _this.validateEmail(user.email)
|
||||
|
||||
const isEmail = await _this.validateEmail(userObj.email)
|
||||
if (userObj.email && !isEmail) {
|
||||
throw new Error("Property 'email' must be email format!")
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
const validator = require('../../middleware/validators')
|
||||
const VALIDATOR = require('../../middleware/validators')
|
||||
const validator = new VALIDATOR()
|
||||
|
||||
const CONTROLLER = require('./controller')
|
||||
const controller = new CONTROLLER()
|
||||
|
||||
|
||||
+324
-131
@@ -2,6 +2,7 @@ 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 }
|
||||
@@ -10,6 +11,12 @@ 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)}`)
|
||||
@@ -38,6 +45,14 @@ describe('Users', () => {
|
||||
// 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 {
|
||||
@@ -49,11 +64,11 @@ describe('Users', () => {
|
||||
}
|
||||
}
|
||||
|
||||
const result = await axios(options)
|
||||
await axios(options)
|
||||
|
||||
console.log(
|
||||
/* 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.')
|
||||
@@ -128,6 +143,28 @@ describe('Users', () => {
|
||||
}
|
||||
})
|
||||
|
||||
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',
|
||||
@@ -251,6 +288,22 @@ describe('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', () => {
|
||||
@@ -321,6 +374,40 @@ describe('Users', () => {
|
||||
'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', () => {
|
||||
@@ -362,6 +449,240 @@ describe('Users', () => {
|
||||
}
|
||||
})
|
||||
|
||||
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
|
||||
@@ -435,134 +756,6 @@ describe('Users', () => {
|
||||
assert.equal(user.email, 'testToUpdate@test.com')
|
||||
assert.equal(user.username, 'myUsername')
|
||||
})
|
||||
|
||||
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'
|
||||
}
|
||||
}
|
||||
}
|
||||
const result = 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'
|
||||
}
|
||||
}
|
||||
}
|
||||
const result = 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 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 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 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, 'not a valid Email format')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('DELETE /users/:id', () => {
|
||||
|
||||
+51
-18
@@ -1,9 +1,23 @@
|
||||
const assert = require('chai').assert
|
||||
|
||||
const NodeMailer = require('../src/lib/nodemailer')
|
||||
const nodemailer = new 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 {
|
||||
@@ -13,7 +27,7 @@ describe('NodeMailer', () => {
|
||||
subject: 'test subject',
|
||||
to: ['test2@email.com']
|
||||
}
|
||||
await nodemailer.sendEmail(data)
|
||||
await uut.sendEmail(data)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'Property \'email\' must be a string!')
|
||||
@@ -28,7 +42,7 @@ describe('NodeMailer', () => {
|
||||
subject: 'test subject',
|
||||
to: ['test2@email.com']
|
||||
}
|
||||
await nodemailer.sendEmail(data)
|
||||
await uut.sendEmail(data)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'Property \'email\' must be email format!')
|
||||
@@ -42,7 +56,7 @@ describe('NodeMailer', () => {
|
||||
subject: 'test subject',
|
||||
to: ['test2@email.com']
|
||||
}
|
||||
await nodemailer.sendEmail(data)
|
||||
await uut.sendEmail(data)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'Property \'message\' must be a string!')
|
||||
@@ -55,7 +69,7 @@ describe('NodeMailer', () => {
|
||||
name: 'test name',
|
||||
subject: 'test subject'
|
||||
}
|
||||
await nodemailer.sendEmail(data)
|
||||
await uut.sendEmail(data)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'Property \'to\' must be a array!')
|
||||
@@ -71,7 +85,7 @@ describe('NodeMailer', () => {
|
||||
subject: 'test subject',
|
||||
to: ['test']
|
||||
}
|
||||
await nodemailer.sendEmail(data)
|
||||
await uut.sendEmail(data)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'Array must contain emails format!')
|
||||
@@ -86,7 +100,7 @@ describe('NodeMailer', () => {
|
||||
name: 'test name',
|
||||
to: ['test2@email.com']
|
||||
}
|
||||
await nodemailer.sendEmail(data)
|
||||
await uut.sendEmail(data)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'Property \'subject\' must be a string!')
|
||||
@@ -101,34 +115,53 @@ describe('NodeMailer', () => {
|
||||
subject: 'test subject',
|
||||
to: ['test2@email.com']
|
||||
}
|
||||
await nodemailer.sendEmail(data)
|
||||
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 () => {
|
||||
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',
|
||||
to: ['test2@email.com'],
|
||||
subject: 'test subject',
|
||||
payloadTitle: 'test title'
|
||||
}
|
||||
await nodemailer.sendEmail(data)
|
||||
assert(false, 'Unexpected result')
|
||||
const info = await uut.sendEmail(data)
|
||||
assert.isObject(info)
|
||||
assert.isString(info.messageId)
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'Property \'subject\' must be a string!')
|
||||
assert(false, 'Unexpected result')
|
||||
}
|
||||
}) */
|
||||
})
|
||||
})
|
||||
describe('validateEmailArray()', () => {
|
||||
it('should throw error if email list is not provided ', async () => {
|
||||
try {
|
||||
await nodemailer.validateEmailArray()
|
||||
await uut.validateEmailArray()
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'Property \'emailList\' must be a array!')
|
||||
@@ -137,7 +170,7 @@ describe('NodeMailer', () => {
|
||||
it('should throw error if email list is empty', async () => {
|
||||
try {
|
||||
const emailList = []
|
||||
await nodemailer.validateEmailArray(emailList)
|
||||
await uut.validateEmailArray(emailList)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'Property \'emailList\' cant be empty!')
|
||||
@@ -149,7 +182,7 @@ describe('NodeMailer', () => {
|
||||
'wrongEmail',
|
||||
'bad format'
|
||||
]
|
||||
await nodemailer.validateEmailArray(emailList)
|
||||
await uut.validateEmailArray(emailList)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'Array must contain emails format!')
|
||||
@@ -161,7 +194,7 @@ describe('NodeMailer', () => {
|
||||
'test@email.com',
|
||||
'simple@email.com'
|
||||
]
|
||||
const result = await nodemailer.validateEmailArray(emailList)
|
||||
const result = await uut.validateEmailArray(emailList)
|
||||
assert.isTrue(result)
|
||||
} catch (err) {
|
||||
assert(false, 'Unexpected result')
|
||||
|
||||
+142
-17
@@ -1,13 +1,27 @@
|
||||
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 {
|
||||
@@ -28,10 +42,11 @@ describe('Contact', () => {
|
||||
// console.log(`result stringified: ${JSON.stringify(result, null, 2)}`)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 500)
|
||||
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 = {
|
||||
@@ -52,13 +67,14 @@ describe('Contact', () => {
|
||||
// console.log(`result stringified: ${JSON.stringify(result, null, 2)}`)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 500)
|
||||
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 = {
|
||||
@@ -78,13 +94,14 @@ describe('Contact', () => {
|
||||
// console.log(`result stringified: ${JSON.stringify(result, null, 2)}`)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 500)
|
||||
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 = {
|
||||
@@ -105,19 +122,15 @@ describe('Contact', () => {
|
||||
// console.log(`result stringified: ${JSON.stringify(result, null, 2)}`)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.equal(err.response.status, 500)
|
||||
assert.equal(err.response.status, 422)
|
||||
assert.include(
|
||||
err.response.data,
|
||||
"Property 'payloadTitle' must be a string!"
|
||||
)
|
||||
}
|
||||
})
|
||||
/* I presented problems here trying to use sandbox in this test */
|
||||
/* it('should send email with all input', async () => {
|
||||
// Mock live network calls.
|
||||
sandbox.stub(
|
||||
contactController.transporter, 'sendMail')
|
||||
.resolves(mockData)
|
||||
|
||||
it('should throw error if payloadTitle property is not string', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'POST',
|
||||
@@ -125,18 +138,130 @@ describe('Contact', () => {
|
||||
data: {
|
||||
obj: {
|
||||
email: 'email@email.com',
|
||||
formMessage: 'test email',
|
||||
name: 'test name',
|
||||
payloadtitle: 'test title'
|
||||
formMessage: 'test message',
|
||||
payloadTitle: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
const result = await axios(options)
|
||||
console.log('RESULT', result.data)
|
||||
|
||||
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) {
|
||||
// console.log(err)
|
||||
assert(false, 'Unexpected result')
|
||||
}
|
||||
}) */
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
+18
-12
@@ -1,17 +1,24 @@
|
||||
const assert = require('chai').assert
|
||||
const PassportLib = require('../src/lib/passport')
|
||||
|
||||
describe('#passport.js', () => {
|
||||
let passportLib
|
||||
const sinon = require('sinon')
|
||||
|
||||
beforeEach(async () => {
|
||||
passportLib = new PassportLib()
|
||||
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 passportLib.authUser()
|
||||
await uut.authUser()
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'ctx is required')
|
||||
@@ -20,17 +27,16 @@ describe('#passport.js', () => {
|
||||
|
||||
it('Should throw error if the passport library fails', async () => {
|
||||
try {
|
||||
// This is a mock to handle the callback error
|
||||
// when the passport library fails or throws error
|
||||
const error = new Error('cant auth user')
|
||||
const user = null
|
||||
const authMock = (value, callback) => {
|
||||
callback(error, user)
|
||||
}
|
||||
passportLib.passport.authenticate = authMock
|
||||
|
||||
// Mock calls
|
||||
// https://sinonjs.org/releases/latest/stubs/
|
||||
// About yields
|
||||
sandbox.stub(uut.passport, 'authenticate').yields(error, user)
|
||||
|
||||
const ctx = {}
|
||||
await passportLib.authUser(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}/${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,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
|
||||
}
|
||||
Reference in New Issue
Block a user