UNPKG

remjson

Version:

JSON-RPC 1.0/2.0 compliant server and client

323 lines (255 loc) 9.53 kB
var should = require('should'); var remjson = require(__dirname + '/..'); var support = require(__dirname + '/support'); var common = support.common; describe('RemJson.Client', function () { it('should return an instance without using "new"', function () { var client = remjson.client(remjson.server()); client.should.be.instanceof(remjson.client); }); it('should return a raw request if not passed a server', function () { var client = new remjson.Client(); var request = client.request('add', [11, 9]); should.exist(request); request.should.have.property('method', 'add'); request.should.have.property('id'); request.should.have.property('params', [11, 9]); request.should.have.property('jsonrpc', '2.0'); }); it('should return a raw request without a version 2 jsonrpc field if client is version 1', function () { var client = new remjson.Client({version: 1}); var request = client.request('add', [11, 9]); should.exist(request); request.should.not.have.property('jsonrpc'); }); describe('instance', function () { var server = remjson.server(support.server.methods, support.server.options); var client = remjson.client(server, support.server.options); describe('common tests', common(client)); it('should not talk to a version 2.0 server when client is 1.0', function (done) { client.options.version = 1; // change option client.request('add', [11, 9], function (err, response) { if (err) throw err; should.not.exist(response.result); response.should.containDeep({error: {code: -32600}}); // "Request Error" client.options.version = 2; // reset option done(); }); }); it('should return the response as received if given a callback with length 2', function (done) { client.request('add', [11, 12], function (err, response) { if (err) throw err; arguments.length.should.equal(2); response.should.containDeep({result: 11 + 12}); done(); }); }); it('should split out a response when given a length 3 callback', function (done) { client.request('add', [4, 3], function (err, error, result) { if (err) throw err; should(error).not.exist; should(result).exist; result.should.equal(4 + 3); done(); }); }); it('should out an error when given a length 3 callback', function (done) { client.request('error', [], function (err, error, result) { if (err) throw err; should(error).exist; error.should.have.property('code', -1000); should(result).not.exist; done(); }); }); describe('_parseResponse', function () { it('should correctly split an ambiguous error response', function (done) { var response = { error: null, // should not be here result: 5 }; client._parseResponse(null, response, function (err, error, response) { if (err) throw err; should(error).equal(null); should(response).equal(5); done(); }); }); it('should send both error and response as is if ambiguous', function (done) { var response = { error: {code: 10000}, // missing message result: 2 // should not be here }; client._parseResponse(null, response, function (err, error, response) { if (err) throw err; should(error).have.property('code', 10000); should(response).equal(2); done(); }); }); }); it('should support specifying a request id generator', function (done) { var ordinal = 0; client.options.generator = function (request) { return ordinal++; }; client.request('add', [9, 2], function (err, response) { if (err) throw err; response.should.containDeep({ id: 0, result: 9 + 2 }); ordinal.should.equal(1); delete client.options.generator; // remove option done(); }); }); it('should emit "request" when a request is dispatched', function (done) { var hasFired = false; client.once('request', function (request) { hasFired = true; request.should.containDeep({params: [6, 9]}); }); client.request('add', [6, 9], function (err) { if (err) return done(err); hasFired.should.be.ok; done(); }); }); it('should emit "response" when a response is received', function (done) { var hasFired = false; client.once('response', function (request, response) { hasFired = true; request.should.containDeep({params: [5, 8]}); response.should.containDeep({result: 5 + 8}); }); client.request('add', [5, 8], function (err) { if (err) throw err; hasFired.should.be.ok; done(); }); }); it('should be able to execute a notification request', function (done) { client.request('add', [3, 4], null, function (err, response) { if (err) throw err; arguments.length.should.equal(0); should.not.exist(response); done(); }); }); it('should be able to execute a named-parameter request', function (done) { client.request('add', {a: 5, b: -2}, function (err, error, response) { if (err) throw err; should.not.exist(error); response.should.equal(5 - 2); done(); }); }); describe('batch requests', function () { it('should execute a simple batch', function (done) { var batch = [ client.request('add', [1, 1]), client.request('add', [-1, -1]), client.request('add', [16, 4]), ]; client.request(batch, function (err, responses) { if (err) throw err; responses.should.be.instanceof(Array).and.have.length(3); responses.forEach(function (response, index) { var params = batch[index].params; should.exist(response.result); response.result.should.equal(params[0] + params[1]); }); done(); }); }); it('should propagate errors', function (done) { var batch = [ client.request('add', [5, 10]), client.request('add', [6, 2], null), // notification client.request('does_not_exist', []) ]; client.request(batch, function (err, responses) { if (err) throw err; responses.should.be.instanceof(Array).and.have.length(2); responses[0].should.containDeep({ result: 5 + 10 }); responses[1].should.containDeep({ error: {code: -32601} // "Method not found" }); done(); }); }); it('should split errors and responses when given a length 3 callback', function (done) { var batch = [ client.request('add', [12, 13]), client.request('add', [6, 2], null), // notification client.request('does_not_exist', []) ]; client.request(batch, function (err, errors, successes) { if (err) throw err; errors.should.be.instanceof(Array).and.have.length(1); successes.should.be.instanceof(Array).and.have.length(1); errors[0].should.containDeep({ error: {code: -32601} // Method not found }); successes[0].should.containDeep({ result: 12 + 13 }); done(); }); }); it('should not callback anything when given only notifications', function (done) { var batch = [ client.request('add', [5, 2], null), client.request('add', [7, 6], null) ]; client.request(batch, function (err, response) { if (err) throw err; should.not.exist(response); done(); }); }); }); }); describe('request with options', function () { function OptionalClient() { remjson.Client.apply(this, arguments); } require('util').inherits(OptionalClient, remjson.Client); OptionalClient.prototype._request = function (request, options, callback) { var self = this; options = options || {}; var timeout = options.timeout; var expired; // serializes the request as a JSON string so that we get a copy and can run the replacer as intended remjson.Utils.JSON.stringify(request, this.options, function (err, message) { if (err) throw err; self.server.call(message, function (error, success) { if (!expired) callback(null, error || success); }); if (timeout) { setTimeout(function () { callback(new Error('expired')); expired = true; }, timeout); } }); }; var server = remjson.server(support.server.methods, support.server.options); var client = new OptionalClient(server, support.server.options); it('should support request options', function (done) { var time = Date.now(); client.request('delay', [1000], {timeout: 500}, function (err, result) { should.exist(err); should.exist(err.message); err.message.should.eql('expired'); time = Date.now() - time; time.should.within(500, 510); should.not.exists(result); done(); }); }); }); });