Adds many more static workflow parser tests (#30)
This commit is contained in:
parent
e47c1ddb80
commit
c879e9b78f
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name": "seira-frontend",
|
||||
"name": "pipelines-frontend",
|
||||
"version": "0.1.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
|
|
@ -594,15 +594,6 @@
|
|||
"@types/react-router": "4.0.31"
|
||||
}
|
||||
},
|
||||
"@types/react-swipeable-views": {
|
||||
"version": "0.12.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-swipeable-views/-/react-swipeable-views-0.12.2.tgz",
|
||||
"integrity": "sha512-c+OFdmEMUtdGeADR7OmnIUTNoejJTBjO64vBPFkdIpKiABS+DXtUHCzM34U/+Wy7FGeLoPHpKGnm2tcMMst/LA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/react": "16.4.14"
|
||||
}
|
||||
},
|
||||
"@types/react-test-renderer": {
|
||||
"version": "16.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-16.0.2.tgz",
|
||||
|
|
@ -11354,41 +11345,6 @@
|
|||
"whatwg-fetch": "2.0.3"
|
||||
}
|
||||
},
|
||||
"react-swipeable-views": {
|
||||
"version": "0.13.0",
|
||||
"resolved": "https://registry.npmjs.org/react-swipeable-views/-/react-swipeable-views-0.13.0.tgz",
|
||||
"integrity": "sha512-r6H8lbtcI99oKykpLxYrI6O9im1lJ4D5/hf8bkNeQLdHZ9ftxS03qgEtguy3GpT5VB9yS4gErYWeaTrhCrysEg==",
|
||||
"requires": {
|
||||
"@babel/runtime": "7.0.0",
|
||||
"dom-helpers": "3.3.1",
|
||||
"prop-types": "15.6.2",
|
||||
"react-swipeable-views-core": "0.13.0",
|
||||
"react-swipeable-views-utils": "0.13.0",
|
||||
"warning": "4.0.2"
|
||||
}
|
||||
},
|
||||
"react-swipeable-views-core": {
|
||||
"version": "0.13.0",
|
||||
"resolved": "https://registry.npmjs.org/react-swipeable-views-core/-/react-swipeable-views-core-0.13.0.tgz",
|
||||
"integrity": "sha512-MAe119eSN4obiqsIp+qoUWtLbyjz+dWEfz+qPurPvyIFoXxuxpBnsDy36+C7cBaCi5z4dRmfoMlm1dBAdIzvig==",
|
||||
"requires": {
|
||||
"@babel/runtime": "7.0.0",
|
||||
"warning": "4.0.2"
|
||||
}
|
||||
},
|
||||
"react-swipeable-views-utils": {
|
||||
"version": "0.13.0",
|
||||
"resolved": "https://registry.npmjs.org/react-swipeable-views-utils/-/react-swipeable-views-utils-0.13.0.tgz",
|
||||
"integrity": "sha512-1I4BhDqA6qkRdW0nexnudh/QdvVAVy0a7M5OyU2TrjaTovg6ufBouzqfqjZfUZUxVdOftTkPtisHmcqqZ+b1TA==",
|
||||
"requires": {
|
||||
"@babel/runtime": "7.0.0",
|
||||
"fbjs": "0.8.17",
|
||||
"keycode": "2.2.0",
|
||||
"prop-types": "15.6.2",
|
||||
"react-event-listener": "0.6.3",
|
||||
"react-swipeable-views-core": "0.13.0"
|
||||
}
|
||||
},
|
||||
"react-test-renderer": {
|
||||
"version": "16.5.2",
|
||||
"resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.5.2.tgz",
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { createGraph } from './StaticGraphParser';
|
||||
import { createGraph, getNodeInfo } from './StaticGraphParser';
|
||||
|
||||
describe('StaticGraphParser', () => {
|
||||
|
||||
|
|
@ -24,21 +24,66 @@ describe('StaticGraphParser', () => {
|
|||
entrypoint: 'template-1',
|
||||
templates: [
|
||||
{
|
||||
dag: {
|
||||
tasks: [
|
||||
{
|
||||
name: 'task-1',
|
||||
template: 'task-1',
|
||||
},
|
||||
],
|
||||
},
|
||||
dag: { tasks: [{ name: 'task-1', template: 'task-1', },], },
|
||||
name: 'template-1',
|
||||
},
|
||||
{
|
||||
container: {},
|
||||
name: 'container-1',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// In this pipeline, the conditionals are the templates: 'condition-1' and 'condition-2'
|
||||
// 'condition-1-child' and 'condition-2-child' are not displayed in the static graph, but they
|
||||
// are used by the parser to properly connect the nodes.
|
||||
function newConditionalWorkflow(): any {
|
||||
return {
|
||||
spec: {
|
||||
entrypoint: 'pipeline-flip-coin',
|
||||
templates: [
|
||||
{
|
||||
name: 'condition-1',
|
||||
steps: [[{
|
||||
name: 'condition-1-child',
|
||||
template: 'condition-1-child',
|
||||
when: '{{inputs.parameters.flip-output}} == heads'
|
||||
}]]
|
||||
},
|
||||
{ dag: { tasks: [{ name: 'heads', template: 'heads' }] }, name: 'condition-1-child' },
|
||||
{
|
||||
name: 'condition-2',
|
||||
steps: [[{
|
||||
name: 'condition-2-child',
|
||||
template: 'condition-2-child',
|
||||
when: '{{inputs.parameters.flip-output}} == tails'
|
||||
}]]
|
||||
},
|
||||
{ dag: { tasks: [{ name: 'tails', template: 'tails' }] }, name: 'condition-2-child' },
|
||||
{
|
||||
dag: {
|
||||
tasks: [
|
||||
{ dependencies: ['flip'], name: 'condition-1', template: 'condition-1' },
|
||||
{ dependencies: ['flip'], name: 'condition-2', template: 'condition-2' },
|
||||
{ name: 'flip', template: 'flip' }
|
||||
]
|
||||
},
|
||||
name: 'pipeline-flip-coin'
|
||||
},
|
||||
{
|
||||
container: { args: [ /* omitted */], command: ['sh', '-c'], },
|
||||
name: 'flip',
|
||||
outputs: { parameters: [{ name: 'flip-output', valueFrom: { path: '/tmp/output' } }] }
|
||||
},
|
||||
{ container: { command: ['echo', '\'heads\''], }, name: 'heads', },
|
||||
{ container: { command: ['echo', '\'tails\''], }, name: 'tails', },
|
||||
]
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
describe('createGraph', () => {
|
||||
it('Creates a single node with no edges for a workflow with one step.', () => {
|
||||
const workflow = newWorkflow();
|
||||
|
|
@ -79,6 +124,56 @@ describe('StaticGraphParser', () => {
|
|||
expect(g.edges()[0].w).toBe('task-2');
|
||||
});
|
||||
|
||||
it('Shows conditional nodes without adding conditional children as nodes', () => {
|
||||
const g = createGraph(newConditionalWorkflow());
|
||||
expect(g.nodeCount()).toBe(5);
|
||||
['flip', 'condition-1', 'condition-2', 'heads', 'tails'].forEach((nodeName) => {
|
||||
expect(g.nodes()).toContain(nodeName);
|
||||
});
|
||||
});
|
||||
|
||||
it('Connects conditional graph correctly', () => {
|
||||
const g = createGraph(newConditionalWorkflow());
|
||||
// 'flip' has two possible outcomes: 'condition-1' and 'condition-2'
|
||||
expect(g.edges()[0].v).toBe('flip');
|
||||
expect(g.edges()[0].w).toBe('condition-1');
|
||||
expect(g.edges()[1].v).toBe('flip');
|
||||
expect(g.edges()[1].w).toBe('condition-2');
|
||||
// condition-1 means the 'heads' node will run
|
||||
expect(g.edges()[2].v).toBe('condition-1');
|
||||
expect(g.edges()[2].w).toBe('heads');
|
||||
// condition-2 means the 'tails' node will run
|
||||
expect(g.edges()[3].v).toBe('condition-2');
|
||||
expect(g.edges()[3].w).toBe('tails');
|
||||
// Confirm there are no other nodes or edges
|
||||
expect(g.nodeCount()).toBe(5);
|
||||
expect(g.edgeCount()).toBe(4);
|
||||
});
|
||||
|
||||
it('Finds conditionals and colors them', () => {
|
||||
const g = createGraph(newConditionalWorkflow());
|
||||
g.nodes().forEach((nodeName) => {
|
||||
const node = g.node(nodeName);
|
||||
if (nodeName.startsWith('condition')) {
|
||||
expect(node.bgColor).toBe('cornsilk');
|
||||
} else {
|
||||
expect(node.bgColor).toBeUndefined();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('Renders extremely simple workflows with no steps or DAGs', () => {
|
||||
const simpleWorkflow = {
|
||||
spec: {
|
||||
entrypoint: 'template-1',
|
||||
templates: [ { container: {}, name: 'template-1', }, ],
|
||||
},
|
||||
} as any;
|
||||
const g = createGraph(simpleWorkflow);
|
||||
expect(g.nodeCount()).toBe(1);
|
||||
expect(g.edgeCount()).toBe(0);
|
||||
});
|
||||
|
||||
// This test covers the way that compiled Pipelines handle DSL-defined exit-handlers.
|
||||
// These exit-handlers are different from Argo's 'onExit'.
|
||||
// The compiled Pipelines will contain 1 DAG for the entry point, that has a single task which
|
||||
|
|
@ -129,7 +224,7 @@ describe('StaticGraphParser', () => {
|
|||
expect(() => createGraph(workflow)).toThrowError('Pipeline had template with multiple steps.');
|
||||
});
|
||||
|
||||
it('Returns an error message if the workflow spec has a template with multiple steps', () => {
|
||||
it('Throws an error message if the workflow spec has a template with multiple steps', () => {
|
||||
const workflow = {
|
||||
spec: {
|
||||
templates: [
|
||||
|
|
@ -147,4 +242,320 @@ describe('StaticGraphParser', () => {
|
|||
expect(() => createGraph(workflow)).toThrowError('Pipeline had template with multiple steps');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getNodeInfo', () => {
|
||||
it('Returns nodeInfo containing only \'unknown\' nodeType if nodeId is undefined', () => {
|
||||
const nodeInfo = getNodeInfo(newWorkflow(), undefined);
|
||||
expect(nodeInfo).toEqual({ nodeType: 'unknown' });
|
||||
});
|
||||
|
||||
it('Returns nodeInfo containing only \'unknown\' nodeType if nodeId is empty', () => {
|
||||
const nodeInfo = getNodeInfo(newWorkflow(), '');
|
||||
expect(nodeInfo).toEqual({ nodeType: 'unknown' });
|
||||
});
|
||||
|
||||
it('Returns nodeInfo containing only \'unknown\' nodeType if workflow is undefined', () => {
|
||||
const nodeInfo = getNodeInfo(undefined, 'someId');
|
||||
expect(nodeInfo).toEqual({ nodeType: 'unknown' });
|
||||
});
|
||||
|
||||
it('Returns nodeInfo containing only \'unknown\' nodeType if workflow.spec is undefined', () => {
|
||||
const workflow = newWorkflow();
|
||||
workflow.spec = undefined;
|
||||
const nodeInfo = getNodeInfo(workflow, 'someId');
|
||||
expect(nodeInfo).toEqual({ nodeType: 'unknown' });
|
||||
});
|
||||
|
||||
it('Returns nodeInfo containing only \'unknown\' nodeType if workflow.spec.templates is undefined', () => {
|
||||
const workflow = newWorkflow();
|
||||
workflow.spec.templates = undefined;
|
||||
const nodeInfo = getNodeInfo(workflow, 'someId');
|
||||
expect(nodeInfo).toEqual({ nodeType: 'unknown' });
|
||||
});
|
||||
|
||||
it('Returns nodeInfo containing only \'unknown\' nodeType if no template matches provided ID', () => {
|
||||
const workflow = {
|
||||
spec: {
|
||||
entrypoint: 'template-1',
|
||||
templates: [
|
||||
{
|
||||
container: {},
|
||||
name: 'template-1',
|
||||
},
|
||||
],
|
||||
},
|
||||
} as any;
|
||||
const nodeInfo = getNodeInfo(workflow, 'id-not-in-spec');
|
||||
expect(nodeInfo).toEqual({ nodeType: 'unknown' });
|
||||
});
|
||||
|
||||
it('Returns nodeInfo containing only \'unknown\' nodeType if template matching provided ID has no container or steps', () => {
|
||||
const workflow = {
|
||||
spec: {
|
||||
entrypoint: 'template-1',
|
||||
templates: [
|
||||
{
|
||||
// No container or steps here
|
||||
name: 'template-1',
|
||||
},
|
||||
],
|
||||
},
|
||||
} as any;
|
||||
const nodeInfo = getNodeInfo(workflow, 'template-1');
|
||||
expect(nodeInfo).toEqual({ nodeType: 'unknown' });
|
||||
});
|
||||
|
||||
it('Returns nodeInfo containing only \'unknown\' nodeType if template matching provided ID has no container and empty steps', () => {
|
||||
const workflow = {
|
||||
spec: {
|
||||
entrypoint: 'template-1',
|
||||
templates: [
|
||||
{
|
||||
// No container here
|
||||
name: 'template-1',
|
||||
steps: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
} as any;
|
||||
const nodeInfo = getNodeInfo(workflow, 'template-1');
|
||||
expect(nodeInfo).toEqual({ nodeType: 'unknown' });
|
||||
});
|
||||
|
||||
it('Returns nodeInfo containing only \'unknown\' nodeType if template matching provided ID has no container and array of empty steps', () => {
|
||||
const workflow = {
|
||||
spec: {
|
||||
entrypoint: 'template-1',
|
||||
templates: [
|
||||
{
|
||||
// No container here
|
||||
name: 'template-1',
|
||||
steps: [[]],
|
||||
},
|
||||
],
|
||||
},
|
||||
} as any;
|
||||
const nodeInfo = getNodeInfo(workflow, 'template-1');
|
||||
expect(nodeInfo).toEqual({ nodeType: 'unknown' });
|
||||
});
|
||||
|
||||
it('Returns nodeInfo containing container args', () => {
|
||||
const workflow = {
|
||||
spec: {
|
||||
entrypoint: 'template-1',
|
||||
templates: [
|
||||
{
|
||||
container: {
|
||||
args: ['arg1', 'arg2'],
|
||||
},
|
||||
name: 'template-1',
|
||||
},
|
||||
],
|
||||
},
|
||||
} as any;
|
||||
const nodeInfo = getNodeInfo(workflow, 'template-1');
|
||||
expect(nodeInfo.nodeType).toBe('container');
|
||||
expect(nodeInfo.containerInfo!.args).toEqual(['arg1', 'arg2']);
|
||||
});
|
||||
|
||||
it('Returns nodeInfo containing container commands', () => {
|
||||
const workflow = {
|
||||
spec: {
|
||||
entrypoint: 'template-1',
|
||||
templates: [
|
||||
{
|
||||
container: {
|
||||
command: ['command1', 'command2']
|
||||
},
|
||||
name: 'template-1',
|
||||
},
|
||||
],
|
||||
},
|
||||
} as any;
|
||||
const nodeInfo = getNodeInfo(workflow, 'template-1');
|
||||
expect(nodeInfo.nodeType).toBe('container');
|
||||
expect(nodeInfo.containerInfo!.command).toEqual(['command1', 'command2']);
|
||||
});
|
||||
|
||||
it('Returns nodeInfo containing container image name', () => {
|
||||
const workflow = {
|
||||
spec: {
|
||||
entrypoint: 'template-1',
|
||||
templates: [
|
||||
{
|
||||
container: {
|
||||
image: 'someImageID'
|
||||
},
|
||||
name: 'template-1',
|
||||
},
|
||||
],
|
||||
},
|
||||
} as any;
|
||||
const nodeInfo = getNodeInfo(workflow, 'template-1');
|
||||
expect(nodeInfo.nodeType).toBe('container');
|
||||
expect(nodeInfo.containerInfo!.image).toBe('someImageID');
|
||||
});
|
||||
|
||||
it('Returns nodeInfo containing container inputs as list of name/value duples', () => {
|
||||
const workflow = {
|
||||
spec: {
|
||||
entrypoint: 'template-1',
|
||||
templates: [
|
||||
{
|
||||
container: {},
|
||||
inputs: {
|
||||
parameters: [
|
||||
{ name: 'param1', value: 'val1' },
|
||||
{ name: 'param2', value: 'val2' },
|
||||
],
|
||||
},
|
||||
name: 'template-1',
|
||||
},
|
||||
],
|
||||
},
|
||||
} as any;
|
||||
const nodeInfo = getNodeInfo(workflow, 'template-1');
|
||||
expect(nodeInfo.nodeType).toBe('container');
|
||||
expect(nodeInfo.containerInfo!.inputs).toEqual([['param1', 'val1'], ['param2', 'val2']]);
|
||||
});
|
||||
|
||||
it('Returns empty strings for inputs with no specified value', () => {
|
||||
const workflow = {
|
||||
spec: {
|
||||
entrypoint: 'template-1',
|
||||
templates: [
|
||||
{
|
||||
container: {},
|
||||
inputs: {
|
||||
parameters: [
|
||||
{ name: 'param1' },
|
||||
{ name: 'param2' },
|
||||
],
|
||||
},
|
||||
name: 'template-1',
|
||||
},
|
||||
],
|
||||
},
|
||||
} as any;
|
||||
const nodeInfo = getNodeInfo(workflow, 'template-1');
|
||||
expect(nodeInfo.nodeType).toBe('container');
|
||||
expect(nodeInfo.containerInfo!.inputs).toEqual([['param1', ''], ['param2', '']]);
|
||||
});
|
||||
|
||||
it('Returns nodeInfo containing container outputs as list of name/value duples, pulling from valueFrom if necessary', () => {
|
||||
const workflow = {
|
||||
spec: {
|
||||
entrypoint: 'template-1',
|
||||
templates: [
|
||||
{
|
||||
container: {},
|
||||
name: 'template-1',
|
||||
outputs: {
|
||||
parameters: [
|
||||
{ name: 'param1', value: 'val1' },
|
||||
{ name: 'param2', valueFrom: { jsonPath: 'jsonPath' } },
|
||||
{ name: 'param3', valueFrom: { path: 'path' } },
|
||||
{ name: 'param4', valueFrom: { parameter: 'parameterReference' } },
|
||||
{ name: 'param5', valueFrom: { jqFilter: 'jqFilter' } },
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
} as any;
|
||||
const nodeInfo = getNodeInfo(workflow, 'template-1');
|
||||
expect(nodeInfo.nodeType).toBe('container');
|
||||
expect(nodeInfo.containerInfo!.outputs).toEqual([
|
||||
['param1', 'val1'],
|
||||
['param2', 'jsonPath'],
|
||||
['param3', 'path'],
|
||||
['param4', 'parameterReference'],
|
||||
['param5', 'jqFilter'],
|
||||
]);
|
||||
});
|
||||
|
||||
it('Returns empty strings for outputs with no specified value', () => {
|
||||
const workflow = {
|
||||
spec: {
|
||||
entrypoint: 'template-1',
|
||||
templates: [
|
||||
{
|
||||
container: {},
|
||||
name: 'template-1',
|
||||
outputs: {
|
||||
parameters: [
|
||||
{ name: 'param1' },
|
||||
{ name: 'param2' },
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
} as any;
|
||||
const nodeInfo = getNodeInfo(workflow, 'template-1');
|
||||
expect(nodeInfo.nodeType).toBe('container');
|
||||
expect(nodeInfo.containerInfo!.outputs).toEqual([['param1', ''], ['param2', '']]);
|
||||
});
|
||||
|
||||
it('Returns nodeType \'steps\' when node template has steps', () => {
|
||||
const workflow = {
|
||||
spec: {
|
||||
entrypoint: 'template-1',
|
||||
templates: [
|
||||
{
|
||||
name: 'template-1',
|
||||
steps: [[
|
||||
'something',
|
||||
]]
|
||||
},
|
||||
],
|
||||
},
|
||||
} as any;
|
||||
const nodeInfo = getNodeInfo(workflow, 'template-1');
|
||||
expect(nodeInfo.nodeType).toBe('steps');
|
||||
expect(nodeInfo.stepsInfo).toEqual({ conditional: '', parameters: [[]]);
|
||||
});
|
||||
|
||||
it('Returns nodeInfo with step template\'s conditional when node template has \'when\' property', () => {
|
||||
const workflow = {
|
||||
spec: {
|
||||
entrypoint: 'template-1',
|
||||
templates: [
|
||||
{
|
||||
name: 'template-1',
|
||||
steps: [[ { when: '{{someVar}} == something' } ]]
|
||||
},
|
||||
],
|
||||
},
|
||||
} as any;
|
||||
const nodeInfo = getNodeInfo(workflow, 'template-1');
|
||||
expect(nodeInfo.nodeType).toBe('steps');
|
||||
expect(nodeInfo.stepsInfo).toEqual({ conditional: '{{someVar}} == something', parameters: [[]]);
|
||||
});
|
||||
|
||||
it('Returns nodeInfo with step template\'s arguments when node template has \'when\' property', () => {
|
||||
const workflow = {
|
||||
spec: {
|
||||
entrypoint: 'template-1',
|
||||
templates: [
|
||||
{
|
||||
name: 'template-1',
|
||||
steps: [[{
|
||||
arguments: {
|
||||
parameters: [
|
||||
{ name: 'param1', value: 'val1' },
|
||||
{ name: 'param2', value: 'val2' },
|
||||
],
|
||||
},
|
||||
}]]
|
||||
},
|
||||
],
|
||||
},
|
||||
} as any;
|
||||
const nodeInfo = getNodeInfo(workflow, 'template-1');
|
||||
expect(nodeInfo.nodeType).toBe('steps');
|
||||
expect(nodeInfo.stepsInfo)
|
||||
.toEqual({ conditional: '', parameters: [['param1', 'val1'], ['param2', 'val2']]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue