@onlineapps/conn-orch-validator 2.0.6 → 2.0.9

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 (36) hide show
  1. package/README.md +30 -1
  2. package/TESTING_STRATEGY.md +0 -0
  3. package/docs/DESIGN.md +0 -0
  4. package/examples/service-wrapper-usage.js +0 -0
  5. package/examples/three-tier-testing.js +0 -0
  6. package/jest.config.js +0 -0
  7. package/onlineapps-conn-e2e-testing-1.0.0.tgz +0 -0
  8. package/package.json +1 -1
  9. package/src/CookbookTestRunner.js +4 -4
  10. package/src/CookbookTestUtils.js +0 -0
  11. package/src/ServiceReadinessValidator.js +0 -0
  12. package/src/ServiceTestHarness.js +0 -0
  13. package/src/ServiceValidator.js +0 -0
  14. package/src/TestOrchestrator.js +0 -0
  15. package/src/ValidationOrchestrator.js +29 -22
  16. package/src/WorkflowTestRunner.js +0 -0
  17. package/src/helpers/README.md +0 -0
  18. package/src/helpers/createPreValidationTests.js +0 -0
  19. package/src/helpers/createServiceReadinessTests.js +0 -0
  20. package/src/index.js +0 -0
  21. package/src/mocks/MockMQClient.js +0 -0
  22. package/src/mocks/MockRegistry.js +0 -0
  23. package/src/mocks/MockStorage.js +0 -0
  24. package/src/validators/ServiceStructureValidator.js +0 -0
  25. package/src/validators/ValidationProofGenerator.js +0 -0
  26. package/test-mq-flow.js +0 -0
  27. package/tests/component/testing-framework-integration.test.js +0 -313
  28. package/tests/integration/ServiceReadiness.test.js +0 -265
  29. package/tests/monitoring-e2e.test.js +0 -315
  30. package/tests/run-example.js +0 -257
  31. package/tests/unit/CookbookTestRunner.test.js +0 -353
  32. package/tests/unit/MockMQClient.test.js +0 -190
  33. package/tests/unit/MockRegistry.test.js +0 -233
  34. package/tests/unit/MockStorage.test.js +0 -257
  35. package/tests/unit/ServiceValidator.test.js +0 -429
  36. package/tests/unit/WorkflowTestRunner.test.js +0 -546
@@ -1,546 +0,0 @@
1
- 'use strict';
2
-
3
- const WorkflowTestRunner = require('../../src/WorkflowTestRunner');
4
- const MockMQClient = require('../../src/mocks/MockMQClient');
5
- const MockRegistry = require('../../src/mocks/MockRegistry');
6
- const MockStorage = require('../../src/mocks/MockStorage');
7
-
8
- describe('WorkflowTestRunner @unit', () => {
9
- let runner;
10
- let mqClient;
11
- let registry;
12
- let storage;
13
-
14
- beforeEach(async () => {
15
- mqClient = new MockMQClient();
16
- registry = new MockRegistry();
17
- storage = new MockStorage();
18
-
19
- await mqClient.connect();
20
- await registry.register({
21
- name: 'test-service',
22
- openapi: {
23
- paths: {
24
- '/test': {
25
- get: { operationId: 'testOp' }
26
- }
27
- }
28
- }
29
- });
30
-
31
- runner = new WorkflowTestRunner({
32
- mqClient,
33
- registry,
34
- storage,
35
- timeout: 1000,
36
- debug: false
37
- });
38
- });
39
-
40
- describe('Workflow Execution', () => {
41
- it('should run simple workflow', async () => {
42
- const cookbook = {
43
- steps: [
44
- {
45
- id: 'step1',
46
- type: 'task',
47
- service: 'test-service',
48
- operation: 'testOp',
49
- input: { test: 'data' }
50
- }
51
- ]
52
- };
53
-
54
- const workflow = await runner.runWorkflow(cookbook);
55
-
56
- expect(workflow.status).toBe('completed');
57
- expect(workflow.results).toHaveLength(1);
58
- expect(workflow.results[0].success).toBe(true);
59
- expect(workflow.results[0].stepId).toBe('step1');
60
- });
61
-
62
- it('should generate workflow ID', async () => {
63
- const workflow = await runner.runWorkflow({ steps: [] });
64
- expect(workflow.id).toMatch(/^test-workflow-\d+$/);
65
- });
66
-
67
- it('should track workflow state', async () => {
68
- const cookbook = {
69
- steps: [
70
- { id: 'step1', type: 'task', service: 'test-service' },
71
- { id: 'step2', type: 'task', service: 'test-service' }
72
- ]
73
- };
74
-
75
- const workflow = await runner.runWorkflow(cookbook);
76
-
77
- expect(workflow.currentStep).toBe(1); // Last executed step index
78
- expect(workflow.startedAt).toBeDefined();
79
- expect(workflow.completedAt).toBeDefined();
80
- });
81
-
82
- it('should pass initial context', async () => {
83
- const initialContext = { userId: '123', data: 'test' };
84
- const workflow = await runner.runWorkflow(
85
- { steps: [] },
86
- initialContext
87
- );
88
-
89
- expect(workflow.context.api_input).toEqual(initialContext);
90
- });
91
-
92
- it('should update context with step results', async () => {
93
- const cookbook = {
94
- steps: [
95
- {
96
- id: 'step1',
97
- type: 'task',
98
- service: 'test-service',
99
- operation: 'testOp'
100
- }
101
- ]
102
- };
103
-
104
- const workflow = await runner.runWorkflow(cookbook);
105
- expect(workflow.context.steps['step1']).toBeDefined();
106
- });
107
- });
108
-
109
- describe('Step Execution', () => {
110
- it('should execute task step', async () => {
111
- const step = {
112
- id: 'task1',
113
- type: 'task',
114
- service: 'test-service',
115
- operation: 'testOp',
116
- input: { data: 'test' }
117
- };
118
-
119
- const workflow = {
120
- id: 'wf-1',
121
- context: { api_input: {}, steps: {} }
122
- };
123
-
124
- const result = await runner.executeStep(step, workflow);
125
-
126
- expect(result.stepId).toBe('task1');
127
- expect(result.type).toBe('task');
128
- expect(result.success).toBe(true);
129
- expect(result.duration).toBeGreaterThanOrEqual(0);
130
- });
131
-
132
- it('should handle step errors', async () => {
133
- const step = {
134
- id: 'task1',
135
- type: 'task',
136
- service: 'unknown-service'
137
- };
138
-
139
- const workflow = {
140
- id: 'wf-1',
141
- context: { api_input: {}, steps: {} }
142
- };
143
-
144
- const result = await runner.executeStep(step, workflow);
145
-
146
- expect(result.success).toBe(false);
147
- expect(result.error).toContain('Service not found');
148
- });
149
-
150
- it('should handle unknown step type', async () => {
151
- const step = {
152
- id: 'unknown1',
153
- type: 'unknown-type'
154
- };
155
-
156
- const workflow = {
157
- id: 'wf-1',
158
- context: { api_input: {}, steps: {} }
159
- };
160
-
161
- const result = await runner.executeStep(step, workflow);
162
-
163
- expect(result.success).toBe(false);
164
- expect(result.error).toContain('Unknown step type');
165
- });
166
- });
167
-
168
- describe('Foreach Step', () => {
169
- it('should execute foreach over array', async () => {
170
- const step = {
171
- id: 'foreach1',
172
- type: 'foreach',
173
- items: '$api_input.items',
174
- body: {
175
- id: 'process',
176
- type: 'task',
177
- service: 'test-service',
178
- operation: 'testOp'
179
- }
180
- };
181
-
182
- const workflow = {
183
- id: 'wf-1',
184
- context: {
185
- api_input: { items: ['a', 'b', 'c'] },
186
- steps: {}
187
- }
188
- };
189
-
190
- const result = await runner.executeForeachStep(step, workflow);
191
-
192
- expect(result.items).toBe(3);
193
- expect(result.results).toHaveLength(3);
194
- expect(result.results.every(r => r.success)).toBe(true);
195
- });
196
-
197
- it('should error if items not an array', async () => {
198
- const step = {
199
- id: 'foreach1',
200
- type: 'foreach',
201
- items: '$api_input.notArray',
202
- body: { id: 'process', type: 'task' }
203
- };
204
-
205
- const workflow = {
206
- id: 'wf-1',
207
- context: {
208
- api_input: { notArray: 'string' },
209
- steps: {}
210
- }
211
- };
212
-
213
- await expect(runner.executeForeachStep(step, workflow))
214
- .rejects.toThrow('Foreach items must resolve to an array');
215
- });
216
-
217
- it('should provide current_item in context', async () => {
218
- let capturedContexts = [];
219
-
220
- const originalExecuteStep = runner.executeStep.bind(runner);
221
- runner.executeStep = jest.fn(async (step, workflow) => {
222
- capturedContexts.push({ ...workflow.context });
223
- return originalExecuteStep(step, workflow);
224
- });
225
-
226
- const step = {
227
- id: 'foreach1',
228
- type: 'foreach',
229
- items: '$api_input.items',
230
- body: { id: 'process', type: 'task', service: 'test-service' }
231
- };
232
-
233
- const workflow = {
234
- id: 'wf-1',
235
- context: {
236
- api_input: { items: ['first', 'second'] },
237
- steps: {}
238
- }
239
- };
240
-
241
- await runner.executeForeachStep(step, workflow);
242
-
243
- expect(capturedContexts).toHaveLength(2);
244
- expect(capturedContexts[0].current_item).toBe('first');
245
- expect(capturedContexts[1].current_item).toBe('second');
246
- });
247
- });
248
-
249
- describe('Switch Step', () => {
250
- it('should execute matching case', async () => {
251
- const step = {
252
- id: 'switch1',
253
- type: 'switch',
254
- condition: '$api_input.type',
255
- cases: [
256
- {
257
- value: 'typeA',
258
- step: { id: 'caseA', type: 'task', service: 'test-service' }
259
- },
260
- {
261
- value: 'typeB',
262
- step: { id: 'caseB', type: 'task', service: 'test-service' }
263
- }
264
- ]
265
- };
266
-
267
- const workflow = {
268
- id: 'wf-1',
269
- context: {
270
- api_input: { type: 'typeB' },
271
- steps: {}
272
- }
273
- };
274
-
275
- const result = await runner.executeSwitchStep(step, workflow);
276
-
277
- expect(result.condition).toBe('typeB');
278
- expect(result.executed).toBe('caseB');
279
- expect(result.result.success).toBe(true);
280
- });
281
-
282
- it('should execute default case', async () => {
283
- const step = {
284
- id: 'switch1',
285
- type: 'switch',
286
- condition: '$api_input.type',
287
- cases: [
288
- {
289
- value: 'typeA',
290
- step: { id: 'caseA', type: 'task', service: 'test-service' }
291
- }
292
- ],
293
- default: { id: 'defaultCase', type: 'task', service: 'test-service' }
294
- };
295
-
296
- const workflow = {
297
- id: 'wf-1',
298
- context: {
299
- api_input: { type: 'typeC' },
300
- steps: {}
301
- }
302
- };
303
-
304
- const result = await runner.executeSwitchStep(step, workflow);
305
-
306
- expect(result.executed).toBe('defaultCase');
307
- });
308
-
309
- it('should error if no matching case and no default', async () => {
310
- const step = {
311
- id: 'switch1',
312
- type: 'switch',
313
- condition: '$api_input.type',
314
- cases: [
315
- {
316
- value: 'typeA',
317
- step: { id: 'caseA', type: 'task' }
318
- }
319
- ]
320
- };
321
-
322
- const workflow = {
323
- id: 'wf-1',
324
- context: {
325
- api_input: { type: 'typeB' },
326
- steps: {}
327
- }
328
- };
329
-
330
- await expect(runner.executeSwitchStep(step, workflow))
331
- .rejects.toThrow('No matching case for condition value: typeB');
332
- });
333
- });
334
-
335
- describe('Fork-Join Step', () => {
336
- it('should execute branches in parallel', async () => {
337
- const step = {
338
- id: 'fork1',
339
- type: 'fork_join',
340
- branches: [
341
- { id: 'branch1', type: 'task', service: 'test-service' },
342
- { id: 'branch2', type: 'task', service: 'test-service' },
343
- { id: 'branch3', type: 'task', service: 'test-service' }
344
- ],
345
- join: { strategy: 'all' }
346
- };
347
-
348
- const workflow = {
349
- id: 'wf-1',
350
- context: { api_input: {}, steps: {} }
351
- };
352
-
353
- const result = await runner.executeForkJoinStep(step, workflow);
354
-
355
- expect(result.branches).toBe(3);
356
- expect(result.strategy).toBe('all');
357
- expect(result.result).toHaveLength(3);
358
- });
359
-
360
- it('should merge results', async () => {
361
- // Mock executeStep to return specific data
362
- runner.executeStep = jest.fn()
363
- .mockResolvedValueOnce({ success: true, data: { a: 1 } })
364
- .mockResolvedValueOnce({ success: true, data: { b: 2 } });
365
-
366
- const step = {
367
- id: 'fork1',
368
- type: 'fork_join',
369
- branches: [
370
- { id: 'branch1', type: 'task' },
371
- { id: 'branch2', type: 'task' }
372
- ],
373
- join: { strategy: 'merge' }
374
- };
375
-
376
- const workflow = {
377
- id: 'wf-1',
378
- context: { api_input: {}, steps: {} }
379
- };
380
-
381
- const result = await runner.executeForkJoinStep(step, workflow);
382
-
383
- expect(result.result).toEqual({ a: 1, b: 2 });
384
- });
385
-
386
- it('should use first strategy', async () => {
387
- runner.executeStep = jest.fn()
388
- .mockResolvedValueOnce({ success: true, data: 'first' })
389
- .mockResolvedValueOnce({ success: true, data: 'second' });
390
-
391
- const step = {
392
- id: 'fork1',
393
- type: 'fork_join',
394
- branches: [
395
- { id: 'branch1', type: 'task' },
396
- { id: 'branch2', type: 'task' }
397
- ],
398
- join: { strategy: 'first' }
399
- };
400
-
401
- const workflow = {
402
- id: 'wf-1',
403
- context: { api_input: {}, steps: {} }
404
- };
405
-
406
- const result = await runner.executeForkJoinStep(step, workflow);
407
-
408
- expect(result.result).toEqual({ success: true, data: 'first' });
409
- });
410
- });
411
-
412
- describe('Input Resolution', () => {
413
- it('should resolve JSONPath', () => {
414
- const context = {
415
- api_input: { user: { name: 'John' } },
416
- steps: { step1: { result: 'data' } }
417
- };
418
-
419
- expect(runner.resolveInput('$api_input.user.name', context)).toBe('John');
420
- expect(runner.resolveInput('$steps.step1.result', context)).toBe('data');
421
- });
422
-
423
- it('should return null for invalid path', () => {
424
- const context = { api_input: {} };
425
- expect(runner.resolveInput('$api_input.nonexistent.path', context)).toBeNull();
426
- });
427
-
428
- it('should resolve nested objects', () => {
429
- const context = {
430
- api_input: { data: 'test' }
431
- };
432
-
433
- const input = {
434
- field1: '$api_input.data',
435
- field2: 'literal',
436
- nested: {
437
- field3: '$api_input.data'
438
- }
439
- };
440
-
441
- const resolved = runner.resolveInput(input, context);
442
-
443
- expect(resolved).toEqual({
444
- field1: 'test',
445
- field2: 'literal',
446
- nested: {
447
- field3: 'test'
448
- }
449
- });
450
- });
451
-
452
- it('should return literals unchanged', () => {
453
- const context = {};
454
- expect(runner.resolveInput('literal', context)).toBe('literal');
455
- expect(runner.resolveInput(123, context)).toBe(123);
456
- expect(runner.resolveInput(true, context)).toBe(true);
457
- expect(runner.resolveInput(null, context)).toBeNull();
458
- });
459
- });
460
-
461
- describe('Event Emitting', () => {
462
- it('should emit workflow events', async () => {
463
- const events = [];
464
-
465
- runner.on('workflow:started', (e) => events.push({ type: 'started', ...e }));
466
- runner.on('workflow:completed', (e) => events.push({ type: 'completed', ...e }));
467
-
468
- await runner.runWorkflow({ steps: [] });
469
-
470
- expect(events).toHaveLength(2);
471
- expect(events[0].type).toBe('started');
472
- expect(events[1].type).toBe('completed');
473
- });
474
-
475
- it('should emit step events', async () => {
476
- const events = [];
477
-
478
- runner.on('step:started', (e) => events.push({ type: 'started', ...e }));
479
- runner.on('step:completed', (e) => events.push({ type: 'completed', ...e }));
480
-
481
- const cookbook = {
482
- steps: [
483
- { id: 'step1', type: 'task', service: 'test-service' }
484
- ]
485
- };
486
-
487
- await runner.runWorkflow(cookbook);
488
-
489
- expect(events).toHaveLength(2);
490
- expect(events[0].stepId).toBe('step1');
491
- expect(events[1].stepId).toBe('step1');
492
- });
493
-
494
- it('should emit error events', async () => {
495
- const events = [];
496
-
497
- runner.on('workflow:failed', (e) => events.push(e));
498
- runner.on('step:failed', (e) => events.push(e));
499
-
500
- const cookbook = {
501
- steps: [
502
- { id: 'step1', type: 'task', service: 'unknown' }
503
- ]
504
- };
505
-
506
- await runner.runWorkflow(cookbook);
507
-
508
- expect(events.length).toBeGreaterThan(0);
509
- });
510
- });
511
-
512
- describe('Workflow Management', () => {
513
- it('should track running workflows', async () => {
514
- const cookbook = { steps: [] };
515
- const workflow = await runner.runWorkflow(cookbook);
516
-
517
- const status = runner.getWorkflowStatus(workflow.id);
518
- expect(status).toBeDefined();
519
- expect(status.id).toBe(workflow.id);
520
- });
521
-
522
- it('should stop workflow', async () => {
523
- const events = [];
524
- runner.on('workflow:stopped', (e) => events.push(e));
525
-
526
- const cookbook = { steps: [] };
527
- const workflow = await runner.runWorkflow(cookbook);
528
-
529
- runner.stopWorkflow(workflow.id);
530
-
531
- expect(events).toHaveLength(1);
532
- expect(events[0].workflowId).toBe(workflow.id);
533
- });
534
-
535
- it('should clear completed workflows', async () => {
536
- const cookbook = { steps: [] };
537
- const wf1 = await runner.runWorkflow(cookbook);
538
- const wf2 = await runner.runWorkflow(cookbook);
539
-
540
- runner.clearCompleted();
541
-
542
- expect(runner.getWorkflowStatus(wf1.id)).toBeUndefined();
543
- expect(runner.getWorkflowStatus(wf2.id)).toBeUndefined();
544
- });
545
- });
546
- });