blob: 2a42dd5db56d33f887db56299ec63a2877a20986 [file] [log] [blame]
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
'use strict';
var assert = require('assert');
var surface_client = require('../src/client.js');
var ProtoBuf = require('protobufjs');
var grpc = require('..');
var math_proto = ProtoBuf.loadProtoFile(__dirname +
'/../../proto/math/math.proto');
var mathService = math_proto.lookup('math.Math');
var _ = require('lodash');
/**
* This is used for testing functions with multiple asynchronous calls that
* can happen in different orders. This should be passed the number of async
* function invocations that can occur last, and each of those should call this
* function's return value
* @param {function()} done The function that should be called when a test is
* complete.
* @param {number} count The number of calls to the resulting function if the
* test passes.
* @return {function()} The function that should be called at the end of each
* sequence of asynchronous functions.
*/
function multiDone(done, count) {
return function() {
count -= 1;
if (count <= 0) {
done();
}
};
}
var server_insecure_creds = grpc.ServerCredentials.createInsecure();
describe('File loader', function() {
it('Should load a proto file by default', function() {
assert.doesNotThrow(function() {
grpc.load(__dirname + '/test_service.proto');
});
});
it('Should load a proto file with the proto format', function() {
assert.doesNotThrow(function() {
grpc.load(__dirname + '/test_service.proto', 'proto');
});
});
it('Should load a json file with the json format', function() {
assert.doesNotThrow(function() {
grpc.load(__dirname + '/test_service.json', 'json');
});
});
it('Should fail to load a file with an unknown format', function() {
assert.throws(function() {
grpc.load(__dirname + '/test_service.proto', 'fake_format');
});
});
});
describe('surface Server', function() {
var server;
beforeEach(function() {
server = new grpc.Server();
});
afterEach(function() {
server.forceShutdown();
});
it('should error if started twice', function() {
server.start();
assert.throws(function() {
server.start();
});
});
it('should error if a port is bound after the server starts', function() {
server.start();
assert.throws(function() {
server.bind('localhost:0', grpc.ServerCredentials.createInsecure());
});
});
it('should successfully shutdown if tryShutdown is called', function(done) {
server.start();
server.tryShutdown(done);
});
});
describe('Server.prototype.addProtoService', function() {
var server;
var dummyImpls = {
'div': function() {},
'divMany': function() {},
'fib': function() {},
'sum': function() {}
};
beforeEach(function() {
server = new grpc.Server();
});
afterEach(function() {
server.forceShutdown();
});
it('Should succeed with a single service', function() {
assert.doesNotThrow(function() {
server.addProtoService(mathService, dummyImpls);
});
});
it('Should fail with conflicting method names', function() {
server.addProtoService(mathService, dummyImpls);
assert.throws(function() {
server.addProtoService(mathService, dummyImpls);
});
});
it('Should fail if the server has been started', function() {
server.start();
assert.throws(function() {
server.addProtoService(mathService, dummyImpls);
});
});
describe('Default handlers', function() {
var client;
beforeEach(function() {
server.addProtoService(mathService, {});
var port = server.bind('localhost:0', server_insecure_creds);
var Client = surface_client.makeProtobufClientConstructor(mathService);
client = new Client('localhost:' + port,
grpc.credentials.createInsecure());
server.start();
});
it('should respond to a unary call with UNIMPLEMENTED', function(done) {
client.div({divisor: 4, dividend: 3}, function(error, response) {
assert(error);
assert.strictEqual(error.code, grpc.status.UNIMPLEMENTED);
done();
});
});
it('should respond to a client stream with UNIMPLEMENTED', function(done) {
var call = client.sum(function(error, respones) {
assert(error);
assert.strictEqual(error.code, grpc.status.UNIMPLEMENTED);
done();
});
call.end();
});
it('should respond to a server stream with UNIMPLEMENTED', function(done) {
var call = client.fib({limit: 5});
call.on('data', function(value) {
assert.fail('No messages expected');
});
call.on('error', function(err) {
assert.strictEqual(err.code, grpc.status.UNIMPLEMENTED);
done();
});
});
it('should respond to a bidi call with UNIMPLEMENTED', function(done) {
var call = client.divMany();
call.on('data', function(value) {
assert.fail('No messages expected');
});
call.on('error', function(err) {
assert.strictEqual(err.code, grpc.status.UNIMPLEMENTED);
done();
});
call.end();
});
});
});
describe('Client constructor building', function() {
var illegal_service_attrs = {
$method : {
path: '/illegal/$method',
requestStream: false,
responseStream: false,
requestSerialize: _.identity,
requestDeserialize: _.identity,
responseSerialize: _.identity,
responseDeserialize: _.identity
}
};
it('Should reject method names starting with $', function() {
assert.throws(function() {
grpc.makeGenericClientConstructor(illegal_service_attrs);
}, /\$/);
});
});
describe('waitForClientReady', function() {
var server;
var port;
var Client;
var client;
before(function() {
server = new grpc.Server();
port = server.bind('localhost:0', grpc.ServerCredentials.createInsecure());
server.start();
Client = surface_client.makeProtobufClientConstructor(mathService);
});
beforeEach(function() {
client = new Client('localhost:' + port, grpc.credentials.createInsecure());
});
after(function() {
server.forceShutdown();
});
it('should complete when called alone', function(done) {
grpc.waitForClientReady(client, Infinity, function(error) {
assert.ifError(error);
done();
});
});
it('should complete when a call is initiated', function(done) {
grpc.waitForClientReady(client, Infinity, function(error) {
assert.ifError(error);
done();
});
var call = client.div({}, function(err, response) {});
call.cancel();
});
it('should complete if called more than once', function(done) {
done = multiDone(done, 2);
grpc.waitForClientReady(client, Infinity, function(error) {
assert.ifError(error);
done();
});
grpc.waitForClientReady(client, Infinity, function(error) {
assert.ifError(error);
done();
});
});
it('should complete if called when already ready', function(done) {
grpc.waitForClientReady(client, Infinity, function(error) {
assert.ifError(error);
grpc.waitForClientReady(client, Infinity, function(error) {
assert.ifError(error);
done();
});
});
});
it('should time out if the server does not exist', function(done) {
var bad_client = new Client('nonexistent_hostname',
grpc.credentials.createInsecure());
var deadline = new Date();
deadline.setSeconds(deadline.getSeconds() + 1);
grpc.waitForClientReady(bad_client, deadline, function(error) {
assert(error);
done();
});
});
});
describe('Echo service', function() {
var server;
var client;
before(function() {
var test_proto = ProtoBuf.loadProtoFile(__dirname + '/echo_service.proto');
var echo_service = test_proto.lookup('EchoService');
server = new grpc.Server();
server.addProtoService(echo_service, {
echo: function(call, callback) {
callback(null, call.request);
}
});
var port = server.bind('localhost:0', server_insecure_creds);
var Client = surface_client.makeProtobufClientConstructor(echo_service);
client = new Client('localhost:' + port, grpc.credentials.createInsecure());
server.start();
});
after(function() {
server.forceShutdown();
});
it('should echo the recieved message directly', function(done) {
client.echo({value: 'test value', value2: 3}, function(error, response) {
assert.ifError(error);
assert.deepEqual(response, {value: 'test value', value2: 3});
done();
});
});
});
describe('Generic client and server', function() {
function toString(val) {
return val.toString();
}
function toBuffer(str) {
return new Buffer(str);
}
var string_service_attrs = {
'capitalize' : {
path: '/string/capitalize',
requestStream: false,
responseStream: false,
requestSerialize: toBuffer,
requestDeserialize: toString,
responseSerialize: toBuffer,
responseDeserialize: toString
}
};
describe('String client and server', function() {
var client;
var server;
before(function() {
server = new grpc.Server();
server.addService(string_service_attrs, {
capitalize: function(call, callback) {
callback(null, _.capitalize(call.request));
}
});
var port = server.bind('localhost:0', server_insecure_creds);
server.start();
var Client = grpc.makeGenericClientConstructor(string_service_attrs);
client = new Client('localhost:' + port,
grpc.credentials.createInsecure());
});
after(function() {
server.forceShutdown();
});
it('Should respond with a capitalized string', function(done) {
client.capitalize('abc', function(err, response) {
assert.ifError(err);
assert.strictEqual(response, 'Abc');
done();
});
});
});
});
describe('Server-side getPeer', function() {
function toString(val) {
return val.toString();
}
function toBuffer(str) {
return new Buffer(str);
}
var string_service_attrs = {
'getPeer' : {
path: '/string/getPeer',
requestStream: false,
responseStream: false,
requestSerialize: toBuffer,
requestDeserialize: toString,
responseSerialize: toBuffer,
responseDeserialize: toString
}
};
var client;
var server;
before(function() {
server = new grpc.Server();
server.addService(string_service_attrs, {
getPeer: function(call, callback) {
try {
callback(null, call.getPeer());
} catch (e) {
call.emit('error', e);
}
}
});
var port = server.bind('localhost:0', server_insecure_creds);
server.start();
var Client = grpc.makeGenericClientConstructor(string_service_attrs);
client = new Client('localhost:' + port,
grpc.credentials.createInsecure());
});
after(function() {
server.forceShutdown();
});
it('should respond with a string representing the client', function(done) {
client.getPeer('', function(err, response) {
assert.ifError(err);
// We don't expect a specific value, just that it worked without error
done();
});
});
});
describe('Echo metadata', function() {
var client;
var server;
var metadata;
before(function() {
var test_proto = ProtoBuf.loadProtoFile(__dirname + '/test_service.proto');
var test_service = test_proto.lookup('TestService');
server = new grpc.Server();
server.addProtoService(test_service, {
unary: function(call, cb) {
call.sendMetadata(call.metadata);
cb(null, {});
},
clientStream: function(stream, cb){
stream.on('data', function(data) {});
stream.on('end', function() {
stream.sendMetadata(stream.metadata);
cb(null, {});
});
},
serverStream: function(stream) {
stream.sendMetadata(stream.metadata);
stream.end();
},
bidiStream: function(stream) {
stream.on('data', function(data) {});
stream.on('end', function() {
stream.sendMetadata(stream.metadata);
stream.end();
});
}
});
var port = server.bind('localhost:0', server_insecure_creds);
var Client = surface_client.makeProtobufClientConstructor(test_service);
client = new Client('localhost:' + port, grpc.credentials.createInsecure());
server.start();
metadata = new grpc.Metadata();
metadata.set('key', 'value');
});
after(function() {
server.forceShutdown();
});
it('with unary call', function(done) {
var call = client.unary({}, metadata, function(err, data) {
assert.ifError(err);
});
call.on('metadata', function(metadata) {
assert.deepEqual(metadata.get('key'), ['value']);
done();
});
});
it('with client stream call', function(done) {
var call = client.clientStream(metadata, function(err, data) {
assert.ifError(err);
});
call.on('metadata', function(metadata) {
assert.deepEqual(metadata.get('key'), ['value']);
done();
});
call.end();
});
it('with server stream call', function(done) {
var call = client.serverStream({}, metadata);
call.on('data', function() {});
call.on('metadata', function(metadata) {
assert.deepEqual(metadata.get('key'), ['value']);
done();
});
});
it('with bidi stream call', function(done) {
var call = client.bidiStream(metadata);
call.on('data', function() {});
call.on('metadata', function(metadata) {
assert.deepEqual(metadata.get('key'), ['value']);
done();
});
call.end();
});
it('shows the correct user-agent string', function(done) {
var version = require('../../../package.json').version;
var call = client.unary({}, metadata,
function(err, data) { assert.ifError(err); });
call.on('metadata', function(metadata) {
assert(_.startsWith(metadata.get('user-agent')[0],
'grpc-node/' + version));
done();
});
});
it('properly handles duplicate values', function(done) {
var dup_metadata = metadata.clone();
dup_metadata.add('key', 'value2');
var call = client.unary({}, dup_metadata,
function(err, data) {assert.ifError(err); });
call.on('metadata', function(resp_metadata) {
// Two arrays are equal iff their symmetric difference is empty
assert.deepEqual(_.xor(dup_metadata.get('key'), resp_metadata.get('key')),
[]);
done();
});
});
});
describe('Client malformed response handling', function() {
var server;
var client;
var badArg = new Buffer([0xFF]);
before(function() {
var test_proto = ProtoBuf.loadProtoFile(__dirname + '/test_service.proto');
var test_service = test_proto.lookup('TestService');
var malformed_test_service = {
unary: {
path: '/TestService/Unary',
requestStream: false,
responseStream: false,
requestDeserialize: _.identity,
responseSerialize: _.identity
},
clientStream: {
path: '/TestService/ClientStream',
requestStream: true,
responseStream: false,
requestDeserialize: _.identity,
responseSerialize: _.identity
},
serverStream: {
path: '/TestService/ServerStream',
requestStream: false,
responseStream: true,
requestDeserialize: _.identity,
responseSerialize: _.identity
},
bidiStream: {
path: '/TestService/BidiStream',
requestStream: true,
responseStream: true,
requestDeserialize: _.identity,
responseSerialize: _.identity
}
};
server = new grpc.Server();
server.addService(malformed_test_service, {
unary: function(call, cb) {
cb(null, badArg);
},
clientStream: function(stream, cb) {
stream.on('data', function() {/* Ignore requests */});
stream.on('end', function() {
cb(null, badArg);
});
},
serverStream: function(stream) {
stream.write(badArg);
stream.end();
},
bidiStream: function(stream) {
stream.on('data', function() {
// Ignore requests
stream.write(badArg);
});
stream.on('end', function() {
stream.end();
});
}
});
var port = server.bind('localhost:0', server_insecure_creds);
var Client = surface_client.makeProtobufClientConstructor(test_service);
client = new Client('localhost:' + port, grpc.credentials.createInsecure());
server.start();
});
after(function() {
server.forceShutdown();
});
it('should get an INTERNAL status with a unary call', function(done) {
client.unary({}, function(err, data) {
assert(err);
assert.strictEqual(err.code, grpc.status.INTERNAL);
done();
});
});
it('should get an INTERNAL status with a client stream call', function(done) {
var call = client.clientStream(function(err, data) {
assert(err);
assert.strictEqual(err.code, grpc.status.INTERNAL);
done();
});
call.write({});
call.end();
});
it('should get an INTERNAL status with a server stream call', function(done) {
var call = client.serverStream({});
call.on('data', function(){});
call.on('error', function(err) {
assert.strictEqual(err.code, grpc.status.INTERNAL);
done();
});
});
it('should get an INTERNAL status with a bidi stream call', function(done) {
var call = client.bidiStream();
call.on('data', function(){});
call.on('error', function(err) {
assert.strictEqual(err.code, grpc.status.INTERNAL);
done();
});
call.write({});
call.end();
});
});
describe('Other conditions', function() {
var test_service;
var Client;
var client;
var server;
var port;
before(function() {
var test_proto = ProtoBuf.loadProtoFile(__dirname + '/test_service.proto');
test_service = test_proto.lookup('TestService');
server = new grpc.Server();
var trailer_metadata = new grpc.Metadata();
trailer_metadata.add('trailer-present', 'yes');
server.addProtoService(test_service, {
unary: function(call, cb) {
var req = call.request;
if (req.error) {
cb({code: grpc.status.UNKNOWN,
details: 'Requested error'}, null, trailer_metadata);
} else {
cb(null, {count: 1}, trailer_metadata);
}
},
clientStream: function(stream, cb){
var count = 0;
var errored;
stream.on('data', function(data) {
if (data.error) {
errored = true;
cb(new Error('Requested error'), null, trailer_metadata);
} else {
count += 1;
}
});
stream.on('end', function() {
if (!errored) {
cb(null, {count: count}, trailer_metadata);
}
});
},
serverStream: function(stream) {
var req = stream.request;
if (req.error) {
var err = {code: grpc.status.UNKNOWN,
details: 'Requested error'};
err.metadata = trailer_metadata;
stream.emit('error', err);
} else {
for (var i = 0; i < 5; i++) {
stream.write({count: i});
}
stream.end(trailer_metadata);
}
},
bidiStream: function(stream) {
var count = 0;
stream.on('data', function(data) {
if (data.error) {
var err = new Error('Requested error');
err.metadata = trailer_metadata.clone();
err.metadata.add('count', '' + count);
stream.emit('error', err);
} else {
stream.write({count: count});
count += 1;
}
});
stream.on('end', function() {
stream.end(trailer_metadata);
});
}
});
port = server.bind('localhost:0', server_insecure_creds);
Client = surface_client.makeProtobufClientConstructor(test_service);
client = new Client('localhost:' + port, grpc.credentials.createInsecure());
server.start();
});
after(function() {
server.forceShutdown();
});
it('channel.getTarget should be available', function() {
assert.strictEqual(typeof grpc.getClientChannel(client).getTarget(),
'string');
});
it('client should be able to pause and resume a stream', function(done) {
var call = client.bidiStream();
call.on('data', function(data) {
assert(data.count < 3);
call.pause();
setTimeout(function() {
call.resume();
}, 10);
});
call.on('end', function() {
done();
});
call.write({});
call.write({});
call.write({});
call.end();
});
describe('Server recieving bad input', function() {
var misbehavingClient;
var badArg = new Buffer([0xFF]);
before(function() {
var test_service_attrs = {
unary: {
path: '/TestService/Unary',
requestStream: false,
responseStream: false,
requestSerialize: _.identity,
responseDeserialize: _.identity
},
clientStream: {
path: '/TestService/ClientStream',
requestStream: true,
responseStream: false,
requestSerialize: _.identity,
responseDeserialize: _.identity
},
serverStream: {
path: '/TestService/ServerStream',
requestStream: false,
responseStream: true,
requestSerialize: _.identity,
responseDeserialize: _.identity
},
bidiStream: {
path: '/TestService/BidiStream',
requestStream: true,
responseStream: true,
requestSerialize: _.identity,
responseDeserialize: _.identity
}
};
var Client = surface_client.makeClientConstructor(test_service_attrs,
'TestService');
misbehavingClient = new Client('localhost:' + port,
grpc.credentials.createInsecure());
});
it('should respond correctly to a unary call', function(done) {
misbehavingClient.unary(badArg, function(err, data) {
assert(err);
assert.strictEqual(err.code, grpc.status.INTERNAL);
done();
});
});
it('should respond correctly to a client stream', function(done) {
var call = misbehavingClient.clientStream(function(err, data) {
assert(err);
assert.strictEqual(err.code, grpc.status.INTERNAL);
done();
});
call.write(badArg);
// TODO(mlumish): Remove call.end()
call.end();
});
it('should respond correctly to a server stream', function(done) {
var call = misbehavingClient.serverStream(badArg);
call.on('data', function(data) {
assert.fail(data, null, 'Unexpected data', '===');
});
call.on('error', function(err) {
assert.strictEqual(err.code, grpc.status.INTERNAL);
done();
});
});
it('should respond correctly to a bidi stream', function(done) {
var call = misbehavingClient.bidiStream();
call.on('data', function(data) {
assert.fail(data, null, 'Unexpected data', '===');
});
call.on('error', function(err) {
assert.strictEqual(err.code, grpc.status.INTERNAL);
done();
});
call.write(badArg);
// TODO(mlumish): Remove call.end()
call.end();
});
});
describe('Trailing metadata', function() {
it('should be present when a unary call succeeds', function(done) {
var call = client.unary({error: false}, function(err, data) {
assert.ifError(err);
});
call.on('status', function(status) {
assert.deepEqual(status.metadata.get('trailer-present'), ['yes']);
done();
});
});
it('should be present when a unary call fails', function(done) {
var call = client.unary({error: true}, function(err, data) {
assert(err);
});
call.on('status', function(status) {
assert.deepEqual(status.metadata.get('trailer-present'), ['yes']);
done();
});
});
it('should be present when a client stream call succeeds', function(done) {
var call = client.clientStream(function(err, data) {
assert.ifError(err);
});
call.write({error: false});
call.write({error: false});
call.end();
call.on('status', function(status) {
assert.deepEqual(status.metadata.get('trailer-present'), ['yes']);
done();
});
});
it('should be present when a client stream call fails', function(done) {
var call = client.clientStream(function(err, data) {
assert(err);
});
call.write({error: false});
call.write({error: true});
call.end();
call.on('status', function(status) {
assert.deepEqual(status.metadata.get('trailer-present'), ['yes']);
done();
});
});
it('should be present when a server stream call succeeds', function(done) {
var call = client.serverStream({error: false});
call.on('data', function(){});
call.on('status', function(status) {
assert.strictEqual(status.code, grpc.status.OK);
assert.deepEqual(status.metadata.get('trailer-present'), ['yes']);
done();
});
});
it('should be present when a server stream call fails', function(done) {
var call = client.serverStream({error: true});
call.on('data', function(){});
call.on('error', function(error) {
assert.deepEqual(error.metadata.get('trailer-present'), ['yes']);
done();
});
});
it('should be present when a bidi stream succeeds', function(done) {
var call = client.bidiStream();
call.write({error: false});
call.write({error: false});
call.end();
call.on('data', function(){});
call.on('status', function(status) {
assert.strictEqual(status.code, grpc.status.OK);
assert.deepEqual(status.metadata.get('trailer-present'), ['yes']);
done();
});
});
it('should be present when a bidi stream fails', function(done) {
var call = client.bidiStream();
call.write({error: false});
call.write({error: true});
call.end();
call.on('data', function(){});
call.on('error', function(error) {
assert.deepEqual(error.metadata.get('trailer-present'), ['yes']);
done();
});
});
});
describe('Error object should contain the status', function() {
it('for a unary call', function(done) {
client.unary({error: true}, function(err, data) {
assert(err);
assert.strictEqual(err.code, grpc.status.UNKNOWN);
assert.strictEqual(err.message, 'Requested error');
done();
});
});
it('for a client stream call', function(done) {
var call = client.clientStream(function(err, data) {
assert(err);
assert.strictEqual(err.code, grpc.status.UNKNOWN);
assert.strictEqual(err.message, 'Requested error');
done();
});
call.write({error: false});
call.write({error: true});
call.end();
});
it('for a server stream call', function(done) {
var call = client.serverStream({error: true});
call.on('data', function(){});
call.on('error', function(error) {
assert.strictEqual(error.code, grpc.status.UNKNOWN);
assert.strictEqual(error.message, 'Requested error');
done();
});
});
it('for a bidi stream call', function(done) {
var call = client.bidiStream();
call.write({error: false});
call.write({error: true});
call.end();
call.on('data', function(){});
call.on('error', function(error) {
assert.strictEqual(error.code, grpc.status.UNKNOWN);
assert.strictEqual(error.message, 'Requested error');
done();
});
});
});
describe('call.getPeer should return the peer', function() {
it('for a unary call', function(done) {
var call = client.unary({error: false}, function(err, data) {
assert.ifError(err);
done();
});
assert.strictEqual(typeof call.getPeer(), 'string');
});
it('for a client stream call', function(done) {
var call = client.clientStream(function(err, data) {
assert.ifError(err);
done();
});
assert.strictEqual(typeof call.getPeer(), 'string');
call.write({error: false});
call.end();
});
it('for a server stream call', function(done) {
var call = client.serverStream({error: false});
assert.strictEqual(typeof call.getPeer(), 'string');
call.on('data', function(){});
call.on('status', function(status) {
assert.strictEqual(status.code, grpc.status.OK);
done();
});
});
it('for a bidi stream call', function(done) {
var call = client.bidiStream();
assert.strictEqual(typeof call.getPeer(), 'string');
call.write({error: false});
call.end();
call.on('data', function(){});
call.on('status', function(status) {
done();
});
});
});
});
describe('Call propagation', function() {
var proxy;
var proxy_impl;
var test_service;
var Client;
var client;
var server;
before(function() {
var test_proto = ProtoBuf.loadProtoFile(__dirname + '/test_service.proto');
test_service = test_proto.lookup('TestService');
server = new grpc.Server();
server.addProtoService(test_service, {
unary: function(call) {},
clientStream: function(stream) {},
serverStream: function(stream) {},
bidiStream: function(stream) {}
});
var port = server.bind('localhost:0', server_insecure_creds);
Client = surface_client.makeProtobufClientConstructor(test_service);
client = new Client('localhost:' + port, grpc.credentials.createInsecure());
server.start();
});
after(function() {
server.forceShutdown();
});
beforeEach(function() {
proxy = new grpc.Server();
proxy_impl = {
unary: function(call) {},
clientStream: function(stream) {},
serverStream: function(stream) {},
bidiStream: function(stream) {}
};
});
afterEach(function() {
proxy.forceShutdown();
});
describe('Cancellation', function() {
it('With a unary call', function(done) {
done = multiDone(done, 2);
var call;
proxy_impl.unary = function(parent, callback) {
client.unary(parent.request, {parent: parent}, function(err, value) {
try {
assert(err);
assert.strictEqual(err.code, grpc.status.CANCELLED);
} finally {
callback(err, value);
done();
}
});
call.cancel();
};
proxy.addProtoService(test_service, proxy_impl);
var proxy_port = proxy.bind('localhost:0', server_insecure_creds);
proxy.start();
var proxy_client = new Client('localhost:' + proxy_port,
grpc.credentials.createInsecure());
call = proxy_client.unary({}, function(err, value) { done(); });
});
it('With a client stream call', function(done) {
done = multiDone(done, 2);
var call;
proxy_impl.clientStream = function(parent, callback) {
client.clientStream({parent: parent}, function(err, value) {
try {
assert(err);
assert.strictEqual(err.code, grpc.status.CANCELLED);
} finally {
callback(err, value);
done();
}
});
call.cancel();
};
proxy.addProtoService(test_service, proxy_impl);
var proxy_port = proxy.bind('localhost:0', server_insecure_creds);
proxy.start();
var proxy_client = new Client('localhost:' + proxy_port,
grpc.credentials.createInsecure());
call = proxy_client.clientStream(function(err, value) { done(); });
});
it('With a server stream call', function(done) {
done = multiDone(done, 2);
var call;
proxy_impl.serverStream = function(parent) {
var child = client.serverStream(parent.request, {parent: parent});
child.on('data', function() {});
child.on('error', function(err) {
assert(err);
assert.strictEqual(err.code, grpc.status.CANCELLED);
done();
});
call.cancel();
};
proxy.addProtoService(test_service, proxy_impl);
var proxy_port = proxy.bind('localhost:0', server_insecure_creds);
proxy.start();
var proxy_client = new Client('localhost:' + proxy_port,
grpc.credentials.createInsecure());
call = proxy_client.serverStream({});
call.on('data', function() {});
call.on('error', function(err) {
done();
});
});
it('With a bidi stream call', function(done) {
done = multiDone(done, 2);
var call;
proxy_impl.bidiStream = function(parent) {
var child = client.bidiStream({parent: parent});
child.on('data', function() {});
child.on('error', function(err) {
assert(err);
assert.strictEqual(err.code, grpc.status.CANCELLED);
done();
});
call.cancel();
};
proxy.addProtoService(test_service, proxy_impl);
var proxy_port = proxy.bind('localhost:0', server_insecure_creds);
proxy.start();
var proxy_client = new Client('localhost:' + proxy_port,
grpc.credentials.createInsecure());
call = proxy_client.bidiStream();
call.on('data', function() {});
call.on('error', function(err) {
done();
});
});
});
describe('Deadline', function() {
/* jshint bitwise:false */
var deadline_flags = (grpc.propagate.DEFAULTS &
~grpc.propagate.CANCELLATION);
it('With a client stream call', function(done) {
done = multiDone(done, 2);
proxy_impl.clientStream = function(parent, callback) {
var options = {parent: parent, propagate_flags: deadline_flags};
client.clientStream(options, function(err, value) {
try {
assert(err);
assert(err.code === grpc.status.DEADLINE_EXCEEDED ||
err.code === grpc.status.INTERNAL);
} finally {
callback(err, value);
done();
}
});
};
proxy.addProtoService(test_service, proxy_impl);
var proxy_port = proxy.bind('localhost:0', server_insecure_creds);
proxy.start();
var proxy_client = new Client('localhost:' + proxy_port,
grpc.credentials.createInsecure());
var deadline = new Date();
deadline.setSeconds(deadline.getSeconds() + 1);
proxy_client.clientStream({deadline: deadline}, function(err, value) {
done();
});
});
it('With a bidi stream call', function(done) {
done = multiDone(done, 2);
proxy_impl.bidiStream = function(parent) {
var child = client.bidiStream(
{parent: parent, propagate_flags: deadline_flags});
child.on('data', function() {});
child.on('error', function(err) {
assert(err);
assert(err.code === grpc.status.DEADLINE_EXCEEDED ||
err.code === grpc.status.INTERNAL);
done();
});
};
proxy.addProtoService(test_service, proxy_impl);
var proxy_port = proxy.bind('localhost:0', server_insecure_creds);
proxy.start();
var proxy_client = new Client('localhost:' + proxy_port,
grpc.credentials.createInsecure());
var deadline = new Date();
deadline.setSeconds(deadline.getSeconds() + 1);
var call = proxy_client.bidiStream({deadline: deadline});
call.on('data', function() {});
call.on('error', function(err) {
done();
});
});
});
});
describe('Cancelling surface client', function() {
var client;
var server;
before(function() {
server = new grpc.Server();
server.addProtoService(mathService, {
'div': function(stream) {},
'divMany': function(stream) {},
'fib': function(stream) {},
'sum': function(stream) {}
});
var port = server.bind('localhost:0', server_insecure_creds);
var Client = surface_client.makeProtobufClientConstructor(mathService);
client = new Client('localhost:' + port, grpc.credentials.createInsecure());
server.start();
});
after(function() {
server.forceShutdown();
});
it('Should correctly cancel a unary call', function(done) {
var call = client.div({'divisor': 0, 'dividend': 0}, function(err, resp) {
assert.strictEqual(err.code, surface_client.status.CANCELLED);
done();
});
call.cancel();
});
it('Should correctly cancel a client stream call', function(done) {
var call = client.sum(function(err, resp) {
assert.strictEqual(err.code, surface_client.status.CANCELLED);
done();
});
call.cancel();
});
it('Should correctly cancel a server stream call', function(done) {
var call = client.fib({'limit': 5});
call.on('data', function() {});
call.on('error', function(error) {
assert.strictEqual(error.code, surface_client.status.CANCELLED);
done();
});
call.cancel();
});
it('Should correctly cancel a bidi stream call', function(done) {
var call = client.divMany();
call.on('data', function() {});
call.on('error', function(error) {
assert.strictEqual(error.code, surface_client.status.CANCELLED);
done();
});
call.cancel();
});
});