kibana/src/plugins/expressions/common/executor/executor.test.ts
2020-10-30 06:01:45 +01:00

229 lines
7.4 KiB
TypeScript

/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { Executor } from './executor';
import * as expressionTypes from '../expression_types';
import * as expressionFunctions from '../expression_functions';
import { Execution } from '../execution';
import { ExpressionAstFunction, parseExpression } from '../ast';
import { MigrateFunction } from '../../../kibana_utils/common/persistable_state';
describe('Executor', () => {
test('can instantiate', () => {
new Executor();
});
describe('type registry', () => {
test('can register a type', () => {
const executor = new Executor();
executor.registerType(expressionTypes.datatable);
});
test('can register all types', () => {
const executor = new Executor();
for (const type of expressionTypes.typeSpecs) executor.registerType(type);
});
test('can retrieve all types', () => {
const executor = new Executor();
executor.registerType(expressionTypes.datatable);
const types = executor.getTypes();
expect(Object.keys(types)).toEqual(['datatable']);
});
test('can retrieve all types - 2', () => {
const executor = new Executor();
for (const type of expressionTypes.typeSpecs) executor.registerType(type);
const types = executor.getTypes();
expect(Object.keys(types).sort()).toEqual(
expressionTypes.typeSpecs.map((spec) => spec.name).sort()
);
});
});
describe('function registry', () => {
test('can register a function', () => {
const executor = new Executor();
executor.registerFunction(expressionFunctions.clog);
});
test('can register all functions', () => {
const executor = new Executor();
for (const functionDefinition of expressionFunctions.functionSpecs)
executor.registerFunction(functionDefinition);
});
test('can retrieve all functions', () => {
const executor = new Executor();
executor.registerFunction(expressionFunctions.clog);
const functions = executor.getFunctions();
expect(Object.keys(functions)).toEqual(['clog']);
});
test('can retrieve all functions - 2', () => {
const executor = new Executor();
for (const functionDefinition of expressionFunctions.functionSpecs)
executor.registerFunction(functionDefinition);
const functions = executor.getFunctions();
expect(Object.keys(functions).sort()).toEqual(
expressionFunctions.functionSpecs.map((spec) => spec.name).sort()
);
});
});
describe('context', () => {
test('context is empty by default', () => {
const executor = new Executor();
expect(executor.context).toEqual({});
});
test('can extend context', () => {
const executor = new Executor();
executor.extendContext({
foo: 'bar',
});
expect(executor.context).toEqual({
foo: 'bar',
});
});
test('can extend context multiple times with multiple keys', () => {
const executor = new Executor();
const abortSignal = {};
const env = {};
executor.extendContext({
foo: 'bar',
});
executor.extendContext({
abortSignal,
env,
});
expect(executor.context).toEqual({
foo: 'bar',
abortSignal,
env,
});
});
});
describe('execution', () => {
describe('createExecution()', () => {
test('returns Execution object from string', () => {
const executor = new Executor();
const execution = executor.createExecution('foo bar="baz"');
expect(execution).toBeInstanceOf(Execution);
expect(execution.state.get().ast.chain[0].function).toBe('foo');
});
test('returns Execution object from AST', () => {
const executor = new Executor();
const ast = parseExpression('foo bar="baz"');
const execution = executor.createExecution(ast);
expect(execution).toBeInstanceOf(Execution);
expect(execution.state.get().ast.chain[0].function).toBe('foo');
});
test('Execution inherits context from Executor', () => {
const executor = new Executor();
const foo = {};
executor.extendContext({ foo });
const execution = executor.createExecution('foo bar="baz"');
expect((execution.context as any).foo).toBe(foo);
});
});
});
describe('.inject', () => {
const executor = new Executor();
const injectFn = jest.fn().mockImplementation((args, references) => args);
const extractFn = jest.fn().mockReturnValue({ args: {}, references: [] });
const migrateFn = jest.fn().mockImplementation((args) => args);
const fooFn = {
name: 'foo',
help: 'test',
args: {
bar: {
types: ['string'],
help: 'test',
},
},
extract: (state: ExpressionAstFunction['arguments']) => {
return extractFn(state);
},
inject: (state: ExpressionAstFunction['arguments']) => {
return injectFn(state);
},
migrations: {
'7.10.0': (((state: ExpressionAstFunction, version: string): ExpressionAstFunction => {
return migrateFn(state, version);
}) as any) as MigrateFunction,
'7.10.1': (((state: ExpressionAstFunction, version: string): ExpressionAstFunction => {
return migrateFn(state, version);
}) as any) as MigrateFunction,
},
fn: jest.fn(),
};
executor.registerFunction(fooFn);
test('calls inject function for every expression function in expression', () => {
executor.inject(
parseExpression('foo bar="baz" | foo bar={foo bar="baz" | foo bar={foo bar="baz"}}'),
[]
);
expect(injectFn).toBeCalledTimes(5);
});
describe('.extract', () => {
test('calls extract function for every expression function in expression', () => {
executor.extract(
parseExpression('foo bar="baz" | foo bar={foo bar="baz" | foo bar={foo bar="baz"}}')
);
expect(extractFn).toBeCalledTimes(5);
});
});
describe('.migrate', () => {
test('calls migrate function for every expression function in expression', () => {
executor.migrate(
parseExpression('foo bar="baz" | foo bar={foo bar="baz" | foo bar={foo bar="baz"}}'),
'7.10.0'
);
expect(migrateFn).toBeCalledTimes(5);
});
});
describe('.migrateToLatest', () => {
test('calls extract function for every expression function in expression', () => {
migrateFn.mockClear();
executor.migrateToLatest(
parseExpression('foo bar="baz" | foo bar={foo bar="baz" | foo bar={foo bar="baz"}}'),
'7.10.0'
);
expect(migrateFn).toBeCalledTimes(10);
});
});
});
});