noflo 1.4.3 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/.ecrc +3 -0
  2. package/.eslintignore +2 -0
  3. package/{CHANGES.md → CHANGELOG.md} +527 -527
  4. package/README.md +1 -1
  5. package/bin/noflo-cache-preheat +4 -4
  6. package/components/Graph.d.ts +50 -15
  7. package/components/Graph.js +94 -68
  8. package/examples/http/HelloController.js +9 -6
  9. package/examples/spreadsheet/parse.fbp +3 -3
  10. package/lib/AsCallback.d.ts +22 -9
  11. package/lib/AsCallback.js +69 -18
  12. package/lib/AsComponent.d.ts +1 -1
  13. package/lib/AsComponent.js +5 -3
  14. package/lib/BaseNetwork.d.ts +16 -6
  15. package/lib/BaseNetwork.js +65 -31
  16. package/lib/BasePort.d.ts +39 -12
  17. package/lib/BasePort.js +34 -6
  18. package/lib/Component.d.ts +8 -8
  19. package/lib/Component.js +23 -20
  20. package/lib/ComponentLoader.d.ts +3 -4
  21. package/lib/ComponentLoader.js +9 -10
  22. package/lib/IP.d.ts +12 -8
  23. package/lib/IP.js +6 -4
  24. package/lib/InPort.d.ts +64 -9
  25. package/lib/InPort.js +72 -13
  26. package/lib/InternalSocket.d.ts +53 -7
  27. package/lib/InternalSocket.js +51 -14
  28. package/lib/LegacyNetwork.d.ts +12 -2
  29. package/lib/LegacyNetwork.js +5 -5
  30. package/lib/Network.d.ts +13 -2
  31. package/lib/Network.js +10 -10
  32. package/lib/NoFlo.d.ts +48 -13
  33. package/lib/NoFlo.js +55 -27
  34. package/lib/OutPort.d.ts +64 -13
  35. package/lib/OutPort.js +73 -15
  36. package/lib/Platform.d.ts +1 -1
  37. package/lib/Platform.js +9 -4
  38. package/lib/Ports.d.ts +11 -12
  39. package/lib/Ports.js +8 -4
  40. package/lib/ProcessInput.d.ts +5 -9
  41. package/lib/ProcessInput.js +8 -9
  42. package/lib/ProcessOutput.d.ts +2 -2
  43. package/lib/ProcessOutput.js +5 -5
  44. package/lib/loader/NodeJs.d.ts +0 -1
  45. package/lib/loader/NodeJs.js +152 -161
  46. package/lib/loader/register.d.ts +1 -1
  47. package/lib/loader/register.js +8 -4
  48. package/package.json +25 -16
  49. package/spec/.eslintrc +5 -2
  50. package/spec/AsCallback.js +9 -13
  51. package/spec/AsComponent.js +10 -4
  52. package/spec/AsPromise.js +38 -0
  53. package/spec/CommonJS.cjs +10 -0
  54. package/spec/ComponentLoader.js +19 -7
  55. package/spec/ESModule.mjs +11 -0
  56. package/spec/Network.js +32 -11
  57. package/spec/NetworkSync.js +892 -0
  58. package/spec/Scoping.js +27 -42
  59. package/spec/Subgraph.js +6 -11
  60. package/spec/fixtures/componentloader/components/Output.js +1 -1
  61. package/spec/fixtures/componentloader/components/Repeat.ts +1 -1
  62. package/spec/fixtures/componentloader/components/RepeatAsync.coffee +1 -1
  63. package/spec/fixtures/componentloader/node_modules/example/components/Forward.js +1 -1
  64. package/spec/fixtures/componentloader/node_modules/example/package.json +1 -1
  65. package/spec/fixtures/componentloader/package.json +2 -1
  66. package/spec/fixtures/componentloader/spec/Repeat.yaml +1 -1
  67. package/spec/utils/inject.js +1 -1
  68. package/src/.eslintrc +9 -2
  69. package/src/components/Graph.js +105 -71
  70. package/src/lib/AsCallback.js +71 -16
  71. package/src/lib/AsComponent.js +5 -4
  72. package/src/lib/BaseNetwork.js +48 -15
  73. package/src/lib/BasePort.js +43 -9
  74. package/src/lib/Component.js +8 -8
  75. package/src/lib/ComponentLoader.js +3 -4
  76. package/src/lib/IP.js +7 -4
  77. package/src/lib/InPort.js +74 -13
  78. package/src/lib/InternalSocket.js +49 -9
  79. package/src/lib/LegacyNetwork.js +2 -2
  80. package/src/lib/Network.js +2 -2
  81. package/src/lib/NoFlo.js +43 -13
  82. package/src/lib/OutPort.js +77 -14
  83. package/src/lib/Platform.js +9 -4
  84. package/src/lib/Ports.js +6 -2
  85. package/src/lib/ProcessInput.js +7 -9
  86. package/src/lib/ProcessOutput.js +1 -1
  87. package/src/lib/loader/NodeJs.js +185 -178
  88. package/src/lib/loader/register.js +2 -2
  89. /package/{karma.config.js → karma.config.cjs} +0 -0
  90. /package/{webpack.config.js → webpack.config.cjs} +0 -0
@@ -0,0 +1,892 @@
1
+ describe('NoFlo Network (synchronous delivery)', () => {
2
+ const Split = () => new noflo.Component({
3
+ inPorts: {
4
+ in: { datatype: 'all' },
5
+ },
6
+ outPorts: {
7
+ out: { datatype: 'all' },
8
+ },
9
+ process(input, output) {
10
+ output.sendDone({ out: input.get('in') });
11
+ },
12
+ });
13
+ const Merge = () => new noflo.Component({
14
+ inPorts: {
15
+ in: { datatype: 'all' },
16
+ },
17
+ outPorts: {
18
+ out: { datatype: 'all' },
19
+ },
20
+ process(input, output) {
21
+ output.sendDone({ out: input.get('in') });
22
+ },
23
+ });
24
+ const Callback = () => new noflo.Component({
25
+ inPorts: {
26
+ in: { datatype: 'all' },
27
+ callback: {
28
+ datatype: 'all',
29
+ control: true,
30
+ },
31
+ },
32
+ process(input, output) {
33
+ // Drop brackets
34
+ if (!input.hasData('callback', 'in')) { return; }
35
+ const cb = input.getData('callback');
36
+ const data = input.getData('in');
37
+ cb(data);
38
+ output.done();
39
+ },
40
+ });
41
+ describe('with an empty graph', () => {
42
+ let g = null;
43
+ let n = null;
44
+ before(() => {
45
+ g = new noflo.Graph();
46
+ return noflo.createNetwork(g, {
47
+ subscribeGraph: false,
48
+ asyncDelivery: false,
49
+ delay: true,
50
+ baseDir,
51
+ })
52
+ .then((network) => {
53
+ n = network;
54
+ return n.connect();
55
+ });
56
+ });
57
+ it('should initially be marked as stopped', () => {
58
+ chai.expect(n.isStarted()).to.equal(false);
59
+ });
60
+ it('should initially have no processes', () => {
61
+ chai.expect(n.processes).to.be.empty;
62
+ });
63
+ it('should initially have no active processes', () => {
64
+ chai.expect(n.getActiveProcesses()).to.eql([]);
65
+ });
66
+ it('should initially have to connections', () => {
67
+ chai.expect(n.connections).to.be.empty;
68
+ });
69
+ it('should initially have no IIPs', () => {
70
+ chai.expect(n.initials).to.be.empty;
71
+ });
72
+ it('should have reference to the graph', () => {
73
+ chai.expect(n.graph).to.equal(g);
74
+ });
75
+ it('should know its baseDir', () => {
76
+ chai.expect(n.baseDir).to.equal(baseDir);
77
+ });
78
+ it('should have a ComponentLoader', () => {
79
+ chai.expect(n.loader).to.be.an('object');
80
+ });
81
+ it('should have transmitted the baseDir to the Component Loader', () => {
82
+ chai.expect(n.loader.baseDir).to.equal(baseDir);
83
+ });
84
+ it('should be able to list components', function () {
85
+ this.timeout(60 * 1000);
86
+ return n.loader.listComponents()
87
+ .then((components) => {
88
+ chai.expect(components).to.be.an('object');
89
+ });
90
+ });
91
+ it('should have an uptime', () => {
92
+ chai.expect(n.uptime()).to.be.at.least(0);
93
+ });
94
+ describe('with new node', () => {
95
+ it('should contain the node', () => n
96
+ .addNode({
97
+ id: 'Graph',
98
+ component: 'Graph',
99
+ metadata: {
100
+ foo: 'Bar',
101
+ },
102
+ }));
103
+ it('should have registered the node with the graph', () => {
104
+ const node = g.getNode('Graph');
105
+ chai.expect(node).to.be.an('object');
106
+ chai.expect(node.component).to.equal('Graph');
107
+ });
108
+ it('should have transmitted the node metadata to the process', () => {
109
+ chai.expect(n.processes.Graph.component.metadata).to.exist;
110
+ chai.expect(n.processes.Graph.component.metadata).to.be.an('object');
111
+ chai.expect(n.processes.Graph.component.metadata).to.eql(g.getNode('Graph').metadata);
112
+ });
113
+ it('adding the same node again should be a no-op', () => {
114
+ const originalProcess = n.getNode('Graph');
115
+ const graphNode = g.getNode('Graph');
116
+ return n.addNode(graphNode)
117
+ .then((newProcess) => {
118
+ chai.expect(newProcess).to.equal(originalProcess);
119
+ });
120
+ });
121
+ it('should not contain the node after removal', () => n.removeNode({
122
+ id: 'Graph',
123
+ })
124
+ .then(() => {
125
+ chai.expect(n.processes).to.be.empty;
126
+ }));
127
+ it('should have removed the node from the graph', () => {
128
+ const node = g.getNode('graph');
129
+ chai.expect(node).to.be.a('null');
130
+ });
131
+ it('should fail when removing the removed node again', () => n.removeNode({
132
+ id: 'Graph',
133
+ })
134
+ .then(
135
+ () => Promise.reject(new Error('Unexpected success')),
136
+ (err) => {
137
+ chai.expect(err).to.be.an('error');
138
+ chai.expect(err.message).to.contain('not found');
139
+ },
140
+ ));
141
+ });
142
+ describe('with new edge', () => {
143
+ before(() => {
144
+ n.loader.components.Split = Split;
145
+ return n.addNode({
146
+ id: 'A',
147
+ component: 'Split',
148
+ })
149
+ .then(() => n.addNode({
150
+ id: 'B',
151
+ component: 'Split',
152
+ }));
153
+ });
154
+ after(() => n.removeNode({
155
+ id: 'A',
156
+ })
157
+ .then(() => n.removeNode({
158
+ id: 'B',
159
+ })));
160
+ it('should contain the edge', () => n.addEdge({
161
+ from: {
162
+ node: 'A',
163
+ port: 'out',
164
+ },
165
+ to: {
166
+ node: 'B',
167
+ port: 'in',
168
+ },
169
+ })
170
+ .then(() => {
171
+ chai.expect(n.connections).not.to.be.empty;
172
+ chai.expect(n.connections[0].from).to.eql({
173
+ process: n.getNode('A'),
174
+ port: 'out',
175
+ index: undefined,
176
+ });
177
+ chai.expect(n.connections[0].to).to.eql({
178
+ process: n.getNode('B'),
179
+ port: 'in',
180
+ index: undefined,
181
+ });
182
+ }));
183
+ it('should have registered the edge with the graph', () => {
184
+ const edge = g.getEdge('A', 'out', 'B', 'in');
185
+ chai.expect(edge).to.not.be.a('null');
186
+ });
187
+ it('should not contain the edge after removal', () => n.removeEdge({
188
+ from: {
189
+ node: 'A',
190
+ port: 'out',
191
+ },
192
+ to: {
193
+ node: 'B',
194
+ port: 'in',
195
+ },
196
+ })
197
+ .then(() => {
198
+ chai.expect(n.connections).to.be.empty;
199
+ }));
200
+ it('should have removed the edge from the graph', () => {
201
+ const edge = g.getEdge('A', 'out', 'B', 'in');
202
+ chai.expect(edge).to.be.a('null');
203
+ });
204
+ });
205
+ });
206
+ describe('with a simple graph', () => {
207
+ let g = null;
208
+ let n = null;
209
+ before(function () {
210
+ this.timeout(60 * 1000);
211
+ g = new noflo.Graph();
212
+ g.addNode('Merge', 'Merge');
213
+ g.addNode('Callback', 'Callback');
214
+ g.addEdge('Merge', 'out', 'Callback', 'in');
215
+ g.addInitial(
216
+ (data) => {
217
+ chai.expect(data).to.equal('Foo');
218
+ },
219
+ 'Callback',
220
+ 'callback',
221
+ );
222
+ g.addInitial('Foo', 'Merge', 'in');
223
+ return noflo.createNetwork(g, {
224
+ subscribeGraph: false,
225
+ asyncDelivery: false,
226
+ delay: true,
227
+ baseDir,
228
+ })
229
+ .then((nw) => {
230
+ nw.loader.components.Split = Split;
231
+ nw.loader.components.Merge = Merge;
232
+ nw.loader.components.Callback = Callback;
233
+ n = nw;
234
+ return nw.connect();
235
+ });
236
+ });
237
+ it('should send some initials when started', () => {
238
+ chai.expect(n.initials).not.to.be.empty;
239
+ return n.start();
240
+ });
241
+ it('should contain two processes', () => {
242
+ chai.expect(n.processes).to.not.be.empty;
243
+ chai.expect(n.processes.Merge).to.exist;
244
+ chai.expect(n.processes.Merge).to.be.an('Object');
245
+ chai.expect(n.processes.Callback).to.exist;
246
+ chai.expect(n.processes.Callback).to.be.an('Object');
247
+ });
248
+ it('the ports of the processes should know the node names', () => {
249
+ Object.keys(n.processes.Callback.component.inPorts.ports).forEach((name) => {
250
+ const port = n.processes.Callback.component.inPorts.ports[name];
251
+ chai.expect(port.name).to.equal(name);
252
+ chai.expect(port.node).to.equal('Callback');
253
+ chai.expect(port.getId()).to.equal(`Callback ${name.toUpperCase()}`);
254
+ });
255
+ Object.keys(n.processes.Callback.component.outPorts.ports).forEach((name) => {
256
+ const port = n.processes.Callback.component.outPorts.ports[name];
257
+ chai.expect(port.name).to.equal(name);
258
+ chai.expect(port.node).to.equal('Callback');
259
+ chai.expect(port.getId()).to.equal(`Callback ${name.toUpperCase()}`);
260
+ });
261
+ });
262
+ it('should contain 1 connection between processes and 2 for IIPs', () => {
263
+ chai.expect(n.connections).to.not.be.empty;
264
+ chai.expect(n.connections.length).to.equal(3);
265
+ });
266
+ it('should have started in debug mode', () => {
267
+ chai.expect(n.debug).to.equal(true);
268
+ chai.expect(n.getDebug()).to.equal(true);
269
+ });
270
+ it('should emit a process-error when a component throws', () => Promise.resolve()
271
+ .then(() => n.removeInitial({
272
+ to: {
273
+ node: 'Callback',
274
+ port: 'callback',
275
+ },
276
+ }))
277
+ .then(() => n.removeInitial({
278
+ to: {
279
+ node: 'Merge',
280
+ port: 'in',
281
+ },
282
+ }))
283
+ .then(() => n.addInitial({
284
+ from: {
285
+ data() { throw new Error('got Foo'); },
286
+ },
287
+ to: {
288
+ node: 'Callback',
289
+ port: 'callback',
290
+ },
291
+ }))
292
+ .then(() => n.addInitial({
293
+ from: {
294
+ data: 'Foo',
295
+ },
296
+ to: {
297
+ node: 'Merge',
298
+ port: 'in',
299
+ },
300
+ }))
301
+ .then(() => new Promise((resolve, reject) => {
302
+ n.once('process-error', (err) => {
303
+ chai.expect(err).to.be.an('object');
304
+ chai.expect(err.id).to.equal('Callback');
305
+ chai.expect(err.metadata).to.be.an('object');
306
+ chai.expect(err.error).to.be.an('error');
307
+ chai.expect(err.error.message).to.equal('got Foo');
308
+ resolve();
309
+ });
310
+ n.sendInitials().catch(reject);
311
+ })));
312
+ describe('with a renamed node', () => {
313
+ it('should have the process in a new location', () => n.renameNode('Callback', 'Func')
314
+ .then(() => {
315
+ chai.expect(n.processes.Func).to.be.an('object');
316
+ }));
317
+ it('shouldn\'t have the process in the old location', () => {
318
+ chai.expect(n.processes.Callback).to.be.undefined;
319
+ });
320
+ it('should have updated the name in the graph', () => {
321
+ chai.expect(n.getNode('Callback')).to.not.exist;
322
+ chai.expect(n.getNode('Func')).to.exist;
323
+ });
324
+ it('should fail to rename with the old name', () => n.renameNode('Callback', 'Func')
325
+ .then(
326
+ () => Promise.reject(new Error('Unexpected success')),
327
+ (err) => {
328
+ chai.expect(err).to.be.an('error');
329
+ chai.expect(err.message).to.contain('not found');
330
+ },
331
+ ));
332
+ it('should have informed the ports of their new node name', () => {
333
+ Object.keys(n.processes.Func.component.inPorts.ports).forEach((name) => {
334
+ const port = n.processes.Func.component.inPorts.ports[name];
335
+ chai.expect(port.name).to.equal(name);
336
+ chai.expect(port.node).to.equal('Func');
337
+ chai.expect(port.getId()).to.equal(`Func ${name.toUpperCase()}`);
338
+ });
339
+ Object.keys(n.processes.Func.component.outPorts.ports).forEach((name) => {
340
+ const port = n.processes.Func.component.outPorts.ports[name];
341
+ chai.expect(port.name).to.equal(name);
342
+ chai.expect(port.node).to.equal('Func');
343
+ chai.expect(port.getId()).to.equal(`Func ${name.toUpperCase()}`);
344
+ });
345
+ });
346
+ });
347
+ describe('with process icon change', () => {
348
+ it('should emit an icon event', (done) => {
349
+ n.once('icon', (data) => {
350
+ chai.expect(data).to.be.an('object');
351
+ chai.expect(data.id).to.equal('Func');
352
+ chai.expect(data.icon).to.equal('flask');
353
+ done();
354
+ });
355
+ n.processes.Func.component.setIcon('flask');
356
+ });
357
+ });
358
+ describe('once stopped', () => {
359
+ it('should be marked as stopped', () => n.stop()
360
+ .then(() => {
361
+ chai.expect(n.isStarted()).to.equal(false);
362
+ }));
363
+ });
364
+ describe('without the delay option', () => {
365
+ it('should auto-start', (done) => {
366
+ g.removeInitial('Func', 'callback');
367
+ noflo.graph.loadJSON(g.toJSON())
368
+ .then((graph) => {
369
+ // Pass the already-initialized component loader
370
+ graph.addInitial(
371
+ (data) => {
372
+ chai.expect(data).to.equal('Foo');
373
+ done();
374
+ },
375
+ 'Func',
376
+ 'callback',
377
+ );
378
+ return noflo.createNetwork(graph, {
379
+ subscribeGraph: false,
380
+ asyncDelivery: false,
381
+ delay: false,
382
+ componentLoader: n.loader,
383
+ });
384
+ })
385
+ .catch(done);
386
+ });
387
+ });
388
+ });
389
+ describe('with nodes containing default ports', () => {
390
+ let g = null;
391
+ let testCallback = null;
392
+ let c = null;
393
+ let cb = null;
394
+
395
+ beforeEach(() => {
396
+ testCallback = null;
397
+ c = null;
398
+ cb = null;
399
+
400
+ c = new noflo.Component();
401
+ c.inPorts.add('in', {
402
+ required: true,
403
+ datatype: 'string',
404
+ default: 'default-value',
405
+ });
406
+ c.outPorts.add('out');
407
+ c.process((input, output) => {
408
+ output.sendDone(input.get('in'));
409
+ });
410
+ cb = new noflo.Component();
411
+ cb.inPorts.add('in', {
412
+ required: true,
413
+ datatype: 'all',
414
+ });
415
+ cb.process((input) => {
416
+ if (!input.hasData('in')) { return; }
417
+ testCallback(input.getData('in'));
418
+ });
419
+ g = new noflo.Graph();
420
+ g.addNode('Def', 'Def');
421
+ g.addNode('Cb', 'Cb');
422
+ g.addEdge('Def', 'out', 'Cb', 'in');
423
+ });
424
+ it('should send default values to nodes without an edge', function (done) {
425
+ this.timeout(60 * 1000);
426
+ testCallback = function (data) {
427
+ chai.expect(data).to.equal('default-value');
428
+ done();
429
+ };
430
+ noflo.createNetwork(g, {
431
+ subscribeGraph: false,
432
+ asyncDelivery: false,
433
+ delay: true,
434
+ baseDir,
435
+ })
436
+ .then((nw) => {
437
+ nw.loader.components.Def = () => c;
438
+ nw.loader.components.Cb = () => cb;
439
+ return nw.connect();
440
+ })
441
+ .then((nw) => nw.start())
442
+ .catch(done);
443
+ });
444
+ it('should not send default values to nodes with an edge', function (done) {
445
+ this.timeout(60 * 1000);
446
+ testCallback = function (data) {
447
+ chai.expect(data).to.equal('from-edge');
448
+ done();
449
+ };
450
+ g.addNode('Merge', 'Merge');
451
+ g.addEdge('Merge', 'out', 'Def', 'in');
452
+ g.addInitial('from-edge', 'Merge', 'in');
453
+ noflo.createNetwork(g, {
454
+ subscribeGraph: false,
455
+ asyncDelivery: false,
456
+ delay: true,
457
+ baseDir,
458
+ })
459
+ .then((nw) => {
460
+ nw.loader.components.Def = () => c;
461
+ nw.loader.components.Cb = () => cb;
462
+ nw.loader.components.Merge = Merge;
463
+ return nw.connect();
464
+ })
465
+ .then((nw) => nw.start())
466
+ .catch(done);
467
+ });
468
+ it('should not send default values to nodes with IIP', function (done) {
469
+ this.timeout(60 * 1000);
470
+ testCallback = function (data) {
471
+ chai.expect(data).to.equal('from-IIP');
472
+ done();
473
+ };
474
+ g.addInitial('from-IIP', 'Def', 'in');
475
+ noflo.createNetwork(g, {
476
+ subscribeGraph: false,
477
+ asyncDelivery: false,
478
+ delay: true,
479
+ baseDir,
480
+ })
481
+ .then((nw) => {
482
+ nw.loader.components.Def = () => c;
483
+ nw.loader.components.Cb = () => cb;
484
+ nw.loader.components.Merge = Merge;
485
+ return nw.connect();
486
+ })
487
+ .then((nw) => nw.start())
488
+ .catch(done);
489
+ });
490
+ });
491
+ describe('with an existing IIP', () => {
492
+ let g = null;
493
+ let n = null;
494
+ before(() => {
495
+ g = new noflo.Graph()
496
+ .addNode('Callback', 'Callback')
497
+ .addNode('Repeat', 'Split')
498
+ .addEdge('Repeat', 'out', 'Callback', 'in');
499
+ });
500
+ it('should call the Callback with the original IIP value', function (done) {
501
+ this.timeout(6000);
502
+ const cb = function (packet) {
503
+ chai.expect(packet).to.equal('Foo');
504
+ done();
505
+ };
506
+ g.addInitial(cb, 'Callback', 'callback');
507
+ g.addInitial('Foo', 'Repeat', 'in');
508
+ setTimeout(() => {
509
+ noflo.createNetwork(g, {
510
+ delay: true,
511
+ subscribeGraph: false,
512
+ asyncDelivery: false,
513
+ baseDir,
514
+ })
515
+ .then((nw) => {
516
+ nw.loader.components.Split = Split;
517
+ nw.loader.components.Merge = Merge;
518
+ nw.loader.components.Callback = Callback;
519
+ n = nw;
520
+ return nw.connect();
521
+ })
522
+ .then((nw) => nw.start())
523
+ .catch(done);
524
+ }, 10);
525
+ });
526
+ it('should allow removing the IIPs', () => Promise.resolve()
527
+ .then(() => n.removeInitial({
528
+ to: {
529
+ node: 'Callback',
530
+ port: 'callback',
531
+ },
532
+ }))
533
+ .then(() => n.removeInitial({
534
+ to: {
535
+ node: 'Repeat',
536
+ port: 'in',
537
+ },
538
+ }))
539
+ .then(() => {
540
+ chai.expect(n.initials.length).to.equal(0, 'No IIPs left');
541
+ chai.expect(n.connections.length).to.equal(1, 'Only one connection');
542
+ }));
543
+ it('new IIPs to replace original ones should work correctly', (done) => {
544
+ const cb = function (packet) {
545
+ chai.expect(packet).to.equal('Baz');
546
+ done();
547
+ };
548
+ Promise.resolve()
549
+ .then(() => n.addInitial({
550
+ from: {
551
+ data: cb,
552
+ },
553
+ to: {
554
+ node: 'Callback',
555
+ port: 'callback',
556
+ },
557
+ }))
558
+ .then(() => n.addInitial({
559
+ from: {
560
+ data: 'Baz',
561
+ },
562
+ to: {
563
+ node: 'Repeat',
564
+ port: 'in',
565
+ },
566
+ }))
567
+ .then(() => n.start())
568
+ .catch(done);
569
+ });
570
+ describe('on stopping', () => {
571
+ it('processes should be running before the stop call', () => {
572
+ chai.expect(n.started).to.be.true;
573
+ chai.expect(n.processes.Repeat.component.started).to.equal(true);
574
+ });
575
+ it('should emit the end event', function (done) {
576
+ this.timeout(5000);
577
+ // Ensure we have a connection open
578
+ n.once('end', (endTimes) => {
579
+ chai.expect(endTimes).to.be.an('object');
580
+ done();
581
+ });
582
+ n.stop().catch(done);
583
+ });
584
+ it('should have called the shutdown method of each process', () => {
585
+ chai.expect(n.processes.Repeat.component.started).to.equal(false);
586
+ });
587
+ });
588
+ });
589
+ describe('with a very large network', () => {
590
+ it('should be able to connect without errors', function (done) {
591
+ let n;
592
+ this.timeout(100000);
593
+ const g = new noflo.Graph();
594
+ let called = 0;
595
+ for (n = 0; n <= 10000; n++) {
596
+ g.addNode(`Repeat${n}`, 'Split');
597
+ }
598
+ g.addNode('Callback', 'Callback');
599
+ for (n = 0; n <= 10000; n++) {
600
+ g.addEdge(`Repeat${n}`, 'out', 'Callback', 'in');
601
+ }
602
+ g.addInitial(() => {
603
+ called++;
604
+ },
605
+ 'Callback', 'callback');
606
+ for (n = 0; n <= 10000; n++) {
607
+ g.addInitial(n, `Repeat${n}`, 'in');
608
+ }
609
+
610
+ noflo.createNetwork(g, {
611
+ delay: true,
612
+ subscribeGraph: false,
613
+ asyncDelivery: false,
614
+ baseDir,
615
+ })
616
+ .then((nw) => {
617
+ nw.loader.components.Split = Split;
618
+ nw.loader.components.Callback = Callback;
619
+ nw.once('end', () => {
620
+ chai.expect(called).to.equal(10001);
621
+ done();
622
+ });
623
+ return nw.connect();
624
+ })
625
+ .then((nw) => nw.start())
626
+ .catch(done);
627
+ });
628
+ });
629
+ describe('with a faulty graph', () => {
630
+ let loader = null;
631
+ before(() => {
632
+ loader = new noflo.ComponentLoader(baseDir);
633
+ return loader.listComponents()
634
+ .then(() => {
635
+ loader.components.Split = Split;
636
+ });
637
+ });
638
+ it('should fail on connect with non-existing component', () => {
639
+ const g = new noflo.Graph();
640
+ g.addNode('Repeat1', 'Baz');
641
+ g.addNode('Repeat2', 'Split');
642
+ g.addEdge('Repeat1', 'out', 'Repeat2', 'in');
643
+ return noflo.createNetwork(g, {
644
+ delay: true,
645
+ subscribeGraph: false,
646
+ asyncDelivery: false,
647
+ componentLoader: loader,
648
+ })
649
+ .then((nw) => nw.connect()
650
+ .then(
651
+ () => Promise.reject(new Error('Unexpected success')),
652
+ (err) => {
653
+ chai.expect(err).to.be.an('error');
654
+ chai.expect(err.message).to.contain('not available');
655
+ },
656
+ ));
657
+ });
658
+ it('should fail on connect with missing target port', () => {
659
+ const g = new noflo.Graph();
660
+ g.addNode('Repeat1', 'Split');
661
+ g.addNode('Repeat2', 'Split');
662
+ g.addEdge('Repeat1', 'out', 'Repeat2', 'foo');
663
+ return noflo.createNetwork(g, {
664
+ delay: true,
665
+ subscribeGraph: false,
666
+ asyncDelivery: false,
667
+ componentLoader: loader,
668
+ })
669
+ .then((nw) => nw.connect()
670
+ .then(
671
+ () => Promise.reject(new Error('Unexpected success')),
672
+ (err) => {
673
+ chai.expect(err).to.be.an('error');
674
+ chai.expect(err.message).to.contain('No inport');
675
+ },
676
+ ));
677
+ });
678
+ it('should fail on connect with missing source port', () => {
679
+ const g = new noflo.Graph();
680
+ g.addNode('Repeat1', 'Split');
681
+ g.addNode('Repeat2', 'Split');
682
+ g.addEdge('Repeat1', 'foo', 'Repeat2', 'in');
683
+ return noflo.createNetwork(g, {
684
+ delay: true,
685
+ subscribeGraph: false,
686
+ asyncDelivery: false,
687
+ componentLoader: loader,
688
+ })
689
+ .then((nw) => nw.connect()
690
+ .then(
691
+ () => Promise.reject(new Error('Unexpected success')),
692
+ (err) => {
693
+ chai.expect(err).to.be.an('error');
694
+ chai.expect(err.message).to.contain('No outport');
695
+ },
696
+ ));
697
+ });
698
+ it('should fail on connect with missing IIP target port', () => {
699
+ const g = new noflo.Graph();
700
+ g.addNode('Repeat1', 'Split');
701
+ g.addNode('Repeat2', 'Split');
702
+ g.addEdge('Repeat1', 'out', 'Repeat2', 'in');
703
+ g.addInitial('hello', 'Repeat1', 'baz');
704
+ return noflo.createNetwork(g, {
705
+ delay: true,
706
+ subscribeGraph: false,
707
+ asyncDelivery: false,
708
+ componentLoader: loader,
709
+ })
710
+ .then((nw) => nw.connect()
711
+ .then(
712
+ () => Promise.reject(new Error('Unexpected success')),
713
+ (err) => {
714
+ chai.expect(err).to.be.an('error');
715
+ chai.expect(err.message).to.contain('No inport');
716
+ },
717
+ ));
718
+ });
719
+ it('should fail on connect with node without component', () => {
720
+ const g = new noflo.Graph();
721
+ g.addNode('Repeat1', 'Split');
722
+ g.addNode('Repeat2');
723
+ g.addEdge('Repeat1', 'out', 'Repeat2', 'in');
724
+ g.addInitial('hello', 'Repeat1', 'in');
725
+ return noflo.createNetwork(g, {
726
+ delay: true,
727
+ subscribeGraph: false,
728
+ asyncDelivery: false,
729
+ componentLoader: loader,
730
+ })
731
+ .then((nw) => nw.connect()
732
+ .then(
733
+ () => Promise.reject(new Error('Unexpected success')),
734
+ (err) => {
735
+ chai.expect(err).to.be.an('error');
736
+ chai.expect(err.message).to.contain('No component defined');
737
+ },
738
+ ));
739
+ });
740
+ it('should fail to add an edge to a missing outbound node', () => {
741
+ const g = new noflo.Graph();
742
+ g.addNode('Repeat1', 'Split');
743
+ return noflo.createNetwork(g, {
744
+ delay: true,
745
+ subscribeGraph: false,
746
+ asyncDelivery: false,
747
+ componentLoader: loader,
748
+ })
749
+ .then((nw) => nw.connect())
750
+ .then((nw) => nw.addEdge({
751
+ from: {
752
+ node: 'Repeat2',
753
+ port: 'out',
754
+ },
755
+ to: {
756
+ node: 'Repeat1',
757
+ port: 'in',
758
+ },
759
+ }))
760
+ .then(
761
+ () => Promise.reject(new Error('Unexpected success')),
762
+ (err) => {
763
+ chai.expect(err).to.be.an('error');
764
+ chai.expect(err.message).to.contain('No process defined for outbound node');
765
+ },
766
+ );
767
+ });
768
+ it('should fail to add an edge to a missing inbound node', () => {
769
+ const g = new noflo.Graph();
770
+ g.addNode('Repeat1', 'Split');
771
+ return noflo.createNetwork(g, {
772
+ delay: true,
773
+ subscribeGraph: false,
774
+ asyncDelivery: false,
775
+ componentLoader: loader,
776
+ })
777
+ .then((nw) => nw.connect())
778
+ .then((nw) => nw.addEdge({
779
+ from: {
780
+ node: 'Repeat1',
781
+ port: 'out',
782
+ },
783
+ to: {
784
+ node: 'Repeat2',
785
+ port: 'in',
786
+ },
787
+ }))
788
+ .then(
789
+ () => Promise.reject(new Error('Unexpected success')),
790
+ (err) => {
791
+ chai.expect(err).to.be.an('error');
792
+ chai.expect(err.message).to.contain('No process defined for inbound node');
793
+ },
794
+ );
795
+ });
796
+ });
797
+ describe('baseDir setting', () => {
798
+ it('should set baseDir based on given graph (deprecated)', () => {
799
+ const g = new noflo.Graph();
800
+ g.properties.baseDir = baseDir;
801
+ return noflo.createNetwork(g, {
802
+ delay: true,
803
+ subscribeGraph: false,
804
+ asyncDelivery: false,
805
+ })
806
+ .then((nw) => {
807
+ chai.expect(nw.baseDir).to.equal(baseDir);
808
+ });
809
+ });
810
+ it('should fall back to CWD if graph has no baseDir', function () {
811
+ if (noflo.isBrowser()) {
812
+ this.skip();
813
+ return;
814
+ }
815
+ const g = new noflo.Graph();
816
+ return noflo.createNetwork(g, {
817
+ delay: true,
818
+ subscribeGraph: false,
819
+ asyncDelivery: false,
820
+ })
821
+ .then((nw) => {
822
+ chai.expect(nw.baseDir).to.equal(process.cwd());
823
+ });
824
+ });
825
+ it('should set the baseDir for the component loader', () => {
826
+ const g = new noflo.Graph();
827
+ return noflo.createNetwork(g, {
828
+ delay: true,
829
+ subscribeGraph: false,
830
+ asyncDelivery: false,
831
+ baseDir,
832
+ })
833
+ .then((nw) => {
834
+ chai.expect(nw.baseDir).to.equal(baseDir);
835
+ chai.expect(nw.loader.baseDir).to.equal(baseDir);
836
+ });
837
+ });
838
+ });
839
+ describe('debug setting', () => {
840
+ let n = null;
841
+ let g = null;
842
+ before(() => {
843
+ g = new noflo.Graph();
844
+ return noflo.createNetwork(g, {
845
+ subscribeGraph: false,
846
+ asyncDelivery: false,
847
+ delay: true,
848
+ baseDir,
849
+ })
850
+ .then((network) => {
851
+ n = network;
852
+ n.loader.components.Split = Split;
853
+ return Promise.resolve()
854
+ .then(() => n.addNode({
855
+ id: 'A',
856
+ component: 'Split',
857
+ }))
858
+ .then(() => n.addNode({
859
+ id: 'B',
860
+ component: 'Split',
861
+ }))
862
+ .then(() => n.addEdge({
863
+ from: {
864
+ node: 'A',
865
+ port: 'out',
866
+ },
867
+ to: {
868
+ node: 'B',
869
+ port: 'in',
870
+ },
871
+ }))
872
+ .then(() => network.connect());
873
+ });
874
+ });
875
+ it('should initially have debug enabled', () => {
876
+ chai.expect(n.getDebug()).to.equal(true);
877
+ });
878
+ it('should have propagated debug setting to connections', () => {
879
+ chai.expect(n.connections[0].debug).to.equal(n.getDebug());
880
+ });
881
+ it('calling setDebug with same value should be no-op', () => {
882
+ n.setDebug(true);
883
+ chai.expect(n.getDebug()).to.equal(true);
884
+ chai.expect(n.connections[0].debug).to.equal(n.getDebug());
885
+ });
886
+ it('disabling debug should get propagated to connections', () => {
887
+ n.setDebug(false);
888
+ chai.expect(n.getDebug()).to.equal(false);
889
+ chai.expect(n.connections[0].debug).to.equal(n.getDebug());
890
+ });
891
+ });
892
+ });