@onlineapps/conn-orch-validator 2.0.6 → 2.0.9

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.
Files changed (36) hide show
  1. package/README.md +30 -1
  2. package/TESTING_STRATEGY.md +0 -0
  3. package/docs/DESIGN.md +0 -0
  4. package/examples/service-wrapper-usage.js +0 -0
  5. package/examples/three-tier-testing.js +0 -0
  6. package/jest.config.js +0 -0
  7. package/onlineapps-conn-e2e-testing-1.0.0.tgz +0 -0
  8. package/package.json +1 -1
  9. package/src/CookbookTestRunner.js +4 -4
  10. package/src/CookbookTestUtils.js +0 -0
  11. package/src/ServiceReadinessValidator.js +0 -0
  12. package/src/ServiceTestHarness.js +0 -0
  13. package/src/ServiceValidator.js +0 -0
  14. package/src/TestOrchestrator.js +0 -0
  15. package/src/ValidationOrchestrator.js +29 -22
  16. package/src/WorkflowTestRunner.js +0 -0
  17. package/src/helpers/README.md +0 -0
  18. package/src/helpers/createPreValidationTests.js +0 -0
  19. package/src/helpers/createServiceReadinessTests.js +0 -0
  20. package/src/index.js +0 -0
  21. package/src/mocks/MockMQClient.js +0 -0
  22. package/src/mocks/MockRegistry.js +0 -0
  23. package/src/mocks/MockStorage.js +0 -0
  24. package/src/validators/ServiceStructureValidator.js +0 -0
  25. package/src/validators/ValidationProofGenerator.js +0 -0
  26. package/test-mq-flow.js +0 -0
  27. package/tests/component/testing-framework-integration.test.js +0 -313
  28. package/tests/integration/ServiceReadiness.test.js +0 -265
  29. package/tests/monitoring-e2e.test.js +0 -315
  30. package/tests/run-example.js +0 -257
  31. package/tests/unit/CookbookTestRunner.test.js +0 -353
  32. package/tests/unit/MockMQClient.test.js +0 -190
  33. package/tests/unit/MockRegistry.test.js +0 -233
  34. package/tests/unit/MockStorage.test.js +0 -257
  35. package/tests/unit/ServiceValidator.test.js +0 -429
  36. package/tests/unit/WorkflowTestRunner.test.js +0 -546
package/README.md CHANGED
@@ -66,6 +66,35 @@ services/my-service/
66
66
 
67
67
  ---
68
68
 
69
+ ## Validation Proof Structure
70
+
71
+ **Location:** `conn-runtime/validation-proof.json`
72
+
73
+ ```json
74
+ {
75
+ "validationProof": "sha256-hash-of-validationData",
76
+ "validationData": {
77
+ "serviceName": "hello-service",
78
+ "version": "1.0.0",
79
+ "validator": "@onlineapps/conn-orch-validator",
80
+ "validatorVersion": "2.0.6",
81
+ "validatedAt": "2025-10-22T10:30:45.123Z",
82
+ "testsRun": 15,
83
+ "testsPassed": 15,
84
+ "testsFailed": 0,
85
+ "durationMs": 234
86
+ }
87
+ }
88
+ ```
89
+
90
+ **Proof Lifecycle:**
91
+ - **Valid for:** 30 days OR until service fingerprint changes
92
+ - **Fingerprint:** SHA256 hash of (version + operations + dependencies + config)
93
+ - **Revalidation:** Automatic if proof missing/invalid/expired
94
+ - **Registry:** Accepts service with valid proof (skips Tier 2 validation)
95
+
96
+ ---
97
+
69
98
  ## Related Documentation
70
99
 
71
100
  - [SERVICE_REGISTRATION_FLOW.md](/services/hello-service/docs/SERVICE_REGISTRATION_FLOW.md)
@@ -75,4 +104,4 @@ services/my-service/
75
104
 
76
105
  ---
77
106
 
78
- *Last updated: 2025-10-21*
107
+ *Last updated: 2025-10-22*
File without changes
package/docs/DESIGN.md CHANGED
File without changes
File without changes
File without changes
package/jest.config.js CHANGED
File without changes
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onlineapps/conn-orch-validator",
3
- "version": "2.0.6",
3
+ "version": "2.0.9",
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": {
@@ -76,14 +76,14 @@ class CookbookTestRunner {
76
76
 
77
77
  // Calculate results
78
78
  const duration = Date.now() - startTime;
79
- const passed = stepResults.every(r => r.passed);
79
+ const passedCount = stepResults.filter(r => r.passed).length;
80
80
  const failed = stepResults.filter(r => !r.passed).length;
81
81
 
82
82
  const results = {
83
83
  cookbook: cookbookData.description || 'Unnamed',
84
- passed,
84
+ passed: passedCount === stepResults.length, // Boolean: all steps passed
85
85
  total: stepResults.length,
86
- passed: stepResults.filter(r => r.passed).length,
86
+ passedCount, // Number: count of passed steps
87
87
  failed,
88
88
  duration,
89
89
  steps: stepResults
@@ -91,7 +91,7 @@ class CookbookTestRunner {
91
91
 
92
92
  // Update aggregate results
93
93
  this.results.total += results.total;
94
- this.results.passed += results.passed;
94
+ this.results.passed += results.passedCount;
95
95
  this.results.failed += results.failed;
96
96
  this.results.duration += results.duration;
97
97
  this.results.steps.push(...results.steps);
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -2,7 +2,7 @@
2
2
 
3
3
  const fs = require('fs');
4
4
  const path = require('path');
5
- const { ValidationProofCodec, FingerprintUtils } = require('@onlineapps/service-validator-core');
5
+ const { ValidationProofCodec } = require('@onlineapps/service-validator-core');
6
6
  const { ServiceStructureValidator } = require('./validators/ServiceStructureValidator');
7
7
  const ServiceReadinessValidator = require('./ServiceReadinessValidator');
8
8
  const CookbookTestRunner = require('./CookbookTestRunner');
@@ -88,33 +88,30 @@ class ValidationOrchestrator {
88
88
 
89
89
  /**
90
90
  * Check if proof is still valid
91
- * Valid = correct fingerprint + not expired
91
+ * Valid = correct signature + fingerprint + not expired
92
92
  */
93
93
  async isProofValid(proof) {
94
94
  try {
95
- // Calculate current fingerprint
96
- const currentFingerprint = await this.calculateFingerprint();
97
-
98
- // Check if fingerprint matches
99
- if (proof.fingerprint !== currentFingerprint) {
100
- console.log('[ValidationOrchestrator] Fingerprint mismatch (service changed)');
95
+ if (!proof) {
101
96
  return false;
102
97
  }
103
98
 
104
- // Check expiration (proof valid for 30 days)
105
- const proofDate = new Date(proof.validatedAt);
106
- const now = new Date();
107
- const daysSinceValidation = (now - proofDate) / (1000 * 60 * 60 * 24);
99
+ // Decode and verify proof using ValidationProofCodec
100
+ const verificationResult = ValidationProofCodec.decode(proof, {
101
+ maxProofAge: 30 * 24 * 60 * 60 * 1000 // 30 days
102
+ });
108
103
 
109
- if (daysSinceValidation > 30) {
110
- console.log('[ValidationOrchestrator] Proof expired (>30 days old)');
104
+ if (!verificationResult.valid) {
105
+ console.log(`[ValidationOrchestrator] Proof validation failed: ${verificationResult.reason}`);
111
106
  return false;
112
107
  }
113
108
 
114
- // Verify proof signature
115
- const isValid = ValidationProofCodec.verify(proof);
116
- if (!isValid) {
117
- console.warn('[ValidationOrchestrator] Proof signature invalid');
109
+ // Calculate current fingerprint
110
+ const currentFingerprint = await this.calculateFingerprint();
111
+
112
+ // Check if fingerprint matches
113
+ if (proof.validationProof !== currentFingerprint) {
114
+ console.log('[ValidationOrchestrator] Fingerprint mismatch (service changed)');
118
115
  return false;
119
116
  }
120
117
 
@@ -135,6 +132,10 @@ class ValidationOrchestrator {
135
132
  const operationsFile = path.join(this.configPath, 'operations.json');
136
133
  const packageFile = path.join(this.serviceRoot, 'package.json');
137
134
 
135
+ if (!fs.existsSync(configFile)) {
136
+ throw new Error('config.json not found');
137
+ }
138
+
138
139
  const config = JSON.parse(fs.readFileSync(configFile, 'utf8'));
139
140
  const operations = JSON.parse(fs.readFileSync(operationsFile, 'utf8'));
140
141
  const pkg = JSON.parse(fs.readFileSync(packageFile, 'utf8'));
@@ -149,12 +150,18 @@ class ValidationOrchestrator {
149
150
  });
150
151
  }
151
152
 
152
- return FingerprintUtils.generate({
153
+ // Generate fingerprint using crypto SHA256
154
+ const fingerprintData = {
153
155
  serviceVersion: config.service?.version || pkg.version,
154
156
  operations: operations,
155
157
  dependencies: deps,
156
- configHash: FingerprintUtils.generate(config)
157
- });
158
+ config: config
159
+ };
160
+
161
+ // Stringify with sorted keys for deterministic output
162
+ const hashInput = JSON.stringify(fingerprintData, Object.keys(fingerprintData).sort());
163
+ const crypto = require('crypto');
164
+ return crypto.createHash('sha256').update(hashInput).digest('hex');
158
165
  } catch (error) {
159
166
  throw new Error(`Failed to calculate fingerprint: ${error.message}`);
160
167
  }
@@ -471,7 +478,7 @@ class ValidationOrchestrator {
471
478
  }
472
479
  } else {
473
480
  console.error(`[ValidationOrchestrator] ❌ Validation FAILED (${duration}ms)`);
474
- console.error(`[ValidationOrchestrator] Errors: ${results.errors.join(', ')}`);
481
+ console.error(`[ValidationOrchestrator] Errors: ${JSON.stringify(results.errors, null, 2)}`);
475
482
  }
476
483
 
477
484
  return results;
File without changes
File without changes
File without changes
File without changes
package/src/index.js CHANGED
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
package/test-mq-flow.js CHANGED
File without changes
@@ -1,313 +0,0 @@
1
- 'use strict';
2
-
3
- /**
4
- * Component tests for E2E Testing Framework
5
- * Tests the integration between different testing utilities
6
- */
7
-
8
- describe('E2E Testing Framework Integration @component', () => {
9
- let framework;
10
-
11
- beforeEach(() => {
12
- jest.clearAllMocks();
13
- delete require.cache[require.resolve('../../src/index.js')];
14
- framework = require('../../src/index.js');
15
- });
16
-
17
- describe('Framework Exports', () => {
18
- it('should export all mock components', () => {
19
- expect(framework.MockMQClient).toBeDefined();
20
- expect(framework.MockRegistry).toBeDefined();
21
- expect(framework.MockStorage).toBeDefined();
22
- });
23
-
24
- it('should export all test utilities', () => {
25
- expect(framework.ServiceTestHarness).toBeDefined();
26
- expect(framework.ServiceValidator).toBeDefined();
27
- expect(framework.WorkflowTestRunner).toBeDefined();
28
- expect(framework.CookbookTestUtils).toBeDefined();
29
- expect(framework.ServiceReadinessValidator).toBeDefined();
30
- expect(framework.TestOrchestrator).toBeDefined();
31
- });
32
-
33
- it('should export factory functions', () => {
34
- expect(typeof framework.createTestHarness).toBe('function');
35
- expect(typeof framework.createValidator).toBe('function');
36
- expect(typeof framework.createMockMQ).toBe('function');
37
- expect(typeof framework.createMockRegistry).toBe('function');
38
- expect(typeof framework.createMockStorage).toBe('function');
39
- });
40
- });
41
-
42
- describe('Factory Functions', () => {
43
- it('should create MockMQClient instance', () => {
44
- const mq = framework.createMockMQ();
45
- expect(mq.constructor.name).toBe('MockMQClient');
46
- expect(typeof mq.connect).toBe('function');
47
- expect(typeof mq.publish).toBe('function');
48
- });
49
-
50
- it('should create MockRegistry instance', () => {
51
- const registry = framework.createMockRegistry();
52
- expect(registry.constructor.name).toBe('MockRegistry');
53
- expect(typeof registry.register).toBe('function');
54
- expect(typeof registry.discover).toBe('function');
55
- });
56
-
57
- it('should create MockStorage instance', () => {
58
- const storage = framework.createMockStorage();
59
- expect(storage.constructor.name).toBe('MockStorage');
60
- expect(typeof storage.upload).toBe('function');
61
- expect(typeof storage.download).toBe('function');
62
- });
63
-
64
- it('should create ServiceTestHarness with options', () => {
65
- const harness = framework.createTestHarness({
66
- serviceName: 'test-service'
67
- });
68
- expect(harness.constructor.name).toBe('ServiceTestHarness');
69
- expect(harness.serviceName).toBe('test-service');
70
- });
71
-
72
- it('should create ServiceValidator with options', () => {
73
- const validator = framework.createValidator({
74
- services: ['service1', 'service2']
75
- });
76
- expect(validator.constructor.name).toBe('ServiceValidator');
77
- expect(validator.services).toEqual(['service1', 'service2']);
78
- });
79
- });
80
-
81
- describe('Component Integration', () => {
82
- it('should allow ServiceTestHarness to use mocks', async () => {
83
- const harness = new framework.ServiceTestHarness({
84
- serviceName: 'integration-test'
85
- });
86
-
87
- await harness.setup();
88
-
89
- // Harness should have created mock instances
90
- expect(harness.mq).toBeDefined();
91
- expect(harness.registry).toBeDefined();
92
- expect(harness.storage).toBeDefined();
93
-
94
- await harness.cleanup();
95
- });
96
-
97
- it('should allow WorkflowTestRunner to use MockMQClient', async () => {
98
- const mq = new framework.MockMQClient();
99
- const runner = new framework.WorkflowTestRunner({
100
- mq: mq
101
- });
102
-
103
- expect(runner.mq).toBe(mq);
104
- expect(runner.state).toBeDefined();
105
- });
106
-
107
- it('should allow ServiceValidator to work with OpenAPI specs', () => {
108
- const validator = new framework.ServiceValidator();
109
-
110
- const spec = {
111
- openapi: '3.0.0',
112
- paths: {
113
- '/test': {
114
- get: {
115
- responses: {
116
- '200': { description: 'Success' }
117
- }
118
- }
119
- }
120
- }
121
- };
122
-
123
- const paths = validator.countEndpoints(spec.paths);
124
- expect(paths).toBeGreaterThan(0);
125
- });
126
-
127
- it('should allow CookbookTestUtils to generate valid cookbooks', () => {
128
- const utils = new framework.CookbookTestUtils();
129
-
130
- const cookbook = utils.generateCookbook({
131
- name: 'test-cookbook',
132
- steps: 3
133
- });
134
-
135
- expect(cookbook.name).toBe('test-cookbook');
136
- expect(cookbook.version).toBeDefined();
137
- expect(cookbook.steps).toHaveLength(3);
138
- expect(cookbook.steps[0]).toHaveProperty('service');
139
- expect(cookbook.steps[0]).toHaveProperty('operation');
140
- });
141
-
142
- it('should allow TestOrchestrator to coordinate components', () => {
143
- const harness = new framework.ServiceTestHarness({
144
- serviceName: 'orchestrated-test'
145
- });
146
- const validator = new framework.ServiceValidator();
147
- const runner = new framework.WorkflowTestRunner({
148
- mq: new framework.MockMQClient()
149
- });
150
-
151
- const orchestrator = new framework.TestOrchestrator({
152
- harness,
153
- validator,
154
- runner
155
- });
156
-
157
- expect(orchestrator.harness).toBe(harness);
158
- expect(orchestrator.validator).toBe(validator);
159
- expect(orchestrator.runner).toBe(runner);
160
- });
161
- });
162
-
163
- describe('Mock Components Behavior', () => {
164
- it('MockMQClient should handle basic operations', async () => {
165
- const mq = new framework.MockMQClient();
166
-
167
- // Should connect
168
- await mq.connect();
169
- expect(mq.isConnected).toBe(true);
170
-
171
- // Should publish messages
172
- await mq.publish('test-queue', { test: true });
173
- const messages = mq.getMessages('test-queue');
174
- expect(messages).toHaveLength(1);
175
- expect(messages[0]).toEqual({ test: true });
176
-
177
- // Should disconnect
178
- await mq.disconnect();
179
- expect(mq.isConnected).toBe(false);
180
- });
181
-
182
- it('MockRegistry should handle service registration', async () => {
183
- const registry = new framework.MockRegistry();
184
-
185
- // Should register service
186
- await registry.register({
187
- name: 'test-service',
188
- version: '1.0.0'
189
- });
190
-
191
- // Should discover service
192
- const services = await registry.discover('test-service');
193
- expect(services).toHaveLength(1);
194
- expect(services[0].name).toBe('test-service');
195
- });
196
-
197
- it('MockStorage should handle file operations', async () => {
198
- const storage = new framework.MockStorage();
199
-
200
- // Should upload file
201
- const data = Buffer.from('test data');
202
- await storage.upload('bucket', 'key', data);
203
-
204
- // Should download file
205
- const retrieved = await storage.download('bucket', 'key');
206
- expect(retrieved.toString()).toBe('test data');
207
-
208
- // Should list files
209
- const files = await storage.list('bucket');
210
- expect(files).toContain('key');
211
- });
212
- });
213
-
214
- describe('Testing Utilities Behavior', () => {
215
- it('ServiceReadinessValidator should check readiness', async () => {
216
- const validator = new framework.ServiceReadinessValidator({
217
- services: ['api', 'worker']
218
- });
219
-
220
- // Mock service readiness
221
- validator.mockServiceReady('api');
222
- validator.mockServiceReady('worker');
223
-
224
- const allReady = await validator.checkAll();
225
- expect(allReady).toBe(true);
226
- });
227
-
228
- it('CookbookTestUtils should validate cookbook structure', () => {
229
- const utils = new framework.CookbookTestUtils();
230
-
231
- const validCookbook = {
232
- name: 'valid-cookbook',
233
- version: '1.0.0',
234
- steps: [
235
- {
236
- service: 'service1',
237
- operation: 'process'
238
- }
239
- ]
240
- };
241
-
242
- const validation = utils.validate(validCookbook);
243
- expect(validation.valid).toBe(true);
244
-
245
- const invalidCookbook = { steps: [] };
246
- const invalidValidation = utils.validate(invalidCookbook);
247
- expect(invalidValidation.valid).toBe(false);
248
- });
249
- });
250
-
251
- describe('Error Handling', () => {
252
- it('should handle missing configuration gracefully', () => {
253
- expect(() => new framework.ServiceTestHarness()).not.toThrow();
254
- expect(() => new framework.ServiceValidator()).not.toThrow();
255
- expect(() => new framework.WorkflowTestRunner()).not.toThrow();
256
- });
257
-
258
- it('should handle invalid operations gracefully', async () => {
259
- const mq = new framework.MockMQClient();
260
-
261
- // Should fail when not connected
262
- await expect(mq.publish('queue', {}))
263
- .rejects.toThrow('Not connected');
264
-
265
- // Storage should fail on non-existent files
266
- const storage = new framework.MockStorage();
267
- await expect(storage.download('bucket', 'nonexistent'))
268
- .rejects.toThrow();
269
- });
270
- });
271
-
272
- describe('Complete Testing Workflow', () => {
273
- it('should support complete test workflow', async () => {
274
- // 1. Setup test environment
275
- const harness = new framework.ServiceTestHarness({
276
- serviceName: 'complete-test'
277
- });
278
- await harness.setup();
279
-
280
- // 2. Register mock services
281
- await harness.registry.register({
282
- name: 'service1',
283
- version: '1.0.0'
284
- });
285
-
286
- // 3. Create test cookbook
287
- const utils = new framework.CookbookTestUtils();
288
- const cookbook = utils.generateCookbook({
289
- name: 'test-workflow',
290
- steps: 2
291
- });
292
-
293
- // 4. Run workflow
294
- const runner = new framework.WorkflowTestRunner({
295
- mq: harness.mq
296
- });
297
-
298
- // Mock step execution
299
- runner.mockStepResponse('test-service-0', { success: true });
300
- runner.mockStepResponse('test-service-1', { complete: true });
301
-
302
- const result = await runner.runWorkflow(cookbook, { input: 'data' });
303
- expect(result.status).toBe('completed');
304
-
305
- // 5. Validate results
306
- const validator = new framework.ServiceValidator();
307
- expect(validator).toBeDefined();
308
-
309
- // 6. Cleanup
310
- await harness.cleanup();
311
- });
312
- });
313
- });