@onlineapps/conn-orch-validator 2.0.32 → 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.32",
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 || 'http://127.0.0.1:3000';
27
+ this.serviceUrl = options.serviceUrl;
19
28
  this.servicePath = options.servicePath;
20
- this.mockInfrastructure = options.mockInfrastructure !== false; // default true
21
- this.timeout = options.timeout || 30000;
22
- this.logger = options.logger || console;
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 || console;
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
@@ -347,64 +350,44 @@ class ServiceReadinessValidator {
347
350
  }
348
351
 
349
352
  /**
350
- * Generate test input based on operation input schema
351
- * Supports Content Descriptor types (content, file) with proper test values
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
352
360
  */
353
361
  generateTestInput(inputSchema) {
354
362
  const testData = {};
355
363
 
356
364
  for (const [fieldName, fieldSpec] of Object.entries(inputSchema)) {
357
- if (fieldSpec.required) {
358
- // Generate appropriate test value based on type
359
- switch (fieldSpec.type) {
360
- case 'string':
361
- testData[fieldName] = fieldSpec.default || 'Test';
362
- break;
363
- case 'number':
364
- testData[fieldName] = fieldSpec.default || 0;
365
- break;
366
- case 'boolean':
367
- testData[fieldName] = fieldSpec.default || false;
368
- break;
369
- case 'array':
370
- testData[fieldName] = fieldSpec.default || [];
371
- break;
372
- case 'object':
373
- testData[fieldName] = fieldSpec.default || {};
374
- break;
375
- // Content Descriptor types - generate valid test content
376
- case 'content':
377
- // Generate content based on field name hints
378
- if (fieldName.toLowerCase().includes('html')) {
379
- testData[fieldName] = fieldSpec.default || '<html><body><h1>Test Validation</h1><p>Auto-generated test content.</p></body></html>';
380
- } else if (fieldName.toLowerCase().includes('markdown') || fieldName.toLowerCase().includes('md')) {
381
- testData[fieldName] = fieldSpec.default || '# Test Validation\n\nAuto-generated test content.';
382
- } else {
383
- testData[fieldName] = fieldSpec.default || 'Test content for validation';
384
- }
385
- break;
386
- case 'file':
387
- // For file types, generate a VALID Content Descriptor object (type=file)
388
- // This keeps endpoint checks predictable and avoids service-specific file upload flows.
389
- testData[fieldName] = fieldSpec.default || {
390
- _descriptor: true,
391
- type: 'file',
392
- storage_ref: 'minio://test/validation/placeholder.txt',
393
- filename: 'placeholder.txt',
394
- content_type: 'text/plain',
395
- size: 1,
396
- fingerprint: 'test-fingerprint'
397
- };
398
- break;
399
- default:
400
- testData[fieldName] = null;
401
- }
402
- }
365
+ if (!fieldSpec.required) continue;
366
+
367
+ const value = this._resolveTestValue(fieldName, fieldSpec);
368
+ testData[fieldName] = value;
403
369
  }
404
370
 
405
371
  return testData;
406
372
  }
407
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
+
408
391
  /**
409
392
  * Get recommendation based on results
410
393
  */
@@ -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
- this.logger = options.logger || console;
21
- this.integrationConfig = options.integrationConfig || {};
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
- this.logger = options.logger || console;
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 for ValidationOrchestrator');
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/
@@ -39,11 +41,13 @@ class ValidationOrchestrator {
39
41
 
40
42
  // Validators
41
43
  this.structureValidator = new ServiceStructureValidator(this.serviceRoot);
42
- this.readinessValidator = new ServiceReadinessValidator();
44
+ this.readinessValidator = new ServiceReadinessValidator({ logger: this.logger });
43
45
  this.cookbookRunner = new CookbookTestRunner({
44
46
  servicePath: this.serviceRoot,
45
47
  serviceName: this.serviceName,
46
- serviceUrl: this.serviceUrl // Pass serviceUrl to CookbookTestRunner
48
+ serviceUrl: this.serviceUrl,
49
+ logger: this.logger,
50
+ timeout: 30000
47
51
  });
48
52
  }
49
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,