Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: enable testing for grpc-js #127

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 103 additions & 35 deletions test/any_grpc.js
Original file line number Diff line number Diff line change
@@ -1,39 +1,107 @@
// TODO: Instead of attempting to expose both implementations of gRPC in
// a single object, the tests should be re-written in a way that makes it clear
// that two separate implementations are being tested against one another.

const _ = require('lodash');

function getImplementation(globalField) {
if (global[globalField] !== 'js' && global[globalField] !== 'native') {
throw new Error([
`Invalid value for global.${globalField}: ${global.globalField}.`,
'If running from the command line, please --require a fixture first.'
].join(' '));
const assert = require('assert');
const callerId = require('caller-id');
const path = require('path');
const semver = require('semver');
const shimmer = require('shimmer');
const Module = require('module');

const http2Available = semver.satisfies(process.version, '8.x');

// Handle Promise rejections by failing
process.on('unhandledRejection', err => assert.ifError(err));

const grpcProtobuf = require('../packages/grpc-protobufjs');

/**
* Returns a function that appears to be similar to require(), but substitutes
* the return value of require('grpc') to grpcImpl.
*
* If a module does call require('grpc'), its entry will be deleted in the
* require cache, to prevent cross-contamination between "client-side" and
* "server-side" modules.
*
* There are likely other subtle differences between this function and require
* itself, but we assume that this doesn't matter for our tests.
*
* @param grpcImpl The gRPC implementation to use.
*/
const requireAsFn = (grpcImpl) => (p) => {
// Use caller-id to get information about the file where requireAs*
// was called, so we can adjust the input path to be relative to this
// file path instead.
if (p.startsWith('.')) {
p = path.resolve(path.dirname(callerId.getData().filePath), p);
}
const impl = global[globalField];
return {
surface: require(`../packages/grpc-${impl}`),
pjson: require(`../packages/grpc-${impl}/package.json`),
core: require(`../packages/grpc-${impl}-core`),
corePjson: require(`../packages/grpc-${impl}-core/package.json`)
};
// Wrap Module._load, which is called by require(), to short-circuit when
// called with 'grpc'.
shimmer.wrap(Module, '_load', (moduleLoad) => {
const uncache = new Set();
return function Module_load(path, parent) {
if (path.startsWith('grpc')) {
// Mark the module that required 'grpc' to have its entry deleted from
// the require cache.
uncache.add(parent.filename);
return grpcImpl;
} else {
const result = moduleLoad.apply(this, arguments);
// Get the path of the loaded module.
const filename = Module._resolveFilename.apply(this, arguments);
// If this module called require('grpc'), immediately delete its entry
// from the cache.
if (uncache.has(filename)) {
uncache.delete(filename);
delete require.cache[filename];
}
return result;
}
}
});
const result = require(p);
shimmer.unwrap(Module, '_load');
delete require.cache[p];
return result;
}

// Load implementations

const implementations = {
js: http2Available ? require('../packages/grpc-js') : {},
native: require('../packages/grpc-native')
}

const clientImpl = getImplementation('_client_implementation');
const serverImpl = getImplementation('_server_implementation');

// We export a "merged" gRPC API by merging client and server specified
// APIs together. Any function that is unspecific to client/server defaults
// to client-side implementation.
// This object also has a test-only field from which details about the
// modules may be read.
module.exports = Object.assign({
'$implementationInfo': {
client: clientImpl,
server: serverImpl
const versions = {
js: require('../packages/grpc-js-core/package').version,
native: require('../packages/grpc-native-core/package').version
}

const server = implementations[global._server_implementation];
const client = implementations[global._client_implementation];
const serverVersion = versions[global._server_implementation];
const clientVersion = versions[global._client_implementation];

if (!client || !server) {
throw new Error('If running from the command line, please --require a ' +
'fixture in ./fixtures first.');
}

// prefer requireAs* instead of these.
Object.assign(server, grpcProtobuf(server));
Object.assign(client, grpcProtobuf(client));

module.exports = {
server,
client,
serverVersion,
clientVersion,
requireAsServer: requireAsFn(server),
requireAsClient: requireAsFn(client),
runAsServer: (fn) => fn(server),
runAsClient: (fn) => fn(client),
skipIfJsClient: (mochaVerb) => {
if (client === implementations.js) {
return mochaVerb.skip;
} else {
return mochaVerb;
}
}
}, clientImpl.surface, _.pick(serverImpl.surface, [
'Server',
'ServerCredentials'
]));
};
14 changes: 7 additions & 7 deletions test/api/async_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@

var assert = require('assert');

var grpc = require('../any_grpc');
var math = grpc.load(
var anyGrpc = require('../any_grpc');
var math = anyGrpc.client.load(
__dirname + '/../../packages/grpc-native-core/deps/grpc/src/proto/math/math.proto').math;


Expand All @@ -33,21 +33,21 @@ var math_client;
/**
* Server to test against
*/
var getServer = require('./math/math_server.js');
var getServer = anyGrpc.requireAsServer('./math/math_server.js');

var server = getServer();

describe('Async functionality', function() {
before(function(done) {
var port_num = server.bind('0.0.0.0:0',
grpc.ServerCredentials.createInsecure());
anyGrpc.server.ServerCredentials.createInsecure());
server.start();
math_client = new math.Math('localhost:' + port_num,
grpc.credentials.createInsecure());
anyGrpc.client.credentials.createInsecure());
done();
});
after(function() {
grpc.closeClient(math_client);
anyGrpc.client.closeClient(math_client);
server.forceShutdown();
});
it('should not hang', function(done) {
Expand Down Expand Up @@ -77,7 +77,7 @@ describe('Async functionality', function() {
});

call.on('status', function checkStatus(status) {
assert.strictEqual(status.code, grpc.status.OK);
assert.strictEqual(status.code, anyGrpc.client.status.OK);
done();
});
});
Expand Down
6 changes: 4 additions & 2 deletions test/api/credentials_api_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ var assert = require('assert');
var fs = require('fs');
var path = require('path');

var grpc = require('../any_grpc');
var anyGrpc = require('../any_grpc');

var key_data, pem_data, ca_data;

Expand All @@ -36,6 +36,7 @@ before(function() {
});

describe('channel credentials', function() {
var grpc = anyGrpc.requireAsClient('grpc');
describe('#createSsl', function() {
it('works with no arguments', function() {
var creds;
Expand Down Expand Up @@ -92,6 +93,7 @@ describe('channel credentials', function() {
});

describe('server credentials', function() {
var grpc = anyGrpc.requireAsServer('grpc');
describe('#createSsl', function() {
it('accepts a buffer and array as the first 2 arguments', function() {
var creds;
Expand Down Expand Up @@ -162,4 +164,4 @@ describe('server credentials', function() {
}, TypeError);
});
});
});
});
Loading