mirror of
https://github.com/nioc/xmpp-bot.git
synced 2024-12-04 22:23:36 +01:00
Add webhook and outgoing and refactoring tests
This commit is contained in:
parent
42e0d1ca7b
commit
84c2ce90aa
11 changed files with 455 additions and 125 deletions
|
@ -7,7 +7,7 @@ node_js:
|
||||||
- lts/*
|
- lts/*
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
- cp test/config.json config.json
|
- cp test/config.json lib/config/config.json
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- npm run lint
|
- npm run lint
|
||||||
|
|
|
@ -9,10 +9,11 @@
|
||||||
* @license AGPL-3.0+
|
* @license AGPL-3.0+
|
||||||
*/
|
*/
|
||||||
|
|
||||||
module.exports = (logger, config, xmpp, user, destination, message, sendToGroup, code) => {
|
module.exports = (logger, config, xmpp, user, destination, message, sendToGroup, code, callback = () => {}) => {
|
||||||
let webhook = config.getOutgoingWebhook(code)
|
let webhook = config.getOutgoingWebhook(code)
|
||||||
if (!webhook) {
|
if (!webhook) {
|
||||||
logger.warn(`There is no webhook with code "${code}"`)
|
logger.warn(`There is no webhook with code "${code}"`)
|
||||||
|
callback(new Error(`There is no webhook with code "${code}"`), null, null)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const request = require('request')
|
const request = require('request')
|
||||||
|
@ -71,14 +72,20 @@ module.exports = (logger, config, xmpp, user, destination, message, sendToGroup,
|
||||||
logger.debug('statusCode:', response && response.statusCode)
|
logger.debug('statusCode:', response && response.statusCode)
|
||||||
if (error) {
|
if (error) {
|
||||||
logger.error('HTTP error:', error)
|
logger.error('HTTP error:', error)
|
||||||
|
callback(new Error('HTTP error:', error), null, null)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (response.statusCode === 200) {
|
if (response.statusCode === 200) {
|
||||||
logger.trace('Response:', body)
|
logger.trace('Response:', body)
|
||||||
if (body && 'reply' in body === true) {
|
if (body && typeof (body) === 'object' && 'reply' in body === true) {
|
||||||
logger.debug(`There is a reply to send back in chat ${destination}: ${body.reply}`)
|
logger.debug(`There is a reply to send back in chat ${destination}: ${body.reply}`)
|
||||||
xmpp.send(destination, body.reply, sendToGroup)
|
xmpp.send(destination, body.reply, sendToGroup)
|
||||||
|
callback(null, `Message sent. There is a reply to send back in chat ${destination}: ${body.reply}`, null)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
callback(null, 'Message sent', null)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
callback(new Error('HTTP error:', response.statusCode), null, null)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,11 +13,7 @@ logger.updateConfig(config.logger)
|
||||||
const xmpp = require('./xmpp')(logger, config)
|
const xmpp = require('./xmpp')(logger, config)
|
||||||
|
|
||||||
// load webhook module
|
// load webhook module
|
||||||
const webhookListener = require('./webhook')(logger, config, xmpp)
|
require('./webhook')(logger, config, xmpp)
|
||||||
|
|
||||||
// handle error and process ending
|
// handle error and process ending
|
||||||
require('./error')(logger, xmpp)
|
require('./error')(logger, xmpp)
|
||||||
|
|
||||||
exports.close = () => {
|
|
||||||
webhookListener.close()
|
|
||||||
}
|
|
||||||
|
|
37
package-lock.json
generated
37
package-lock.json
generated
|
@ -3919,6 +3919,37 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"nock": {
|
||||||
|
"version": "11.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/nock/-/nock-11.4.0.tgz",
|
||||||
|
"integrity": "sha512-UrVEbEAvhyDoUttrS0fv3znhZ5nEJvlxqgmrC6Gb2Mf9cFci65RMK17e6EjDDQB57g5iwZw1TFnVvyeL0eUlhQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"chai": "^4.1.2",
|
||||||
|
"debug": "^4.1.0",
|
||||||
|
"json-stringify-safe": "^5.0.1",
|
||||||
|
"lodash": "^4.17.13",
|
||||||
|
"mkdirp": "^0.5.0",
|
||||||
|
"propagate": "^2.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"debug": {
|
||||||
|
"version": "4.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
|
||||||
|
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ms": "^2.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ms": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node-cleanup": {
|
"node-cleanup": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/node-cleanup/-/node-cleanup-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/node-cleanup/-/node-cleanup-2.1.2.tgz",
|
||||||
|
@ -4544,6 +4575,12 @@
|
||||||
"integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
|
"integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"propagate": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"proxy-addr": {
|
"proxy-addr": {
|
||||||
"version": "2.0.5",
|
"version": "2.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz",
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
"eslint-plugin-standard": "^4.0.1",
|
"eslint-plugin-standard": "^4.0.1",
|
||||||
"mocha": "^6.2.1",
|
"mocha": "^6.2.1",
|
||||||
"mock-require": "^3.0.3",
|
"mock-require": "^3.0.3",
|
||||||
|
"nock": "^11.4.0",
|
||||||
"nodemon": "^1.19.3",
|
"nodemon": "^1.19.3",
|
||||||
"nyc": "^14.1.1",
|
"nyc": "^14.1.1",
|
||||||
"sinon": "^7.5.0"
|
"sinon": "^7.5.0"
|
||||||
|
|
|
@ -86,13 +86,43 @@
|
||||||
"outgoingWebhooks": [
|
"outgoingWebhooks": [
|
||||||
{
|
{
|
||||||
"code": "w1",
|
"code": "w1",
|
||||||
"url": "https://domain.ltd:port/path/resource?parameter1=value1",
|
"url": "https://domain.ltd:port/path/resource",
|
||||||
"strictSSL": true,
|
"strictSSL": true,
|
||||||
"contentType": "application/json",
|
"contentType": "application/json",
|
||||||
"authMethod": "basic",
|
"authMethod": "basic",
|
||||||
"user": "user3",
|
"user": "user3",
|
||||||
"password": "3pass",
|
"password": "3pass",
|
||||||
"bearer": null
|
"bearer": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": "w2",
|
||||||
|
"url": "https://domain.ltd:port/path/resource",
|
||||||
|
"strictSSL": true,
|
||||||
|
"contentType": "application/x-www-form-urlencoded",
|
||||||
|
"authMethod": "bearer",
|
||||||
|
"user": null,
|
||||||
|
"password": null,
|
||||||
|
"bearer": "abcdefgh"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": "w3",
|
||||||
|
"url": "https://domain.ltd:port/path/protectedresource",
|
||||||
|
"strictSSL": true,
|
||||||
|
"contentType": "application/json",
|
||||||
|
"authMethod": null,
|
||||||
|
"user": null,
|
||||||
|
"password": null,
|
||||||
|
"bearer": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": "w4",
|
||||||
|
"url": "https://domain.ltd:port/path/errorresource",
|
||||||
|
"strictSSL": true,
|
||||||
|
"contentType": "application/json",
|
||||||
|
"authMethod": null,
|
||||||
|
"user": null,
|
||||||
|
"password": null,
|
||||||
|
"bearer": null
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
121
test/outgoing.js
Normal file
121
test/outgoing.js
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
'use strict'
|
||||||
|
/* eslint-disable handle-callback-err */
|
||||||
|
process.env.NODE_ENV = 'production'
|
||||||
|
|
||||||
|
const sinon = require('sinon')
|
||||||
|
const should = require('chai').should()
|
||||||
|
const nock = require('nock')
|
||||||
|
const Outgoing = require('./../lib/outgoing')
|
||||||
|
require('chai').should()
|
||||||
|
|
||||||
|
describe('Outgoing webhook component', () => {
|
||||||
|
let logger, config, xmppSendStub, xmpp, scope, scopeUnauthorized, scopeWithError, reqSpy
|
||||||
|
|
||||||
|
before('Setup', () => {
|
||||||
|
// create default logger
|
||||||
|
logger = require('./../lib/logger')()
|
||||||
|
|
||||||
|
// get configuration
|
||||||
|
config = require('./../lib/config')(logger, './test/config.json')
|
||||||
|
|
||||||
|
// update logger with configuration
|
||||||
|
logger.updateConfig(config.logger)
|
||||||
|
|
||||||
|
// mock xmpp component
|
||||||
|
xmppSendStub = sinon.stub()
|
||||||
|
xmpp = {
|
||||||
|
send: xmppSendStub
|
||||||
|
}
|
||||||
|
|
||||||
|
// spy nock requests
|
||||||
|
reqSpy = sinon.spy()
|
||||||
|
})
|
||||||
|
|
||||||
|
beforeEach('Reset XMPP stub history', function (done) {
|
||||||
|
xmppSendStub.resetHistory()
|
||||||
|
reqSpy.resetHistory()
|
||||||
|
|
||||||
|
// mock remote server
|
||||||
|
scope = nock('https://domain.ltd:port')
|
||||||
|
.post('/path/resource')
|
||||||
|
.reply(200, { reply: 'This is a reply' })
|
||||||
|
scope.on('request', reqSpy)
|
||||||
|
scopeUnauthorized = nock('https://domain.ltd:port')
|
||||||
|
.post('/path/protectedresource')
|
||||||
|
.reply(401, {})
|
||||||
|
scopeUnauthorized.on('request', reqSpy)
|
||||||
|
scopeWithError = nock('https://domain.ltd:port')
|
||||||
|
.post('/path/errorresource')
|
||||||
|
.replyWithError('')
|
||||||
|
scopeWithError.on('request', reqSpy)
|
||||||
|
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Unkwnow outgoing webhook', () => {
|
||||||
|
it('Should not execute request', (done) => {
|
||||||
|
Outgoing(logger, config, xmpp, 'user', 'destination', 'message', true, 'code', (error, response, body) => {
|
||||||
|
should.not.equal(error, null)
|
||||||
|
sinon.assert.notCalled(reqSpy)
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('POST with basic authorization and JSON content-type and reply message to XMPP', () => {
|
||||||
|
it('Should send basic authentication and JSON content-type in header and send an XMPP message', (done) => {
|
||||||
|
Outgoing(logger, config, xmpp, 'user', 'destination', 'This a first message', true, 'w1', (error, response, body) => {
|
||||||
|
should.equal(error, null)
|
||||||
|
sinon.assert.calledOnce(reqSpy)
|
||||||
|
const req = reqSpy.args[0][0]
|
||||||
|
const bodyReq = JSON.parse(reqSpy.args[0][2])
|
||||||
|
req.headers.authorization.should.equal('Basic dXNlcjM6M3Bhc3M=')
|
||||||
|
req.headers['content-type'].should.equal('application/json')
|
||||||
|
bodyReq.from.should.equal('user')
|
||||||
|
bodyReq.channel.should.equal('destination')
|
||||||
|
bodyReq.message.should.equal('This a first message')
|
||||||
|
sinon.assert.calledOnce(xmppSendStub)
|
||||||
|
const xmppSendArgs = xmppSendStub.args[0]
|
||||||
|
xmppSendArgs[0].should.equal('destination')
|
||||||
|
xmppSendArgs[1].should.equal('This is a reply')
|
||||||
|
xmppSendArgs[2].should.equal(true)
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('POST with bearer authorization and JSON content-type', () => {
|
||||||
|
it('Should send basic authentication in header', (done) => {
|
||||||
|
Outgoing(logger, config, xmpp, 'user', 'destination', 'This a second message', true, 'w2', (error, response, body) => {
|
||||||
|
should.equal(error, null)
|
||||||
|
sinon.assert.calledOnce(reqSpy)
|
||||||
|
const req = reqSpy.args[0][0]
|
||||||
|
const bodyReq = decodeURIComponent(reqSpy.args[0][2])
|
||||||
|
req.headers.authorization.should.equal('Bearer abcdefgh')
|
||||||
|
req.headers['content-type'].should.equal('application/x-www-form-urlencoded')
|
||||||
|
bodyReq.should.equal('from=user&message=This a second message&channel=destination')
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('POST without authorization', () => {
|
||||||
|
it('Should not send authorization in header and handle 401', (done) => {
|
||||||
|
Outgoing(logger, config, xmpp, 'user', 'destination', 'This a second message', true, 'w3', (error, response, body) => {
|
||||||
|
should.not.equal(error, null)
|
||||||
|
sinon.assert.calledOnce(reqSpy)
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('POST with HTTP error', () => {
|
||||||
|
it('Should handle error', (done) => {
|
||||||
|
Outgoing(logger, config, xmpp, 'user', 'destination', 'This a second message', true, 'w4', (error, response, body) => {
|
||||||
|
should.not.equal(error, null)
|
||||||
|
sinon.assert.calledOnce(reqSpy)
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
44
test/server.js
Normal file
44
test/server.js
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
'use strict'
|
||||||
|
process.env.NODE_ENV = 'production'
|
||||||
|
require('chai').should()
|
||||||
|
const mock = require('mock-require')
|
||||||
|
const sinon = require('sinon')
|
||||||
|
|
||||||
|
describe('Server', () => {
|
||||||
|
let xmppStub, webhookStub
|
||||||
|
|
||||||
|
before('Setup', (done) => {
|
||||||
|
// mock XMPP component
|
||||||
|
xmppStub = sinon.stub()
|
||||||
|
webhookStub = sinon.stub()
|
||||||
|
mock('./../lib/xmpp', () => {
|
||||||
|
let xmpp = {}
|
||||||
|
xmppStub()
|
||||||
|
return xmpp
|
||||||
|
})
|
||||||
|
|
||||||
|
// mock webhook component
|
||||||
|
mock('./../lib/webhook', webhookStub)
|
||||||
|
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
|
||||||
|
after('Remove mock', () => {
|
||||||
|
mock.stopAll()
|
||||||
|
})
|
||||||
|
|
||||||
|
beforeEach('Reset stub', (done) => {
|
||||||
|
xmppStub.resetHistory()
|
||||||
|
webhookStub.resetHistory()
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Start server', () => {
|
||||||
|
it('Should call XMPP and webhook components', (done) => {
|
||||||
|
require('../lib/server')
|
||||||
|
sinon.assert.calledOnce(xmppStub)
|
||||||
|
sinon.assert.calledOnce(webhookStub)
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
|
@ -1,91 +0,0 @@
|
||||||
/* eslint-disable handle-callback-err */
|
|
||||||
/* eslint-disable no-undef */
|
|
||||||
process.env.NODE_ENV = 'production'
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
const should = require('chai').should()
|
|
||||||
let request = require('request')
|
|
||||||
let server = require('../lib/server')
|
|
||||||
const baseUrl = 'http://localhost:8000/'
|
|
||||||
|
|
||||||
after((done) => {
|
|
||||||
server.close()
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('Webhook', () => {
|
|
||||||
options = {
|
|
||||||
method: 'POST',
|
|
||||||
url: baseUrl + 'webhooks/',
|
|
||||||
auth: {
|
|
||||||
user: 'login1',
|
|
||||||
pass: '1pass'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
describe('POST unauthorized', () => {
|
|
||||||
it('Should return 401', (done) => {
|
|
||||||
request.post(baseUrl, (error, response, body) => {
|
|
||||||
response.statusCode.should.equal(401)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
describe('GET', () => {
|
|
||||||
it('Should return 405', (done) => {
|
|
||||||
request.get(baseUrl, { auth: options.auth }, (error, response, body) => {
|
|
||||||
response.statusCode.should.equal(405)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
describe('POST unknown webhook', () => {
|
|
||||||
it('Should return 404', (done) => {
|
|
||||||
request(options, (error, response, body) => {
|
|
||||||
response.statusCode.should.equal(404)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
describe('POST dummy webhook', () => {
|
|
||||||
it('Should return 204', (done) => {
|
|
||||||
options.url = baseUrl + 'webhooks/dummy'
|
|
||||||
request(options, (error, response, body) => {
|
|
||||||
response.statusCode.should.equal(204)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
describe('POST missing destination webhook', () => {
|
|
||||||
it('Should return 400 and error detail', (done) => {
|
|
||||||
options.url = baseUrl + 'webhooks/w1'
|
|
||||||
request(options, (error, response, body) => {
|
|
||||||
response.statusCode.should.equal(400)
|
|
||||||
response.body.should.equal('Destination not found')
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
describe('POST missing message webhook', () => {
|
|
||||||
it('Should return 400 and error detail', (done) => {
|
|
||||||
options.json = {
|
|
||||||
destination: 'destination'
|
|
||||||
}
|
|
||||||
request(options, (error, response, body) => {
|
|
||||||
response.statusCode.should.equal(400)
|
|
||||||
response.body.should.equal('Message not found')
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
describe('POST valid webhook', () => {
|
|
||||||
it('Should return 200', (done) => {
|
|
||||||
options.json = {
|
|
||||||
destination: 'destination',
|
|
||||||
message: 'message'
|
|
||||||
}
|
|
||||||
request(options, (error, response, body) => {
|
|
||||||
response.statusCode.should.equal(200)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
173
test/webhook.js
Normal file
173
test/webhook.js
Normal file
|
@ -0,0 +1,173 @@
|
||||||
|
'use strict'
|
||||||
|
/* eslint-disable handle-callback-err */
|
||||||
|
process.env.NODE_ENV = 'production'
|
||||||
|
|
||||||
|
require('chai').should()
|
||||||
|
const sinon = require('sinon')
|
||||||
|
const fs = require('fs')
|
||||||
|
const request = require('request')
|
||||||
|
|
||||||
|
describe('Webhook component', () => {
|
||||||
|
let logger, config, xmppSendStub, xmpp, baseUrl, webhook, logFile, options
|
||||||
|
|
||||||
|
before('Setup', () => {
|
||||||
|
// create default logger
|
||||||
|
logger = require('./../lib/logger')()
|
||||||
|
|
||||||
|
// get configuration
|
||||||
|
config = require('./../lib/config')(logger, './test/config.json')
|
||||||
|
|
||||||
|
// update logger with configuration
|
||||||
|
logger.updateConfig(config.logger)
|
||||||
|
|
||||||
|
// mock xmpp component
|
||||||
|
xmppSendStub = sinon.stub()
|
||||||
|
xmpp = {
|
||||||
|
send: xmppSendStub
|
||||||
|
}
|
||||||
|
|
||||||
|
// configure webhook
|
||||||
|
baseUrl = 'http://localhost:' + config.listener.port
|
||||||
|
webhook = require('./../lib/webhook')(logger, config, xmpp)
|
||||||
|
logFile = config.listener.log.path + config.listener.log.filename
|
||||||
|
})
|
||||||
|
|
||||||
|
beforeEach('Reset XMPP stub history and request option', function () {
|
||||||
|
xmppSendStub.resetHistory()
|
||||||
|
options = {
|
||||||
|
method: 'POST',
|
||||||
|
url: baseUrl + config.listener.path + '/',
|
||||||
|
auth: {
|
||||||
|
user: 'login1',
|
||||||
|
pass: '1pass'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
after('Close listener', (done) => {
|
||||||
|
webhook.close()
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('POST without authorization', () => {
|
||||||
|
it('Should return 401 and be logged', (done) => {
|
||||||
|
request.post(baseUrl, (error, response, body) => {
|
||||||
|
response.statusCode.should.equal(401)
|
||||||
|
fs.readFile(logFile, 'utf8', (err, data) => {
|
||||||
|
if (err) {
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
data.should.match(new RegExp('"POST / HTTP/1.1" 401 '))
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Wrong method (GET)', () => {
|
||||||
|
it('Should return 405', (done) => {
|
||||||
|
request.get(baseUrl, { auth: options.auth }, (error, response, body) => {
|
||||||
|
response.statusCode.should.equal(405)
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('POST unknown webhook', () => {
|
||||||
|
it('Should return 404 and be logged', (done) => {
|
||||||
|
options.url += 'unknown'
|
||||||
|
request(options, (error, response, body) => {
|
||||||
|
response.statusCode.should.equal(404)
|
||||||
|
fs.readFile(logFile, 'utf8', (err, data) => {
|
||||||
|
if (err) {
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
data.should.match(new RegExp('"POST ' + config.listener.path + '/unknown HTTP/1.1" 404 '))
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('POST dummy webhook', () => {
|
||||||
|
it('Should return 204', (done) => {
|
||||||
|
options.url += 'dummy'
|
||||||
|
request(options, (error, response, body) => {
|
||||||
|
response.statusCode.should.equal(204)
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('POST missing destination webhook', () => {
|
||||||
|
it('Should return 400 and error detail', (done) => {
|
||||||
|
options.url += 'w1'
|
||||||
|
request(options, (error, response, body) => {
|
||||||
|
response.statusCode.should.equal(400)
|
||||||
|
response.body.should.equal('Destination not found')
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('POST missing message webhook', () => {
|
||||||
|
it('Should return 400 and error detail', (done) => {
|
||||||
|
options.json = {
|
||||||
|
destination: 'destination'
|
||||||
|
}
|
||||||
|
options.url += 'w1'
|
||||||
|
request(options, (error, response, body) => {
|
||||||
|
response.statusCode.should.equal(400)
|
||||||
|
response.body.should.equal('Message not found')
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('POST valid webhook (send message)', () => {
|
||||||
|
it('Should return 200 and send XMPP message', (done) => {
|
||||||
|
options.json = {
|
||||||
|
destination: 'destination',
|
||||||
|
message: 'This is a message'
|
||||||
|
}
|
||||||
|
options.url += 'w1'
|
||||||
|
request(options, (error, response, body) => {
|
||||||
|
response.statusCode.should.equal(200)
|
||||||
|
sinon.assert.calledOnce(xmppSendStub)
|
||||||
|
const args = xmppSendStub.args[0]
|
||||||
|
args.should.have.length(3)
|
||||||
|
args[0].should.equal(options.json.destination)
|
||||||
|
args[1].should.equal(options.json.message)
|
||||||
|
args[2].should.equal(false)
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('POST valid webhook (send template)', () => {
|
||||||
|
it('Should return 200 and send XMPP message', (done) => {
|
||||||
|
options.json = {
|
||||||
|
title: 'This is a title',
|
||||||
|
message: 'This is a message',
|
||||||
|
evalMatches: [
|
||||||
|
{
|
||||||
|
metric: 'metric',
|
||||||
|
value: 'value'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
imageUrl: 'https://domain.ltd:port/path/image'
|
||||||
|
}
|
||||||
|
options.url += 'grafana'
|
||||||
|
request(options, (error, response, body) => {
|
||||||
|
response.statusCode.should.equal(200)
|
||||||
|
sinon.assert.calledOnce(xmppSendStub)
|
||||||
|
const args = xmppSendStub.args[0]
|
||||||
|
args.should.have.length(3)
|
||||||
|
args[0].should.equal('grafana@conference.domain-xmpp.ltd')
|
||||||
|
args[1].should.equal('This is a title\r\nThis is a message\r\nmetric: value\r\nhttps://domain.ltd:port/path/image')
|
||||||
|
args[2].should.equal(true)
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
62
test/xmpp.js
62
test/xmpp.js
|
@ -1,3 +1,4 @@
|
||||||
|
'use strict'
|
||||||
process.env.NODE_ENV = 'production'
|
process.env.NODE_ENV = 'production'
|
||||||
|
|
||||||
const should = require('chai').should()
|
const should = require('chai').should()
|
||||||
|
@ -5,32 +6,43 @@ const sinon = require('sinon')
|
||||||
const EventEmitter = require('events').EventEmitter
|
const EventEmitter = require('events').EventEmitter
|
||||||
const mock = require('mock-require')
|
const mock = require('mock-require')
|
||||||
|
|
||||||
// create default logger
|
|
||||||
let logger = require('./../lib/logger')()
|
|
||||||
|
|
||||||
// get configuration
|
|
||||||
let config = require('./../lib/config')(logger, './test/config.json')
|
|
||||||
|
|
||||||
// update logger with configuration
|
|
||||||
logger.updateConfig(config.logger)
|
|
||||||
|
|
||||||
// mock simple-xmpp module
|
|
||||||
const simpleXmppEvents = new EventEmitter()
|
|
||||||
let xmppJoinStub = sinon.stub()
|
|
||||||
mock('simple-xmpp', {
|
|
||||||
connect: () => {},
|
|
||||||
join: xmppJoinStub,
|
|
||||||
on: (eventName, callback) => {
|
|
||||||
simpleXmppEvents.on(eventName, callback)
|
|
||||||
},
|
|
||||||
getRoster: () => {}
|
|
||||||
})
|
|
||||||
|
|
||||||
// mock outgoing
|
|
||||||
let outgoingStub = sinon.stub()
|
|
||||||
mock('./../lib/outgoing', outgoingStub)
|
|
||||||
|
|
||||||
describe('XMPP component', () => {
|
describe('XMPP component', () => {
|
||||||
|
const simpleXmppEvents = new EventEmitter()
|
||||||
|
let logger, config, outgoingStub, xmppJoinStub
|
||||||
|
|
||||||
|
before('Setup', (done) => {
|
||||||
|
// create default logger
|
||||||
|
logger = require('./../lib/logger')()
|
||||||
|
|
||||||
|
// get configuration
|
||||||
|
config = require('./../lib/config')(logger, './test/config.json')
|
||||||
|
|
||||||
|
// update logger with configuration
|
||||||
|
logger.updateConfig(config.logger)
|
||||||
|
|
||||||
|
// mock simple-xmpp module
|
||||||
|
xmppJoinStub = sinon.stub()
|
||||||
|
mock('simple-xmpp', {
|
||||||
|
connect: () => {},
|
||||||
|
join: xmppJoinStub,
|
||||||
|
on: (eventName, callback) => {
|
||||||
|
simpleXmppEvents.on(eventName, callback)
|
||||||
|
},
|
||||||
|
getRoster: () => {}
|
||||||
|
})
|
||||||
|
|
||||||
|
// mock outgoing
|
||||||
|
outgoingStub = sinon.stub()
|
||||||
|
mock('./../lib/outgoing', outgoingStub)
|
||||||
|
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
|
||||||
|
after('Remove mocks', (done) => {
|
||||||
|
mock.stopAll()
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
|
||||||
beforeEach('Reset outgoing stub history', function () {
|
beforeEach('Reset outgoing stub history', function () {
|
||||||
outgoingStub.resetHistory()
|
outgoingStub.resetHistory()
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in a new issue