@onlineapps/service-wrapper 2.0.20 → 2.0.22
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.
- package/API.md +0 -0
- package/README.md +0 -0
- package/docs/CONFIGURATION_GUIDE.md +0 -0
- package/docs/FINAL_ARCHITECTURE.md +0 -0
- package/docs/MIGRATION_FROM_OPENAPI.md +0 -0
- package/docs/STANDARDS_OVERVIEW.md +0 -0
- package/docs/archived-2025-09-29/API_STRUCTURE_STANDARD.md +0 -0
- package/docs/archived-2025-09-29/ARCHITECTURE_DECISION.md +0 -0
- package/docs/archived-2025-09-29/HANDLER_VS_HTTP_COMPARISON.md +0 -0
- package/docs/archived-2025-09-29/INSTALLATION_GUIDE.md +0 -0
- package/docs/archived-2025-09-29/OPERATIONS_SCHEMA.md +0 -0
- package/docs/archived-2025-09-29/PERFORMANCE.md +0 -0
- package/docs/archived-2025-09-29/PROCESS_FLOWS.md +0 -0
- package/docs/archived-2025-09-29/SERVICE_TESTING_STANDARD.md +0 -0
- package/docs/archived-2025-09-29/WRAPPER_ARCHITECTURE.md +0 -0
- package/examples/README.md +0 -0
- package/examples/cookbook-file-output.json +0 -0
- package/examples/cookbook-multi-step.json +0 -0
- package/examples/cookbook-single-operation.json +0 -0
- package/jest.config.js +0 -0
- package/jsdoc.json +0 -0
- package/package.json +1 -1
- package/src/ServiceWrapper.js +120 -25
- package/src/index.js +0 -0
- package/tests/component/ServiceWrapper.component.test.js +0 -407
- package/tests/component/connector-integration.test.js +0 -293
- package/tests/e2e/full-flow.test.js +0 -293
- package/tests/integration/orchestrator-integration.test.js +0 -170
- package/tests/mocks/connectors.js +0 -304
- package/tests/monitoring-integration.test.js +0 -150
- package/tests/run-tests.js +0 -135
- package/tests/setup.js +0 -31
- package/tests/unit/ServiceWrapper.test.js +0 -372
|
@@ -1,407 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Component tests for ServiceWrapper
|
|
5
|
-
* These tests verify integration with actual connectors
|
|
6
|
-
* and help us identify what needs to be implemented
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
const ServiceWrapper = require('../../src/ServiceWrapper');
|
|
10
|
-
const express = require('express');
|
|
11
|
-
|
|
12
|
-
describe('ServiceWrapper Component Tests @component', () => {
|
|
13
|
-
let wrapper;
|
|
14
|
-
let app;
|
|
15
|
-
let server;
|
|
16
|
-
const TEST_PORT = 3456;
|
|
17
|
-
|
|
18
|
-
beforeAll(() => {
|
|
19
|
-
// Create a real Express app for testing
|
|
20
|
-
app = express();
|
|
21
|
-
app.use(express.json());
|
|
22
|
-
|
|
23
|
-
// Add test endpoints
|
|
24
|
-
app.get('/api/health', (req, res) => {
|
|
25
|
-
res.json({ status: 'healthy' });
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
app.get('/api/test/:id', (req, res) => {
|
|
29
|
-
res.json({
|
|
30
|
-
id: req.params.id,
|
|
31
|
-
message: 'Test response'
|
|
32
|
-
});
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
app.post('/api/test', (req, res) => {
|
|
36
|
-
res.status(201).json({
|
|
37
|
-
created: true,
|
|
38
|
-
data: req.body
|
|
39
|
-
});
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
// Start the server
|
|
43
|
-
server = app.listen(TEST_PORT);
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
afterAll((done) => {
|
|
47
|
-
if (server) {
|
|
48
|
-
server.close(done);
|
|
49
|
-
}
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
afterEach(async () => {
|
|
53
|
-
if (wrapper && wrapper.isRunning) {
|
|
54
|
-
await wrapper.stop();
|
|
55
|
-
}
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
describe('Basic Integration', () => {
|
|
59
|
-
test('should fail gracefully when connectors are not available', async () => {
|
|
60
|
-
// This test helps us identify what connectors need to be implemented
|
|
61
|
-
const openApiSpec = {
|
|
62
|
-
openapi: '3.0.0',
|
|
63
|
-
info: {
|
|
64
|
-
title: 'Test Service',
|
|
65
|
-
version: '1.0.0'
|
|
66
|
-
},
|
|
67
|
-
paths: {
|
|
68
|
-
'/api/test/{id}': {
|
|
69
|
-
get: {
|
|
70
|
-
operationId: 'getTest',
|
|
71
|
-
parameters: [
|
|
72
|
-
{
|
|
73
|
-
name: 'id',
|
|
74
|
-
in: 'path',
|
|
75
|
-
required: true,
|
|
76
|
-
schema: { type: 'string' }
|
|
77
|
-
}
|
|
78
|
-
]
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
wrapper = new ServiceWrapper({
|
|
85
|
-
service: app,
|
|
86
|
-
serviceName: 'test-service',
|
|
87
|
-
openApiSpec,
|
|
88
|
-
config: {
|
|
89
|
-
port: TEST_PORT
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
try {
|
|
94
|
-
await wrapper.start();
|
|
95
|
-
// If this succeeds, connectors are properly mocked
|
|
96
|
-
expect(wrapper.isRunning).toBe(true);
|
|
97
|
-
} catch (error) {
|
|
98
|
-
// This will tell us which connector is missing
|
|
99
|
-
console.log('Missing connector:', error.message);
|
|
100
|
-
expect(error.message).toMatch(/Cannot find module|is not a constructor/);
|
|
101
|
-
}
|
|
102
|
-
});
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
describe('Connector Requirements', () => {
|
|
106
|
-
// These tests document what each connector needs to provide
|
|
107
|
-
|
|
108
|
-
test('MQ Connector requirements', () => {
|
|
109
|
-
const MQConnectorAPI = {
|
|
110
|
-
constructor: ['config'],
|
|
111
|
-
methods: [
|
|
112
|
-
'connect',
|
|
113
|
-
'disconnect',
|
|
114
|
-
'isConnected',
|
|
115
|
-
'consume',
|
|
116
|
-
'publish'
|
|
117
|
-
],
|
|
118
|
-
expectedBehavior: {
|
|
119
|
-
connect: 'returns Promise<void>',
|
|
120
|
-
disconnect: 'returns Promise<void>',
|
|
121
|
-
isConnected: 'returns boolean',
|
|
122
|
-
consume: 'accepts (handler, options) returns Promise<void>',
|
|
123
|
-
publish: 'accepts (queue, message) returns Promise<void>'
|
|
124
|
-
}
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
// Document the API
|
|
128
|
-
expect(MQConnectorAPI.methods).toContain('connect');
|
|
129
|
-
expect(MQConnectorAPI.methods).toContain('consume');
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
test('Registry Connector requirements', () => {
|
|
133
|
-
const RegistryConnectorAPI = {
|
|
134
|
-
ServiceRegistryClient: {
|
|
135
|
-
constructor: ['config'],
|
|
136
|
-
methods: [
|
|
137
|
-
'register',
|
|
138
|
-
'unregister',
|
|
139
|
-
'sendHeartbeat',
|
|
140
|
-
'isConnected'
|
|
141
|
-
],
|
|
142
|
-
expectedBehavior: {
|
|
143
|
-
register: 'accepts (serviceInfo) returns Promise<void>',
|
|
144
|
-
unregister: 'accepts (serviceName) returns Promise<void>',
|
|
145
|
-
sendHeartbeat: 'accepts (serviceName) returns Promise<void>',
|
|
146
|
-
isConnected: 'returns boolean'
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
};
|
|
150
|
-
|
|
151
|
-
expect(RegistryConnectorAPI.ServiceRegistryClient.methods).toContain('register');
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
test('Orchestrator Connector requirements', () => {
|
|
155
|
-
const OrchestratorConnectorAPI = {
|
|
156
|
-
create: 'factory function',
|
|
157
|
-
WorkflowOrchestrator: {
|
|
158
|
-
constructor: ['config with mqClient, registryClient, apiMapper, cookbook'],
|
|
159
|
-
methods: [
|
|
160
|
-
'processWorkflowMessage'
|
|
161
|
-
],
|
|
162
|
-
expectedBehavior: {
|
|
163
|
-
processWorkflowMessage: 'accepts (message, serviceName) returns Promise<result>'
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
expect(OrchestratorConnectorAPI.create).toBe('factory function');
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
test('API Mapper Connector requirements', () => {
|
|
172
|
-
const ApiMapperConnectorAPI = {
|
|
173
|
-
create: 'factory function',
|
|
174
|
-
ApiMapper: {
|
|
175
|
-
constructor: ['config with openApiSpec, serviceUrl'],
|
|
176
|
-
methods: [
|
|
177
|
-
'callOperation',
|
|
178
|
-
'mapOperationToEndpoint',
|
|
179
|
-
'transformRequest',
|
|
180
|
-
'transformResponse'
|
|
181
|
-
]
|
|
182
|
-
}
|
|
183
|
-
};
|
|
184
|
-
|
|
185
|
-
expect(ApiMapperConnectorAPI.create).toBe('factory function');
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
test('Cookbook Connector requirements', () => {
|
|
189
|
-
const CookbookConnectorAPI = {
|
|
190
|
-
staticMethods: [
|
|
191
|
-
'validateCookbook',
|
|
192
|
-
'createRouter'
|
|
193
|
-
],
|
|
194
|
-
classes: {
|
|
195
|
-
CookbookExecutor: 'for control flow execution',
|
|
196
|
-
CookbookGenerator: 'for generating cookbooks from OpenAPI',
|
|
197
|
-
ResponseMapper: 'for response transformation'
|
|
198
|
-
}
|
|
199
|
-
};
|
|
200
|
-
|
|
201
|
-
expect(CookbookConnectorAPI.staticMethods).toContain('validateCookbook');
|
|
202
|
-
expect(Object.keys(CookbookConnectorAPI.classes)).toContain('CookbookExecutor');
|
|
203
|
-
});
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
describe('Workflow Processing Flow', () => {
|
|
207
|
-
// This test documents the expected flow through connectors
|
|
208
|
-
|
|
209
|
-
test('should document the complete message flow', () => {
|
|
210
|
-
const messageFlow = [
|
|
211
|
-
{
|
|
212
|
-
step: 1,
|
|
213
|
-
description: 'Message arrives via MQ',
|
|
214
|
-
connector: 'MQConnector',
|
|
215
|
-
method: 'consume callback'
|
|
216
|
-
},
|
|
217
|
-
{
|
|
218
|
-
step: 2,
|
|
219
|
-
description: 'ServiceWrapper delegates to Orchestrator',
|
|
220
|
-
connector: 'OrchestratorConnector',
|
|
221
|
-
method: 'processWorkflowMessage'
|
|
222
|
-
},
|
|
223
|
-
{
|
|
224
|
-
step: 3,
|
|
225
|
-
description: 'Orchestrator validates cookbook',
|
|
226
|
-
connector: 'CookbookConnector',
|
|
227
|
-
method: 'validateCookbook'
|
|
228
|
-
},
|
|
229
|
-
{
|
|
230
|
-
step: 4,
|
|
231
|
-
description: 'Orchestrator checks cache (if available)',
|
|
232
|
-
connector: 'CacheConnector',
|
|
233
|
-
method: 'get'
|
|
234
|
-
},
|
|
235
|
-
{
|
|
236
|
-
step: 5,
|
|
237
|
-
description: 'Orchestrator calls service API',
|
|
238
|
-
connector: 'ApiMapperConnector',
|
|
239
|
-
method: 'callOperation'
|
|
240
|
-
},
|
|
241
|
-
{
|
|
242
|
-
step: 6,
|
|
243
|
-
description: 'Orchestrator stores result in cache',
|
|
244
|
-
connector: 'CacheConnector',
|
|
245
|
-
method: 'set'
|
|
246
|
-
},
|
|
247
|
-
{
|
|
248
|
-
step: 7,
|
|
249
|
-
description: 'Orchestrator routes to next step',
|
|
250
|
-
connector: 'MQConnector or CookbookRouter',
|
|
251
|
-
method: 'publish or routeToService'
|
|
252
|
-
}
|
|
253
|
-
];
|
|
254
|
-
|
|
255
|
-
// Verify flow makes sense
|
|
256
|
-
expect(messageFlow).toHaveLength(7);
|
|
257
|
-
expect(messageFlow[0].connector).toBe('MQConnector');
|
|
258
|
-
expect(messageFlow[messageFlow.length - 1].description).toContain('routes to next');
|
|
259
|
-
});
|
|
260
|
-
});
|
|
261
|
-
|
|
262
|
-
describe('Error Handling Flow', () => {
|
|
263
|
-
test('should document error handling chain', () => {
|
|
264
|
-
const errorFlow = [
|
|
265
|
-
{
|
|
266
|
-
scenario: 'Message processing fails',
|
|
267
|
-
handler: 'OrchestratorConnector',
|
|
268
|
-
action: 'Catches error'
|
|
269
|
-
},
|
|
270
|
-
{
|
|
271
|
-
scenario: 'Error handler available',
|
|
272
|
-
handler: 'ErrorHandlerConnector',
|
|
273
|
-
action: 'handleError method'
|
|
274
|
-
},
|
|
275
|
-
{
|
|
276
|
-
scenario: 'No error handler',
|
|
277
|
-
handler: 'CookbookRouter',
|
|
278
|
-
action: 'routeToDLQ method'
|
|
279
|
-
},
|
|
280
|
-
{
|
|
281
|
-
scenario: 'Transient error',
|
|
282
|
-
handler: 'RetryHandler in Orchestrator',
|
|
283
|
-
action: 'Exponential backoff retry'
|
|
284
|
-
},
|
|
285
|
-
{
|
|
286
|
-
scenario: 'Service unavailable',
|
|
287
|
-
handler: 'CircuitBreaker in ApiMapper',
|
|
288
|
-
action: 'Circuit opens, fast fail'
|
|
289
|
-
}
|
|
290
|
-
];
|
|
291
|
-
|
|
292
|
-
expect(errorFlow).toHaveLength(5);
|
|
293
|
-
expect(errorFlow.some(e => e.action.includes('DLQ'))).toBe(true);
|
|
294
|
-
});
|
|
295
|
-
});
|
|
296
|
-
|
|
297
|
-
describe('Missing Connector Detection', () => {
|
|
298
|
-
// These tests help identify what still needs to be implemented
|
|
299
|
-
|
|
300
|
-
test('should identify conn-base-cache is missing', () => {
|
|
301
|
-
try {
|
|
302
|
-
require('@onlineapps/conn-base-cache');
|
|
303
|
-
expect(true).toBe(false); // Should not reach here
|
|
304
|
-
} catch (error) {
|
|
305
|
-
expect(error.code).toBe('MODULE_NOT_FOUND');
|
|
306
|
-
console.log('TODO: Implement conn-base-cache connector');
|
|
307
|
-
}
|
|
308
|
-
});
|
|
309
|
-
|
|
310
|
-
test('should identify conn-infra-error-handler is missing', () => {
|
|
311
|
-
try {
|
|
312
|
-
require('@onlineapps/conn-infra-error-handler');
|
|
313
|
-
expect(true).toBe(false); // Should not reach here
|
|
314
|
-
} catch (error) {
|
|
315
|
-
expect(error.code).toBe('MODULE_NOT_FOUND');
|
|
316
|
-
console.log('TODO: Implement conn-infra-error-handler connector');
|
|
317
|
-
}
|
|
318
|
-
});
|
|
319
|
-
|
|
320
|
-
test('should verify cookbook connector exports', () => {
|
|
321
|
-
try {
|
|
322
|
-
const cookbook = require('@onlineapps/conn-orch-cookbook');
|
|
323
|
-
|
|
324
|
-
// Check required exports
|
|
325
|
-
const requiredExports = [
|
|
326
|
-
'validateCookbook',
|
|
327
|
-
'createRouter',
|
|
328
|
-
'CookbookExecutor',
|
|
329
|
-
'CookbookGenerator',
|
|
330
|
-
'ResponseMapper'
|
|
331
|
-
];
|
|
332
|
-
|
|
333
|
-
const missingExports = requiredExports.filter(exp => !cookbook[exp]);
|
|
334
|
-
|
|
335
|
-
if (missingExports.length > 0) {
|
|
336
|
-
console.log('TODO: Cookbook connector missing exports:', missingExports);
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
expect(missingExports).toEqual([]);
|
|
340
|
-
} catch (error) {
|
|
341
|
-
console.log('TODO: Fix cookbook connector imports');
|
|
342
|
-
expect(error.code).toBe('MODULE_NOT_FOUND');
|
|
343
|
-
}
|
|
344
|
-
});
|
|
345
|
-
});
|
|
346
|
-
});
|
|
347
|
-
|
|
348
|
-
describe('Integration Test Helpers', () => {
|
|
349
|
-
// Helper functions for integration testing
|
|
350
|
-
|
|
351
|
-
test('should provide test utilities', () => {
|
|
352
|
-
const testUtils = {
|
|
353
|
-
createMockWorkflowMessage: (overrides = {}) => ({
|
|
354
|
-
workflow_id: 'test-wf-123',
|
|
355
|
-
cookbook: {
|
|
356
|
-
steps: [
|
|
357
|
-
{
|
|
358
|
-
id: 'step1',
|
|
359
|
-
type: 'task',
|
|
360
|
-
service: 'test-service',
|
|
361
|
-
operation: 'getTest',
|
|
362
|
-
input: { id: '123' }
|
|
363
|
-
}
|
|
364
|
-
]
|
|
365
|
-
},
|
|
366
|
-
current_step: 'step1',
|
|
367
|
-
context: {},
|
|
368
|
-
...overrides
|
|
369
|
-
}),
|
|
370
|
-
|
|
371
|
-
createMockOpenApiSpec: (paths = {}) => ({
|
|
372
|
-
openapi: '3.0.0',
|
|
373
|
-
info: {
|
|
374
|
-
title: 'Test Service',
|
|
375
|
-
version: '1.0.0'
|
|
376
|
-
},
|
|
377
|
-
paths: {
|
|
378
|
-
'/api/health': {
|
|
379
|
-
get: {
|
|
380
|
-
operationId: 'healthCheck',
|
|
381
|
-
responses: { '200': { description: 'OK' } }
|
|
382
|
-
}
|
|
383
|
-
},
|
|
384
|
-
...paths
|
|
385
|
-
}
|
|
386
|
-
}),
|
|
387
|
-
|
|
388
|
-
waitForCondition: async (conditionFn, timeout = 5000) => {
|
|
389
|
-
const start = Date.now();
|
|
390
|
-
while (Date.now() - start < timeout) {
|
|
391
|
-
if (conditionFn()) {
|
|
392
|
-
return true;
|
|
393
|
-
}
|
|
394
|
-
await new Promise(resolve => setTimeout(resolve, 100));
|
|
395
|
-
}
|
|
396
|
-
throw new Error('Timeout waiting for condition');
|
|
397
|
-
}
|
|
398
|
-
};
|
|
399
|
-
|
|
400
|
-
// Test that utilities work
|
|
401
|
-
const message = testUtils.createMockWorkflowMessage();
|
|
402
|
-
expect(message.workflow_id).toBe('test-wf-123');
|
|
403
|
-
|
|
404
|
-
const spec = testUtils.createMockOpenApiSpec();
|
|
405
|
-
expect(spec.paths['/api/health']).toBeDefined();
|
|
406
|
-
});
|
|
407
|
-
});
|
|
@@ -1,293 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Integration tests for actual connectors
|
|
5
|
-
* These tests verify that connectors work together correctly
|
|
6
|
-
* Run these after implementing each connector
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
describe('Connector Integration Tests @component', () => {
|
|
10
|
-
describe('Orchestrator Connector', () => {
|
|
11
|
-
test.skip('should integrate with real Orchestrator connector', async () => {
|
|
12
|
-
// Enable this test after conn-orch-orchestrator is fully implemented
|
|
13
|
-
const OrchestratorConnector = require('@onlineapps/conn-orch-orchestrator');
|
|
14
|
-
const orchestrator = OrchestratorConnector.create({
|
|
15
|
-
mqClient: {}, // Mock or real MQ client
|
|
16
|
-
registryClient: {}, // Mock or real registry
|
|
17
|
-
apiMapper: {}, // Mock or real API mapper
|
|
18
|
-
cookbook: {}, // Mock or real cookbook
|
|
19
|
-
logger: console
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
expect(orchestrator).toBeDefined();
|
|
23
|
-
expect(orchestrator.processWorkflowMessage).toBeDefined();
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
test('should verify WorkflowOrchestrator API', () => {
|
|
27
|
-
// Document expected API
|
|
28
|
-
const expectedAPI = {
|
|
29
|
-
constructor: {
|
|
30
|
-
required: ['mqClient', 'registryClient', 'apiMapper', 'cookbook'],
|
|
31
|
-
optional: ['cache', 'errorHandler', 'logger', 'defaultTimeout']
|
|
32
|
-
},
|
|
33
|
-
publicMethods: [
|
|
34
|
-
'processWorkflowMessage',
|
|
35
|
-
'generateCookbook',
|
|
36
|
-
'transformResponse'
|
|
37
|
-
],
|
|
38
|
-
privateMethods: [
|
|
39
|
-
'_executeStep',
|
|
40
|
-
'_executeTaskStep',
|
|
41
|
-
'_executeControlFlowStep',
|
|
42
|
-
'_routeToNextStep',
|
|
43
|
-
'_completeWorkflow',
|
|
44
|
-
'_getCacheKey'
|
|
45
|
-
]
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
// This documents what we expect from the orchestrator
|
|
49
|
-
expect(expectedAPI.publicMethods).toContain('processWorkflowMessage');
|
|
50
|
-
});
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
describe('API Mapper Connector', () => {
|
|
54
|
-
test.skip('should integrate with real API Mapper connector', async () => {
|
|
55
|
-
// Enable this test after conn-orch-api-mapper is fully implemented
|
|
56
|
-
const ApiMapperConnector = require('@onlineapps/conn-orch-api-mapper');
|
|
57
|
-
const apiMapper = ApiMapperConnector.create({
|
|
58
|
-
openApiSpec: {
|
|
59
|
-
openapi: '3.0.0',
|
|
60
|
-
paths: {}
|
|
61
|
-
},
|
|
62
|
-
serviceUrl: 'http://localhost:3000',
|
|
63
|
-
logger: console
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
expect(apiMapper).toBeDefined();
|
|
67
|
-
expect(apiMapper.callOperation).toBeDefined();
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
test('should verify ApiMapper API', () => {
|
|
71
|
-
const expectedAPI = {
|
|
72
|
-
constructor: {
|
|
73
|
-
required: ['openApiSpec'],
|
|
74
|
-
optional: ['serviceUrl', 'service', 'directCall', 'logger', 'port']
|
|
75
|
-
},
|
|
76
|
-
publicMethods: [
|
|
77
|
-
'loadOpenApiSpec',
|
|
78
|
-
'mapOperationToEndpoint',
|
|
79
|
-
'transformRequest',
|
|
80
|
-
'transformResponse',
|
|
81
|
-
'callOperation'
|
|
82
|
-
],
|
|
83
|
-
privateMethods: [
|
|
84
|
-
'_parseOpenApiSpec',
|
|
85
|
-
'_resolveVariables',
|
|
86
|
-
'_getValueFromPath',
|
|
87
|
-
'_buildRequest',
|
|
88
|
-
'_callDirectly',
|
|
89
|
-
'_callViaHttp'
|
|
90
|
-
]
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
expect(expectedAPI.publicMethods).toContain('callOperation');
|
|
94
|
-
});
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
describe('Cookbook Connector', () => {
|
|
98
|
-
test('should identify missing exports in cookbook connector', () => {
|
|
99
|
-
// This test helps us know what to fix in cookbook connector
|
|
100
|
-
const requiredExports = {
|
|
101
|
-
functions: [
|
|
102
|
-
'validateCookbook',
|
|
103
|
-
'createRouter'
|
|
104
|
-
],
|
|
105
|
-
classes: [
|
|
106
|
-
'CookbookExecutor',
|
|
107
|
-
'CookbookGenerator',
|
|
108
|
-
'ResponseMapper'
|
|
109
|
-
]
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
console.log('Cookbook connector must export:');
|
|
113
|
-
console.log('Functions:', requiredExports.functions);
|
|
114
|
-
console.log('Classes:', requiredExports.classes);
|
|
115
|
-
|
|
116
|
-
// Document the expected behavior
|
|
117
|
-
const expectedBehaviors = {
|
|
118
|
-
validateCookbook: 'Validates cookbook JSON structure, throws on error',
|
|
119
|
-
createRouter: 'Creates router instance with MQ and registry clients',
|
|
120
|
-
CookbookExecutor: 'Executes control flow steps (foreach, switch, etc)',
|
|
121
|
-
CookbookGenerator: 'Generates cookbook from OpenAPI spec',
|
|
122
|
-
ResponseMapper: 'Maps/transforms API responses'
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
expect(Object.keys(expectedBehaviors)).toHaveLength(5);
|
|
126
|
-
});
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
describe('Missing Connectors', () => {
|
|
130
|
-
test('conn-base-cache requirements', () => {
|
|
131
|
-
const cacheConnectorAPI = {
|
|
132
|
-
purpose: 'Redis caching with namespace isolation',
|
|
133
|
-
methods: {
|
|
134
|
-
'get(key)': 'Returns cached value or null',
|
|
135
|
-
'set(key, value, options)': 'Stores value with optional TTL',
|
|
136
|
-
'delete(key)': 'Removes cached value',
|
|
137
|
-
'clear(pattern)': 'Clears keys matching pattern',
|
|
138
|
-
'exists(key)': 'Checks if key exists'
|
|
139
|
-
},
|
|
140
|
-
configuration: {
|
|
141
|
-
redis: 'Redis connection string or options',
|
|
142
|
-
namespace: 'Cache key prefix for isolation',
|
|
143
|
-
ttl: 'Default TTL in seconds',
|
|
144
|
-
serialization: 'JSON by default'
|
|
145
|
-
}
|
|
146
|
-
};
|
|
147
|
-
|
|
148
|
-
console.log('TODO: Implement conn-base-cache with:', cacheConnectorAPI);
|
|
149
|
-
expect(Object.keys(cacheConnectorAPI.methods)).toHaveLength(5);
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
test('conn-infra-error-handler requirements', () => {
|
|
153
|
-
const errorHandlerAPI = {
|
|
154
|
-
purpose: 'Unified error handling with strategies',
|
|
155
|
-
methods: {
|
|
156
|
-
'handleError(error, context)': 'Main error handling method',
|
|
157
|
-
'categorizeError(error)': 'Determines error type',
|
|
158
|
-
'shouldRetry(error)': 'Decides if error is retryable',
|
|
159
|
-
'formatError(error)': 'Standardizes error format',
|
|
160
|
-
'logError(error, level)': 'Structured error logging'
|
|
161
|
-
},
|
|
162
|
-
errorTypes: {
|
|
163
|
-
'TransientError': 'Temporary, retryable',
|
|
164
|
-
'BusinessError': 'Domain logic error',
|
|
165
|
-
'ValidationError': 'Input validation failed',
|
|
166
|
-
'SystemError': 'Infrastructure failure',
|
|
167
|
-
'FatalError': 'Non-recoverable'
|
|
168
|
-
},
|
|
169
|
-
strategies: {
|
|
170
|
-
'retry': 'Exponential backoff retry',
|
|
171
|
-
'dlq': 'Send to dead letter queue',
|
|
172
|
-
'alert': 'Trigger monitoring alert',
|
|
173
|
-
'fallback': 'Use fallback value',
|
|
174
|
-
'circuit-break': 'Open circuit breaker'
|
|
175
|
-
}
|
|
176
|
-
};
|
|
177
|
-
|
|
178
|
-
console.log('TODO: Implement conn-infra-error-handler with:', errorHandlerAPI);
|
|
179
|
-
expect(Object.keys(errorHandlerAPI.errorTypes)).toHaveLength(5);
|
|
180
|
-
});
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
describe('End-to-End Message Flow', () => {
|
|
184
|
-
test('should document complete message processing flow', async () => {
|
|
185
|
-
// This test documents the complete flow
|
|
186
|
-
const messageFlow = `
|
|
187
|
-
1. Message arrives at RabbitMQ queue 'service.workflow'
|
|
188
|
-
2. MQConnector receives message via consume callback
|
|
189
|
-
3. ServiceWrapper passes message to Orchestrator.processWorkflowMessage()
|
|
190
|
-
4. Orchestrator validates cookbook structure with CookbookConnector
|
|
191
|
-
5. Orchestrator finds current step in cookbook
|
|
192
|
-
6. Orchestrator checks if step is for this service
|
|
193
|
-
7. If cache available, check cache for result
|
|
194
|
-
8. If not cached, execute step:
|
|
195
|
-
- Task step: ApiMapper.callOperation()
|
|
196
|
-
- Control flow: CookbookExecutor.executeStep()
|
|
197
|
-
9. Store result in cache if available
|
|
198
|
-
10. Update workflow context with result
|
|
199
|
-
11. Determine next step
|
|
200
|
-
12. Route to next step or complete workflow
|
|
201
|
-
`;
|
|
202
|
-
|
|
203
|
-
console.log('Message Processing Flow:', messageFlow);
|
|
204
|
-
expect(messageFlow).toContain('Orchestrator');
|
|
205
|
-
});
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
describe('Performance Benchmarks', () => {
|
|
209
|
-
test('should define performance targets', () => {
|
|
210
|
-
const performanceTargets = {
|
|
211
|
-
messageProcessing: {
|
|
212
|
-
p50: 10, // ms
|
|
213
|
-
p95: 50,
|
|
214
|
-
p99: 100
|
|
215
|
-
},
|
|
216
|
-
startupTime: {
|
|
217
|
-
target: 1000, // ms
|
|
218
|
-
includes: [
|
|
219
|
-
'MQ connection',
|
|
220
|
-
'Registry registration',
|
|
221
|
-
'Orchestrator initialization'
|
|
222
|
-
]
|
|
223
|
-
},
|
|
224
|
-
memoryUsage: {
|
|
225
|
-
baseline: 50, // MB
|
|
226
|
-
perMessage: 0.5 // MB
|
|
227
|
-
},
|
|
228
|
-
throughput: {
|
|
229
|
-
messagesPerSecond: 100,
|
|
230
|
-
withPrefetch: 10
|
|
231
|
-
}
|
|
232
|
-
};
|
|
233
|
-
|
|
234
|
-
console.log('Performance targets:', performanceTargets);
|
|
235
|
-
expect(performanceTargets.messageProcessing.p50).toBeLessThan(20);
|
|
236
|
-
});
|
|
237
|
-
});
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
describe('Test Utilities', () => {
|
|
241
|
-
test('should provide connector test helpers', () => {
|
|
242
|
-
const testHelpers = {
|
|
243
|
-
/**
|
|
244
|
-
* Create a test wrapper with all mocked dependencies
|
|
245
|
-
*/
|
|
246
|
-
createTestWrapper: (overrides = {}) => {
|
|
247
|
-
const ServiceWrapper = require('../../src/ServiceWrapper');
|
|
248
|
-
return new ServiceWrapper({
|
|
249
|
-
service: overrides.service || jest.fn(),
|
|
250
|
-
serviceName: overrides.serviceName || 'test-service',
|
|
251
|
-
openApiSpec: overrides.openApiSpec || {
|
|
252
|
-
openapi: '3.0.0',
|
|
253
|
-
paths: {}
|
|
254
|
-
},
|
|
255
|
-
config: overrides.config || {}
|
|
256
|
-
});
|
|
257
|
-
},
|
|
258
|
-
|
|
259
|
-
/**
|
|
260
|
-
* Wait for message to be processed
|
|
261
|
-
*/
|
|
262
|
-
waitForMessage: async (wrapper, timeout = 1000) => {
|
|
263
|
-
const start = Date.now();
|
|
264
|
-
while (Date.now() - start < timeout) {
|
|
265
|
-
if (wrapper.orchestrator?.processedMessages?.length > 0) {
|
|
266
|
-
return true;
|
|
267
|
-
}
|
|
268
|
-
await new Promise(r => setTimeout(r, 10));
|
|
269
|
-
}
|
|
270
|
-
return false;
|
|
271
|
-
},
|
|
272
|
-
|
|
273
|
-
/**
|
|
274
|
-
* Create mock Express app with test endpoints
|
|
275
|
-
*/
|
|
276
|
-
createMockApp: () => {
|
|
277
|
-
const express = require('express');
|
|
278
|
-
const app = express();
|
|
279
|
-
app.use(express.json());
|
|
280
|
-
|
|
281
|
-
app.get('/api/health', (req, res) => {
|
|
282
|
-
res.json({ status: 'healthy' });
|
|
283
|
-
});
|
|
284
|
-
|
|
285
|
-
return app;
|
|
286
|
-
}
|
|
287
|
-
};
|
|
288
|
-
|
|
289
|
-
// Test the helpers work
|
|
290
|
-
const wrapper = testHelpers.createTestWrapper();
|
|
291
|
-
expect(wrapper.serviceName).toBe('test-service');
|
|
292
|
-
});
|
|
293
|
-
});
|