@onlineapps/service-validator-core 1.0.2
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/README.md +127 -0
- package/coverage/clover.xml +468 -0
- package/coverage/coverage-final.json +8 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +146 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +210 -0
- package/coverage/lcov-report/src/index.html +116 -0
- package/coverage/lcov-report/src/index.js.html +643 -0
- package/coverage/lcov-report/src/security/certificateManager.js.html +799 -0
- package/coverage/lcov-report/src/security/index.html +131 -0
- package/coverage/lcov-report/src/security/tokenManager.js.html +622 -0
- package/coverage/lcov-report/src/validators/connectorValidator.js.html +787 -0
- package/coverage/lcov-report/src/validators/endpointValidator.js.html +577 -0
- package/coverage/lcov-report/src/validators/healthValidator.js.html +655 -0
- package/coverage/lcov-report/src/validators/index.html +161 -0
- package/coverage/lcov-report/src/validators/openApiValidator.js.html +517 -0
- package/coverage/lcov.info +982 -0
- package/jest.config.js +21 -0
- package/package.json +31 -0
- package/src/index.js +212 -0
- package/src/security/ValidationProofVerifier.js +178 -0
- package/src/security/certificateManager.js +239 -0
- package/src/security/tokenManager.js +194 -0
- package/src/validators/connectorValidator.js +235 -0
- package/src/validators/endpointValidator.js +165 -0
- package/src/validators/healthValidator.js +191 -0
- package/src/validators/openApiValidator.js +145 -0
- package/test/component/validation-flow.test.js +353 -0
- package/test/integration/real-validation.test.js +548 -0
- package/test/unit/ValidationCore.test.js +320 -0
|
@@ -0,0 +1,548 @@
|
|
|
1
|
+
const ValidationCore = require('../../src/index');
|
|
2
|
+
const fs = require('fs').promises;
|
|
3
|
+
const path = require('path');
|
|
4
|
+
|
|
5
|
+
describe('ValidationCore Integration Tests', () => {
|
|
6
|
+
let validationCore;
|
|
7
|
+
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
validationCore = new ValidationCore();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
describe('Real service validation', () => {
|
|
13
|
+
it('should validate a complete service configuration', async () => {
|
|
14
|
+
const serviceData = {
|
|
15
|
+
openApiSpec: {
|
|
16
|
+
openapi: '3.0.0',
|
|
17
|
+
info: {
|
|
18
|
+
title: 'Hello Service API',
|
|
19
|
+
version: '1.0.0',
|
|
20
|
+
description: 'Simple hello/goodbye service'
|
|
21
|
+
},
|
|
22
|
+
servers: [
|
|
23
|
+
{ url: 'http://localhost:3000', description: 'Local development' }
|
|
24
|
+
],
|
|
25
|
+
paths: {
|
|
26
|
+
'/health': {
|
|
27
|
+
get: {
|
|
28
|
+
summary: 'Health check',
|
|
29
|
+
tags: ['Health'],
|
|
30
|
+
responses: {
|
|
31
|
+
'200': {
|
|
32
|
+
description: 'Service is healthy',
|
|
33
|
+
content: {
|
|
34
|
+
'application/json': {
|
|
35
|
+
schema: {
|
|
36
|
+
type: 'object',
|
|
37
|
+
properties: {
|
|
38
|
+
status: { type: 'string' },
|
|
39
|
+
timestamp: { type: 'string' },
|
|
40
|
+
uptime: { type: 'number' },
|
|
41
|
+
version: { type: 'string' }
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
'/api/hello': {
|
|
51
|
+
post: {
|
|
52
|
+
summary: 'Say hello',
|
|
53
|
+
tags: ['Greetings'],
|
|
54
|
+
requestBody: {
|
|
55
|
+
required: true,
|
|
56
|
+
content: {
|
|
57
|
+
'application/json': {
|
|
58
|
+
schema: {
|
|
59
|
+
type: 'object',
|
|
60
|
+
required: ['name'],
|
|
61
|
+
properties: {
|
|
62
|
+
name: { type: 'string', minLength: 1 },
|
|
63
|
+
language: {
|
|
64
|
+
type: 'string',
|
|
65
|
+
enum: ['en', 'es', 'fr', 'de'],
|
|
66
|
+
default: 'en'
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
responses: {
|
|
74
|
+
'200': {
|
|
75
|
+
description: 'Greeting response',
|
|
76
|
+
content: {
|
|
77
|
+
'application/json': {
|
|
78
|
+
schema: {
|
|
79
|
+
type: 'object',
|
|
80
|
+
properties: {
|
|
81
|
+
message: { type: 'string' },
|
|
82
|
+
timestamp: { type: 'string' },
|
|
83
|
+
language: { type: 'string' }
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
'400': {
|
|
90
|
+
description: 'Bad request'
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
'/api/goodbye': {
|
|
96
|
+
post: {
|
|
97
|
+
summary: 'Say goodbye',
|
|
98
|
+
tags: ['Greetings'],
|
|
99
|
+
requestBody: {
|
|
100
|
+
required: true,
|
|
101
|
+
content: {
|
|
102
|
+
'application/json': {
|
|
103
|
+
schema: {
|
|
104
|
+
type: 'object',
|
|
105
|
+
required: ['name'],
|
|
106
|
+
properties: {
|
|
107
|
+
name: { type: 'string', minLength: 1 }
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
responses: {
|
|
114
|
+
'200': {
|
|
115
|
+
description: 'Goodbye response',
|
|
116
|
+
content: {
|
|
117
|
+
'application/json': {
|
|
118
|
+
schema: {
|
|
119
|
+
type: 'object',
|
|
120
|
+
properties: {
|
|
121
|
+
message: { type: 'string' },
|
|
122
|
+
timestamp: { type: 'string' }
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
components: {
|
|
133
|
+
schemas: {
|
|
134
|
+
Error: {
|
|
135
|
+
type: 'object',
|
|
136
|
+
properties: {
|
|
137
|
+
error: { type: 'string' },
|
|
138
|
+
message: { type: 'string' },
|
|
139
|
+
timestamp: { type: 'string' }
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
endpoints: [
|
|
146
|
+
{ path: '/health', method: 'GET', handler: 'healthController.check' },
|
|
147
|
+
{ path: '/api/hello', method: 'POST', handler: 'greetingController.hello' },
|
|
148
|
+
{ path: '/api/goodbye', method: 'POST', handler: 'greetingController.goodbye' }
|
|
149
|
+
],
|
|
150
|
+
metadata: {
|
|
151
|
+
serviceName: 'hello-service',
|
|
152
|
+
version: '1.0.0',
|
|
153
|
+
description: 'Hello/Goodbye microservice',
|
|
154
|
+
connectors: [
|
|
155
|
+
'connector-logger',
|
|
156
|
+
'connector-storage',
|
|
157
|
+
'connector-registry-client',
|
|
158
|
+
'connector-mq-client',
|
|
159
|
+
'connector-cookbook',
|
|
160
|
+
'connector-hub'
|
|
161
|
+
],
|
|
162
|
+
dependencies: {
|
|
163
|
+
'@onlineapps/connector-logger': '^1.0.0',
|
|
164
|
+
'@onlineapps/connector-storage': '^1.0.0',
|
|
165
|
+
'@onlineapps/connector-registry-client': '^1.1.0',
|
|
166
|
+
'@onlineapps/connector-mq-client': '^1.1.0',
|
|
167
|
+
'@onlineapps/connector-cookbook': '^1.1.0',
|
|
168
|
+
'@onlineapps/connector-hub': '^1.0.0'
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
health: {
|
|
172
|
+
status: 'healthy',
|
|
173
|
+
uptime: 3600,
|
|
174
|
+
version: '1.0.0',
|
|
175
|
+
checks: {
|
|
176
|
+
rabbitmq: 'connected',
|
|
177
|
+
registry: 'registered',
|
|
178
|
+
redis: 'connected'
|
|
179
|
+
},
|
|
180
|
+
metrics: {
|
|
181
|
+
requestsTotal: 150,
|
|
182
|
+
requestsPerMinute: 10,
|
|
183
|
+
errorRate: 0.01,
|
|
184
|
+
avgResponseTime: 45
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
const result = await validationCore.validate(serviceData);
|
|
190
|
+
|
|
191
|
+
expect(result.success).toBe(true);
|
|
192
|
+
expect(result.validated).toBe(true);
|
|
193
|
+
expect(result.errors).toHaveLength(0);
|
|
194
|
+
expect(result.checks.openApi).toBeDefined();
|
|
195
|
+
expect(result.checks.endpoints).toBeDefined();
|
|
196
|
+
expect(result.checks.connectors).toBeDefined();
|
|
197
|
+
expect(result.checks.health).toBeDefined();
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
it('should detect missing required connectors', async () => {
|
|
201
|
+
const serviceData = {
|
|
202
|
+
metadata: {
|
|
203
|
+
serviceName: 'incomplete-service',
|
|
204
|
+
version: '1.0.0',
|
|
205
|
+
connectors: [
|
|
206
|
+
'connector-logger',
|
|
207
|
+
'connector-hub'
|
|
208
|
+
// Missing: storage, registry-client, mq-client, cookbook
|
|
209
|
+
]
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
const result = await validationCore.validate(serviceData);
|
|
214
|
+
|
|
215
|
+
expect(result.validated).toBe(true);
|
|
216
|
+
expect(result.warnings.length).toBeGreaterThan(0);
|
|
217
|
+
// Check for specific missing connectors
|
|
218
|
+
const warningsStr = result.warnings.join(' ');
|
|
219
|
+
expect(warningsStr).toMatch(/storage|registry|mq|cookbook/i);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it('should validate in strict mode', async () => {
|
|
223
|
+
const strictCore = new ValidationCore({ strictMode: true });
|
|
224
|
+
|
|
225
|
+
const serviceData = {
|
|
226
|
+
metadata: {
|
|
227
|
+
serviceName: 'strict-service',
|
|
228
|
+
version: '1.0.0',
|
|
229
|
+
connectors: ['connector-logger'] // Missing most required connectors
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
const result = await strictCore.validate(serviceData);
|
|
234
|
+
|
|
235
|
+
expect(result.success).toBe(false); // Should fail in strict mode
|
|
236
|
+
expect(result.errors.length).toBeGreaterThan(0);
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
describe('Token and certificate operations', () => {
|
|
241
|
+
it('should complete full validation-token-certificate flow', async () => {
|
|
242
|
+
const serviceData = {
|
|
243
|
+
metadata: {
|
|
244
|
+
serviceName: 'token-test-service',
|
|
245
|
+
version: '2.0.0',
|
|
246
|
+
connectors: [
|
|
247
|
+
'connector-logger',
|
|
248
|
+
'connector-storage',
|
|
249
|
+
'connector-registry-client',
|
|
250
|
+
'connector-mq-client',
|
|
251
|
+
'connector-cookbook'
|
|
252
|
+
]
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
// Step 1: Validate
|
|
257
|
+
const validationResults = await validationCore.validate(serviceData);
|
|
258
|
+
expect(validationResults.success).toBe(true);
|
|
259
|
+
|
|
260
|
+
// Step 2: Generate pre-validation token
|
|
261
|
+
const tokenData = await validationCore.generatePreValidationToken(
|
|
262
|
+
validationResults,
|
|
263
|
+
serviceData.metadata.serviceName
|
|
264
|
+
);
|
|
265
|
+
expect(tokenData).toBeDefined();
|
|
266
|
+
expect(tokenData.token).toBeDefined();
|
|
267
|
+
expect(typeof tokenData.token).toBe('string');
|
|
268
|
+
expect(tokenData.secret).toBeDefined();
|
|
269
|
+
expect(typeof tokenData.secret).toBe('string');
|
|
270
|
+
|
|
271
|
+
// Step 3: Verify token
|
|
272
|
+
const tokenPayload = await validationCore.verifyPreValidationToken(tokenData.token, tokenData.secret);
|
|
273
|
+
expect(tokenPayload.serviceName).toBe('token-test-service');
|
|
274
|
+
expect(tokenPayload.type).toBe('pre-validation');
|
|
275
|
+
|
|
276
|
+
// Step 4: Generate certificate
|
|
277
|
+
const certificate = await validationCore.generateCertificate(
|
|
278
|
+
validationResults,
|
|
279
|
+
serviceData.metadata.serviceName,
|
|
280
|
+
serviceData.metadata.version
|
|
281
|
+
);
|
|
282
|
+
expect(certificate).toBeDefined();
|
|
283
|
+
expect(certificate.subject.serviceName).toBe('token-test-service');
|
|
284
|
+
expect(certificate.subject.serviceVersion).toBe('2.0.0');
|
|
285
|
+
|
|
286
|
+
// Step 5: Certificate structure validation (verification would fail without proper fingerprint)
|
|
287
|
+
expect(certificate.id).toBeDefined();
|
|
288
|
+
expect(certificate.signature).toBeDefined();
|
|
289
|
+
expect(certificate.issuer).toBeDefined();
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
it('should reject invalid tokens', async () => {
|
|
293
|
+
await expect(
|
|
294
|
+
validationCore.verifyPreValidationToken('invalid.token.here')
|
|
295
|
+
).rejects.toThrow();
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
it('should reject certificate generation for failed validation', async () => {
|
|
299
|
+
const failedValidation = {
|
|
300
|
+
success: false,
|
|
301
|
+
errors: ['Validation failed'],
|
|
302
|
+
validated: true
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
await expect(
|
|
306
|
+
validationCore.generateCertificate(failedValidation, 'service', '1.0.0')
|
|
307
|
+
).rejects.toThrow('Cannot generate certificate for failed validation');
|
|
308
|
+
});
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
describe('Functional test generation', () => {
|
|
312
|
+
it('should generate comprehensive tests from OpenAPI spec', async () => {
|
|
313
|
+
const complexSpec = {
|
|
314
|
+
openapi: '3.0.0',
|
|
315
|
+
info: { title: 'Complex API', version: '1.0.0' },
|
|
316
|
+
paths: {
|
|
317
|
+
'/users': {
|
|
318
|
+
get: {
|
|
319
|
+
summary: 'List all users',
|
|
320
|
+
parameters: [
|
|
321
|
+
{ name: 'page', in: 'query', schema: { type: 'integer' } },
|
|
322
|
+
{ name: 'limit', in: 'query', schema: { type: 'integer' } },
|
|
323
|
+
{ name: 'sort', in: 'query', schema: { type: 'string', enum: ['asc', 'desc'] } }
|
|
324
|
+
],
|
|
325
|
+
responses: {
|
|
326
|
+
'200': { description: 'Success' },
|
|
327
|
+
'401': { description: 'Unauthorized' }
|
|
328
|
+
}
|
|
329
|
+
},
|
|
330
|
+
post: {
|
|
331
|
+
summary: 'Create new user',
|
|
332
|
+
requestBody: {
|
|
333
|
+
required: true,
|
|
334
|
+
content: {
|
|
335
|
+
'application/json': {
|
|
336
|
+
schema: {
|
|
337
|
+
type: 'object',
|
|
338
|
+
required: ['email', 'password'],
|
|
339
|
+
properties: {
|
|
340
|
+
email: { type: 'string', format: 'email' },
|
|
341
|
+
password: { type: 'string', minLength: 8 },
|
|
342
|
+
name: { type: 'string' }
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
},
|
|
348
|
+
responses: {
|
|
349
|
+
'201': { description: 'Created' },
|
|
350
|
+
'400': { description: 'Bad request' },
|
|
351
|
+
'409': { description: 'Conflict' }
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
},
|
|
355
|
+
'/users/{id}': {
|
|
356
|
+
parameters: [
|
|
357
|
+
{ name: 'id', in: 'path', required: true, schema: { type: 'string' } }
|
|
358
|
+
],
|
|
359
|
+
get: {
|
|
360
|
+
summary: 'Get user by ID',
|
|
361
|
+
responses: {
|
|
362
|
+
'200': { description: 'Success' },
|
|
363
|
+
'404': { description: 'Not found' }
|
|
364
|
+
}
|
|
365
|
+
},
|
|
366
|
+
put: {
|
|
367
|
+
summary: 'Update user',
|
|
368
|
+
requestBody: {
|
|
369
|
+
required: true,
|
|
370
|
+
content: {
|
|
371
|
+
'application/json': {
|
|
372
|
+
schema: {
|
|
373
|
+
type: 'object',
|
|
374
|
+
properties: {
|
|
375
|
+
email: { type: 'string', format: 'email' },
|
|
376
|
+
name: { type: 'string' }
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
},
|
|
382
|
+
responses: {
|
|
383
|
+
'200': { description: 'Updated' },
|
|
384
|
+
'400': { description: 'Bad request' },
|
|
385
|
+
'404': { description: 'Not found' }
|
|
386
|
+
}
|
|
387
|
+
},
|
|
388
|
+
delete: {
|
|
389
|
+
summary: 'Delete user',
|
|
390
|
+
responses: {
|
|
391
|
+
'204': { description: 'No content' },
|
|
392
|
+
'404': { description: 'Not found' }
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
},
|
|
396
|
+
'/users/{id}/avatar': {
|
|
397
|
+
post: {
|
|
398
|
+
summary: 'Upload avatar',
|
|
399
|
+
parameters: [
|
|
400
|
+
{ name: 'id', in: 'path', required: true, schema: { type: 'string' } }
|
|
401
|
+
],
|
|
402
|
+
requestBody: {
|
|
403
|
+
required: true,
|
|
404
|
+
content: {
|
|
405
|
+
'multipart/form-data': {
|
|
406
|
+
schema: {
|
|
407
|
+
type: 'object',
|
|
408
|
+
properties: {
|
|
409
|
+
file: { type: 'string', format: 'binary' }
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
},
|
|
415
|
+
responses: {
|
|
416
|
+
'200': { description: 'Success' }
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
};
|
|
422
|
+
|
|
423
|
+
const tests = await validationCore.generateFunctionalTests(complexSpec);
|
|
424
|
+
|
|
425
|
+
expect(tests).toHaveLength(6);
|
|
426
|
+
|
|
427
|
+
// Verify test structure
|
|
428
|
+
tests.forEach(test => {
|
|
429
|
+
expect(test).toHaveProperty('name');
|
|
430
|
+
expect(test).toHaveProperty('method');
|
|
431
|
+
expect(test).toHaveProperty('path');
|
|
432
|
+
expect(test).toHaveProperty('expectedResponses');
|
|
433
|
+
expect(test.expectedResponses.length).toBeGreaterThan(0);
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
// Check specific tests
|
|
437
|
+
const createUserTest = tests.find(t => t.path === '/users' && t.method === 'POST');
|
|
438
|
+
expect(createUserTest).toBeDefined();
|
|
439
|
+
expect(createUserTest.requestBody).toBeDefined();
|
|
440
|
+
expect(createUserTest.expectedSuccessResponse).toBe('201');
|
|
441
|
+
|
|
442
|
+
const uploadAvatarTest = tests.find(t => t.path === '/users/{id}/avatar');
|
|
443
|
+
expect(uploadAvatarTest).toBeDefined();
|
|
444
|
+
expect(uploadAvatarTest.parameters).toHaveLength(1);
|
|
445
|
+
|
|
446
|
+
// Check parameterized paths
|
|
447
|
+
const userByIdTests = tests.filter(t => t.path === '/users/{id}');
|
|
448
|
+
expect(userByIdTests.length).toBe(3); // GET, PUT, DELETE
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
it('should handle edge cases in test generation', async () => {
|
|
452
|
+
const edgeCaseSpec = {
|
|
453
|
+
paths: {
|
|
454
|
+
'/webhook': {
|
|
455
|
+
post: {
|
|
456
|
+
summary: 'Webhook endpoint',
|
|
457
|
+
responses: {
|
|
458
|
+
'200': { description: 'OK' }
|
|
459
|
+
}
|
|
460
|
+
},
|
|
461
|
+
options: { // Should be ignored
|
|
462
|
+
summary: 'OPTIONS request',
|
|
463
|
+
responses: {
|
|
464
|
+
'200': { description: 'OK' }
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
};
|
|
470
|
+
|
|
471
|
+
const tests = await validationCore.generateFunctionalTests(edgeCaseSpec);
|
|
472
|
+
|
|
473
|
+
expect(tests).toHaveLength(1); // Only POST, not OPTIONS
|
|
474
|
+
expect(tests[0].method).toBe('POST');
|
|
475
|
+
});
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
describe('Performance tests', () => {
|
|
479
|
+
it('should handle large OpenAPI specs efficiently', async () => {
|
|
480
|
+
const largeSpec = {
|
|
481
|
+
openapi: '3.0.0',
|
|
482
|
+
info: { title: 'Large API', version: '1.0.0' },
|
|
483
|
+
paths: {}
|
|
484
|
+
};
|
|
485
|
+
|
|
486
|
+
// Generate 100 endpoints
|
|
487
|
+
for (let i = 0; i < 100; i++) {
|
|
488
|
+
largeSpec.paths[`/resource${i}`] = {
|
|
489
|
+
get: { summary: `Get resource ${i}`, responses: { '200': { description: 'OK' } } },
|
|
490
|
+
post: { summary: `Create resource ${i}`, responses: { '201': { description: 'Created' } } }
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
const startTime = Date.now();
|
|
495
|
+
const tests = await validationCore.generateFunctionalTests(largeSpec);
|
|
496
|
+
const duration = Date.now() - startTime;
|
|
497
|
+
|
|
498
|
+
expect(tests).toHaveLength(200); // 100 resources * 2 methods
|
|
499
|
+
expect(duration).toBeLessThan(1000); // Should complete within 1 second
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
it('should validate complex service data efficiently', async () => {
|
|
503
|
+
const complexData = {
|
|
504
|
+
openApiSpec: {
|
|
505
|
+
openapi: '3.0.0',
|
|
506
|
+
info: { title: 'Test', version: '1.0.0' },
|
|
507
|
+
paths: {
|
|
508
|
+
'/test': {
|
|
509
|
+
get: { responses: { '200': { description: 'OK' } } }
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
},
|
|
513
|
+
endpoints: Array.from({ length: 50 }, (_, i) => ({
|
|
514
|
+
path: `/endpoint${i}`,
|
|
515
|
+
method: 'GET',
|
|
516
|
+
handler: `handler${i}`
|
|
517
|
+
})),
|
|
518
|
+
metadata: {
|
|
519
|
+
serviceName: 'perf-test',
|
|
520
|
+
version: '1.0.0',
|
|
521
|
+
connectors: [
|
|
522
|
+
'connector-logger',
|
|
523
|
+
'connector-storage',
|
|
524
|
+
'connector-registry-client',
|
|
525
|
+
'connector-mq-client',
|
|
526
|
+
'connector-cookbook'
|
|
527
|
+
]
|
|
528
|
+
},
|
|
529
|
+
health: {
|
|
530
|
+
status: 'healthy',
|
|
531
|
+
uptime: 1000000,
|
|
532
|
+
checks: {
|
|
533
|
+
db: 'ok',
|
|
534
|
+
cache: 'ok',
|
|
535
|
+
queue: 'ok'
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
};
|
|
539
|
+
|
|
540
|
+
const startTime = Date.now();
|
|
541
|
+
const result = await validationCore.validate(complexData);
|
|
542
|
+
const duration = Date.now() - startTime;
|
|
543
|
+
|
|
544
|
+
expect(result.validated).toBe(true);
|
|
545
|
+
expect(duration).toBeLessThan(500); // Should complete within 500ms
|
|
546
|
+
});
|
|
547
|
+
});
|
|
548
|
+
});
|