@onlineapps/conn-orch-validator 2.0.33 → 2.0.34
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 +8 -5
- package/src/config.js +1 -1
- package/src/index.js +13 -19
- 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
|
@@ -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 };
|
|
@@ -1,256 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const MockMQClient = require('./mocks/MockMQClient');
|
|
4
|
-
const MockRegistry = require('./mocks/MockRegistry');
|
|
5
|
-
const MockStorage = require('./mocks/MockStorage');
|
|
6
|
-
const axios = require('axios');
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* ServiceTestHarness - Complete test environment for services
|
|
10
|
-
*/
|
|
11
|
-
class ServiceTestHarness {
|
|
12
|
-
constructor(options = {}) {
|
|
13
|
-
this.service = options.service; // Express app
|
|
14
|
-
this.serviceName = options.serviceName || 'test-service';
|
|
15
|
-
this.openApiSpec = options.openApiSpec || {};
|
|
16
|
-
this.mockInfrastructure = options.mockInfrastructure !== false;
|
|
17
|
-
|
|
18
|
-
// Initialize mocks if requested
|
|
19
|
-
if (this.mockInfrastructure) {
|
|
20
|
-
this.mqClient = new MockMQClient();
|
|
21
|
-
this.registry = new MockRegistry();
|
|
22
|
-
this.storage = new MockStorage();
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// Test state
|
|
26
|
-
this.server = null;
|
|
27
|
-
this.port = options.port || 0; // 0 = random port
|
|
28
|
-
this.baseUrl = null;
|
|
29
|
-
this.isRunning = false;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Start test harness
|
|
34
|
-
*/
|
|
35
|
-
async start() {
|
|
36
|
-
if (this.isRunning) {
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Start Express server if provided
|
|
41
|
-
if (this.service) {
|
|
42
|
-
await this.startServer();
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// Connect mocks
|
|
46
|
-
if (this.mockInfrastructure) {
|
|
47
|
-
await this.mqClient.connect();
|
|
48
|
-
await this.registry.register({
|
|
49
|
-
name: this.serviceName,
|
|
50
|
-
url: this.baseUrl,
|
|
51
|
-
openapi: this.openApiSpec
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
this.isRunning = true;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Stop test harness
|
|
60
|
-
*/
|
|
61
|
-
async stop() {
|
|
62
|
-
if (!this.isRunning) {
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Stop server
|
|
67
|
-
if (this.server) {
|
|
68
|
-
await new Promise(resolve => {
|
|
69
|
-
this.server.close(resolve);
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Disconnect mocks
|
|
74
|
-
if (this.mockInfrastructure) {
|
|
75
|
-
await this.mqClient.disconnect();
|
|
76
|
-
await this.registry.unregister(this.serviceName);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
this.isRunning = false;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Start Express server
|
|
84
|
-
* @private
|
|
85
|
-
*/
|
|
86
|
-
async startServer() {
|
|
87
|
-
return new Promise((resolve) => {
|
|
88
|
-
this.server = this.service.listen(this.port, () => {
|
|
89
|
-
const actualPort = this.server.address().port;
|
|
90
|
-
this.baseUrl = `http://127.0.0.1:${actualPort}`;
|
|
91
|
-
resolve();
|
|
92
|
-
});
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Call API endpoint
|
|
98
|
-
*/
|
|
99
|
-
async callApi(method, path, data = null, headers = {}) {
|
|
100
|
-
if (!this.baseUrl) {
|
|
101
|
-
throw new Error('Service not started');
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const config = {
|
|
105
|
-
method,
|
|
106
|
-
url: `${this.baseUrl}${path}`,
|
|
107
|
-
headers,
|
|
108
|
-
timeout: 5000
|
|
109
|
-
};
|
|
110
|
-
|
|
111
|
-
if (data) {
|
|
112
|
-
if (method === 'GET') {
|
|
113
|
-
config.params = data;
|
|
114
|
-
} else {
|
|
115
|
-
config.data = data;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const response = await axios(config);
|
|
120
|
-
return response.data;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Simulate workflow execution
|
|
125
|
-
*/
|
|
126
|
-
async simulateWorkflow(workflow) {
|
|
127
|
-
const results = [];
|
|
128
|
-
|
|
129
|
-
for (const step of workflow.steps) {
|
|
130
|
-
if (step.type === 'task' && step.service === this.serviceName) {
|
|
131
|
-
// Execute task step
|
|
132
|
-
const result = await this.executeStep(step);
|
|
133
|
-
results.push(result);
|
|
134
|
-
|
|
135
|
-
// Simulate publishing result to queue
|
|
136
|
-
if (this.mockInfrastructure) {
|
|
137
|
-
await this.mqClient.publish('workflow.completed', {
|
|
138
|
-
step: step.id,
|
|
139
|
-
result
|
|
140
|
-
});
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
return {
|
|
146
|
-
workflow: workflow,
|
|
147
|
-
results,
|
|
148
|
-
completed: true
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Execute a single workflow step
|
|
154
|
-
* @private
|
|
155
|
-
*/
|
|
156
|
-
async executeStep(step) {
|
|
157
|
-
// Map step to API call based on operation
|
|
158
|
-
const operation = step.operation || step.id;
|
|
159
|
-
const endpoint = this.findEndpoint(operation);
|
|
160
|
-
|
|
161
|
-
if (!endpoint) {
|
|
162
|
-
throw new Error(`No endpoint found for operation: ${operation}`);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// Call the API
|
|
166
|
-
const response = await this.callApi(
|
|
167
|
-
endpoint.method,
|
|
168
|
-
endpoint.path,
|
|
169
|
-
step.input
|
|
170
|
-
);
|
|
171
|
-
|
|
172
|
-
return {
|
|
173
|
-
stepId: step.id,
|
|
174
|
-
operation,
|
|
175
|
-
response
|
|
176
|
-
};
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* Find endpoint in OpenAPI spec
|
|
181
|
-
* @private
|
|
182
|
-
*/
|
|
183
|
-
findEndpoint(operationId) {
|
|
184
|
-
if (!this.openApiSpec.paths) {
|
|
185
|
-
return null;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
for (const [path, pathItem] of Object.entries(this.openApiSpec.paths)) {
|
|
189
|
-
for (const [method, operation] of Object.entries(pathItem)) {
|
|
190
|
-
if (operation.operationId === operationId) {
|
|
191
|
-
return { method: method.toUpperCase(), path };
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
return null;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* Get published messages from mock MQ
|
|
201
|
-
*/
|
|
202
|
-
getPublishedMessages(queue = null) {
|
|
203
|
-
if (!this.mockInfrastructure) {
|
|
204
|
-
throw new Error('Mock infrastructure not enabled');
|
|
205
|
-
}
|
|
206
|
-
return this.mqClient.getPublishedMessages(queue);
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
/**
|
|
210
|
-
* Get service registration from mock registry
|
|
211
|
-
*/
|
|
212
|
-
getServiceRegistration() {
|
|
213
|
-
if (!this.mockInfrastructure) {
|
|
214
|
-
throw new Error('Mock infrastructure not enabled');
|
|
215
|
-
}
|
|
216
|
-
return this.registry.getService(this.serviceName);
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* Get storage contents from mock storage
|
|
221
|
-
*/
|
|
222
|
-
async getStorageObject(bucket, key) {
|
|
223
|
-
if (!this.mockInfrastructure) {
|
|
224
|
-
throw new Error('Mock infrastructure not enabled');
|
|
225
|
-
}
|
|
226
|
-
return await this.storage.get(bucket, key);
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
/**
|
|
230
|
-
* Clear all test data
|
|
231
|
-
*/
|
|
232
|
-
clear() {
|
|
233
|
-
if (this.mockInfrastructure) {
|
|
234
|
-
this.mqClient.clear();
|
|
235
|
-
this.registry.clear();
|
|
236
|
-
this.storage.clear();
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
/**
|
|
241
|
-
* Get test statistics
|
|
242
|
-
*/
|
|
243
|
-
getStats() {
|
|
244
|
-
if (!this.mockInfrastructure) {
|
|
245
|
-
return { mocks: 'disabled' };
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
return {
|
|
249
|
-
mq: this.mqClient.getStats(),
|
|
250
|
-
registry: this.registry.getStats(),
|
|
251
|
-
storage: this.storage.getStats()
|
|
252
|
-
};
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
module.exports = ServiceTestHarness;
|