diff --git a/test/fixtures/application.js b/test/fixtures/application.js index 6f496dbd..39356a38 100644 --- a/test/fixtures/application.js +++ b/test/fixtures/application.js @@ -7,7 +7,25 @@ const login = 'login'; const password = 'password'; const sessionId = '12892e85-5bd7-4c77-a0c5-a0aecfcbc93a'; -const interfaces = {}; +const expectedErrorMessage = 'Zero division'; + +const interfaces = { + calculator: { + answer(connection, callback) { + callback(null, 42); + }, + divide(connection, divident, divisor, callback) { + if (!divisor) { + callback(new Error(expectedErrorMessage)); + } else { + callback(null, divident / divisor); + } + }, + doNothing(connection, callback) { + callback(null); + } + } +}; const authCallback = ( connection, application, strategy, credentials, callback @@ -42,5 +60,6 @@ module.exports = { login, password, sessionId, - authCallback + authCallback, + expectedErrorMessage }; diff --git a/test/node/connection-call.js b/test/node/connection-call.js new file mode 100644 index 00000000..c6ca5f98 --- /dev/null +++ b/test/node/connection-call.js @@ -0,0 +1,98 @@ +'use strict'; + +const test = require('tap'); + +const jstp = require('../..'); + +const app = require('../fixtures/application'); + +const application = new jstp.Application(app.name, app.interfaces); + +let server; +let client; + +test.beforeEach((done) => { + server = jstp.tcp.createServer(0, [application], app.authCallback); + server.listen(() => { + const port = server.address().port; + client = jstp.tcp.createClient({ host: 'localhost', port }); + done(); + }); +}); + +test.afterEach((done) => { + client.disconnect(); + server.close(); + done(); +}); + +test.test('must perform call with no arguments and no return value', (test) => { + client.connectAndHandshake(app.name, null, null, (error, connection) => { + connection.callMethod('calculator', 'doNothing', [], (error) => { + test.assertNot(error, 'callMethod must not return an error'); + test.end(); + }); + }); +}); + +test.test('must perform call with no arguments and return value', (test) => { + client.connectAndHandshake(app.name, null, null, (error, connection) => { + connection.callMethod('calculator', 'answer', [], (error, result) => { + test.assertNot(error, 'callMethod must not return an error'); + test.equal(result, 42); + test.end(); + }); + }); +}); + +test.test('must perform call with arguments and return value', (test) => { + client.connectAndHandshake(app.name, null, null, (error, connection) => { + connection.callMethod('calculator', 'divide', [20, 10], + (error, result) => { + test.assertNot(error, 'callMethod must not return an error'); + test.equal(result, 2); + test.end(); + } + ); + }); +}); + +test.test('must perform call that returns an error', (test) => { + client.connectAndHandshake(app.name, null, null, (error, connection) => { + connection.callMethod('calculator', 'divide', [10, 0], (error) => { + test.assert(error, 'callMethod must return an error'); + test.equal(error.message, + new jstp.RemoteError(new Error(app.expectedErrorMessage)).message); + test.end(); + }); + }); +}); + +test.test('must return error on call to nonexistent interface', (test) => { + client.connectAndHandshake(app.name, null, null, (error, connection) => { + connection.callMethod( + '__nonexistent_interface__', + '__nonexistent_method__', + [], + (error) => { + test.assert(error, 'callMethod must return an error'); + test.equal(error.code, jstp.ERR_INTERFACE_NOT_FOUND, + 'error must be an ERR_INTERFACE_NOT_FOUND'); + test.end(); + } + ); + }); +}); + +test.test('must return error on call to nonexistent method', (test) => { + client.connectAndHandshake(app.name, null, null, (error, connection) => { + connection.callMethod('calculator', '__nonexistent_method__', [], + (error) => { + test.assert(error, 'callMethod must return an error'); + test.equal(error.code, jstp.ERR_METHOD_NOT_FOUND, + 'error must be an ERR_METHOD_NOT_FOUND'); + test.end(); + } + ); + }); +}); diff --git a/test/unit/connection.test.js b/test/unit/connection.test.js index 2d3bd377..b60a2ba7 100644 --- a/test/unit/connection.test.js +++ b/test/unit/connection.test.js @@ -135,213 +135,6 @@ describe('JSTP Connection', () => { }); }); - describe('call', () => { - testPacketSending('call', (connection, transport) => { - let packetId; - - const sendSpy = chai.spy((data) => { - const packet = jstp.parse(data); - - expect(packet).to.have.all.keys(['call', 'method1']); - expect(packet.call).to.be.an('array'); - - packetId = packet.call[0]; - expect(packet.call[1]).to.equal(constants.TEST_INTERFACE); - }); - - const callback = chai.spy((error, result) => { - expect(error).to.not.exist; - expect(result).to.equal(42); - }); - - transport.on('dataSent', sendSpy); - - connection.callMethod(constants.TEST_INTERFACE, 'method1', [], callback); - expect(sendSpy).to.have.been.called(); - - transport.emitPacket({ - callback: [packetId], - ok: [42] - }); - - expect(callback).to.have.been.called(); - }); - - it('must process a method call with no arguments and result', () => { - emulateHandshakeOnServer(); - - const sendSpy = chai.spy.on(serverTransportMock, 'send'); - - serverTransportMock.emitPacket({ - call: [1, constants.TEST_INTERFACE], - method1: [] - }); - - expect(sendSpy).to.be.called.with(jstp.stringify({ - callback: [1], - ok: [] - })); - - sendSpy.reset(); - }); - - it('must process a method call with arguments and a result', () => { - emulateHandshakeOnServer(); - - const sendSpy = chai.spy.on(serverTransportMock, 'send'); - - serverTransportMock.emitPacket({ - call: [1, constants.TEST_INTERFACE], - method2: [10, 20] - }); - - expect(sendSpy).to.be.called.with(jstp.stringify({ - callback: [1], - ok: [30] - })); - - sendSpy.reset(); - }); - - it('must process a method call that returns an error', () => { - emulateHandshakeOnServer(); - - const sendSpy = chai.spy.on(serverTransportMock, 'send'); - - serverTransportMock.emitPacket({ - call: [1, constants.TEST_INTERFACE], - method3: [] - }); - - expect(sendSpy).to.be.called.with(jstp.stringify({ - callback: [1], - error: [1, 'Error: Example error'] - })); - - sendSpy.reset(); - }); - - it('must process a method that throws an error', () => { - emulateHandshakeOnServer(); - - const sendSpy = chai.spy.on(serverTransportMock, 'send'); - - expect(() => { - serverTransportMock.emitPacket({ - call: [1, constants.TEST_INTERFACE], - method4: [] - }); - }).to.throw(); - - expect(sendSpy).to.be.called.with(jstp.stringify({ - callback: [1], - error: [jstp.ERR_INTERNAL_API_ERROR] - })); - - sendSpy.reset(); - }); - - it('must return an error when an interface does not exist', () => { - emulateHandshakeOnServer(); - - const sendSpy = chai.spy.on(serverTransportMock, 'send'); - - serverTransportMock.emitPacket({ - call: [1, 'dummy interface'], - method1: [] - }); - - expect(sendSpy).to.be.called.with(jstp.stringify({ - callback: [1], - error: [jstp.ERR_INTERFACE_NOT_FOUND] - })); - - sendSpy.reset(); - }); - - it('must return an error when a method does not exist', () => { - emulateHandshakeOnServer(); - - const sendSpy = chai.spy.on(serverTransportMock, 'send'); - - serverTransportMock.emitPacket({ - call: [1, constants.TEST_INTERFACE], - methodThatDoesNotExist: [] - }); - - expect(sendSpy).to.be.called.with(jstp.stringify({ - callback: [1], - error: [jstp.ERR_METHOD_NOT_FOUND] - })); - - sendSpy.reset(); - }); - }); - - describe('callback', () => { - testPacketSending('callback', (connection, transport) => { - const sendSpy = chai.spy((data) => { - const packet = jstp.parse(data); - - expect(packet).to.contain.all.keys(['callback']); - expect(packet).to.contain.any.keys(['ok', 'error']); - - if (packet.ok) { - expect(packet.callback).to.eql([10]); - expect(packet.ok).to.eql([42]); - } else { - expect(packet.callback).to.eql([11]); - expect(packet.error).to.eql([jstp.ERR_METHOD_NOT_FOUND]); - } - }); - - transport.on('dataSent', sendSpy); - - connection.callback(10, null, [42]); - connection.callback(11, new jstp.RemoteError(jstp.ERR_METHOD_NOT_FOUND)); - - expect(sendSpy).to.have.been.called.twice; - }); - - it('must process a callback packet with a result', () => { - const callback = chai.spy((error, result) => { - expect(error).to.not.exist; - expect(result).to.equal('result'); - }); - - performHandshakeFromClient(() => { - clientConnection.callMethod( - constants.TEST_INTERFACE, 'method', [], callback); - - clientTransportMock.emitPacket({ - callback: [1], - ok: ['result'] - }); - - expect(callback).to.be.called(); - }); - }); - - it('must process a callback packet with an error', () => { - const callback = chai.spy((error) => { - expect(error).to.be.an.instanceof(jstp.RemoteError); - expect(error.code).to.equal(jstp.ERR_INTERNAL_API_ERROR); - }); - - performHandshakeFromClient(() => { - clientConnection.callMethod( - constants.TEST_INTERFACE, 'method', [], callback); - - clientTransportMock.emitPacket({ - callback: [1], - error: [jstp.ERR_INTERNAL_API_ERROR] - }); - - expect(callback).to.be.called(); - }); - }); - }); - describe('event', () => { testPacketSending('event', (connection, transport) => { const eventArgs = [ 'value' ];