@onlineapps/conn-orch-validator 2.0.33 → 3.0.0
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/docs/DESIGN.md +64 -118
- package/package.json +2 -2
- package/src/ServiceReadinessValidator.js +35 -153
- package/src/ValidationOrchestrator.js +45 -10
- package/src/config.js +1 -1
- package/src/helpers/createServiceReadinessTests.js +11 -9
- package/src/index.js +13 -19
- package/src/validators/ServiceStructureValidator.js +48 -22
- package/examples/service-wrapper-usage.js +0 -250
- package/examples/three-tier-testing.js +0 -144
- package/src/ServiceTestHarness.js +0 -256
- package/src/ServiceValidator.js +0 -399
- package/src/TestOrchestrator.js +0 -736
package/src/index.js
CHANGED
|
@@ -1,17 +1,25 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* @module @onlineapps/conn-orch-validator
|
|
5
|
+
* @description Service validation framework using the operations.json contract.
|
|
6
|
+
*
|
|
7
|
+
* Production entry point: {@link ValidationOrchestrator} (6-step pre-validation
|
|
8
|
+
* driven by operations.json — OpenAPI path iteration is NOT supported).
|
|
9
|
+
*
|
|
10
|
+
* @see /api/docs/standards/operations-registry-contract.md §3 (operations.json)
|
|
11
|
+
* @see /api/docs/standards/validation-probe-contract.md (validation probes)
|
|
12
|
+
* @see /api/docs/architecture/validator.md (validator architecture)
|
|
13
|
+
*/
|
|
14
|
+
|
|
4
15
|
const MockMQClient = require('./mocks/MockMQClient');
|
|
5
16
|
const MockRegistry = require('./mocks/MockRegistry');
|
|
6
17
|
const MockStorage = require('./mocks/MockStorage');
|
|
7
18
|
|
|
8
|
-
// Base test utilities (no circular dependencies)
|
|
9
|
-
const ServiceValidator = require('./ServiceValidator');
|
|
10
19
|
const CookbookTestUtils = require('./CookbookTestUtils');
|
|
11
20
|
const { ServiceStructureValidator } = require('./validators/ServiceStructureValidator');
|
|
12
21
|
const ValidationProofGenerator = require('./validators/ValidationProofGenerator');
|
|
13
22
|
|
|
14
|
-
// Test runners (depend on mocks)
|
|
15
23
|
let CookbookTestRunner;
|
|
16
24
|
try {
|
|
17
25
|
CookbookTestRunner = require('./CookbookTestRunner');
|
|
@@ -20,43 +28,29 @@ try {
|
|
|
20
28
|
}
|
|
21
29
|
const WorkflowTestRunner = require('./WorkflowTestRunner');
|
|
22
30
|
|
|
23
|
-
// Orchestrators (depend on validators and runners)
|
|
24
31
|
const ServiceReadinessValidator = require('./ServiceReadinessValidator');
|
|
25
|
-
const TestOrchestrator = require('./TestOrchestrator');
|
|
26
|
-
const ServiceTestHarness = require('./ServiceTestHarness');
|
|
27
32
|
const ValidationOrchestrator = require('./ValidationOrchestrator');
|
|
28
33
|
|
|
29
|
-
// Test helpers (generic test suite creators)
|
|
30
34
|
const { createServiceReadinessTests } = require('./helpers/createServiceReadinessTests');
|
|
31
35
|
const { createPreValidationTests } = require('./helpers/createPreValidationTests');
|
|
32
36
|
|
|
33
|
-
// Export all components with getters to avoid circular dependency issues
|
|
34
37
|
module.exports = {
|
|
35
|
-
// Mocks
|
|
36
38
|
get MockMQClient() { return MockMQClient; },
|
|
37
39
|
get MockRegistry() { return MockRegistry; },
|
|
38
40
|
get MockStorage() { return MockStorage; },
|
|
39
41
|
|
|
40
|
-
// Test utilities
|
|
41
|
-
get ServiceTestHarness() { return ServiceTestHarness; },
|
|
42
|
-
get ServiceValidator() { return ServiceValidator; },
|
|
43
42
|
get WorkflowTestRunner() { return WorkflowTestRunner; },
|
|
44
43
|
get CookbookTestUtils() { return CookbookTestUtils; },
|
|
45
44
|
get ServiceReadinessValidator() { return ServiceReadinessValidator; },
|
|
46
|
-
get TestOrchestrator() { return TestOrchestrator; },
|
|
47
45
|
get CookbookTestRunner() { return CookbookTestRunner; },
|
|
48
46
|
get ValidationProofGenerator() { return ValidationProofGenerator; },
|
|
49
47
|
get ServiceStructureValidator() { return ServiceStructureValidator; },
|
|
50
48
|
get ValidationOrchestrator() { return ValidationOrchestrator; },
|
|
51
49
|
|
|
52
|
-
// Test helpers (NEW - generic test suite creators)
|
|
53
50
|
get createServiceReadinessTests() { return createServiceReadinessTests; },
|
|
54
51
|
get createPreValidationTests() { return createPreValidationTests; },
|
|
55
52
|
|
|
56
|
-
// Convenience factory functions
|
|
57
|
-
createTestHarness: (options) => new ServiceTestHarness(options),
|
|
58
|
-
createValidator: (options) => new ServiceValidator(options),
|
|
59
53
|
createMockMQ: () => new MockMQClient(),
|
|
60
54
|
createMockRegistry: () => new MockRegistry(),
|
|
61
55
|
createMockStorage: () => new MockStorage()
|
|
62
|
-
};
|
|
56
|
+
};
|
|
@@ -410,7 +410,8 @@ class ServiceStructureValidator {
|
|
|
410
410
|
}
|
|
411
411
|
|
|
412
412
|
/**
|
|
413
|
-
* Validate operations.json structure
|
|
413
|
+
* Validate operations.json structure (v3 — handler registry dispatch).
|
|
414
|
+
* v3 schema per biz-service-invocation-model.md §5.3.
|
|
414
415
|
*/
|
|
415
416
|
validateOperationsStructure(operations) {
|
|
416
417
|
if (!operations.operations) {
|
|
@@ -418,7 +419,7 @@ class ServiceStructureValidator {
|
|
|
418
419
|
type: 'INVALID_OPERATIONS_STRUCTURE',
|
|
419
420
|
path: 'config/service/operations.json',
|
|
420
421
|
message: 'operations.json must have "operations" key',
|
|
421
|
-
fix: 'Wrap operations in {"operations": {...}}. See:
|
|
422
|
+
fix: 'Wrap operations in {"operations": {...}}. See: biz-service-invocation-model.md §5.3'
|
|
422
423
|
});
|
|
423
424
|
return;
|
|
424
425
|
}
|
|
@@ -434,6 +435,17 @@ class ServiceStructureValidator {
|
|
|
434
435
|
return;
|
|
435
436
|
}
|
|
436
437
|
|
|
438
|
+
if (operations.schema_version && operations.schema_version !== '3.0') {
|
|
439
|
+
this.warnings.push({
|
|
440
|
+
type: 'SCHEMA_VERSION_MISMATCH',
|
|
441
|
+
path: 'config/service/operations.json',
|
|
442
|
+
field: 'schema_version',
|
|
443
|
+
value: operations.schema_version,
|
|
444
|
+
message: `operations.json schema_version is "${operations.schema_version}" — expected "3.0"`,
|
|
445
|
+
fix: 'Update schema_version to "3.0" (RFC §5.3)'
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
|
|
437
449
|
if (Object.keys(ops).length === 0) {
|
|
438
450
|
this.warnings.push({
|
|
439
451
|
type: 'NO_OPERATIONS',
|
|
@@ -444,17 +456,17 @@ class ServiceStructureValidator {
|
|
|
444
456
|
return;
|
|
445
457
|
}
|
|
446
458
|
|
|
447
|
-
// Validate each operation
|
|
448
459
|
for (const [operationName, operationSpec] of Object.entries(ops)) {
|
|
449
460
|
this.validateOperation(operationName, operationSpec);
|
|
450
461
|
}
|
|
451
462
|
}
|
|
452
463
|
|
|
453
464
|
/**
|
|
454
|
-
* Validate single operation structure
|
|
465
|
+
* Validate single operation structure (v3).
|
|
466
|
+
* Required: handler, bundle_scope. Forbidden (v2): endpoint, method, path.
|
|
455
467
|
*/
|
|
456
468
|
validateOperation(name, spec) {
|
|
457
|
-
const requiredFields = ['
|
|
469
|
+
const requiredFields = ['handler', 'bundle_scope'];
|
|
458
470
|
|
|
459
471
|
for (const field of requiredFields) {
|
|
460
472
|
if (!spec[field]) {
|
|
@@ -462,39 +474,53 @@ class ServiceStructureValidator {
|
|
|
462
474
|
type: 'MISSING_OPERATION_FIELD',
|
|
463
475
|
path: 'config/service/operations.json',
|
|
464
476
|
operation: name,
|
|
465
|
-
field
|
|
477
|
+
field,
|
|
466
478
|
message: `Operation "${name}" missing required field: ${field}`,
|
|
467
|
-
fix: `Add "${field}" to operation "${name}"`
|
|
479
|
+
fix: `Add "${field}" to operation "${name}" (v3 schema — RFC §5.3)`
|
|
468
480
|
});
|
|
469
481
|
}
|
|
470
482
|
}
|
|
471
483
|
|
|
472
|
-
|
|
473
|
-
if (spec.endpoint && !spec.endpoint.startsWith('/')) {
|
|
484
|
+
if (spec.handler && !/^handlers\/[a-zA-Z0-9_\/-]+#[a-zA-Z_][a-zA-Z0-9_]*$/.test(spec.handler)) {
|
|
474
485
|
this.errors.push({
|
|
475
|
-
type: '
|
|
486
|
+
type: 'INVALID_HANDLER_REF',
|
|
476
487
|
path: 'config/service/operations.json',
|
|
477
488
|
operation: name,
|
|
478
|
-
field: '
|
|
479
|
-
value: spec.
|
|
480
|
-
message: `
|
|
481
|
-
fix: `Change
|
|
489
|
+
field: 'handler',
|
|
490
|
+
value: spec.handler,
|
|
491
|
+
message: `Handler must be in form 'handlers/<path>#<exportName>': ${spec.handler}`,
|
|
492
|
+
fix: `Change handler to form 'handlers/v3/<file>#<exportName>'`
|
|
482
493
|
});
|
|
483
494
|
}
|
|
484
495
|
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
if (spec.method && !validMethods.includes(spec.method)) {
|
|
496
|
+
const validScopes = ['platform', 'tenant', 'workspace'];
|
|
497
|
+
if (spec.bundle_scope && !validScopes.includes(spec.bundle_scope)) {
|
|
488
498
|
this.errors.push({
|
|
489
|
-
type: '
|
|
499
|
+
type: 'INVALID_BUNDLE_SCOPE',
|
|
490
500
|
path: 'config/service/operations.json',
|
|
491
501
|
operation: name,
|
|
492
|
-
field: '
|
|
493
|
-
value: spec.
|
|
494
|
-
message: `Invalid
|
|
495
|
-
fix: `Use one of: ${
|
|
502
|
+
field: 'bundle_scope',
|
|
503
|
+
value: spec.bundle_scope,
|
|
504
|
+
message: `Invalid bundle_scope: ${spec.bundle_scope}`,
|
|
505
|
+
fix: `Use one of: ${validScopes.join(', ')}`
|
|
496
506
|
});
|
|
497
507
|
}
|
|
508
|
+
|
|
509
|
+
// Reject v2 HTTP-routing fields (clean break per ARCHITECTURE_PRINCIPLES.md §11).
|
|
510
|
+
const forbiddenV2Fields = ['endpoint', 'method', 'path'];
|
|
511
|
+
for (const forbidden of forbiddenV2Fields) {
|
|
512
|
+
if (forbidden in spec) {
|
|
513
|
+
this.errors.push({
|
|
514
|
+
type: 'V2_FIELD_PRESENT',
|
|
515
|
+
path: 'config/service/operations.json',
|
|
516
|
+
operation: name,
|
|
517
|
+
field: forbidden,
|
|
518
|
+
value: spec[forbidden],
|
|
519
|
+
message: `Operation "${name}" has retired v2 field "${forbidden}" — not allowed in v3 schema`,
|
|
520
|
+
fix: `Remove "${forbidden}" — v3 dispatches via handler registry (RFC §5.3, §5.9)`
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
}
|
|
498
524
|
}
|
|
499
525
|
|
|
500
526
|
/**
|
|
@@ -1,250 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Example: How service-wrapper uses connector-testing
|
|
5
|
-
* Shows the integration between service-wrapper and testing framework
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
const { ServiceReadinessValidator, TestOrchestrator } = require('../src');
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Enhanced ServiceWrapper with integrated testing
|
|
12
|
-
*/
|
|
13
|
-
class ServiceWrapperWithTesting {
|
|
14
|
-
constructor(options) {
|
|
15
|
-
this.service = options.service;
|
|
16
|
-
this.serviceName = options.serviceName;
|
|
17
|
-
this.openApiSpec = options.openApiSpec;
|
|
18
|
-
this.config = options.config;
|
|
19
|
-
|
|
20
|
-
// Testing configuration
|
|
21
|
-
this.testingEnabled = options.testingEnabled !== false;
|
|
22
|
-
this.testLevel = options.testLevel || 'component'; // unit, component, or integration
|
|
23
|
-
this.validator = new ServiceReadinessValidator();
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Start service with validation
|
|
28
|
-
*/
|
|
29
|
-
async start() {
|
|
30
|
-
console.log(`Starting ${this.serviceName}...`);
|
|
31
|
-
|
|
32
|
-
// Step 1: Pre-start validation
|
|
33
|
-
if (this.testingEnabled) {
|
|
34
|
-
const validation = await this.validateBeforeStart();
|
|
35
|
-
|
|
36
|
-
if (!validation.ready) {
|
|
37
|
-
throw new Error(`Service validation failed: ${validation.recommendation}`);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
console.log(`Validation passed with score: ${validation.score}/100`);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// Step 2: Start the actual service
|
|
44
|
-
await this.startService();
|
|
45
|
-
|
|
46
|
-
// Step 3: Post-start verification
|
|
47
|
-
if (this.testingEnabled) {
|
|
48
|
-
await this.verifyAfterStart();
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
console.log(`${this.serviceName} started successfully`);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Validate service before starting
|
|
56
|
-
*/
|
|
57
|
-
async validateBeforeStart() {
|
|
58
|
-
console.log('Running pre-start validation...');
|
|
59
|
-
|
|
60
|
-
// Use TestOrchestrator for comprehensive testing
|
|
61
|
-
const orchestrator = new TestOrchestrator({
|
|
62
|
-
service: this.service,
|
|
63
|
-
serviceName: this.serviceName,
|
|
64
|
-
openApiSpec: this.openApiSpec
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
// Run tests based on configured level
|
|
68
|
-
let testResults;
|
|
69
|
-
switch (this.testLevel) {
|
|
70
|
-
case 'unit':
|
|
71
|
-
testResults = await orchestrator.runUnitTests();
|
|
72
|
-
break;
|
|
73
|
-
case 'component':
|
|
74
|
-
testResults = await orchestrator.runComponentTests();
|
|
75
|
-
break;
|
|
76
|
-
case 'integration':
|
|
77
|
-
testResults = await orchestrator.runIntegrationTests();
|
|
78
|
-
break;
|
|
79
|
-
default:
|
|
80
|
-
testResults = await orchestrator.runComponentTests();
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// Convert test results to readiness format
|
|
84
|
-
return {
|
|
85
|
-
ready: testResults.passed,
|
|
86
|
-
score: testResults.coverage || 0,
|
|
87
|
-
recommendation: testResults.passed
|
|
88
|
-
? 'Service passed validation'
|
|
89
|
-
: 'Service failed validation tests',
|
|
90
|
-
details: testResults
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Start the actual service
|
|
96
|
-
*/
|
|
97
|
-
async startService() {
|
|
98
|
-
// This would contain the actual service startup logic
|
|
99
|
-
// - Start Express server
|
|
100
|
-
// - Connect to MQ
|
|
101
|
-
// - Register with registry
|
|
102
|
-
// - etc.
|
|
103
|
-
|
|
104
|
-
return new Promise((resolve) => {
|
|
105
|
-
const PORT = this.config.port || 3000;
|
|
106
|
-
this.server = this.service.listen(PORT, () => {
|
|
107
|
-
console.log(`Service listening on port ${PORT}`);
|
|
108
|
-
resolve();
|
|
109
|
-
});
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Verify service after start
|
|
115
|
-
*/
|
|
116
|
-
async verifyAfterStart() {
|
|
117
|
-
console.log('Running post-start verification...');
|
|
118
|
-
|
|
119
|
-
const serviceUrl = `http://localhost:${this.config.port || 3000}`;
|
|
120
|
-
|
|
121
|
-
// Quick readiness check
|
|
122
|
-
const readiness = await this.validator.validateReadiness({
|
|
123
|
-
name: this.serviceName,
|
|
124
|
-
url: serviceUrl,
|
|
125
|
-
openApiSpec: this.openApiSpec,
|
|
126
|
-
healthEndpoint: '/health'
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
if (!readiness.ready) {
|
|
130
|
-
console.warn('Post-start verification found issues:', readiness.errors);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
return readiness;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Stop service
|
|
138
|
-
*/
|
|
139
|
-
async stop() {
|
|
140
|
-
if (this.server) {
|
|
141
|
-
await new Promise((resolve) => {
|
|
142
|
-
this.server.close(resolve);
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// === Example Usage ===
|
|
149
|
-
|
|
150
|
-
async function demonstrateUsage() {
|
|
151
|
-
const express = require('express');
|
|
152
|
-
|
|
153
|
-
// Create a simple service
|
|
154
|
-
const app = express();
|
|
155
|
-
app.use(express.json());
|
|
156
|
-
|
|
157
|
-
app.get('/health', (req, res) => {
|
|
158
|
-
res.json({ status: 'healthy' });
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
app.post('/api/process', (req, res) => {
|
|
162
|
-
res.json({ processed: true, data: req.body });
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
// OpenAPI spec
|
|
166
|
-
const openApiSpec = {
|
|
167
|
-
openapi: '3.0.0',
|
|
168
|
-
info: { title: 'Demo Service', version: '1.0.0' },
|
|
169
|
-
paths: {
|
|
170
|
-
'/health': {
|
|
171
|
-
get: {
|
|
172
|
-
operationId: 'healthCheck',
|
|
173
|
-
responses: {
|
|
174
|
-
'200': {
|
|
175
|
-
content: {
|
|
176
|
-
'application/json': {
|
|
177
|
-
schema: {
|
|
178
|
-
type: 'object',
|
|
179
|
-
properties: {
|
|
180
|
-
status: { type: 'string' }
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
},
|
|
189
|
-
'/api/process': {
|
|
190
|
-
post: {
|
|
191
|
-
operationId: 'processData',
|
|
192
|
-
requestBody: {
|
|
193
|
-
content: {
|
|
194
|
-
'application/json': {
|
|
195
|
-
schema: { type: 'object' }
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
},
|
|
199
|
-
responses: {
|
|
200
|
-
'200': {
|
|
201
|
-
content: {
|
|
202
|
-
'application/json': {
|
|
203
|
-
schema: { type: 'object' }
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
};
|
|
212
|
-
|
|
213
|
-
// Create wrapper with testing
|
|
214
|
-
const wrapper = new ServiceWrapperWithTesting({
|
|
215
|
-
service: app,
|
|
216
|
-
serviceName: 'demo-service',
|
|
217
|
-
openApiSpec,
|
|
218
|
-
config: {
|
|
219
|
-
port: 3333
|
|
220
|
-
},
|
|
221
|
-
testingEnabled: true,
|
|
222
|
-
testLevel: 'unit' // Start with unit tests for speed
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
try {
|
|
226
|
-
// Start service (will run validation first)
|
|
227
|
-
await wrapper.start();
|
|
228
|
-
|
|
229
|
-
console.log('\nService is running with validation!');
|
|
230
|
-
console.log('Try: curl http://localhost:3333/health');
|
|
231
|
-
|
|
232
|
-
// Keep running for demo
|
|
233
|
-
await new Promise(resolve => setTimeout(resolve, 5000));
|
|
234
|
-
|
|
235
|
-
// Stop service
|
|
236
|
-
await wrapper.stop();
|
|
237
|
-
console.log('\nService stopped');
|
|
238
|
-
|
|
239
|
-
} catch (error) {
|
|
240
|
-
console.error('Failed to start service:', error.message);
|
|
241
|
-
process.exit(1);
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
// Run demonstration
|
|
246
|
-
if (require.main === module) {
|
|
247
|
-
demonstrateUsage().catch(console.error);
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
module.exports = { ServiceWrapperWithTesting };
|
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Example: Three-tier testing strategy
|
|
5
|
-
* Shows how to use connector-testing for comprehensive service validation
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
const { TestOrchestrator } = require('../src');
|
|
9
|
-
const express = require('express');
|
|
10
|
-
|
|
11
|
-
// Example service
|
|
12
|
-
const app = express();
|
|
13
|
-
app.use(express.json());
|
|
14
|
-
|
|
15
|
-
app.get('/health', (req, res) => {
|
|
16
|
-
res.json({ status: 'healthy', service: 'example-service' });
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
app.post('/process', (req, res) => {
|
|
20
|
-
res.json({
|
|
21
|
-
processed: true,
|
|
22
|
-
input: req.body,
|
|
23
|
-
timestamp: Date.now()
|
|
24
|
-
});
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
app.get('/status', (req, res) => {
|
|
28
|
-
res.json({
|
|
29
|
-
running: true,
|
|
30
|
-
uptime: process.uptime()
|
|
31
|
-
});
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
// OpenAPI specification
|
|
35
|
-
const openApiSpec = {
|
|
36
|
-
openapi: '3.0.0',
|
|
37
|
-
info: {
|
|
38
|
-
title: 'Example Service',
|
|
39
|
-
version: '1.0.0'
|
|
40
|
-
},
|
|
41
|
-
paths: {
|
|
42
|
-
'/health': {
|
|
43
|
-
get: {
|
|
44
|
-
operationId: 'healthCheck',
|
|
45
|
-
responses: {
|
|
46
|
-
'200': {
|
|
47
|
-
content: {
|
|
48
|
-
'application/json': {
|
|
49
|
-
schema: {
|
|
50
|
-
type: 'object',
|
|
51
|
-
properties: {
|
|
52
|
-
status: { type: 'string' },
|
|
53
|
-
service: { type: 'string' }
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
},
|
|
62
|
-
'/process': {
|
|
63
|
-
post: {
|
|
64
|
-
operationId: 'processData',
|
|
65
|
-
requestBody: {
|
|
66
|
-
content: {
|
|
67
|
-
'application/json': {
|
|
68
|
-
schema: { type: 'object' }
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
},
|
|
72
|
-
responses: {
|
|
73
|
-
'200': {
|
|
74
|
-
content: {
|
|
75
|
-
'application/json': {
|
|
76
|
-
schema: {
|
|
77
|
-
type: 'object',
|
|
78
|
-
properties: {
|
|
79
|
-
processed: { type: 'boolean' },
|
|
80
|
-
input: { type: 'object' },
|
|
81
|
-
timestamp: { type: 'number' }
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
},
|
|
90
|
-
'/status': {
|
|
91
|
-
get: {
|
|
92
|
-
operationId: 'getStatus',
|
|
93
|
-
responses: {
|
|
94
|
-
'200': {
|
|
95
|
-
content: {
|
|
96
|
-
'application/json': {
|
|
97
|
-
schema: {
|
|
98
|
-
type: 'object',
|
|
99
|
-
properties: {
|
|
100
|
-
running: { type: 'boolean' },
|
|
101
|
-
uptime: { type: 'number' }
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
async function runExample() {
|
|
114
|
-
console.log('=== Three-Tier Testing Example ===\n');
|
|
115
|
-
|
|
116
|
-
// Create test orchestrator
|
|
117
|
-
const orchestrator = new TestOrchestrator({
|
|
118
|
-
service: app,
|
|
119
|
-
serviceName: 'example-service',
|
|
120
|
-
openApiSpec,
|
|
121
|
-
logger: console
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
// Run all three levels of testing
|
|
125
|
-
console.log('Starting comprehensive test suite...\n');
|
|
126
|
-
const results = await orchestrator.runAllTests();
|
|
127
|
-
|
|
128
|
-
// Generate and display report
|
|
129
|
-
const report = orchestrator.generateReport(results);
|
|
130
|
-
console.log('\n' + report);
|
|
131
|
-
|
|
132
|
-
// Return exit code based on results
|
|
133
|
-
process.exit(results.passed ? 0 : 1);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// Run if executed directly
|
|
137
|
-
if (require.main === module) {
|
|
138
|
-
runExample().catch(error => {
|
|
139
|
-
console.error('Test failed:', error);
|
|
140
|
-
process.exit(1);
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
module.exports = { app, openApiSpec, runExample };
|