mirror of https://github.com/grpc/grpc-node.git
193 lines
7.1 KiB
TypeScript
193 lines
7.1 KiB
TypeScript
import * as assert from 'assert';
|
|
import * as path from 'path';
|
|
import { FileDescriptorProto, IFileDescriptorProto } from 'protobufjs/ext/descriptor';
|
|
import * as protoLoader from '@grpc/proto-loader';
|
|
|
|
import { ReflectionV1Implementation } from '../src/implementations/reflection-v1';
|
|
|
|
describe('GrpcReflectionService', () => {
|
|
let reflectionService: ReflectionV1Implementation;
|
|
|
|
beforeEach(async () => {
|
|
const root = protoLoader.loadSync(path.join(__dirname, '../proto/sample/sample.proto'), {
|
|
includeDirs: [path.join(__dirname, '../proto/sample/vendor')]
|
|
});
|
|
|
|
reflectionService = new ReflectionV1Implementation(root);
|
|
});
|
|
|
|
describe('listServices()', () => {
|
|
it('lists all services', () => {
|
|
const { service: services } = reflectionService.listServices('*');
|
|
assert.equal(services.length, 2);
|
|
assert(services.find((s) => s.name === 'sample.SampleService'));
|
|
});
|
|
|
|
it('whitelists services properly', () => {
|
|
const root = protoLoader.loadSync(path.join(__dirname, '../proto/sample/sample.proto'), {
|
|
includeDirs: [path.join(__dirname, '../proto/sample/vendor')]
|
|
});
|
|
|
|
reflectionService = new ReflectionV1Implementation(root, { services: ['sample.SampleService'] });
|
|
|
|
const { service: services } = reflectionService.listServices('*');
|
|
assert.equal(services.length, 1);
|
|
assert(services.find((s) => s.name === 'sample.SampleService'));
|
|
});
|
|
});
|
|
|
|
describe('fileByFilename()', () => {
|
|
it('finds files with transitive dependencies', () => {
|
|
const descriptors = reflectionService
|
|
.fileByFilename('sample.proto')
|
|
.fileDescriptorProto.map(f => FileDescriptorProto.decode(f) as IFileDescriptorProto);
|
|
|
|
const names = descriptors.map((desc) => desc.name);
|
|
assert.deepEqual(
|
|
new Set(names),
|
|
new Set(['sample.proto', 'vendor.proto', 'vendor_dependency.proto'])
|
|
);
|
|
});
|
|
|
|
it('finds files with fewer transitive dependencies', () => {
|
|
const descriptors = reflectionService
|
|
.fileByFilename('vendor.proto')
|
|
.fileDescriptorProto.map(f => FileDescriptorProto.decode(f) as IFileDescriptorProto);
|
|
|
|
const names = descriptors.map((desc) => desc.name);
|
|
assert.deepEqual(new Set(names), new Set(['vendor.proto', 'vendor_dependency.proto']));
|
|
});
|
|
|
|
it('finds files with no transitive dependencies', () => {
|
|
const descriptors = reflectionService
|
|
.fileByFilename('vendor_dependency.proto')
|
|
.fileDescriptorProto.map(f => FileDescriptorProto.decode(f) as IFileDescriptorProto);
|
|
|
|
assert.equal(descriptors.length, 1);
|
|
assert.equal(descriptors[0].name, 'vendor_dependency.proto');
|
|
});
|
|
|
|
it('merges files based on package name', () => {
|
|
const descriptors = reflectionService
|
|
.fileByFilename('vendor.proto')
|
|
.fileDescriptorProto.map(f => FileDescriptorProto.decode(f) as IFileDescriptorProto);
|
|
|
|
const names = descriptors.map((desc) => desc.name);
|
|
assert(!names.includes('common.proto')); // file merged into vendor.proto
|
|
});
|
|
|
|
it('errors with no file found', () => {
|
|
assert.throws(
|
|
() => reflectionService.fileByFilename('nonexistent.proto'),
|
|
'Proto file not found',
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('fileContainingSymbol()', () => {
|
|
it('finds symbols and returns transitive file dependencies', () => {
|
|
const descriptors = reflectionService
|
|
.fileContainingSymbol('sample.HelloRequest')
|
|
.fileDescriptorProto.map(f => FileDescriptorProto.decode(f) as IFileDescriptorProto);
|
|
|
|
const names = descriptors.map((desc) => desc.name);
|
|
assert.deepEqual(
|
|
new Set(names),
|
|
new Set(['sample.proto', 'vendor.proto', 'vendor_dependency.proto']),
|
|
);
|
|
});
|
|
|
|
it('finds imported message types', () => {
|
|
const descriptors = reflectionService
|
|
.fileContainingSymbol('vendor.CommonMessage')
|
|
.fileDescriptorProto.map(f => FileDescriptorProto.decode(f) as IFileDescriptorProto);
|
|
|
|
const names = descriptors.map((desc) => desc.name);
|
|
assert.deepEqual(new Set(names), new Set(['vendor.proto', 'vendor_dependency.proto']));
|
|
});
|
|
|
|
it('finds transitively imported message types', () => {
|
|
const descriptors = reflectionService
|
|
.fileContainingSymbol('vendor.dependency.DependentMessage')
|
|
.fileDescriptorProto.map(f => FileDescriptorProto.decode(f) as IFileDescriptorProto);
|
|
|
|
assert.equal(descriptors.length, 1);
|
|
assert.equal(descriptors[0].name, 'vendor_dependency.proto');
|
|
});
|
|
|
|
it('finds nested message types', () => {
|
|
const descriptors = reflectionService
|
|
.fileContainingSymbol('sample.HelloRequest.HelloNested')
|
|
.fileDescriptorProto.map(f => FileDescriptorProto.decode(f) as IFileDescriptorProto);
|
|
|
|
const names = descriptors.map((desc) => desc.name);
|
|
assert.deepEqual(
|
|
new Set(names),
|
|
new Set(['sample.proto', 'vendor.proto', 'vendor_dependency.proto']),
|
|
);
|
|
});
|
|
|
|
it('merges files based on package name', () => {
|
|
const descriptors = reflectionService
|
|
.fileContainingSymbol('vendor.CommonMessage')
|
|
.fileDescriptorProto.map(f => FileDescriptorProto.decode(f) as IFileDescriptorProto);
|
|
|
|
const names = descriptors.map((desc) => desc.name);
|
|
assert(!names.includes('common.proto')); // file merged into vendor.proto
|
|
});
|
|
|
|
it('errors with no symbol found', () => {
|
|
assert.throws(
|
|
() => reflectionService.fileContainingSymbol('non.existant.symbol'),
|
|
'Symbol not found:',
|
|
);
|
|
});
|
|
|
|
it('resolves references to method types', () => {
|
|
const descriptors = reflectionService
|
|
.fileContainingSymbol('sample.SampleService.Hello2')
|
|
.fileDescriptorProto.map(f => FileDescriptorProto.decode(f) as IFileDescriptorProto);
|
|
|
|
const names = descriptors.map((desc) => desc.name);
|
|
assert.deepEqual(
|
|
new Set(names),
|
|
new Set(['sample.proto', 'vendor.proto', 'vendor_dependency.proto']),
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('fileContainingExtension()', () => {
|
|
it('finds extensions and returns transitive file dependencies', () => {
|
|
const descriptors = reflectionService
|
|
.fileContainingExtension('.vendor.CommonMessage', 101)
|
|
.fileDescriptorProto.map(f => FileDescriptorProto.decode(f) as IFileDescriptorProto);
|
|
|
|
const names = descriptors.map((desc) => desc.name);
|
|
assert.deepEqual(new Set(names), new Set(['vendor.proto', 'vendor_dependency.proto']));
|
|
});
|
|
|
|
it('errors with no symbol found', () => {
|
|
assert.throws(
|
|
() => reflectionService.fileContainingExtension('non.existant.symbol', 0),
|
|
'Extension not found',
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('allExtensionNumbersOfType()', () => {
|
|
it('finds extensions and returns transitive file dependencies', () => {
|
|
const response = reflectionService.allExtensionNumbersOfType('.vendor.CommonMessage');
|
|
|
|
assert.equal(response.extensionNumber.length, 1);
|
|
assert.equal(response.extensionNumber[0], 101);
|
|
});
|
|
|
|
it('errors with no symbol found', () => {
|
|
assert.throws(
|
|
() => reflectionService.allExtensionNumbersOfType('non.existant.symbol'),
|
|
'Extensions not found',
|
|
);
|
|
});
|
|
});
|
|
});
|