@onlineapps/conn-orch-validator 2.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/README.md +78 -0
- package/TESTING_STRATEGY.md +92 -0
- package/docs/DESIGN.md +134 -0
- package/examples/service-wrapper-usage.js +250 -0
- package/examples/three-tier-testing.js +144 -0
- package/jest.config.js +23 -0
- package/onlineapps-conn-e2e-testing-1.0.0.tgz +0 -0
- package/package.json +43 -0
- package/src/CookbookTestRunner.js +434 -0
- package/src/CookbookTestUtils.js +237 -0
- package/src/ServiceReadinessValidator.js +430 -0
- package/src/ServiceTestHarness.js +256 -0
- package/src/ServiceValidator.js +387 -0
- package/src/TestOrchestrator.js +727 -0
- package/src/ValidationOrchestrator.js +506 -0
- package/src/WorkflowTestRunner.js +396 -0
- package/src/helpers/README.md +235 -0
- package/src/helpers/createPreValidationTests.js +321 -0
- package/src/helpers/createServiceReadinessTests.js +245 -0
- package/src/index.js +62 -0
- package/src/mocks/MockMQClient.js +176 -0
- package/src/mocks/MockRegistry.js +164 -0
- package/src/mocks/MockStorage.js +186 -0
- package/src/validators/ServiceStructureValidator.js +487 -0
- package/src/validators/ValidationProofGenerator.js +79 -0
- package/test-mq-flow.js +72 -0
- package/test-orchestrator.js +95 -0
- package/tests/component/testing-framework-integration.test.js +313 -0
- package/tests/integration/ServiceReadiness.test.js +265 -0
- package/tests/monitoring-e2e.test.js +315 -0
- package/tests/run-example.js +257 -0
- package/tests/unit/CookbookTestRunner.test.js +353 -0
- package/tests/unit/MockMQClient.test.js +190 -0
- package/tests/unit/MockRegistry.test.js +233 -0
- package/tests/unit/MockStorage.test.js +257 -0
- package/tests/unit/ServiceValidator.test.js +429 -0
- package/tests/unit/WorkflowTestRunner.test.js +546 -0
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create Pre-Validation (Tier 1) Process Tests
|
|
3
|
+
*
|
|
4
|
+
* Generic test suite that validates Tier 1 pre-validation process.
|
|
5
|
+
* Works for ALL business services - no service-specific code needed.
|
|
6
|
+
*
|
|
7
|
+
* @module helpers/createPreValidationTests
|
|
8
|
+
*
|
|
9
|
+
* Purpose:
|
|
10
|
+
* - Validates cookbook tests execution
|
|
11
|
+
* - Validates ValidationProof generation
|
|
12
|
+
* - Validates proof integrity
|
|
13
|
+
* - Tests with MOCKED infrastructure (offline)
|
|
14
|
+
*
|
|
15
|
+
* Part of Two-Tier Validation System (Tier 1: Pre-Validation)
|
|
16
|
+
* See: /docs/architecture/validator.md
|
|
17
|
+
*
|
|
18
|
+
* Usage:
|
|
19
|
+
* const { createPreValidationTests } = require('@onlineapps/conn-orch-validator');
|
|
20
|
+
* createPreValidationTests(__dirname); // Pass tests/integration directory
|
|
21
|
+
*
|
|
22
|
+
* Related:
|
|
23
|
+
* - /docs/architecture/validator.md - Two-tier validation system
|
|
24
|
+
* - /docs/standards/TESTING.md - Testing standards
|
|
25
|
+
* - /tests/TESTING.md - SPOT principles
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
'use strict';
|
|
29
|
+
|
|
30
|
+
const path = require('path');
|
|
31
|
+
const fs = require('fs');
|
|
32
|
+
const { execSync } = require('child_process');
|
|
33
|
+
const { ValidationProofCodec } = require('@onlineapps/service-validator-core');
|
|
34
|
+
const { ServiceStructureValidator } = require('../validators/ServiceStructureValidator');
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Create pre-validation (Tier 1) test suite
|
|
38
|
+
*
|
|
39
|
+
* @param {string} testsDir - Path to tests/integration directory (use __dirname)
|
|
40
|
+
* @param {Object} [options] - Optional configuration
|
|
41
|
+
* @param {number} [options.timeout=60000] - Test timeout in ms
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* // In services/my-service/tests/integration/pre-validation-process.test.js
|
|
45
|
+
* const { createPreValidationTests } = require('@onlineapps/conn-orch-validator');
|
|
46
|
+
*
|
|
47
|
+
* createPreValidationTests(__dirname);
|
|
48
|
+
*/
|
|
49
|
+
function createPreValidationTests(testsDir, options = {}) {
|
|
50
|
+
// Calculate service root (2 levels up from tests/integration/)
|
|
51
|
+
const serviceRoot = path.resolve(testsDir, '../..');
|
|
52
|
+
|
|
53
|
+
const {
|
|
54
|
+
timeout = 60000
|
|
55
|
+
} = options;
|
|
56
|
+
|
|
57
|
+
// Validate service structure FIRST
|
|
58
|
+
console.log('\n🔍 Validating service structure for pre-validation...\n');
|
|
59
|
+
const structureValidator = new ServiceStructureValidator(serviceRoot);
|
|
60
|
+
const structureResult = structureValidator.validate();
|
|
61
|
+
|
|
62
|
+
// Log validation result
|
|
63
|
+
console.log(ServiceStructureValidator.formatResult(structureResult));
|
|
64
|
+
|
|
65
|
+
// FAIL FAST if structure is invalid
|
|
66
|
+
if (!structureResult.valid) {
|
|
67
|
+
throw new Error(
|
|
68
|
+
`Service structure validation failed. Fix errors above before running pre-validation tests.`
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Load configuration
|
|
73
|
+
const configPath = path.join(serviceRoot, 'conn-config/config.json');
|
|
74
|
+
const operationsPath = path.join(serviceRoot, 'conn-config/operations.json');
|
|
75
|
+
const proofPath = path.join(serviceRoot, '.validation-proof.json');
|
|
76
|
+
|
|
77
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
78
|
+
const operations = JSON.parse(fs.readFileSync(operationsPath, 'utf-8'));
|
|
79
|
+
|
|
80
|
+
const serviceName = config.service.name;
|
|
81
|
+
|
|
82
|
+
// Create test suite
|
|
83
|
+
describe(`${serviceName} Pre-Validation (Tier 1) Process @integration`, () => {
|
|
84
|
+
afterEach(() => {
|
|
85
|
+
// Cleanup validation proof after each test
|
|
86
|
+
if (fs.existsSync(proofPath)) {
|
|
87
|
+
fs.unlinkSync(proofPath);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
describe('1. Pre-requisites Check', () => {
|
|
92
|
+
test('service has tests/cookbooks directory', () => {
|
|
93
|
+
const cookbooksDir = path.join(serviceRoot, 'tests/cookbooks');
|
|
94
|
+
expect(fs.existsSync(cookbooksDir)).toBe(true);
|
|
95
|
+
|
|
96
|
+
const cookbookFiles = fs.readdirSync(cookbooksDir).filter(f => f.endsWith('.json'));
|
|
97
|
+
expect(cookbookFiles.length).toBeGreaterThan(0);
|
|
98
|
+
|
|
99
|
+
console.log(`\n✓ Found ${cookbookFiles.length} cookbook test(s)\n`);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test('service has operations.json', () => {
|
|
103
|
+
expect(fs.existsSync(operationsPath)).toBe(true);
|
|
104
|
+
|
|
105
|
+
expect(operations.operations).toBeDefined();
|
|
106
|
+
expect(Object.keys(operations.operations).length).toBeGreaterThan(0);
|
|
107
|
+
|
|
108
|
+
console.log(`\n✓ Found ${Object.keys(operations.operations).length} operation(s)\n`);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test('service has pre-validation script', () => {
|
|
112
|
+
const packageJson = JSON.parse(
|
|
113
|
+
fs.readFileSync(path.join(serviceRoot, 'package.json'), 'utf-8')
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
const hasScript = packageJson.scripts['test:cookbooks'] ||
|
|
117
|
+
fs.existsSync(path.join(serviceRoot, 'scripts/run-pre-validation.js'));
|
|
118
|
+
|
|
119
|
+
expect(hasScript).toBeTruthy();
|
|
120
|
+
|
|
121
|
+
console.log('\n✓ Pre-validation script configured\n');
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
describe('2. Cookbook Test Execution', () => {
|
|
126
|
+
test('runs cookbook tests successfully', () => {
|
|
127
|
+
console.log('\n🧪 Running cookbook tests...\n');
|
|
128
|
+
|
|
129
|
+
try {
|
|
130
|
+
const output = execSync('npm run test:cookbooks', {
|
|
131
|
+
cwd: serviceRoot,
|
|
132
|
+
encoding: 'utf-8',
|
|
133
|
+
stdio: 'pipe'
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
console.log(output);
|
|
137
|
+
|
|
138
|
+
expect(output).toContain('Pre-Validation Started');
|
|
139
|
+
expect(output).toContain('Passed:');
|
|
140
|
+
expect(output).not.toContain('Failed: 1');
|
|
141
|
+
|
|
142
|
+
console.log('\n✅ Cookbook tests PASSED\n');
|
|
143
|
+
} catch (error) {
|
|
144
|
+
console.error('\n❌ Cookbook tests FAILED:\n');
|
|
145
|
+
console.error(error.stdout || error.message);
|
|
146
|
+
throw error;
|
|
147
|
+
}
|
|
148
|
+
}, timeout);
|
|
149
|
+
|
|
150
|
+
test('mocks infrastructure during tests', () => {
|
|
151
|
+
const runPreValidationPath = path.join(serviceRoot, 'scripts/run-pre-validation.js');
|
|
152
|
+
|
|
153
|
+
if (fs.existsSync(runPreValidationPath)) {
|
|
154
|
+
const scriptContent = fs.readFileSync(runPreValidationPath, 'utf-8');
|
|
155
|
+
expect(scriptContent).toContain('mockInfrastructure: true');
|
|
156
|
+
|
|
157
|
+
console.log('\n✓ Infrastructure mocking confirmed\n');
|
|
158
|
+
} else {
|
|
159
|
+
console.log('\n⚠ No run-pre-validation.js script found (using npm script)\n');
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
describe('3. ValidationProof Generation', () => {
|
|
165
|
+
beforeEach(() => {
|
|
166
|
+
// Run pre-validation to generate proof
|
|
167
|
+
console.log('\n🔐 Generating validation proof...\n');
|
|
168
|
+
execSync('npm run test:cookbooks', {
|
|
169
|
+
cwd: serviceRoot,
|
|
170
|
+
stdio: 'pipe'
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
test('creates .validation-proof.json file', () => {
|
|
175
|
+
expect(fs.existsSync(proofPath)).toBe(true);
|
|
176
|
+
console.log('\n✓ Validation proof file created\n');
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
test('proof has correct structure', () => {
|
|
180
|
+
const proof = JSON.parse(fs.readFileSync(proofPath, 'utf-8'));
|
|
181
|
+
|
|
182
|
+
// Required fields
|
|
183
|
+
expect(proof.validationProof).toBeDefined();
|
|
184
|
+
expect(proof.validationData).toBeDefined();
|
|
185
|
+
|
|
186
|
+
// ValidationData structure
|
|
187
|
+
const { validationData } = proof;
|
|
188
|
+
expect(validationData.serviceName).toBe(serviceName);
|
|
189
|
+
expect(validationData.version).toBeDefined();
|
|
190
|
+
expect(validationData.validator).toBe('@onlineapps/conn-orch-validator');
|
|
191
|
+
expect(validationData.validatorVersion).toBeDefined();
|
|
192
|
+
expect(validationData.validatedAt).toBeDefined();
|
|
193
|
+
expect(validationData.durationMs).toBeGreaterThan(0);
|
|
194
|
+
expect(validationData.testsRun).toBeGreaterThan(0);
|
|
195
|
+
expect(validationData.testsPassed).toBe(validationData.testsRun);
|
|
196
|
+
expect(validationData.testsFailed).toBe(0);
|
|
197
|
+
|
|
198
|
+
console.log('\n📋 Validation Proof Details:');
|
|
199
|
+
console.log(` Service: ${validationData.serviceName} v${validationData.version}`);
|
|
200
|
+
console.log(` Validator: ${validationData.validator} v${validationData.validatorVersion}`);
|
|
201
|
+
console.log(` Tests: ${validationData.testsPassed}/${validationData.testsRun} passed`);
|
|
202
|
+
console.log(` Duration: ${validationData.durationMs}ms`);
|
|
203
|
+
console.log(` Validated: ${validationData.validatedAt}`);
|
|
204
|
+
console.log('');
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
test('proof hash is valid SHA256', () => {
|
|
208
|
+
const proof = JSON.parse(fs.readFileSync(proofPath, 'utf-8'));
|
|
209
|
+
|
|
210
|
+
// SHA256 = 64 hex characters
|
|
211
|
+
expect(proof.validationProof).toMatch(/^[a-f0-9]{64}$/);
|
|
212
|
+
|
|
213
|
+
console.log(`\n✓ Valid SHA256 hash: ${proof.validationProof.substring(0, 16)}...\n`);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
test('all tests passed', () => {
|
|
217
|
+
const proof = JSON.parse(fs.readFileSync(proofPath, 'utf-8'));
|
|
218
|
+
|
|
219
|
+
expect(proof.validationData.testsFailed).toBe(0);
|
|
220
|
+
expect(proof.validationData.testsPassed).toBeGreaterThan(0);
|
|
221
|
+
expect(proof.validationData.testsPassed).toBe(proof.validationData.testsRun);
|
|
222
|
+
|
|
223
|
+
console.log(`\n✅ All ${proof.validationData.testsPassed} tests PASSED\n`);
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
describe('4. Proof Integrity Verification', () => {
|
|
228
|
+
beforeEach(() => {
|
|
229
|
+
// Generate proof
|
|
230
|
+
execSync('npm run test:cookbooks', {
|
|
231
|
+
cwd: serviceRoot,
|
|
232
|
+
stdio: 'pipe'
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
test('ValidationProofCodec can decode proof', () => {
|
|
237
|
+
const proof = JSON.parse(fs.readFileSync(proofPath, 'utf-8'));
|
|
238
|
+
|
|
239
|
+
const result = ValidationProofCodec.decode(proof);
|
|
240
|
+
|
|
241
|
+
expect(result.valid).toBe(true);
|
|
242
|
+
expect(result.reason).toBe('VALID');
|
|
243
|
+
expect(result.details.serviceName).toBe(serviceName);
|
|
244
|
+
|
|
245
|
+
console.log('\n✓ Proof successfully decoded and verified\n');
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
test('proof hash matches data', () => {
|
|
249
|
+
const proof = JSON.parse(fs.readFileSync(proofPath, 'utf-8'));
|
|
250
|
+
|
|
251
|
+
// Re-encode to verify hash
|
|
252
|
+
const reEncoded = ValidationProofCodec.encode(proof.validationData);
|
|
253
|
+
|
|
254
|
+
expect(reEncoded.validationProof).toBe(proof.validationProof);
|
|
255
|
+
|
|
256
|
+
console.log('\n✓ Proof hash integrity verified\n');
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
test('proof age is recent (< 7 days)', () => {
|
|
260
|
+
const proof = JSON.parse(fs.readFileSync(proofPath, 'utf-8'));
|
|
261
|
+
|
|
262
|
+
const validatedAt = new Date(proof.validationData.validatedAt);
|
|
263
|
+
const now = new Date();
|
|
264
|
+
const ageMs = now - validatedAt;
|
|
265
|
+
const ageDays = ageMs / (24 * 60 * 60 * 1000);
|
|
266
|
+
|
|
267
|
+
expect(ageDays).toBeLessThan(7);
|
|
268
|
+
|
|
269
|
+
console.log(`\n✓ Proof age: ${Math.round(ageMs / 1000)}s (recent)\n`);
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
test('tampered proof fails verification', () => {
|
|
273
|
+
const proof = JSON.parse(fs.readFileSync(proofPath, 'utf-8'));
|
|
274
|
+
|
|
275
|
+
// Tamper with data
|
|
276
|
+
proof.validationData.testsPassed = 999;
|
|
277
|
+
|
|
278
|
+
const result = ValidationProofCodec.decode(proof);
|
|
279
|
+
|
|
280
|
+
expect(result.valid).toBe(false);
|
|
281
|
+
expect(result.reason).toBe('HASH_MISMATCH');
|
|
282
|
+
|
|
283
|
+
console.log('\n✓ Tamper detection working correctly\n');
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
describe('5. Dependencies Tracking', () => {
|
|
288
|
+
beforeEach(() => {
|
|
289
|
+
execSync('npm run test:cookbooks', {
|
|
290
|
+
cwd: serviceRoot,
|
|
291
|
+
stdio: 'pipe'
|
|
292
|
+
});
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
test('proof includes service dependencies', () => {
|
|
296
|
+
const proof = JSON.parse(fs.readFileSync(proofPath, 'utf-8'));
|
|
297
|
+
|
|
298
|
+
expect(proof.validationData.dependencies).toBeDefined();
|
|
299
|
+
expect(typeof proof.validationData.dependencies).toBe('object');
|
|
300
|
+
|
|
301
|
+
// Should include key connectors
|
|
302
|
+
const deps = proof.validationData.dependencies;
|
|
303
|
+
const hasConnector = deps['@onlineapps/service-wrapper'] ||
|
|
304
|
+
deps['@onlineapps/conn-orch-registry'] ||
|
|
305
|
+
deps['@onlineapps/conn-orch-validator'];
|
|
306
|
+
|
|
307
|
+
expect(hasConnector).toBeDefined();
|
|
308
|
+
|
|
309
|
+
console.log('\n📦 Tracked Dependencies:');
|
|
310
|
+
for (const [name, version] of Object.entries(deps)) {
|
|
311
|
+
if (name.startsWith('@onlineapps/')) {
|
|
312
|
+
console.log(` ${name}: ${version}`);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
console.log('');
|
|
316
|
+
});
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
module.exports = { createPreValidationTests };
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create Service Readiness Integration Tests
|
|
3
|
+
*
|
|
4
|
+
* Generic test suite that validates service HTTP API readiness.
|
|
5
|
+
* Works for ALL business services - no service-specific code needed.
|
|
6
|
+
*
|
|
7
|
+
* @module helpers/createServiceReadinessTests
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* const { createServiceReadinessTests } = require('@onlineapps/conn-orch-validator');
|
|
11
|
+
* createServiceReadinessTests(__dirname); // Pass tests/integration directory
|
|
12
|
+
*
|
|
13
|
+
* Related:
|
|
14
|
+
* - /docs/standards/TESTING.md - Testing standards
|
|
15
|
+
* - /tests/TESTING.md - SPOT principles
|
|
16
|
+
* - /shared/connector/conn-orch-validator/README.md - Package documentation
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
'use strict';
|
|
20
|
+
|
|
21
|
+
const path = require('path');
|
|
22
|
+
const fs = require('fs');
|
|
23
|
+
const ServiceReadinessValidator = require('../ServiceReadinessValidator');
|
|
24
|
+
const MockRegistry = require('../mocks/MockRegistry');
|
|
25
|
+
const { ServiceStructureValidator } = require('../validators/ServiceStructureValidator');
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Create service readiness integration test suite
|
|
29
|
+
*
|
|
30
|
+
* @param {string} testsDir - Path to tests/integration directory (use __dirname)
|
|
31
|
+
* @param {Object} [options] - Optional configuration
|
|
32
|
+
* @param {number} [options.testPort=5556] - Port for test server
|
|
33
|
+
* @param {boolean} [options.includeOptionalChecks=true] - Include cookbook & registry checks
|
|
34
|
+
* @param {number} [options.timeout=15000] - Test timeout in ms
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* // In services/my-service/tests/integration/service-readiness.test.js
|
|
38
|
+
* const { createServiceReadinessTests } = require('@onlineapps/conn-orch-validator');
|
|
39
|
+
*
|
|
40
|
+
* createServiceReadinessTests(__dirname);
|
|
41
|
+
*/
|
|
42
|
+
function createServiceReadinessTests(testsDir, options = {}) {
|
|
43
|
+
// Calculate service root (2 levels up from tests/integration/)
|
|
44
|
+
const serviceRoot = path.resolve(testsDir, '../..');
|
|
45
|
+
|
|
46
|
+
const {
|
|
47
|
+
testPort = 5556,
|
|
48
|
+
includeOptionalChecks = true,
|
|
49
|
+
timeout = 15000
|
|
50
|
+
} = options;
|
|
51
|
+
|
|
52
|
+
// Validate service structure FIRST
|
|
53
|
+
console.log('\n🔍 Validating service structure...\n');
|
|
54
|
+
const structureValidator = new ServiceStructureValidator(serviceRoot);
|
|
55
|
+
const structureResult = structureValidator.validate();
|
|
56
|
+
|
|
57
|
+
// Log validation result
|
|
58
|
+
console.log(ServiceStructureValidator.formatResult(structureResult));
|
|
59
|
+
|
|
60
|
+
// FAIL FAST if structure is invalid
|
|
61
|
+
if (!structureResult.valid) {
|
|
62
|
+
throw new Error(
|
|
63
|
+
`Service structure validation failed. Fix errors above before running tests.`
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Load configuration files (validated above)
|
|
68
|
+
const configPath = path.join(serviceRoot, 'conn-config/config.json');
|
|
69
|
+
const operationsPath = path.join(serviceRoot, 'conn-config/operations.json');
|
|
70
|
+
const appPath = path.join(serviceRoot, 'src/app.js');
|
|
71
|
+
|
|
72
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
73
|
+
const operations = JSON.parse(fs.readFileSync(operationsPath, 'utf-8'));
|
|
74
|
+
const app = require(appPath);
|
|
75
|
+
|
|
76
|
+
// Extract metadata from config
|
|
77
|
+
const serviceName = config.service.name;
|
|
78
|
+
const serviceVersion = config.service.version;
|
|
79
|
+
const healthEndpoint = config.wrapper?.health?.endpoint || '/health';
|
|
80
|
+
|
|
81
|
+
// Create test suite
|
|
82
|
+
describe(`${serviceName} Service Readiness @integration`, () => {
|
|
83
|
+
let server;
|
|
84
|
+
let baseUrl;
|
|
85
|
+
|
|
86
|
+
beforeAll(async () => {
|
|
87
|
+
// Start service for testing
|
|
88
|
+
server = await new Promise((resolve) => {
|
|
89
|
+
const srv = app.listen(testPort, () => {
|
|
90
|
+
baseUrl = `http://localhost:${testPort}`;
|
|
91
|
+
console.log(`\n✓ Test server started: ${baseUrl}\n`);
|
|
92
|
+
resolve(srv);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
afterAll(async () => {
|
|
98
|
+
// Stop service
|
|
99
|
+
if (server) {
|
|
100
|
+
await new Promise((resolve) => {
|
|
101
|
+
server.close(() => {
|
|
102
|
+
console.log('\n✓ Test server stopped\n');
|
|
103
|
+
resolve();
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test('service passes readiness validation', async () => {
|
|
110
|
+
// Validator expects flat operations object
|
|
111
|
+
const operationsFlat = operations.operations || operations;
|
|
112
|
+
|
|
113
|
+
// Prepare optional checks
|
|
114
|
+
let mockRegistry = null;
|
|
115
|
+
let testCookbook = null;
|
|
116
|
+
|
|
117
|
+
if (includeOptionalChecks) {
|
|
118
|
+
// Create MockRegistry for registry compatibility check (+5 points)
|
|
119
|
+
mockRegistry = new MockRegistry();
|
|
120
|
+
|
|
121
|
+
// Auto-generate test cookbook from operations (+15 points)
|
|
122
|
+
testCookbook = {
|
|
123
|
+
version: '1.0.0',
|
|
124
|
+
steps: Object.entries(operationsFlat).map(([name, op]) => ({
|
|
125
|
+
id: `test-${name}`,
|
|
126
|
+
type: 'task',
|
|
127
|
+
service: serviceName,
|
|
128
|
+
operation: name,
|
|
129
|
+
input: generateMockInput(op.input),
|
|
130
|
+
output: {}
|
|
131
|
+
}))
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Run readiness validation
|
|
136
|
+
const validator = new ServiceReadinessValidator();
|
|
137
|
+
const result = await validator.validateReadiness({
|
|
138
|
+
name: serviceName,
|
|
139
|
+
version: serviceVersion,
|
|
140
|
+
url: baseUrl,
|
|
141
|
+
operations: operationsFlat,
|
|
142
|
+
healthEndpoint: healthEndpoint,
|
|
143
|
+
testCookbook: testCookbook,
|
|
144
|
+
registry: mockRegistry
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
// Log detailed results
|
|
148
|
+
console.log('\n📊 Readiness Validation Results:');
|
|
149
|
+
console.log(` Score: ${result.score}/100`);
|
|
150
|
+
console.log(` Ready: ${result.ready ? '✅' : '❌'}`);
|
|
151
|
+
console.log('\n Checks:');
|
|
152
|
+
for (const [checkName, checkResult] of Object.entries(result.checks || {})) {
|
|
153
|
+
const icon = checkResult.passed ? '✓' : '✗';
|
|
154
|
+
const points = checkResult.score || 0;
|
|
155
|
+
console.log(` ${icon} ${checkName}: ${points} points`);
|
|
156
|
+
}
|
|
157
|
+
if (result.errors && result.errors.length > 0) {
|
|
158
|
+
console.log('\n Errors:');
|
|
159
|
+
for (const error of result.errors) {
|
|
160
|
+
console.log(` ✗ ${error}`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
console.log('');
|
|
164
|
+
|
|
165
|
+
// Assertions
|
|
166
|
+
expect(result).toHaveProperty('ready');
|
|
167
|
+
expect(result).toHaveProperty('score');
|
|
168
|
+
expect(result).toHaveProperty('checks');
|
|
169
|
+
expect(result).toHaveProperty('errors');
|
|
170
|
+
|
|
171
|
+
expect(result.ready).toBe(true);
|
|
172
|
+
|
|
173
|
+
if (includeOptionalChecks) {
|
|
174
|
+
expect(result.score).toBe(100);
|
|
175
|
+
expect(result.checks.health?.passed).toBe(true);
|
|
176
|
+
expect(result.checks.operations?.passed).toBe(true);
|
|
177
|
+
expect(result.checks.endpoints?.passed).toBe(true);
|
|
178
|
+
expect(result.checks.cookbook?.passed).toBe(true);
|
|
179
|
+
expect(result.checks.registry?.passed).toBe(true);
|
|
180
|
+
} else {
|
|
181
|
+
expect(result.score).toBeGreaterThanOrEqual(80);
|
|
182
|
+
expect(result.checks.health?.passed).toBe(true);
|
|
183
|
+
expect(result.checks.operations?.passed).toBe(true);
|
|
184
|
+
expect(result.checks.endpoints?.passed).toBe(true);
|
|
185
|
+
}
|
|
186
|
+
}, timeout);
|
|
187
|
+
|
|
188
|
+
test('health endpoint responds correctly', async () => {
|
|
189
|
+
const response = await fetch(`${baseUrl}${healthEndpoint}`);
|
|
190
|
+
const data = await response.json();
|
|
191
|
+
|
|
192
|
+
expect(response.status).toBe(200);
|
|
193
|
+
expect(data).toHaveProperty('status');
|
|
194
|
+
expect(data.status).toBe('healthy');
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
test('service has valid operations specification', () => {
|
|
198
|
+
expect(operations).toBeDefined();
|
|
199
|
+
expect(operations.operations || operations).toBeDefined();
|
|
200
|
+
|
|
201
|
+
const operationsFlat = operations.operations || operations;
|
|
202
|
+
expect(typeof operationsFlat).toBe('object');
|
|
203
|
+
expect(Object.keys(operationsFlat).length).toBeGreaterThan(0);
|
|
204
|
+
|
|
205
|
+
// Verify operations structure
|
|
206
|
+
for (const [operationName, operationSpec] of Object.entries(operationsFlat)) {
|
|
207
|
+
expect(operationSpec).toHaveProperty('endpoint');
|
|
208
|
+
expect(operationSpec).toHaveProperty('method');
|
|
209
|
+
expect(operationSpec.endpoint).toMatch(/^\//);
|
|
210
|
+
expect(['GET', 'POST', 'PUT', 'DELETE', 'PATCH']).toContain(operationSpec.method);
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Generate mock input data from operation input schema
|
|
218
|
+
*
|
|
219
|
+
* @param {Object} inputSchema - Input schema from operations.json
|
|
220
|
+
* @returns {Object} Mock input data
|
|
221
|
+
* @private
|
|
222
|
+
*/
|
|
223
|
+
function generateMockInput(inputSchema) {
|
|
224
|
+
const mockData = {};
|
|
225
|
+
for (const [fieldName, fieldSpec] of Object.entries(inputSchema || {})) {
|
|
226
|
+
if (fieldSpec.required) {
|
|
227
|
+
switch (fieldSpec.type) {
|
|
228
|
+
case 'string':
|
|
229
|
+
mockData[fieldName] = 'Test';
|
|
230
|
+
break;
|
|
231
|
+
case 'number':
|
|
232
|
+
mockData[fieldName] = 0;
|
|
233
|
+
break;
|
|
234
|
+
case 'boolean':
|
|
235
|
+
mockData[fieldName] = false;
|
|
236
|
+
break;
|
|
237
|
+
default:
|
|
238
|
+
mockData[fieldName] = null;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
return mockData;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
module.exports = { createServiceReadinessTests };
|
package/src/index.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Mock infrastructure components (load first - no dependencies)
|
|
4
|
+
const MockMQClient = require('./mocks/MockMQClient');
|
|
5
|
+
const MockRegistry = require('./mocks/MockRegistry');
|
|
6
|
+
const MockStorage = require('./mocks/MockStorage');
|
|
7
|
+
|
|
8
|
+
// Base test utilities (no circular dependencies)
|
|
9
|
+
const ServiceValidator = require('./ServiceValidator');
|
|
10
|
+
const CookbookTestUtils = require('./CookbookTestUtils');
|
|
11
|
+
const { ServiceStructureValidator } = require('./validators/ServiceStructureValidator');
|
|
12
|
+
const ValidationProofGenerator = require('./validators/ValidationProofGenerator');
|
|
13
|
+
|
|
14
|
+
// Test runners (depend on mocks)
|
|
15
|
+
let CookbookTestRunner;
|
|
16
|
+
try {
|
|
17
|
+
CookbookTestRunner = require('./CookbookTestRunner');
|
|
18
|
+
} catch (error) {
|
|
19
|
+
console.error('[conn-orch-validator] WARNING: Failed to load CookbookTestRunner:', error.message);
|
|
20
|
+
}
|
|
21
|
+
const WorkflowTestRunner = require('./WorkflowTestRunner');
|
|
22
|
+
|
|
23
|
+
// Orchestrators (depend on validators and runners)
|
|
24
|
+
const ServiceReadinessValidator = require('./ServiceReadinessValidator');
|
|
25
|
+
const TestOrchestrator = require('./TestOrchestrator');
|
|
26
|
+
const ServiceTestHarness = require('./ServiceTestHarness');
|
|
27
|
+
const ValidationOrchestrator = require('./ValidationOrchestrator');
|
|
28
|
+
|
|
29
|
+
// Test helpers (generic test suite creators)
|
|
30
|
+
const { createServiceReadinessTests } = require('./helpers/createServiceReadinessTests');
|
|
31
|
+
const { createPreValidationTests } = require('./helpers/createPreValidationTests');
|
|
32
|
+
|
|
33
|
+
// Export all components with getters to avoid circular dependency issues
|
|
34
|
+
module.exports = {
|
|
35
|
+
// Mocks
|
|
36
|
+
get MockMQClient() { return MockMQClient; },
|
|
37
|
+
get MockRegistry() { return MockRegistry; },
|
|
38
|
+
get MockStorage() { return MockStorage; },
|
|
39
|
+
|
|
40
|
+
// Test utilities
|
|
41
|
+
get ServiceTestHarness() { return ServiceTestHarness; },
|
|
42
|
+
get ServiceValidator() { return ServiceValidator; },
|
|
43
|
+
get WorkflowTestRunner() { return WorkflowTestRunner; },
|
|
44
|
+
get CookbookTestUtils() { return CookbookTestUtils; },
|
|
45
|
+
get ServiceReadinessValidator() { return ServiceReadinessValidator; },
|
|
46
|
+
get TestOrchestrator() { return TestOrchestrator; },
|
|
47
|
+
get CookbookTestRunner() { return CookbookTestRunner; },
|
|
48
|
+
get ValidationProofGenerator() { return ValidationProofGenerator; },
|
|
49
|
+
get ServiceStructureValidator() { return ServiceStructureValidator; },
|
|
50
|
+
get ValidationOrchestrator() { return ValidationOrchestrator; },
|
|
51
|
+
|
|
52
|
+
// Test helpers (NEW - generic test suite creators)
|
|
53
|
+
get createServiceReadinessTests() { return createServiceReadinessTests; },
|
|
54
|
+
get createPreValidationTests() { return createPreValidationTests; },
|
|
55
|
+
|
|
56
|
+
// Convenience factory functions
|
|
57
|
+
createTestHarness: (options) => new ServiceTestHarness(options),
|
|
58
|
+
createValidator: (options) => new ServiceValidator(options),
|
|
59
|
+
createMockMQ: () => new MockMQClient(),
|
|
60
|
+
createMockRegistry: () => new MockRegistry(),
|
|
61
|
+
createMockStorage: () => new MockStorage()
|
|
62
|
+
};
|