diff options
Diffstat (limited to 'src/node/test')
-rw-r--r-- | src/node/test/call_test.js | 202 | ||||
-rw-r--r-- | src/node/test/channel_test.js | 88 | ||||
-rw-r--r-- | src/node/test/client_server_test.js | 183 | ||||
-rw-r--r-- | src/node/test/constant_test.js | 130 | ||||
-rw-r--r-- | src/node/test/data/README | 1 | ||||
-rw-r--r-- | src/node/test/data/ca.pem | 15 | ||||
-rw-r--r-- | src/node/test/data/server1.key | 16 | ||||
-rw-r--r-- | src/node/test/data/server1.pem | 16 | ||||
-rw-r--r-- | src/node/test/end_to_end_test.js | 201 | ||||
-rw-r--r-- | src/node/test/math_client_test.js | 209 | ||||
-rw-r--r-- | src/node/test/server_test.js | 121 |
11 files changed, 1182 insertions, 0 deletions
diff --git a/src/node/test/call_test.js b/src/node/test/call_test.js new file mode 100644 index 0000000000..e6dc9664f1 --- /dev/null +++ b/src/node/test/call_test.js @@ -0,0 +1,202 @@ +/* + * + * Copyright 2014, 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. + * + */ + +var assert = require('assert'); +var grpc = require('bindings')('grpc.node'); + +var channel = new grpc.Channel('localhost:7070'); + +/** + * Helper function to return an absolute deadline given a relative timeout in + * seconds. + * @param {number} timeout_secs The number of seconds to wait before timing out + * @return {Date} A date timeout_secs in the future + */ +function getDeadline(timeout_secs) { + var deadline = new Date(); + deadline.setSeconds(deadline.getSeconds() + timeout_secs); + return deadline; +} + +describe('call', function() { + describe('constructor', function() { + it('should reject anything less than 3 arguments', function() { + assert.throws(function() { + new grpc.Call(); + }, TypeError); + assert.throws(function() { + new grpc.Call(channel); + }, TypeError); + assert.throws(function() { + new grpc.Call(channel, 'method'); + }, TypeError); + }); + it('should succeed with a Channel, a string, and a date or number', + function() { + assert.doesNotThrow(function() { + new grpc.Call(channel, 'method', new Date()); + }); + assert.doesNotThrow(function() { + new grpc.Call(channel, 'method', 0); + }); + }); + it('should fail with a closed channel', function() { + var local_channel = new grpc.Channel('hostname'); + local_channel.close(); + assert.throws(function() { + new grpc.Call(channel, 'method'); + }); + }); + it('should fail with other types', function() { + assert.throws(function() { + new grpc.Call({}, 'method', 0); + }, TypeError); + assert.throws(function() { + new grpc.Call(channel, null, 0); + }, TypeError); + assert.throws(function() { + new grpc.Call(channel, 'method', 'now'); + }, TypeError); + }); + }); + describe('addMetadata', function() { + it('should succeed with objects containing keys and values', function() { + var call = new grpc.Call(channel, 'method', getDeadline(1)); + assert.doesNotThrow(function() { + call.addMetadata(); + }); + assert.doesNotThrow(function() { + call.addMetadata({'key' : 'key', + 'value' : new Buffer('value')}); + }); + assert.doesNotThrow(function() { + call.addMetadata({'key' : 'key1', + 'value' : new Buffer('value1')}, + {'key' : 'key2', + 'value' : new Buffer('value2')}); + }); + }); + it('should fail with other parameter types', function() { + var call = new grpc.Call(channel, 'method', getDeadline(1)); + assert.throws(function() { + call.addMetadata(null); + }, TypeError); + assert.throws(function() { + call.addMetadata('value'); + }, TypeError); + assert.throws(function() { + call.addMetadata(5); + }, TypeError); + }); + it('should fail if startInvoke was already called', function(done) { + var call = new grpc.Call(channel, 'method', getDeadline(1)); + call.startInvoke(function() {}, + function() {}, + function() {done();}, + 0); + assert.throws(function() { + call.addMetadata({'key' : 'key', 'value' : new Buffer('value') }); + }, function(err) { + return err.code === grpc.callError.ALREADY_INVOKED; + }); + // Cancel to speed up the test + call.cancel(); + }); + }); + describe('startInvoke', function() { + it('should fail with fewer than 4 arguments', function() { + var call = new grpc.Call(channel, 'method', getDeadline(1)); + assert.throws(function() { + call.startInvoke(); + }, TypeError); + assert.throws(function() { + call.startInvoke(function() {}); + }, TypeError); + assert.throws(function() { + call.startInvoke(function() {}, + function() {}); + }, TypeError); + assert.throws(function() { + call.startInvoke(function() {}, + function() {}, + function() {}); + }, TypeError); + }); + it('should work with 3 args and an int', function(done) { + assert.doesNotThrow(function() { + var call = new grpc.Call(channel, 'method', getDeadline(1)); + call.startInvoke(function() {}, + function() {}, + function() {done();}, + 0); + // Cancel to speed up the test + call.cancel(); + }); + }); + it('should reject incorrectly typed arguments', function() { + var call = new grpc.Call(channel, 'method', getDeadline(1)); + assert.throws(function() { + call.startInvoke(0, 0, 0, 0); + }, TypeError); + assert.throws(function() { + call.startInvoke(function() {}, + function() {}, + function() {}, 'test'); + }); + }); + }); + describe('serverAccept', function() { + it('should fail with fewer than 1 argument1', function() { + var call = new grpc.Call(channel, 'method', getDeadline(1)); + assert.throws(function() { + call.serverAccept(); + }, TypeError); + }); + it('should return an error when called on a client Call', function() { + var call = new grpc.Call(channel, 'method', getDeadline(1)); + assert.throws(function() { + call.serverAccept(function() {}); + }, function(err) { + return err.code === grpc.callError.NOT_ON_CLIENT; + }); + }); + }); + describe('cancel', function() { + it('should succeed', function() { + var call = new grpc.Call(channel, 'method', getDeadline(1)); + assert.doesNotThrow(function() { + call.cancel(); + }); + }); + }); +}); diff --git a/src/node/test/channel_test.js b/src/node/test/channel_test.js new file mode 100644 index 0000000000..4d8cfc4d89 --- /dev/null +++ b/src/node/test/channel_test.js @@ -0,0 +1,88 @@ +/* + * + * Copyright 2014, 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. + * + */ + +var assert = require('assert'); +var grpc = require('bindings')('grpc.node'); + +describe('channel', function() { + describe('constructor', function() { + it('should require a string for the first argument', function() { + assert.doesNotThrow(function() { + new grpc.Channel('hostname'); + }); + assert.throws(function() { + new grpc.Channel(); + }, TypeError); + assert.throws(function() { + new grpc.Channel(5); + }); + }); + it('should accept an object for the second parameter', function() { + assert.doesNotThrow(function() { + new grpc.Channel('hostname', {}); + }); + assert.throws(function() { + new grpc.Channel('hostname', 5); + }); + }); + it('should only accept objects with string or int values', function() { + assert.doesNotThrow(function() { + new grpc.Channel('hostname', {'key' : 'value'}); + }); + assert.doesNotThrow(function() { + new grpc.Channel('hostname', {'key' : 5}); + }); + assert.throws(function() { + new grpc.Channel('hostname', {'key' : null}); + }); + assert.throws(function() { + new grpc.Channel('hostname', {'key' : new Date()}); + }); + }); + }); + describe('close', function() { + it('should succeed silently', function() { + var channel = new grpc.Channel('hostname', {}); + assert.doesNotThrow(function() { + channel.close(); + }); + }); + it('should be idempotent', function() { + var channel = new grpc.Channel('hostname', {}); + assert.doesNotThrow(function() { + channel.close(); + channel.close(); + }); + }); + }); +}); diff --git a/src/node/test/client_server_test.js b/src/node/test/client_server_test.js new file mode 100644 index 0000000000..534a5c464f --- /dev/null +++ b/src/node/test/client_server_test.js @@ -0,0 +1,183 @@ +/* + * + * Copyright 2014, 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. + * + */ + +var assert = require('assert'); +var fs = require('fs'); +var path = require('path'); +var grpc = require('bindings')('grpc.node'); +var Server = require('../server'); +var client = require('../client'); +var port_picker = require('../port_picker'); +var common = require('../common'); +var _ = require('highland'); + +var ca_path = path.join(__dirname, 'data/ca.pem'); + +var key_path = path.join(__dirname, 'data/server1.key'); + +var pem_path = path.join(__dirname, 'data/server1.pem'); + +/** + * Helper function to return an absolute deadline given a relative timeout in + * seconds. + * @param {number} timeout_secs The number of seconds to wait before timing out + * @return {Date} A date timeout_secs in the future + */ +function getDeadline(timeout_secs) { + var deadline = new Date(); + deadline.setSeconds(deadline.getSeconds() + timeout_secs); + return deadline; +} + +/** + * Responds to every request with the same data as a response + * @param {Stream} stream + */ +function echoHandler(stream) { + stream.pipe(stream); +} + +/** + * Responds to every request with an error status + * @param {Stream} stream + */ +function errorHandler(stream) { + throw { + 'code' : grpc.status.UNIMPLEMENTED, + 'details' : 'error details' + }; +} + +describe('echo client', function() { + it('should receive echo responses', function(done) { + port_picker.nextAvailablePort(function(port) { + var server = new Server(); + server.bind(port); + server.register('echo', echoHandler); + server.start(); + + var messages = ['echo1', 'echo2', 'echo3', 'echo4']; + var channel = new grpc.Channel(port); + var stream = client.makeRequest( + channel, + 'echo'); + _(messages).map(function(val) { + return new Buffer(val); + }).pipe(stream); + var index = 0; + stream.on('data', function(chunk) { + assert.equal(messages[index], chunk.toString()); + index += 1; + }); + stream.on('end', function() { + server.shutdown(); + done(); + }); + }); + }); + it('should get an error status that the server throws', function(done) { + port_picker.nextAvailablePort(function(port) { + var server = new Server(); + server.bind(port); + server.register('error', errorHandler); + server.start(); + + var channel = new grpc.Channel(port); + var stream = client.makeRequest( + channel, + 'error', + null, + getDeadline(1)); + + stream.on('data', function() {}); + stream.write(new Buffer('test')); + stream.end(); + stream.on('status', function(status) { + assert.equal(status.code, grpc.status.UNIMPLEMENTED); + assert.equal(status.details, 'error details'); + server.shutdown(); + done(); + }); + + }); + }); +}); +/* TODO(mlumish): explore options for reducing duplication between this test + * and the insecure echo client test */ +describe('secure echo client', function() { + it('should recieve echo responses', function(done) { + port_picker.nextAvailablePort(function(port) { + fs.readFile(ca_path, function(err, ca_data) { + assert.ifError(err); + fs.readFile(key_path, function(err, key_data) { + assert.ifError(err); + fs.readFile(pem_path, function(err, pem_data) { + assert.ifError(err); + var creds = grpc.Credentials.createSsl(ca_data); + var server_creds = grpc.ServerCredentials.createSsl(null, + key_data, + pem_data); + + var server = new Server({'credentials' : server_creds}); + server.bind(port, true); + server.register('echo', echoHandler); + server.start(); + + var messages = ['echo1', 'echo2', 'echo3', 'echo4']; + var channel = new grpc.Channel(port, { + 'grpc.ssl_target_name_override' : 'foo.test.google.com', + 'credentials' : creds + }); + var stream = client.makeRequest( + channel, + 'echo'); + + _(messages).map(function(val) { + return new Buffer(val); + }).pipe(stream); + var index = 0; + stream.on('data', function(chunk) { + assert.equal(messages[index], chunk.toString()); + index += 1; + }); + stream.on('end', function() { + server.shutdown(); + done(); + }); + }); + + }); + }); + }); + }); +}); diff --git a/src/node/test/constant_test.js b/src/node/test/constant_test.js new file mode 100644 index 0000000000..f65eea3cff --- /dev/null +++ b/src/node/test/constant_test.js @@ -0,0 +1,130 @@ +/* + * + * Copyright 2014, 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. + * + */ + +var assert = require('assert'); +var grpc = require('bindings')('grpc.node'); + +/** + * List of all status names + * @const + * @type {Array.<string>} + */ +var statusNames = [ + 'OK', + 'CANCELLED', + 'UNKNOWN', + 'INVALID_ARGUMENT', + 'DEADLINE_EXCEEDED', + 'NOT_FOUND', + 'ALREADY_EXISTS', + 'PERMISSION_DENIED', + 'UNAUTHENTICATED', + 'RESOURCE_EXHAUSTED', + 'FAILED_PRECONDITION', + 'ABORTED', + 'OUT_OF_RANGE', + 'UNIMPLEMENTED', + 'INTERNAL', + 'UNAVAILABLE', + 'DATA_LOSS' +]; + +/** + * List of all call error names + * @const + * @type {Array.<string>} + */ +var callErrorNames = [ + 'OK', + 'ERROR', + 'NOT_ON_SERVER', + 'NOT_ON_CLIENT', + 'ALREADY_INVOKED', + 'NOT_INVOKED', + 'ALREADY_FINISHED', + 'TOO_MANY_OPERATIONS', + 'INVALID_FLAGS' +]; + +/** + * List of all op error names + * @const + * @type {Array.<string>} + */ +var opErrorNames = [ + 'OK', + 'ERROR' +]; + +/** + * List of all completion type names + * @const + * @type {Array.<string>} + */ +var completionTypeNames = [ + 'QUEUE_SHUTDOWN', + 'READ', + 'INVOKE_ACCEPTED', + 'WRITE_ACCEPTED', + 'FINISH_ACCEPTED', + 'CLIENT_METADATA_READ', + 'FINISHED', + 'SERVER_RPC_NEW' +]; + +describe('constants', function() { + it('should have all of the status constants', function() { + for (var i = 0; i < statusNames.length; i++) { + assert(grpc.status.hasOwnProperty(statusNames[i]), + 'status missing: ' + statusNames[i]); + } + }); + it('should have all of the call errors', function() { + for (var i = 0; i < callErrorNames.length; i++) { + assert(grpc.callError.hasOwnProperty(callErrorNames[i]), + 'call error missing: ' + callErrorNames[i]); + } + }); + it('should have all of the op errors', function() { + for (var i = 0; i < opErrorNames.length; i++) { + assert(grpc.opError.hasOwnProperty(opErrorNames[i]), + 'op error missing: ' + opErrorNames[i]); + } + }); + it('should have all of the completion types', function() { + for (var i = 0; i < completionTypeNames.length; i++) { + assert(grpc.completionType.hasOwnProperty(completionTypeNames[i]), + 'completion type missing: ' + completionTypeNames[i]); + } + }); +}); diff --git a/src/node/test/data/README b/src/node/test/data/README new file mode 100644 index 0000000000..888d95b900 --- /dev/null +++ b/src/node/test/data/README @@ -0,0 +1 @@ +CONFIRMEDTESTKEY diff --git a/src/node/test/data/ca.pem b/src/node/test/data/ca.pem new file mode 100644 index 0000000000..6c8511a73c --- /dev/null +++ b/src/node/test/data/ca.pem @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICSjCCAbOgAwIBAgIJAJHGGR4dGioHMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV +BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX +aWRnaXRzIFB0eSBMdGQxDzANBgNVBAMTBnRlc3RjYTAeFw0xNDExMTEyMjMxMjla +Fw0yNDExMDgyMjMxMjlaMFYxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0 +YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxDzANBgNVBAMT +BnRlc3RjYTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwEDfBV5MYdlHVHJ7 ++L4nxrZy7mBfAVXpOc5vMYztssUI7mL2/iYujiIXM+weZYNTEpLdjyJdu7R5gGUu +g1jSVK/EPHfc74O7AyZU34PNIP4Sh33N+/A5YexrNgJlPY+E3GdVYi4ldWJjgkAd +Qah2PH5ACLrIIC6tRka9hcaBlIECAwEAAaMgMB4wDAYDVR0TBAUwAwEB/zAOBgNV +HQ8BAf8EBAMCAgQwDQYJKoZIhvcNAQELBQADgYEAHzC7jdYlzAVmddi/gdAeKPau +sPBG/C2HCWqHzpCUHcKuvMzDVkY/MP2o6JIW2DBbY64bO/FceExhjcykgaYtCH/m +oIU63+CFOTtR7otyQAWHqXa7q4SbCDlG7DyRFxqG0txPtGvy12lgldA2+RgcigQG +Dfcog5wrJytaQ6UA0wE= +-----END CERTIFICATE----- diff --git a/src/node/test/data/server1.key b/src/node/test/data/server1.key new file mode 100644 index 0000000000..143a5b8765 --- /dev/null +++ b/src/node/test/data/server1.key @@ -0,0 +1,16 @@ +-----BEGIN PRIVATE KEY----- +MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAOHDFScoLCVJpYDD +M4HYtIdV6Ake/sMNaaKdODjDMsux/4tDydlumN+fm+AjPEK5GHhGn1BgzkWF+slf +3BxhrA/8dNsnunstVA7ZBgA/5qQxMfGAq4wHNVX77fBZOgp9VlSMVfyd9N8YwbBY +AckOeUQadTi2X1S6OgJXgQ0m3MWhAgMBAAECgYAn7qGnM2vbjJNBm0VZCkOkTIWm +V10okw7EPJrdL2mkre9NasghNXbE1y5zDshx5Nt3KsazKOxTT8d0Jwh/3KbaN+YY +tTCbKGW0pXDRBhwUHRcuRzScjli8Rih5UOCiZkhefUTcRb6xIhZJuQy71tjaSy0p +dHZRmYyBYO2YEQ8xoQJBAPrJPhMBkzmEYFtyIEqAxQ/o/A6E+E4w8i+KM7nQCK7q +K4JXzyXVAjLfyBZWHGM2uro/fjqPggGD6QH1qXCkI4MCQQDmdKeb2TrKRh5BY1LR +81aJGKcJ2XbcDu6wMZK4oqWbTX2KiYn9GB0woM6nSr/Y6iy1u145YzYxEV/iMwff +DJULAkB8B2MnyzOg0pNFJqBJuH29bKCcHa8gHJzqXhNO5lAlEbMK95p/P2Wi+4Hd +aiEIAF1BF326QJcvYKmwSmrORp85AkAlSNxRJ50OWrfMZnBgzVjDx3xG6KsFQVk2 +ol6VhqL6dFgKUORFUWBvnKSyhjJxurlPEahV6oo6+A+mPhFY8eUvAkAZQyTdupP3 +XEFQKctGz+9+gKkemDp7LBBMEMBXrGTLPhpEfcjv/7KPdnFHYmhYeBTBnuVmTVWe +F98XJ7tIFfJq +-----END PRIVATE KEY----- diff --git a/src/node/test/data/server1.pem b/src/node/test/data/server1.pem new file mode 100644 index 0000000000..8e582e571f --- /dev/null +++ b/src/node/test/data/server1.pem @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE----- +MIICmzCCAgSgAwIBAgIBAzANBgkqhkiG9w0BAQUFADBWMQswCQYDVQQGEwJBVTET +MBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQ +dHkgTHRkMQ8wDQYDVQQDDAZ0ZXN0Y2EwHhcNMTQwNzIyMDYwMDU3WhcNMjQwNzE5 +MDYwMDU3WjBkMQswCQYDVQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNV +BAcTB0NoaWNhZ28xFDASBgNVBAoTC0dvb2dsZSBJbmMuMRowGAYDVQQDFBEqLnRl +c3QuZ29vZ2xlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA4cMVJygs +JUmlgMMzgdi0h1XoCR7+ww1pop04OMMyy7H/i0PJ2W6Y35+b4CM8QrkYeEafUGDO +RYX6yV/cHGGsD/x02ye6ey1UDtkGAD/mpDEx8YCrjAc1Vfvt8Fk6Cn1WVIxV/J30 +3xjBsFgByQ55RBp1OLZfVLo6AleBDSbcxaECAwEAAaNrMGkwCQYDVR0TBAIwADAL +BgNVHQ8EBAMCBeAwTwYDVR0RBEgwRoIQKi50ZXN0Lmdvb2dsZS5mcoIYd2F0ZXJ6 +b29pLnRlc3QuZ29vZ2xlLmJlghIqLnRlc3QueW91dHViZS5jb22HBMCoAQMwDQYJ +KoZIhvcNAQEFBQADgYEAM2Ii0LgTGbJ1j4oqX9bxVcxm+/R5Yf8oi0aZqTJlnLYS +wXcBykxTx181s7WyfJ49WwrYXo78zTDAnf1ma0fPq3e4mpspvyndLh1a+OarHa1e +aT0DIIYk7qeEa1YcVljx2KyLd0r1BBAfrwyGaEPVeJQVYWaOJRU2we/KD4ojf9s= +-----END CERTIFICATE----- diff --git a/src/node/test/end_to_end_test.js b/src/node/test/end_to_end_test.js new file mode 100644 index 0000000000..40bb5f3bbd --- /dev/null +++ b/src/node/test/end_to_end_test.js @@ -0,0 +1,201 @@ +/* + * + * Copyright 2014, 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. + * + */ + +var assert = require('assert'); +var grpc = require('bindings')('grpc.node'); +var port_picker = require('../port_picker'); + +/** + * 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(); + } + }; +} + +describe('end-to-end', function() { + it('should start and end a request without error', function(complete) { + port_picker.nextAvailablePort(function(port) { + var server = new grpc.Server(); + var done = multiDone(function() { + complete(); + server.shutdown(); + }, 2); + server.addHttp2Port(port); + var channel = new grpc.Channel(port); + var deadline = new Date(); + deadline.setSeconds(deadline.getSeconds() + 3); + var status_text = 'xyz'; + var call = new grpc.Call(channel, + 'dummy_method', + deadline); + call.startInvoke(function(event) { + assert.strictEqual(event.type, + grpc.completionType.INVOKE_ACCEPTED); + + call.writesDone(function(event) { + assert.strictEqual(event.type, + grpc.completionType.FINISH_ACCEPTED); + assert.strictEqual(event.data, grpc.opError.OK); + }); + },function(event) { + assert.strictEqual(event.type, + grpc.completionType.CLIENT_METADATA_READ); + },function(event) { + assert.strictEqual(event.type, grpc.completionType.FINISHED); + var status = event.data; + assert.strictEqual(status.code, grpc.status.OK); + assert.strictEqual(status.details, status_text); + done(); + }, 0); + + server.start(); + server.requestCall(function(event) { + assert.strictEqual(event.type, grpc.completionType.SERVER_RPC_NEW); + var server_call = event.call; + assert.notEqual(server_call, null); + server_call.serverAccept(function(event) { + assert.strictEqual(event.type, grpc.completionType.FINISHED); + }, 0); + server_call.serverEndInitialMetadata(0); + server_call.startWriteStatus( + grpc.status.OK, + status_text, + function(event) { + assert.strictEqual(event.type, + grpc.completionType.FINISH_ACCEPTED); + assert.strictEqual(event.data, grpc.opError.OK); + done(); + }); + }); + }); + }); + + it('should send and receive data without error', function(complete) { + port_picker.nextAvailablePort(function(port) { + var req_text = 'client_request'; + var reply_text = 'server_response'; + var server = new grpc.Server(); + var done = multiDone(function() { + complete(); + server.shutdown(); + }, 6); + server.addHttp2Port(port); + var channel = new grpc.Channel(port); + var deadline = new Date(); + deadline.setSeconds(deadline.getSeconds() + 3); + var status_text = 'success'; + var call = new grpc.Call(channel, + 'dummy_method', + deadline); + call.startInvoke(function(event) { + assert.strictEqual(event.type, + grpc.completionType.INVOKE_ACCEPTED); + call.startWrite( + new Buffer(req_text), + function(event) { + assert.strictEqual(event.type, + grpc.completionType.WRITE_ACCEPTED); + assert.strictEqual(event.data, grpc.opError.OK); + call.writesDone(function(event) { + assert.strictEqual(event.type, + grpc.completionType.FINISH_ACCEPTED); + assert.strictEqual(event.data, grpc.opError.OK); + done(); + }); + }, 0); + call.startRead(function(event) { + assert.strictEqual(event.type, grpc.completionType.READ); + assert.strictEqual(event.data.toString(), reply_text); + done(); + }); + },function(event) { + assert.strictEqual(event.type, + grpc.completionType.CLIENT_METADATA_READ); + done(); + },function(event) { + assert.strictEqual(event.type, grpc.completionType.FINISHED); + var status = event.data; + assert.strictEqual(status.code, grpc.status.OK); + assert.strictEqual(status.details, status_text); + done(); + }, 0); + + server.start(); + server.requestCall(function(event) { + assert.strictEqual(event.type, grpc.completionType.SERVER_RPC_NEW); + var server_call = event.call; + assert.notEqual(server_call, null); + server_call.serverAccept(function(event) { + assert.strictEqual(event.type, grpc.completionType.FINISHED); + done(); + }); + server_call.serverEndInitialMetadata(0); + server_call.startRead(function(event) { + assert.strictEqual(event.type, grpc.completionType.READ); + assert.strictEqual(event.data.toString(), req_text); + server_call.startWrite( + new Buffer(reply_text), + function(event) { + assert.strictEqual(event.type, + grpc.completionType.WRITE_ACCEPTED); + assert.strictEqual(event.data, + grpc.opError.OK); + server_call.startWriteStatus( + grpc.status.OK, + status_text, + function(event) { + assert.strictEqual(event.type, + grpc.completionType.FINISH_ACCEPTED); + assert.strictEqual(event.data, grpc.opError.OK); + done(); + }); + }, 0); + }); + }); + }); + }); +}); diff --git a/src/node/test/math_client_test.js b/src/node/test/math_client_test.js new file mode 100644 index 0000000000..f3697aca98 --- /dev/null +++ b/src/node/test/math_client_test.js @@ -0,0 +1,209 @@ +/* + * + * Copyright 2014, 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. + * + */ + +var assert = require('assert'); +var client = require('../surface_client.js'); +var ProtoBuf = require('protobufjs'); +var port_picker = require('../port_picker'); + +var builder = ProtoBuf.loadProtoFile(__dirname + '/../examples/math.proto'); +var math = builder.build('math'); + +/** + * Get a function that deserializes a specific type of protobuf. + * @param {function()} cls The constructor of the message type to deserialize + * @return {function(Buffer):cls} The deserialization function + */ +function deserializeCls(cls) { + /** + * Deserialize a buffer to a message object + * @param {Buffer} arg_buf The buffer to deserialize + * @return {cls} The resulting object + */ + return function deserialize(arg_buf) { + return cls.decode(arg_buf); + }; +} + +/** + * Serialize an object to a buffer + * @param {*} arg The object to serialize + * @return {Buffer} The serialized object + */ +function serialize(arg) { + return new Buffer(arg.encode().toBuffer()); +} + +/** + * Sends a Div request on the channel. + * @param {client.Channel} channel The channel on which to make the request + * @param {DivArg} argument The argument to the call. Should be serializable + * with serialize + * @param {function(?Error, value=)} The callback to for when the response is + * received + * @param {array=} Array of metadata key/value pairs to add to the call + * @param {(number|Date)=} deadline The deadline for processing this request. + * Defaults to infinite future + * @return {EventEmitter} An event emitter for stream related events + */ +var div = client.makeUnaryRequestFunction( + '/Math/Div', + serialize, + deserializeCls(math.DivReply)); + +/** + * Sends a Fib request on the channel. + * @param {client.Channel} channel The channel on which to make the request + * @param {*} argument The argument to the call. Should be serializable with + * serialize + * @param {array=} Array of metadata key/value pairs to add to the call + * @param {(number|Date)=} deadline The deadline for processing this request. + * Defaults to infinite future + * @return {EventEmitter} An event emitter for stream related events + */ +var fib = client.makeServerStreamRequestFunction( + '/Math/Fib', + serialize, + deserializeCls(math.Num)); + +/** + * Sends a Sum request on the channel. + * @param {client.Channel} channel The channel on which to make the request + * @param {function(?Error, value=)} The callback to for when the response is + * received + * @param {array=} Array of metadata key/value pairs to add to the call + * @param {(number|Date)=} deadline The deadline for processing this request. + * Defaults to infinite future + * @return {EventEmitter} An event emitter for stream related events + */ +var sum = client.makeClientStreamRequestFunction( + '/Math/Sum', + serialize, + deserializeCls(math.Num)); + +/** + * Sends a DivMany request on the channel. + * @param {client.Channel} channel The channel on which to make the request + * @param {array=} Array of metadata key/value pairs to add to the call + * @param {(number|Date)=} deadline The deadline for processing this request. + * Defaults to infinite future + * @return {EventEmitter} An event emitter for stream related events + */ +var divMany = client.makeBidiStreamRequestFunction( + '/Math/DivMany', + serialize, + deserializeCls(math.DivReply)); + +/** + * Channel to use to make requests to a running server. + */ +var channel; + +/** + * Server to test against + */ +var server = require('../examples/math_server.js'); + + +describe('Math client', function() { + before(function(done) { + port_picker.nextAvailablePort(function(port) { + server.bind(port).listen(); + channel = new client.Channel(port); + done(); + }); + }); + after(function() { + server.shutdown(); + }); + it('should handle a single request', function(done) { + var arg = new math.DivArgs({dividend: 7, divisor: 4}); + var call = div(channel, arg, function handleDivResult(err, value) { + assert.ifError(err); + assert.equal(value.get('quotient'), 1); + assert.equal(value.get('remainder'), 3); + }); + call.on('status', function checkStatus(status) { + assert.strictEqual(status.code, client.status.OK); + done(); + }); + }); + it('should handle a server streaming request', function(done) { + var arg = new math.FibArgs({limit: 7}); + var call = fib(channel, arg); + var expected_results = [1, 1, 2, 3, 5, 8, 13]; + var next_expected = 0; + call.on('data', function checkResponse(value) { + assert.equal(value.get('num'), expected_results[next_expected]); + next_expected += 1; + }); + call.on('status', function checkStatus(status) { + assert.strictEqual(status.code, client.status.OK); + done(); + }); + }); + it('should handle a client streaming request', function(done) { + var call = sum(channel, function handleSumResult(err, value) { + assert.ifError(err); + assert.equal(value.get('num'), 21); + }); + for (var i = 0; i < 7; i++) { + call.write(new math.Num({'num': i})); + } + call.end(); + call.on('status', function checkStatus(status) { + assert.strictEqual(status.code, client.status.OK); + done(); + }); + }); + it('should handle a bidirectional streaming request', function(done) { + function checkResponse(index, value) { + assert.equal(value.get('quotient'), index); + assert.equal(value.get('remainder'), 1); + } + var call = divMany(channel); + var response_index = 0; + call.on('data', function(value) { + checkResponse(response_index, value); + response_index += 1; + }); + for (var i = 0; i < 7; i++) { + call.write(new math.DivArgs({dividend: 2 * i + 1, divisor: 2})); + } + call.end(); + call.on('status', function checkStatus(status) { + assert.strictEqual(status.code, client.status.OK); + done(); + }); + }); +}); diff --git a/src/node/test/server_test.js b/src/node/test/server_test.js new file mode 100644 index 0000000000..79f7b32948 --- /dev/null +++ b/src/node/test/server_test.js @@ -0,0 +1,121 @@ +/* + * + * Copyright 2014, 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. + * + */ + +var assert = require('assert'); +var grpc = require('bindings')('grpc.node'); +var Server = require('../server'); +var port_picker = require('../port_picker'); + +/** + * 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(); + } + }; +} + +/** + * Responds to every request with the same data as a response + * @param {Stream} stream + */ +function echoHandler(stream) { + stream.pipe(stream); +} + +describe('echo server', function() { + it('should echo inputs as responses', function(done) { + done = multiDone(done, 4); + port_picker.nextAvailablePort(function(port) { + var server = new Server(); + server.bind(port); + server.register('echo', echoHandler); + server.start(); + + var req_text = 'echo test string'; + var status_text = 'OK'; + + var channel = new grpc.Channel(port); + var deadline = new Date(); + deadline.setSeconds(deadline.getSeconds() + 3); + var call = new grpc.Call(channel, + 'echo', + deadline); + call.startInvoke(function(event) { + assert.strictEqual(event.type, + grpc.completionType.INVOKE_ACCEPTED); + call.startWrite( + new Buffer(req_text), + function(event) { + assert.strictEqual(event.type, + grpc.completionType.WRITE_ACCEPTED); + assert.strictEqual(event.data, grpc.opError.OK); + call.writesDone(function(event) { + assert.strictEqual(event.type, + grpc.completionType.FINISH_ACCEPTED); + assert.strictEqual(event.data, grpc.opError.OK); + done(); + }); + }, 0); + call.startRead(function(event) { + assert.strictEqual(event.type, grpc.completionType.READ); + assert.strictEqual(event.data.toString(), req_text); + done(); + }); + },function(event) { + assert.strictEqual(event.type, + grpc.completionType.CLIENT_METADATA_READ); + done(); + },function(event) { + assert.strictEqual(event.type, grpc.completionType.FINISHED); + var status = event.data; + assert.strictEqual(status.code, grpc.status.OK); + assert.strictEqual(status.details, status_text); + server.shutdown(); + done(); + }, 0); + }); + }); +}); |