@onlineapps/service-wrapper 2.0.21 → 2.0.23

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 (33) hide show
  1. package/API.md +0 -0
  2. package/README.md +0 -0
  3. package/docs/CONFIGURATION_GUIDE.md +0 -0
  4. package/docs/FINAL_ARCHITECTURE.md +0 -0
  5. package/docs/MIGRATION_FROM_OPENAPI.md +0 -0
  6. package/docs/STANDARDS_OVERVIEW.md +0 -0
  7. package/docs/archived-2025-09-29/API_STRUCTURE_STANDARD.md +0 -0
  8. package/docs/archived-2025-09-29/ARCHITECTURE_DECISION.md +0 -0
  9. package/docs/archived-2025-09-29/HANDLER_VS_HTTP_COMPARISON.md +0 -0
  10. package/docs/archived-2025-09-29/INSTALLATION_GUIDE.md +0 -0
  11. package/docs/archived-2025-09-29/OPERATIONS_SCHEMA.md +0 -0
  12. package/docs/archived-2025-09-29/PERFORMANCE.md +0 -0
  13. package/docs/archived-2025-09-29/PROCESS_FLOWS.md +0 -0
  14. package/docs/archived-2025-09-29/SERVICE_TESTING_STANDARD.md +0 -0
  15. package/docs/archived-2025-09-29/WRAPPER_ARCHITECTURE.md +0 -0
  16. package/examples/README.md +0 -0
  17. package/examples/cookbook-file-output.json +0 -0
  18. package/examples/cookbook-multi-step.json +0 -0
  19. package/examples/cookbook-single-operation.json +0 -0
  20. package/jest.config.js +0 -0
  21. package/jsdoc.json +0 -0
  22. package/package.json +1 -1
  23. package/src/ServiceWrapper.js +110 -24
  24. package/src/index.js +0 -0
  25. package/tests/component/ServiceWrapper.component.test.js +0 -407
  26. package/tests/component/connector-integration.test.js +0 -293
  27. package/tests/e2e/full-flow.test.js +0 -293
  28. package/tests/integration/orchestrator-integration.test.js +0 -170
  29. package/tests/mocks/connectors.js +0 -304
  30. package/tests/monitoring-integration.test.js +0 -150
  31. package/tests/run-tests.js +0 -135
  32. package/tests/setup.js +0 -31
  33. package/tests/unit/ServiceWrapper.test.js +0 -372
@@ -1,372 +0,0 @@
1
- 'use strict';
2
-
3
- const ServiceWrapper = require('../../src/ServiceWrapper');
4
- const {
5
- MockMQConnector,
6
- MockServiceRegistryClient,
7
- MockLogger,
8
- MockOrchestratorConnector,
9
- MockApiMapperConnector,
10
- MockCookbookConnector
11
- } = require('../mocks/connectors');
12
-
13
- // Mock all connector dependencies
14
- jest.mock('@onlineapps/conn-infra-mq', () => MockMQConnector);
15
- jest.mock('@onlineapps/conn-orch-registry', () => ({
16
- ServiceRegistryClient: MockServiceRegistryClient
17
- }));
18
- jest.mock('@onlineapps/conn-base-logger', () => MockLogger);
19
- jest.mock('@onlineapps/conn-orch-orchestrator', () => MockOrchestratorConnector);
20
- jest.mock('@onlineapps/conn-orch-api-mapper', () => MockApiMapperConnector);
21
- jest.mock('@onlineapps/conn-orch-cookbook', () => MockCookbookConnector);
22
-
23
- describe('ServiceWrapper Unit Tests @unit', () => {
24
- let wrapper;
25
- let mockExpressApp;
26
- let mockOpenApiSpec;
27
-
28
- beforeEach(() => {
29
- // Mock Express app
30
- mockExpressApp = jest.fn();
31
-
32
- // Mock OpenAPI spec
33
- mockOpenApiSpec = {
34
- openapi: '3.0.0',
35
- info: {
36
- title: 'Test Service',
37
- version: '1.0.0',
38
- description: 'Test service for unit tests'
39
- },
40
- paths: {
41
- '/test': {
42
- get: {
43
- operationId: 'getTest',
44
- responses: { '200': { description: 'OK' } }
45
- }
46
- }
47
- }
48
- };
49
-
50
- // Clear all mocks
51
- jest.clearAllMocks();
52
- });
53
-
54
- afterEach(async () => {
55
- if (wrapper && wrapper.isRunning) {
56
- await wrapper.stop();
57
- }
58
- });
59
-
60
- describe('Constructor', () => {
61
- test('should create instance with valid options', () => {
62
- wrapper = new ServiceWrapper({
63
- service: mockExpressApp,
64
- serviceName: 'test-service',
65
- openApiSpec: mockOpenApiSpec
66
- });
67
-
68
- expect(wrapper).toBeInstanceOf(ServiceWrapper);
69
- expect(wrapper.serviceName).toBe('test-service');
70
- expect(wrapper.service).toBe(mockExpressApp);
71
- expect(wrapper.openApiSpec).toBe(mockOpenApiSpec);
72
- });
73
-
74
- test('should throw error without service', () => {
75
- expect(() => {
76
- new ServiceWrapper({
77
- serviceName: 'test-service',
78
- openApiSpec: mockOpenApiSpec
79
- });
80
- }).toThrow('Service (Express app) is required');
81
- });
82
-
83
- test('should throw error without serviceName', () => {
84
- expect(() => {
85
- new ServiceWrapper({
86
- service: mockExpressApp,
87
- openApiSpec: mockOpenApiSpec
88
- });
89
- }).toThrow('Service name is required');
90
- });
91
-
92
- test('should throw error without openApiSpec', () => {
93
- expect(() => {
94
- new ServiceWrapper({
95
- service: mockExpressApp,
96
- serviceName: 'test-service'
97
- });
98
- }).toThrow('OpenAPI specification is required');
99
- });
100
-
101
- test('should accept optional config', () => {
102
- wrapper = new ServiceWrapper({
103
- service: mockExpressApp,
104
- serviceName: 'test-service',
105
- openApiSpec: mockOpenApiSpec,
106
- config: {
107
- port: 3001,
108
- prefetch: 5,
109
- heartbeatInterval: 10000
110
- }
111
- });
112
-
113
- expect(wrapper.config.port).toBe(3001);
114
- expect(wrapper.config.prefetch).toBe(5);
115
- expect(wrapper.config.heartbeatInterval).toBe(10000);
116
- });
117
- });
118
-
119
- describe('Start', () => {
120
- beforeEach(() => {
121
- wrapper = new ServiceWrapper({
122
- service: mockExpressApp,
123
- serviceName: 'test-service',
124
- openApiSpec: mockOpenApiSpec
125
- });
126
- });
127
-
128
- test('should start successfully', async () => {
129
- await wrapper.start();
130
-
131
- expect(wrapper.isRunning).toBe(true);
132
- expect(wrapper.mqClient).toBeDefined();
133
- expect(wrapper.mqClient.isConnected()).toBe(true);
134
- expect(wrapper.registryClient).toBeDefined();
135
- expect(wrapper.orchestrator).toBeDefined();
136
- });
137
-
138
- test('should register service with registry', async () => {
139
- await wrapper.start();
140
-
141
- const registeredServices = wrapper.registryClient.registeredServices;
142
- expect(registeredServices.has('test-service')).toBe(true);
143
-
144
- const serviceInfo = registeredServices.get('test-service');
145
- expect(serviceInfo.name).toBe('test-service');
146
- expect(serviceInfo.url).toBe('http://localhost:3000');
147
- expect(serviceInfo.openapi).toBe(mockOpenApiSpec);
148
- });
149
-
150
- test('should subscribe to workflow queue', async () => {
151
- await wrapper.start();
152
-
153
- const consumers = wrapper.mqClient.consumers;
154
- expect(consumers.has('test-service.workflow')).toBe(true);
155
- });
156
-
157
- test('should not start twice', async () => {
158
- await wrapper.start();
159
- const firstMqClient = wrapper.mqClient;
160
-
161
- await wrapper.start(); // Second call
162
-
163
- expect(wrapper.mqClient).toBe(firstMqClient); // Same instance
164
- expect(wrapper.logger.logs.some(log =>
165
- log.level === 'warn' && log.message.includes('already running')
166
- )).toBe(true);
167
- });
168
-
169
- test('should handle start failure gracefully', async () => {
170
- // Force connection to fail
171
- MockMQConnector.prototype.connect = jest.fn()
172
- .mockRejectedValue(new Error('Connection failed'));
173
-
174
- wrapper = new ServiceWrapper({
175
- service: mockExpressApp,
176
- serviceName: 'test-service',
177
- openApiSpec: mockOpenApiSpec
178
- });
179
-
180
- await expect(wrapper.start()).rejects.toThrow('Connection failed');
181
- expect(wrapper.isRunning).toBe(false);
182
-
183
- // Restore original
184
- MockMQConnector.prototype.connect = jest.fn().mockResolvedValue();
185
- });
186
- });
187
-
188
- describe('Stop', () => {
189
- beforeEach(async () => {
190
- wrapper = new ServiceWrapper({
191
- service: mockExpressApp,
192
- serviceName: 'test-service',
193
- openApiSpec: mockOpenApiSpec
194
- });
195
- await wrapper.start();
196
- });
197
-
198
- test('should stop successfully', async () => {
199
- expect(wrapper.isRunning).toBe(true);
200
-
201
- await wrapper.stop();
202
-
203
- expect(wrapper.isRunning).toBe(false);
204
- expect(wrapper.mqClient.isConnected()).toBe(false);
205
- });
206
-
207
- test('should unregister from registry', async () => {
208
- expect(wrapper.registryClient.registeredServices.has('test-service')).toBe(true);
209
-
210
- await wrapper.stop();
211
-
212
- expect(wrapper.registryClient.registeredServices.has('test-service')).toBe(false);
213
- });
214
-
215
- test('should stop heartbeat', async () => {
216
- const intervalSpy = jest.spyOn(global, 'clearInterval');
217
-
218
- await wrapper.stop();
219
-
220
- expect(intervalSpy).toHaveBeenCalled();
221
- intervalSpy.mockRestore();
222
- });
223
-
224
- test('should not stop if not running', async () => {
225
- await wrapper.stop();
226
- const logCount = wrapper.logger.logs.length;
227
-
228
- await wrapper.stop(); // Second call
229
-
230
- expect(wrapper.logger.logs.some((log, index) =>
231
- index >= logCount &&
232
- log.level === 'warn' &&
233
- log.message.includes('not running')
234
- )).toBe(true);
235
- });
236
- });
237
-
238
- describe('Message Processing', () => {
239
- beforeEach(async () => {
240
- wrapper = new ServiceWrapper({
241
- service: mockExpressApp,
242
- serviceName: 'test-service',
243
- openApiSpec: mockOpenApiSpec
244
- });
245
- await wrapper.start();
246
- });
247
-
248
- test('should process workflow message successfully', async () => {
249
- const testMessage = {
250
- workflow_id: 'wf-123',
251
- cookbook: {
252
- steps: [
253
- { id: 'step1', type: 'task', service: 'test-service' }
254
- ]
255
- },
256
- current_step: 'step1',
257
- context: { test: true }
258
- };
259
-
260
- // Simulate message arrival
261
- await wrapper.mqClient.simulateMessage('test-service.workflow', testMessage);
262
-
263
- // Check orchestrator received the message
264
- const processedMessages = wrapper.orchestrator.processedMessages;
265
- expect(processedMessages).toHaveLength(1);
266
- expect(processedMessages[0].message).toEqual(testMessage);
267
- expect(processedMessages[0].serviceName).toBe('test-service');
268
- });
269
-
270
- test('should handle message processing error', async () => {
271
- // Make orchestrator fail
272
- wrapper.orchestrator.setNextProcessingToFail(true);
273
-
274
- const testMessage = {
275
- workflow_id: 'wf-123',
276
- cookbook: { steps: [] },
277
- current_step: 'step1',
278
- context: {}
279
- };
280
-
281
- // Simulate message arrival
282
- await wrapper.mqClient.simulateMessage('test-service.workflow', testMessage);
283
-
284
- // Check error was logged
285
- expect(wrapper.logger.logs.some(log =>
286
- log.level === 'error' &&
287
- log.message.includes('Message processing failed')
288
- )).toBe(true);
289
- });
290
- });
291
-
292
- describe('GetStatus', () => {
293
- test('should return status when running', async () => {
294
- wrapper = new ServiceWrapper({
295
- service: mockExpressApp,
296
- serviceName: 'test-service',
297
- openApiSpec: mockOpenApiSpec,
298
- config: { port: 3001 }
299
- });
300
-
301
- await wrapper.start();
302
- const status = wrapper.getStatus();
303
-
304
- expect(status).toEqual({
305
- serviceName: 'test-service',
306
- isRunning: true,
307
- mqConnected: true,
308
- registryConnected: true,
309
- config: {
310
- port: 3001,
311
- prefetch: 10,
312
- heartbeatInterval: 30000
313
- }
314
- });
315
- });
316
-
317
- test('should return status when not running', () => {
318
- wrapper = new ServiceWrapper({
319
- service: mockExpressApp,
320
- serviceName: 'test-service',
321
- openApiSpec: mockOpenApiSpec
322
- });
323
-
324
- const status = wrapper.getStatus();
325
-
326
- expect(status).toEqual({
327
- serviceName: 'test-service',
328
- isRunning: false,
329
- mqConnected: false,
330
- registryConnected: false,
331
- config: {
332
- port: 3000,
333
- prefetch: 10,
334
- heartbeatInterval: 30000
335
- }
336
- });
337
- });
338
- });
339
-
340
- describe('Heartbeat', () => {
341
- beforeEach(() => {
342
- jest.useFakeTimers();
343
- });
344
-
345
- afterEach(() => {
346
- jest.useRealTimers();
347
- });
348
-
349
- test('should send heartbeats periodically', async () => {
350
- wrapper = new ServiceWrapper({
351
- service: mockExpressApp,
352
- serviceName: 'test-service',
353
- openApiSpec: mockOpenApiSpec,
354
- config: { heartbeatInterval: 5000 }
355
- });
356
-
357
- await wrapper.start();
358
-
359
- // Initially no heartbeats
360
- expect(wrapper.registryClient.heartbeats).toHaveLength(0);
361
-
362
- // Advance time
363
- jest.advanceTimersByTime(5000);
364
-
365
- // Wait for async heartbeat
366
- await Promise.resolve();
367
-
368
- expect(wrapper.registryClient.heartbeats.length).toBeGreaterThan(0);
369
- expect(wrapper.registryClient.heartbeats[0].service).toBe('test-service');
370
- });
371
- });
372
- });