Refactoring XMPP test

This commit is contained in:
nioc 2019-11-24 16:41:51 +01:00
parent a99008b984
commit 7f3de716c1

View file

@ -2,16 +2,16 @@
process.env.NODE_ENV = 'production' process.env.NODE_ENV = 'production'
const sinon = require('sinon') const sinon = require('sinon')
require('chai').should()
const EventEmitter = require('events').EventEmitter const EventEmitter = require('events').EventEmitter
const mock = require('mock-require') const mock = require('mock-require')
const xml = require('@xmpp/xml') const xml = require('@xmpp/xml')
const jid = require('@xmpp/jid') const jid = require('@xmpp/jid')
describe('XMPP component', () => { describe('XMPP component', () => {
const simpleXmppEvents = new EventEmitter() let logger, config, outgoingStub, outgoingStubRejected, xmppSendStub, xmppCloseStub, simpleXmppEvents, xmpp
let logger, config, outgoingStub, xmppSendStub, xmppCloseStub
before('Setup', (done) => { before('Setup logger and config', (done) => {
// create default logger // create default logger
logger = require('./../lib/logger')() logger = require('./../lib/logger')()
@ -21,6 +21,25 @@ describe('XMPP component', () => {
// update logger with configuration // update logger with configuration
logger.updateConfig(config.logger) logger.updateConfig(config.logger)
// mock logger trace
sinon.stub(logger, 'trace')
done()
})
after('Remove mocks', (done) => {
mock.stopAll()
logger.trace.restore()
done()
})
beforeEach('Setup XMPP client with stub', function () {
simpleXmppEvents = new EventEmitter()
// mock outgoing module
outgoingStub = sinon.stub().resolves()
mock('./../lib/outgoing', outgoingStub)
// mock @xmpp/client module // mock @xmpp/client module
xmppSendStub = sinon.stub().resolves() xmppSendStub = sinon.stub().resolves()
xmppCloseStub = sinon.stub().resolves() xmppCloseStub = sinon.stub().resolves()
@ -38,32 +57,21 @@ describe('XMPP component', () => {
jid: require('@xmpp/jid') jid: require('@xmpp/jid')
}) })
// mock outgoing // reset stubs history
outgoingStub = sinon.stub()
mock('./../lib/outgoing', outgoingStub)
// mock logger trace
sinon.stub(logger, 'trace')
done()
})
after('Remove mocks', (done) => {
mock.stopAll()
logger.trace.restore()
done()
})
beforeEach('Reset outgoing and xmpp stub history', function () {
outgoingStub.resetHistory() outgoingStub.resetHistory()
xmppSendStub.resetHistory() xmppSendStub.resetHistory()
xmppCloseStub.resetHistory() xmppCloseStub.resetHistory()
// call module and emit online event
xmpp = require('./../lib/xmpp')(logger, config)
simpleXmppEvents.emit('online', jid('bot@domain-xmpp.ltd/resource'))
}) })
describe('Connect to XMPP server', () => { describe('Connect to XMPP server', () => {
beforeEach(async () => {
await simpleXmppEvents.emit('status', 'connecting')
})
it('Should connect to XMPP server and join rooms when application start', (done) => { it('Should connect to XMPP server and join rooms when application start', (done) => {
require('./../lib/xmpp')(logger, config)
simpleXmppEvents.emit('online', jid('bot@domain-xmpp.ltd/resource'))
sinon.assert.called(xmppSendStub) sinon.assert.called(xmppSendStub)
// 1 "send" call for presence and n "send" calls for joining rooms // 1 "send" call for presence and n "send" calls for joining rooms
let roomsLength = config.xmpp.rooms.length let roomsLength = config.xmpp.rooms.length
@ -87,42 +95,46 @@ describe('XMPP component', () => {
done() done()
}) })
it('Should trace connection status', (done) => { it('Should trace connection status', (done) => {
simpleXmppEvents.emit('status', 'connecting')
sinon.assert.calledWith(logger.trace, 'Status changed to connecting') sinon.assert.calledWith(logger.trace, 'Status changed to connecting')
done() done()
}) })
}) })
describe('Bot receive a presence stanza from someone', () => { describe('Bot receive a presence stanza from someone', () => {
it('Should not trigger outgoing webhook', (done) => { beforeEach(async () => {
simpleXmppEvents.emit('stanza', xml( await simpleXmppEvents.emit('stanza', xml(
'presence', { 'presence', {
from: 'someone@domain-xmpp.ltd', from: 'someone@domain-xmpp.ltd',
to: 'bot@domain-xmpp.ltd' to: 'bot@domain-xmpp.ltd'
} }
)) ))
})
it('Should not trigger outgoing webhook', (done) => {
sinon.assert.notCalled(outgoingStub) sinon.assert.notCalled(outgoingStub)
done() done()
}) })
}) })
describe('Bot receive an empty message from someone', () => { describe('Bot receive an empty message from someone', () => {
it('Should not trigger outgoing webhook', (done) => { beforeEach(async () => {
simpleXmppEvents.emit('stanza', xml( await simpleXmppEvents.emit('stanza', xml(
'message', { 'message', {
from: 'someone@domain-xmpp.ltd', from: 'someone@domain-xmpp.ltd',
to: 'bot@domain-xmpp.ltd', to: 'bot@domain-xmpp.ltd',
type: 'chat' type: 'chat'
} }
)) ))
})
it('Should not trigger outgoing webhook', (done) => {
sinon.assert.notCalled(outgoingStub) sinon.assert.notCalled(outgoingStub)
done() done()
}) })
}) })
describe('Bot receive a message from someone', () => { describe('Bot receive a message from someone', () => {
it('Should trigger outgoing webhook with valid arguments and send a message delivery receipt', (done) => { beforeEach(async () => {
simpleXmppEvents.emit('stanza', xml( xmppSendStub.resetHistory()
await simpleXmppEvents.emit('stanza', xml(
'message', { 'message', {
from: 'someone@domain-xmpp.ltd', from: 'someone@domain-xmpp.ltd',
to: 'bot@domain-xmpp.ltd', to: 'bot@domain-xmpp.ltd',
@ -138,6 +150,8 @@ describe('XMPP component', () => {
xmlns: 'urn:xmpp:receipts' xmlns: 'urn:xmpp:receipts'
}) })
)) ))
})
it('Should trigger outgoing webhook with valid arguments and send a message delivery receipt', (done) => {
sinon.assert.calledOnce(outgoingStub) sinon.assert.calledOnce(outgoingStub)
const args = outgoingStub.args[0] const args = outgoingStub.args[0]
args.should.have.length(8) args.should.have.length(8)
@ -163,9 +177,55 @@ describe('XMPP component', () => {
}) })
}) })
describe('Bot receive a message from someone but webhook call failed', () => {
beforeEach(async () => {
simpleXmppEvents = new EventEmitter()
outgoingStubRejected = sinon.stub().rejects()
mock('./../lib/outgoing', outgoingStubRejected)
require('./../lib/xmpp')(logger, config)
simpleXmppEvents.emit('online', jid('bot@domain-xmpp.ltd/resource'))
xmppSendStub.resetHistory()
await simpleXmppEvents.emit('stanza', xml(
'message', {
from: 'someone@domain-xmpp.ltd',
to: 'bot@domain-xmpp.ltd',
type: 'chat',
id: 'fcdd3d8c'
},
xml(
'body', {
},
'Is it working?')
))
})
it('Should send a XMPP reply if webhook failed', () => {
sinon.assert.calledOnce(outgoingStubRejected)
const args = outgoingStubRejected.args[0]
args.should.have.length(8)
args[3].should.equal('someone')
args[4].should.equal('someone@domain-xmpp.ltd')
args[5].should.equal('Is it working?')
args[6].should.equal('chat')
args[7].should.equal('w1')
sinon.assert.calledOnce(xmppSendStub)
const receiptStanza = xml(
'message', {
to: 'someone@domain-xmpp.ltd',
type: 'chat'
},
xml(
'body', {
},
'Oops, something went wrong :('
)
)
xmppSendStub.args[0][0].should.deep.equal(receiptStanza)
})
})
describe('Bot receive a message from himself in a room', () => { describe('Bot receive a message from himself in a room', () => {
it('Should not trigger outgoing webhook', (done) => { beforeEach(async () => {
simpleXmppEvents.emit('stanza', xml( await simpleXmppEvents.emit('stanza', xml(
'message', { 'message', {
from: 'roomname@conference.domain-xmpp.ltd/bot', from: 'roomname@conference.domain-xmpp.ltd/bot',
to: 'roomname@conference.domain-xmpp.ltd', to: 'roomname@conference.domain-xmpp.ltd',
@ -176,14 +236,16 @@ describe('XMPP component', () => {
}, },
'This is the message text') 'This is the message text')
)) ))
})
it('Should not trigger outgoing webhook', (done) => {
sinon.assert.notCalled(outgoingStub) sinon.assert.notCalled(outgoingStub)
done() done()
}) })
}) })
describe('Bot receive a message in an unknown room', () => { describe('Bot receive a message in an unknown room', () => {
it('Should not trigger outgoing webhook', (done) => { beforeEach(async () => {
simpleXmppEvents.emit('stanza', xml( await simpleXmppEvents.emit('stanza', xml(
'message', { 'message', {
from: 'unknownroomname@conference.domain-xmpp.ltd/someone', from: 'unknownroomname@conference.domain-xmpp.ltd/someone',
to: 'unknownroomname@conference.domain-xmpp.ltd', to: 'unknownroomname@conference.domain-xmpp.ltd',
@ -194,15 +256,16 @@ describe('XMPP component', () => {
}, },
'This is the message text') 'This is the message text')
)) ))
})
it('Should not trigger outgoing webhook', (done) => {
sinon.assert.notCalled(outgoingStub) sinon.assert.notCalled(outgoingStub)
done() done()
}) })
}) })
describe('Bot receive an old message in a room', () => { describe('Bot receive an old message in a room', () => {
it('Should not trigger outgoing webhook', (done) => { beforeEach(async () => {
simpleXmppEvents.emit('groupchat', 'roomname@conference.domain-xmpp.ltd', 'someone', 'This is the message text', 'stamp') await simpleXmppEvents.emit('stanza', xml(
simpleXmppEvents.emit('stanza', xml(
'message', { 'message', {
from: 'roomname@conference.domain-xmpp.ltd/someone', from: 'roomname@conference.domain-xmpp.ltd/someone',
to: 'roomname@conference.domain-xmpp.ltd', to: 'roomname@conference.domain-xmpp.ltd',
@ -219,14 +282,16 @@ describe('XMPP component', () => {
}, },
'This is the message text') 'This is the message text')
)) ))
})
it('Should not trigger outgoing webhook', (done) => {
sinon.assert.notCalled(outgoingStub) sinon.assert.notCalled(outgoingStub)
done() done()
}) })
}) })
describe('Bot receive a message in a room', () => { describe('Bot receive a message in a room', () => {
it('Should trigger outgoing webhook with valid arguments', (done) => { beforeEach(async () => {
simpleXmppEvents.emit('stanza', xml( await simpleXmppEvents.emit('stanza', xml(
'message', { 'message', {
from: 'roomname@conference.domain-xmpp.ltd/someone', from: 'roomname@conference.domain-xmpp.ltd/someone',
to: 'roomname@conference.domain-xmpp.ltd', to: 'roomname@conference.domain-xmpp.ltd',
@ -237,6 +302,8 @@ describe('XMPP component', () => {
}, },
'This is the message text') 'This is the message text')
)) ))
})
it('Should trigger outgoing webhook with valid arguments', (done) => {
sinon.assert.calledOnce(outgoingStub) sinon.assert.calledOnce(outgoingStub)
const args = outgoingStub.args[0] const args = outgoingStub.args[0]
args.should.have.length(8) args.should.have.length(8)
@ -250,9 +317,11 @@ describe('XMPP component', () => {
}) })
describe('Send a message', () => { describe('Send a message', () => {
beforeEach(async () => {
xmppSendStub.resetHistory()
await xmpp.send('someone@domain-xmpp.ltd', 'This is the message text sent by bot', 'chat')
})
it('Should call xmpp/client.send() with valid stanza', (done) => { it('Should call xmpp/client.send() with valid stanza', (done) => {
const xmpp = require('./../lib/xmpp')(logger, config)
xmpp.send('someone@domain-xmpp.ltd', 'This is the message text sent by bot', 'chat')
sinon.assert.calledOnce(xmppSendStub) sinon.assert.calledOnce(xmppSendStub)
let stanza = xml( let stanza = xml(
'message', { 'message', {
@ -272,29 +341,31 @@ describe('XMPP component', () => {
}) })
describe('Close connection', () => { describe('Close connection', () => {
beforeEach(async () => {
await xmpp.close()
})
it('Should call xmpp/client.stop()', (done) => { it('Should call xmpp/client.stop()', (done) => {
const xmpp = require('./../lib/xmpp')(logger, config)
xmpp.close()
sinon.assert.calledOnce(xmppCloseStub) sinon.assert.calledOnce(xmppCloseStub)
done() done()
}) })
}) })
describe('XMPP server send an error', () => { describe('XMPP server send an error', () => {
before(() => { let errorText
beforeEach(async () => {
sinon.stub(process, 'exit') sinon.stub(process, 'exit')
errorText = 'This the error text'
await simpleXmppEvents.emit('error', new Error(errorText))
}) })
after(() => { afterEach(() => {
process.exit.restore() process.exit.restore()
}) })
it('Should log error and exit with 99 code', (done) => { it('Should log error and exit with 99 code', (done) => {
let error = 'This the error text'
simpleXmppEvents.emit('error', new Error(error))
require('fs').readFile(config.logger.file.path + config.logger.file.filename, 'utf8', (err, data) => { require('fs').readFile(config.logger.file.path + config.logger.file.filename, 'utf8', (err, data) => {
if (err) { if (err) {
throw err throw err
} }
data.should.match(new RegExp('XMPP client encountered following error: ' + error + '\n$')) data.should.match(new RegExp('XMPP client encountered following error: ' + errorText + '\n$'))
sinon.assert.calledWith(process.exit, 99) sinon.assert.calledWith(process.exit, 99)
done() done()
}) })