@onlineapps/conn-orch-validator 2.0.31 → 2.0.33
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onlineapps/conn-orch-validator",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.33",
|
|
4
4
|
"description": "Validation orchestrator for OA Drive microservices - coordinates validation across all layers (base, infra, orch, business)",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -14,12 +14,24 @@ const { resolveHeaders } = require('./utils/resolveHeaders');
|
|
|
14
14
|
*/
|
|
15
15
|
class CookbookTestRunner {
|
|
16
16
|
constructor(options = {}) {
|
|
17
|
+
if (!options.serviceName) {
|
|
18
|
+
throw new Error('[CookbookTestRunner] serviceName is required');
|
|
19
|
+
}
|
|
20
|
+
if (!options.serviceUrl) {
|
|
21
|
+
throw new Error('[CookbookTestRunner] serviceUrl is required');
|
|
22
|
+
}
|
|
23
|
+
if (!options.logger || typeof options.logger.warn !== 'function') {
|
|
24
|
+
throw new Error('[CookbookTestRunner] Logger is required — Expected object with warn() method');
|
|
25
|
+
}
|
|
17
26
|
this.serviceName = options.serviceName;
|
|
18
|
-
this.serviceUrl = options.serviceUrl
|
|
27
|
+
this.serviceUrl = options.serviceUrl;
|
|
19
28
|
this.servicePath = options.servicePath;
|
|
20
|
-
this.mockInfrastructure = options.mockInfrastructure !== false;
|
|
21
|
-
this.timeout = options.timeout
|
|
22
|
-
this.
|
|
29
|
+
this.mockInfrastructure = options.mockInfrastructure !== false;
|
|
30
|
+
this.timeout = options.timeout;
|
|
31
|
+
if (!this.timeout || typeof this.timeout !== 'number' || this.timeout <= 0) {
|
|
32
|
+
throw new Error('[CookbookTestRunner] timeout is required — Expected positive number (ms)');
|
|
33
|
+
}
|
|
34
|
+
this.logger = options.logger;
|
|
23
35
|
|
|
24
36
|
// Initialize mocked infrastructure
|
|
25
37
|
if (this.mockInfrastructure) {
|
|
@@ -12,8 +12,11 @@ const { resolveHeaders } = require('./utils/resolveHeaders');
|
|
|
12
12
|
*/
|
|
13
13
|
class ServiceReadinessValidator {
|
|
14
14
|
constructor(options = {}) {
|
|
15
|
+
if (!options.logger || typeof options.logger.warn !== 'function') {
|
|
16
|
+
throw new Error('[ServiceReadinessValidator] Logger is required — Expected object with warn() method');
|
|
17
|
+
}
|
|
15
18
|
this.validator = new ServiceValidator(options);
|
|
16
|
-
this.logger = options.logger
|
|
19
|
+
this.logger = options.logger;
|
|
17
20
|
|
|
18
21
|
// Readiness checks: core (80 points) + optional (20 points) = 100 points max
|
|
19
22
|
// Core checks ALWAYS run, optional checks run if testCookbook/registry provided
|
|
@@ -238,8 +241,9 @@ class ServiceReadinessValidator {
|
|
|
238
241
|
validateStatus: () => true // Accept any status for validation
|
|
239
242
|
});
|
|
240
243
|
|
|
241
|
-
// Check response
|
|
242
|
-
|
|
244
|
+
// Check response: 2xx = success, 4xx = endpoint works but rejected test data (valid)
|
|
245
|
+
// Only 5xx (server error) or connection failure = invalid endpoint
|
|
246
|
+
const isValid = response.status < 500;
|
|
243
247
|
|
|
244
248
|
if (isValid) {
|
|
245
249
|
results.successful++;
|
|
@@ -346,64 +350,44 @@ class ServiceReadinessValidator {
|
|
|
346
350
|
}
|
|
347
351
|
|
|
348
352
|
/**
|
|
349
|
-
* Generate test input based on operation input schema
|
|
350
|
-
*
|
|
353
|
+
* Generate test input based on operation input schema.
|
|
354
|
+
*
|
|
355
|
+
* Resolution order per field: example > default > enum[0]
|
|
356
|
+
* If none found for a required field, throws with actionable message.
|
|
357
|
+
*
|
|
358
|
+
* Contract: every required field in operations.json MUST have 'example' or 'default'.
|
|
359
|
+
* See: docs/standards/validation-probe-contract.md
|
|
351
360
|
*/
|
|
352
361
|
generateTestInput(inputSchema) {
|
|
353
362
|
const testData = {};
|
|
354
363
|
|
|
355
364
|
for (const [fieldName, fieldSpec] of Object.entries(inputSchema)) {
|
|
356
|
-
if (fieldSpec.required)
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
testData[fieldName] = fieldSpec.default || 'Test';
|
|
361
|
-
break;
|
|
362
|
-
case 'number':
|
|
363
|
-
testData[fieldName] = fieldSpec.default || 0;
|
|
364
|
-
break;
|
|
365
|
-
case 'boolean':
|
|
366
|
-
testData[fieldName] = fieldSpec.default || false;
|
|
367
|
-
break;
|
|
368
|
-
case 'array':
|
|
369
|
-
testData[fieldName] = fieldSpec.default || [];
|
|
370
|
-
break;
|
|
371
|
-
case 'object':
|
|
372
|
-
testData[fieldName] = fieldSpec.default || {};
|
|
373
|
-
break;
|
|
374
|
-
// Content Descriptor types - generate valid test content
|
|
375
|
-
case 'content':
|
|
376
|
-
// Generate content based on field name hints
|
|
377
|
-
if (fieldName.toLowerCase().includes('html')) {
|
|
378
|
-
testData[fieldName] = fieldSpec.default || '<html><body><h1>Test Validation</h1><p>Auto-generated test content.</p></body></html>';
|
|
379
|
-
} else if (fieldName.toLowerCase().includes('markdown') || fieldName.toLowerCase().includes('md')) {
|
|
380
|
-
testData[fieldName] = fieldSpec.default || '# Test Validation\n\nAuto-generated test content.';
|
|
381
|
-
} else {
|
|
382
|
-
testData[fieldName] = fieldSpec.default || 'Test content for validation';
|
|
383
|
-
}
|
|
384
|
-
break;
|
|
385
|
-
case 'file':
|
|
386
|
-
// For file types, generate a VALID Content Descriptor object (type=file)
|
|
387
|
-
// This keeps endpoint checks predictable and avoids service-specific file upload flows.
|
|
388
|
-
testData[fieldName] = fieldSpec.default || {
|
|
389
|
-
_descriptor: true,
|
|
390
|
-
type: 'file',
|
|
391
|
-
storage_ref: 'minio://test/validation/placeholder.txt',
|
|
392
|
-
filename: 'placeholder.txt',
|
|
393
|
-
content_type: 'text/plain',
|
|
394
|
-
size: 1,
|
|
395
|
-
fingerprint: 'test-fingerprint'
|
|
396
|
-
};
|
|
397
|
-
break;
|
|
398
|
-
default:
|
|
399
|
-
testData[fieldName] = null;
|
|
400
|
-
}
|
|
401
|
-
}
|
|
365
|
+
if (!fieldSpec.required) continue;
|
|
366
|
+
|
|
367
|
+
const value = this._resolveTestValue(fieldName, fieldSpec);
|
|
368
|
+
testData[fieldName] = value;
|
|
402
369
|
}
|
|
403
370
|
|
|
404
371
|
return testData;
|
|
405
372
|
}
|
|
406
373
|
|
|
374
|
+
/**
|
|
375
|
+
* Resolve a single test value for a required input field.
|
|
376
|
+
* Throws if no value can be determined — forces service authors to provide examples.
|
|
377
|
+
* @private
|
|
378
|
+
*/
|
|
379
|
+
_resolveTestValue(fieldName, fieldSpec) {
|
|
380
|
+
if (fieldSpec.example !== undefined) return fieldSpec.example;
|
|
381
|
+
if (fieldSpec.default !== undefined) return fieldSpec.default;
|
|
382
|
+
if (Array.isArray(fieldSpec.enum) && fieldSpec.enum.length > 0) return fieldSpec.enum[0];
|
|
383
|
+
|
|
384
|
+
throw new Error(
|
|
385
|
+
`[ServiceReadinessValidator] No test value for required field '${fieldName}' ` +
|
|
386
|
+
`(type: ${fieldSpec.type}) — Add 'example' or 'default' to operations.json input schema. ` +
|
|
387
|
+
`See: docs/standards/validation-probe-contract.md`
|
|
388
|
+
);
|
|
389
|
+
}
|
|
390
|
+
|
|
407
391
|
/**
|
|
408
392
|
* Get recommendation based on results
|
|
409
393
|
*/
|
package/src/TestOrchestrator.js
CHANGED
|
@@ -17,8 +17,14 @@ class TestOrchestrator {
|
|
|
17
17
|
this.service = options.service;
|
|
18
18
|
this.serviceName = options.serviceName;
|
|
19
19
|
this.openApiSpec = options.openApiSpec;
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
if (!options.logger || typeof options.logger.warn !== 'function') {
|
|
21
|
+
throw new Error('[TestOrchestrator] Logger is required — Expected object with warn() method');
|
|
22
|
+
}
|
|
23
|
+
this.logger = options.logger;
|
|
24
|
+
if (!options.integrationConfig) {
|
|
25
|
+
throw new Error('[TestOrchestrator] integrationConfig is required');
|
|
26
|
+
}
|
|
27
|
+
this.integrationConfig = options.integrationConfig;
|
|
22
28
|
|
|
23
29
|
// Test configuration
|
|
24
30
|
this.testLevels = {
|
|
@@ -227,7 +233,7 @@ class TestOrchestrator {
|
|
|
227
233
|
const config = this.getIntegrationTestConfig();
|
|
228
234
|
|
|
229
235
|
// Run readiness validator (uses real endpoints)
|
|
230
|
-
const validator = new ServiceReadinessValidator();
|
|
236
|
+
const validator = new ServiceReadinessValidator({ logger: this.logger });
|
|
231
237
|
const readinessTest = await validator.validateReadiness({
|
|
232
238
|
name: this.serviceName,
|
|
233
239
|
url: config.serviceUrl,
|
|
@@ -22,11 +22,13 @@ class ValidationOrchestrator {
|
|
|
22
22
|
this.serviceName = options.serviceName;
|
|
23
23
|
this.serviceVersion = options.serviceVersion;
|
|
24
24
|
this.serviceUrl = options.serviceUrl; // NEW: Service URL for HTTP calls
|
|
25
|
-
|
|
25
|
+
if (!options.logger || typeof options.logger.warn !== 'function') {
|
|
26
|
+
throw new Error('[ValidationOrchestrator] Logger is required — Expected object with warn() method');
|
|
27
|
+
}
|
|
28
|
+
this.logger = options.logger;
|
|
26
29
|
|
|
27
|
-
// Validate required options
|
|
28
30
|
if (!this.serviceUrl) {
|
|
29
|
-
throw new Error('serviceUrl is required
|
|
31
|
+
throw new Error('[ValidationOrchestrator] serviceUrl is required');
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
// Paths — prefer new config/service/ layout, fall back to legacy conn-config/
|
|
@@ -34,18 +36,18 @@ class ValidationOrchestrator {
|
|
|
34
36
|
const legacyConfigDir = path.join(this.serviceRoot, 'conn-config');
|
|
35
37
|
this.configPath = fs.existsSync(newConfigDir) ? newConfigDir : legacyConfigDir;
|
|
36
38
|
|
|
37
|
-
|
|
38
|
-
const legacyRuntimeDir = path.join(this.serviceRoot, 'conn-runtime');
|
|
39
|
-
this.runtimePath = fs.existsSync(newRuntimeDir) ? newRuntimeDir : legacyRuntimeDir;
|
|
39
|
+
this.runtimePath = path.join(this.serviceRoot, 'conn-runtime');
|
|
40
40
|
this.proofPath = path.join(this.runtimePath, 'validation-proof.json');
|
|
41
41
|
|
|
42
42
|
// Validators
|
|
43
43
|
this.structureValidator = new ServiceStructureValidator(this.serviceRoot);
|
|
44
|
-
this.readinessValidator = new ServiceReadinessValidator();
|
|
44
|
+
this.readinessValidator = new ServiceReadinessValidator({ logger: this.logger });
|
|
45
45
|
this.cookbookRunner = new CookbookTestRunner({
|
|
46
46
|
servicePath: this.serviceRoot,
|
|
47
47
|
serviceName: this.serviceName,
|
|
48
|
-
serviceUrl: this.serviceUrl
|
|
48
|
+
serviceUrl: this.serviceUrl,
|
|
49
|
+
logger: this.logger,
|
|
50
|
+
timeout: 30000
|
|
49
51
|
});
|
|
50
52
|
}
|
|
51
53
|
|
|
@@ -138,7 +138,7 @@ function createServiceReadinessTests(testsDir, options = {}) {
|
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
// Run readiness validation
|
|
141
|
-
const validator = new ServiceReadinessValidator();
|
|
141
|
+
const validator = new ServiceReadinessValidator({ logger: console });
|
|
142
142
|
const result = await validator.validateReadiness({
|
|
143
143
|
name: serviceName,
|
|
144
144
|
version: serviceVersion,
|