refactor(contact): Refactored /contact endpoint and tests

This commit is contained in:
Daniel Gonzalez
2021-03-31 20:07:23 -04:00
parent f07d326dad
commit 9833f8f8b9
5 changed files with 465 additions and 55 deletions
+59
View File
@@ -0,0 +1,59 @@
/* eslint-disable no-useless-escape */
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'
const config = require('../../config')
const NodeMailer = require('../lib/nodemailer')
const nodemailer = new NodeMailer()
const wlogger = require('./wlogger')
let _this
class ContactLib {
constructor () {
_this = this
_this.config = config
_this.nodemailer = nodemailer
}
async sendEmail (emailObj) {
try {
// Validate input
if (!emailObj.email || typeof emailObj.email !== 'string') {
throw new Error("Property 'email' must be a string!")
}
if (!emailObj.formMessage || typeof emailObj.formMessage !== 'string') {
throw new Error("Property 'formMessage' must be a string!")
}
// If an email list exists, the email will be sended to that list
// otherwhise will be sended by default to the variable "_this.config.emailUser"
let _to = [_this.config.emailUser]
// Email list is optional
if (emailObj.emailList) {
if (
!Array.isArray(emailObj.emailList) ||
!emailObj.emailList.length > 0
) {
throw new Error("Property 'emailList' must be a array of emails!")
} else {
_to = emailObj.emailList
}
}
console.log(`Trying send message to : ${_to}`)
emailObj.subject = 'Someone wants contact with you.'
emailObj.to = _to
const result = await _this.nodemailer.sendEmail(emailObj)
return result
} catch (err) {
wlogger.error('Error in lib/contact.js/sendEmail()')
throw err
}
}
}
module.exports = ContactLib
+49 -55
View File
@@ -1,78 +1,72 @@
/* eslint-disable no-useless-escape */
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'
const config = require('../../../config')
const NodeMailer = require('../../lib/nodemailer')
const nodemailer = new NodeMailer()
const ContactLib = require('../../lib/contact')
const contactLib = new ContactLib()
let _this
class Contact {
/**
* @api {post} /contact/email Send Email
* @apiName SendMail
* @apiGroup Contact
*
* @apiExample Example usage:
* curl -H "Content-Type: application/json" -X POST -d '{ "obj": { "email": "email@format.com", "formMessage": "a message" } }' localhost:5001/contact/email
*
* @apiParam {Object} obj object (required)
* @apiParam {String} obj.email Sender Email.
* @apiParam {String} obj.formMessage Message.
*
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* {
*
* success:true
*
* }
*
* @apiError UnprocessableEntity Missing required parameters
*
* @apiErrorExample {json} Error-Response:
* HTTP/1.1 422 Unprocessable Entity
* {
* "status": 422,
* "error": "Unprocessable Entity"
* }
*/
class ContactController {
constructor () {
_this = this
_this.config = config
_this.nodemailer = nodemailer
_this.contactLib = contactLib
}
async email (ctx) {
try {
const data = ctx.request.body
const emailObj = data.obj
// Validate input
if (!emailObj.email || typeof emailObj.email !== 'string') {
throw new Error("Property 'email' must be a string!")
}
const isEmail = await _this.nodemailer.validateEmail(emailObj.email)
if (!isEmail) {
throw new Error("Property 'email' must be email format!")
}
if (!emailObj.formMessage || typeof emailObj.formMessage !== 'string') {
throw new Error("Property 'message' must be a string!")
}
if (!emailObj.payloadTitle || typeof emailObj.payloadTitle !== 'string') {
throw new Error("Property 'payloadTitle' must be a string!")
}
// If an email list exists, the email will be sended to that list
// otherwhise will be sended by default to the variable "_this.config.emailUser"
let _to = [_this.config.emailUser]
// Email list is optional
if (emailObj.emailList) {
if (
!Array.isArray(emailObj.emailList) ||
!emailObj.emailList.length > 0
) {
throw new Error("Property 'emailList' must be a array of emails!")
} else {
_to = emailObj.emailList
}
}
console.log(`Trying send message to : ${_to}`)
emailObj.subject = 'Someone wants to share a document with you.'
emailObj.to = _to
await _this.nodemailer.sendEmail(emailObj)
await _this.contactLib.sendEmail(emailObj)
ctx.body = {
success: true
}
} catch (err) {
// ctx.body = {
// success: false
// }
// console.error(`Error: `, err)
// throw err
_this.handleError(ctx, err)
}
}
// DRY error handler
handleError (ctx, err) {
// If an HTTP status is specified by the buisiness logic, use that.
if (err.status) {
if (err.message) {
ctx.throw(err.status, err.message)
} else {
ctx.throw(err.status)
}
} else {
// By default use a 422 error if the HTTP status is not specified.
ctx.throw(422, err.message)
}
}
}
module.exports = Contact
module.exports = ContactController
+178
View File
@@ -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 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',
emailList: ['email@email.com']
}
}
}
await uut.email(ctx)
} catch (err) {
assert(false, 'Unexpected result')
}
})
})
})
+117
View File
@@ -0,0 +1,117 @@
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 trhow 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,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('Users', () => {
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)
})
})
})