@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,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
- });