@onlineapps/service-wrapper 2.0.21 → 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 +110 -24
- 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,293 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* End-to-End Integration Tests for ServiceWrapper
|
|
5
|
-
* Tests the complete flow: MQ → ServiceWrapper → HTTP Service → Response
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
const ServiceWrapper = require('../../src/ServiceWrapper');
|
|
9
|
-
const MQConnector = require('@onlineapps/conn-infra-mq');
|
|
10
|
-
const express = require('express');
|
|
11
|
-
const http = require('http');
|
|
12
|
-
|
|
13
|
-
describe('ServiceWrapper E2E Tests @e2e', () => {
|
|
14
|
-
let testService;
|
|
15
|
-
let testServer;
|
|
16
|
-
let serviceWrapper;
|
|
17
|
-
let mqClient;
|
|
18
|
-
let servicePort;
|
|
19
|
-
const serviceName = 'test-service';
|
|
20
|
-
const rabbitUrl = process.env.RABBITMQ_URL || 'amqp://guest:guest@localhost:33023';
|
|
21
|
-
|
|
22
|
-
beforeAll(async () => {
|
|
23
|
-
// 1. Create test HTTP service
|
|
24
|
-
testService = express();
|
|
25
|
-
testService.use(express.json());
|
|
26
|
-
|
|
27
|
-
// Add test endpoints
|
|
28
|
-
testService.post('/test/hello', (req, res) => {
|
|
29
|
-
res.json({
|
|
30
|
-
message: `Hello ${req.body.name}`,
|
|
31
|
-
timestamp: new Date().toISOString()
|
|
32
|
-
});
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
testService.post('/test/error', (req, res) => {
|
|
36
|
-
res.status(500).json({ error: 'Test error' });
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
testService.get('/health', (req, res) => {
|
|
40
|
-
res.json({ status: 'ok' });
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
// Start test service
|
|
44
|
-
servicePort = 40000 + Math.floor(Math.random() * 1000);
|
|
45
|
-
testServer = http.createServer(testService);
|
|
46
|
-
await new Promise(resolve => {
|
|
47
|
-
testServer.listen(servicePort, resolve);
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
// 2. Create OpenAPI spec
|
|
51
|
-
const openApiSpec = {
|
|
52
|
-
openapi: '3.0.0',
|
|
53
|
-
info: {
|
|
54
|
-
title: 'Test Service',
|
|
55
|
-
version: '1.0.0'
|
|
56
|
-
},
|
|
57
|
-
paths: {
|
|
58
|
-
'/test/hello': {
|
|
59
|
-
post: {
|
|
60
|
-
operationId: 'sayHello',
|
|
61
|
-
requestBody: {
|
|
62
|
-
content: {
|
|
63
|
-
'application/json': {
|
|
64
|
-
schema: {
|
|
65
|
-
type: 'object',
|
|
66
|
-
properties: {
|
|
67
|
-
name: { type: 'string' }
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
},
|
|
75
|
-
'/test/error': {
|
|
76
|
-
post: {
|
|
77
|
-
operationId: 'triggerError'
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
// 3. Initialize ServiceWrapper
|
|
84
|
-
serviceWrapper = new ServiceWrapper({
|
|
85
|
-
serviceUrl: `http://localhost:${servicePort}`,
|
|
86
|
-
serviceName,
|
|
87
|
-
openApiSpec,
|
|
88
|
-
config: {
|
|
89
|
-
rabbitmq: rabbitUrl,
|
|
90
|
-
heartbeatInterval: 60000, // Long interval for tests
|
|
91
|
-
directCall: false // Force HTTP calls
|
|
92
|
-
}
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
// 4. Initialize MQ client for sending test messages
|
|
96
|
-
mqClient = new MQConnector({
|
|
97
|
-
type: 'rabbitmq',
|
|
98
|
-
host: rabbitUrl,
|
|
99
|
-
queue: `${serviceName}.workflow`,
|
|
100
|
-
durable: true
|
|
101
|
-
});
|
|
102
|
-
await mqClient.connect();
|
|
103
|
-
|
|
104
|
-
// 5. Start wrapper (with timeout for registration)
|
|
105
|
-
await serviceWrapper.start();
|
|
106
|
-
}, 60000); // 60s timeout for setup
|
|
107
|
-
|
|
108
|
-
afterAll(async () => {
|
|
109
|
-
// Cleanup
|
|
110
|
-
if (serviceWrapper) {
|
|
111
|
-
await serviceWrapper.stop();
|
|
112
|
-
}
|
|
113
|
-
if (mqClient) {
|
|
114
|
-
await mqClient.disconnect();
|
|
115
|
-
}
|
|
116
|
-
if (testServer) {
|
|
117
|
-
await new Promise(resolve => testServer.close(resolve));
|
|
118
|
-
}
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
describe('Basic Message Flow', () => {
|
|
122
|
-
test('should process simple workflow message', async () => {
|
|
123
|
-
const testMessage = {
|
|
124
|
-
workflow_id: 'test-workflow-1',
|
|
125
|
-
current_step: {
|
|
126
|
-
service: serviceName,
|
|
127
|
-
operation: 'sayHello',
|
|
128
|
-
input: {
|
|
129
|
-
name: 'World'
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
};
|
|
133
|
-
|
|
134
|
-
// Send message to queue
|
|
135
|
-
await mqClient.publish(testMessage);
|
|
136
|
-
|
|
137
|
-
// Wait for processing
|
|
138
|
-
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
139
|
-
|
|
140
|
-
// Verify by checking service logs or response queue
|
|
141
|
-
// Note: In real test, we'd capture response through response queue
|
|
142
|
-
expect(true).toBe(true); // Placeholder
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
test('should handle error responses', async () => {
|
|
146
|
-
const testMessage = {
|
|
147
|
-
workflow_id: 'test-workflow-2',
|
|
148
|
-
current_step: {
|
|
149
|
-
service: serviceName,
|
|
150
|
-
operation: 'triggerError',
|
|
151
|
-
input: {}
|
|
152
|
-
}
|
|
153
|
-
};
|
|
154
|
-
|
|
155
|
-
await mqClient.publish(testMessage);
|
|
156
|
-
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
157
|
-
|
|
158
|
-
// Verify error handling
|
|
159
|
-
expect(true).toBe(true); // Placeholder
|
|
160
|
-
});
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
describe('Workflow Orchestration', () => {
|
|
164
|
-
test('should process multi-step workflow', async () => {
|
|
165
|
-
const workflow = {
|
|
166
|
-
workflow_id: 'multi-step-1',
|
|
167
|
-
steps: [
|
|
168
|
-
{
|
|
169
|
-
service: serviceName,
|
|
170
|
-
operation: 'sayHello',
|
|
171
|
-
input: { name: 'Step1' }
|
|
172
|
-
},
|
|
173
|
-
{
|
|
174
|
-
service: serviceName,
|
|
175
|
-
operation: 'sayHello',
|
|
176
|
-
input: { name: 'Step2' }
|
|
177
|
-
}
|
|
178
|
-
],
|
|
179
|
-
current_step: {
|
|
180
|
-
service: serviceName,
|
|
181
|
-
operation: 'sayHello',
|
|
182
|
-
input: { name: 'Step1' }
|
|
183
|
-
}
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
await mqClient.publish(workflow);
|
|
187
|
-
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
188
|
-
|
|
189
|
-
expect(true).toBe(true); // Placeholder
|
|
190
|
-
});
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
describe('Error Handling & Recovery', () => {
|
|
194
|
-
test('should retry on transient failures', async () => {
|
|
195
|
-
// Test retry logic
|
|
196
|
-
const testMessage = {
|
|
197
|
-
workflow_id: 'retry-test-1',
|
|
198
|
-
current_step: {
|
|
199
|
-
service: serviceName,
|
|
200
|
-
operation: 'triggerError',
|
|
201
|
-
input: {},
|
|
202
|
-
retry_count: 0,
|
|
203
|
-
max_retries: 3
|
|
204
|
-
}
|
|
205
|
-
};
|
|
206
|
-
|
|
207
|
-
await mqClient.publish(testMessage);
|
|
208
|
-
await new Promise(resolve => setTimeout(resolve, 5000));
|
|
209
|
-
|
|
210
|
-
// Verify retries occurred
|
|
211
|
-
expect(true).toBe(true); // Placeholder
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
test('should handle circuit breaker', async () => {
|
|
215
|
-
// Send multiple failing requests to trigger circuit breaker
|
|
216
|
-
for (let i = 0; i < 5; i++) {
|
|
217
|
-
await mqClient.publish({
|
|
218
|
-
workflow_id: `circuit-test-${i}`,
|
|
219
|
-
current_step: {
|
|
220
|
-
service: serviceName,
|
|
221
|
-
operation: 'triggerError',
|
|
222
|
-
input: {}
|
|
223
|
-
}
|
|
224
|
-
});
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
228
|
-
|
|
229
|
-
// Verify circuit breaker activated
|
|
230
|
-
expect(true).toBe(true); // Placeholder
|
|
231
|
-
});
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
describe('Performance & Load Testing', () => {
|
|
235
|
-
test('should handle concurrent messages', async () => {
|
|
236
|
-
const messages = [];
|
|
237
|
-
for (let i = 0; i < 10; i++) {
|
|
238
|
-
messages.push({
|
|
239
|
-
workflow_id: `concurrent-${i}`,
|
|
240
|
-
current_step: {
|
|
241
|
-
service: serviceName,
|
|
242
|
-
operation: 'sayHello',
|
|
243
|
-
input: { name: `User${i}` }
|
|
244
|
-
}
|
|
245
|
-
});
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
// Send all messages concurrently
|
|
249
|
-
await Promise.all(messages.map(msg => mqClient.publish(msg)));
|
|
250
|
-
|
|
251
|
-
// Wait for all to process
|
|
252
|
-
await new Promise(resolve => setTimeout(resolve, 5000));
|
|
253
|
-
|
|
254
|
-
expect(true).toBe(true); // Placeholder
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
test('should respect prefetch limit', async () => {
|
|
258
|
-
// Send more messages than prefetch limit
|
|
259
|
-
const messages = [];
|
|
260
|
-
for (let i = 0; i < 20; i++) {
|
|
261
|
-
messages.push({
|
|
262
|
-
workflow_id: `prefetch-${i}`,
|
|
263
|
-
current_step: {
|
|
264
|
-
service: serviceName,
|
|
265
|
-
operation: 'sayHello',
|
|
266
|
-
input: { name: `Batch${i}` }
|
|
267
|
-
}
|
|
268
|
-
});
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
await Promise.all(messages.map(msg => mqClient.publish(msg)));
|
|
272
|
-
await new Promise(resolve => setTimeout(resolve, 10000));
|
|
273
|
-
|
|
274
|
-
// Verify prefetch was respected
|
|
275
|
-
expect(true).toBe(true); // Placeholder
|
|
276
|
-
});
|
|
277
|
-
});
|
|
278
|
-
|
|
279
|
-
describe('Registry Integration', () => {
|
|
280
|
-
test('should register service on startup', () => {
|
|
281
|
-
// Verify service was registered
|
|
282
|
-
expect(serviceWrapper.isRunning).toBe(true);
|
|
283
|
-
});
|
|
284
|
-
|
|
285
|
-
test('should send heartbeats', async () => {
|
|
286
|
-
// Wait for at least one heartbeat
|
|
287
|
-
await new Promise(resolve => setTimeout(resolve, 5000));
|
|
288
|
-
|
|
289
|
-
// Verify heartbeat was sent (check logs or registry)
|
|
290
|
-
expect(true).toBe(true); // Placeholder
|
|
291
|
-
});
|
|
292
|
-
});
|
|
293
|
-
});
|
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
'use strict';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Integration test for ServiceWrapper with OrchestratorConnector
|
|
6
|
-
* Tests that the thin orchestration layer properly delegates to orchestrator
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
const path = require('path');
|
|
10
|
-
|
|
11
|
-
// Mock setup
|
|
12
|
-
const Module = require('module');
|
|
13
|
-
const originalRequire = Module.prototype.require;
|
|
14
|
-
|
|
15
|
-
// Create test doubles
|
|
16
|
-
const testDoubles = {
|
|
17
|
-
processWorkflowMessage: null,
|
|
18
|
-
lastMessage: null,
|
|
19
|
-
lastServiceName: null
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
Module.prototype.require = function(id) {
|
|
23
|
-
switch(id) {
|
|
24
|
-
case '@onlineapps/conn-infra-mq':
|
|
25
|
-
return class MockMQ {
|
|
26
|
-
constructor() { this.connected = false; }
|
|
27
|
-
async connect() { this.connected = true; }
|
|
28
|
-
async disconnect() { this.connected = false; }
|
|
29
|
-
async consume(handler) { this.handler = handler; }
|
|
30
|
-
isConnected() { return this.connected; }
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
case '@onlineapps/conn-orch-registry':
|
|
34
|
-
return {
|
|
35
|
-
ServiceRegistryClient: class {
|
|
36
|
-
async register() { return true; }
|
|
37
|
-
async unregister() { return true; }
|
|
38
|
-
async sendHeartbeat() { return true; }
|
|
39
|
-
isConnected() { return true; }
|
|
40
|
-
}
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
case '@onlineapps/conn-base-logger':
|
|
44
|
-
return {
|
|
45
|
-
create: () => ({
|
|
46
|
-
info: () => {},
|
|
47
|
-
error: () => {},
|
|
48
|
-
warn: () => {},
|
|
49
|
-
debug: () => {}
|
|
50
|
-
})
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
case '@onlineapps/conn-orch-orchestrator':
|
|
54
|
-
return {
|
|
55
|
-
create: (options) => ({
|
|
56
|
-
processWorkflowMessage: async (message, serviceName) => {
|
|
57
|
-
testDoubles.lastMessage = message;
|
|
58
|
-
testDoubles.lastServiceName = serviceName;
|
|
59
|
-
testDoubles.processWorkflowMessage = true;
|
|
60
|
-
return { processed: true };
|
|
61
|
-
}
|
|
62
|
-
})
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
case '@onlineapps/conn-orch-api-mapper':
|
|
66
|
-
return { create: () => ({ map: () => {} }) };
|
|
67
|
-
|
|
68
|
-
case '@onlineapps/conn-orch-cookbook':
|
|
69
|
-
return { validateCookbook: () => true };
|
|
70
|
-
|
|
71
|
-
case '@onlineapps/conn-base-cache':
|
|
72
|
-
return class {
|
|
73
|
-
async connect() {}
|
|
74
|
-
async disconnect() {}
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
case '@onlineapps/conn-infra-error-handler':
|
|
78
|
-
return class {
|
|
79
|
-
constructor() {}
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
default:
|
|
83
|
-
return originalRequire.apply(this, arguments);
|
|
84
|
-
}
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
// Run test
|
|
88
|
-
async function testOrchestratorIntegration() {
|
|
89
|
-
console.log('=== Orchestrator Integration Test ===\n');
|
|
90
|
-
|
|
91
|
-
try {
|
|
92
|
-
// Load ServiceWrapper with mocked dependencies
|
|
93
|
-
const ServiceWrapper = require('../../src/ServiceWrapper');
|
|
94
|
-
|
|
95
|
-
// Create instance
|
|
96
|
-
const wrapper = new ServiceWrapper({
|
|
97
|
-
service: function expressApp() {},
|
|
98
|
-
serviceName: 'test-service',
|
|
99
|
-
openApiSpec: {
|
|
100
|
-
openapi: '3.0.0',
|
|
101
|
-
info: { version: '1.0.0' },
|
|
102
|
-
paths: {}
|
|
103
|
-
},
|
|
104
|
-
config: {}
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
// Start wrapper
|
|
108
|
-
await wrapper.start();
|
|
109
|
-
console.log('✓ ServiceWrapper started');
|
|
110
|
-
|
|
111
|
-
// Get MQ client handler
|
|
112
|
-
const mqClient = wrapper.mqClient;
|
|
113
|
-
if (!mqClient.handler) {
|
|
114
|
-
throw new Error('No message handler registered');
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// Simulate workflow message
|
|
118
|
-
const testMessage = {
|
|
119
|
-
workflow_id: 'wf-123',
|
|
120
|
-
current_step: {
|
|
121
|
-
id: 'step-1',
|
|
122
|
-
service: 'test-service',
|
|
123
|
-
operation: 'testOp',
|
|
124
|
-
input: { data: 'test' }
|
|
125
|
-
},
|
|
126
|
-
context: {
|
|
127
|
-
variables: {},
|
|
128
|
-
results: {}
|
|
129
|
-
}
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
// Process message through the handler
|
|
133
|
-
console.log('Sending test message to handler...');
|
|
134
|
-
await mqClient.handler(testMessage);
|
|
135
|
-
|
|
136
|
-
// Verify orchestrator was called
|
|
137
|
-
if (testDoubles.processWorkflowMessage) {
|
|
138
|
-
console.log('✓ Orchestrator.processWorkflowMessage was called');
|
|
139
|
-
console.log('✓ Message passed:', JSON.stringify(testDoubles.lastMessage, null, 2));
|
|
140
|
-
console.log('✓ Service name passed:', testDoubles.lastServiceName);
|
|
141
|
-
|
|
142
|
-
// Verify correct delegation
|
|
143
|
-
if (testDoubles.lastMessage === testMessage &&
|
|
144
|
-
testDoubles.lastServiceName === 'test-service') {
|
|
145
|
-
console.log('✓ Message and service name correctly delegated');
|
|
146
|
-
} else {
|
|
147
|
-
console.log('✗ Incorrect delegation parameters');
|
|
148
|
-
}
|
|
149
|
-
} else {
|
|
150
|
-
console.log('✗ Orchestrator.processWorkflowMessage was NOT called');
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// Stop wrapper
|
|
154
|
-
await wrapper.stop();
|
|
155
|
-
console.log('✓ ServiceWrapper stopped');
|
|
156
|
-
|
|
157
|
-
console.log('\n=== Test Result ===');
|
|
158
|
-
console.log('✅ Orchestrator integration working correctly!');
|
|
159
|
-
console.log('ServiceWrapper properly delegates workflow messages to orchestrator.');
|
|
160
|
-
|
|
161
|
-
} catch (error) {
|
|
162
|
-
console.error('\n=== Test Failed ===');
|
|
163
|
-
console.error('✗ Error:', error.message);
|
|
164
|
-
console.error('Stack:', error.stack);
|
|
165
|
-
process.exit(1);
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// Run the test
|
|
170
|
-
testOrchestratorIntegration();
|