@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.
- package/README.md +30 -1
- package/TESTING_STRATEGY.md +0 -0
- package/docs/DESIGN.md +0 -0
- package/examples/service-wrapper-usage.js +0 -0
- package/examples/three-tier-testing.js +0 -0
- package/jest.config.js +0 -0
- package/onlineapps-conn-e2e-testing-1.0.0.tgz +0 -0
- package/package.json +1 -1
- package/src/CookbookTestRunner.js +4 -4
- package/src/CookbookTestUtils.js +0 -0
- package/src/ServiceReadinessValidator.js +0 -0
- package/src/ServiceTestHarness.js +0 -0
- package/src/ServiceValidator.js +0 -0
- package/src/TestOrchestrator.js +0 -0
- package/src/ValidationOrchestrator.js +29 -22
- package/src/WorkflowTestRunner.js +0 -0
- package/src/helpers/README.md +0 -0
- package/src/helpers/createPreValidationTests.js +0 -0
- package/src/helpers/createServiceReadinessTests.js +0 -0
- package/src/index.js +0 -0
- package/src/mocks/MockMQClient.js +0 -0
- package/src/mocks/MockRegistry.js +0 -0
- package/src/mocks/MockStorage.js +0 -0
- package/src/validators/ServiceStructureValidator.js +0 -0
- package/src/validators/ValidationProofGenerator.js +0 -0
- package/test-mq-flow.js +0 -0
- package/tests/component/testing-framework-integration.test.js +0 -313
- package/tests/integration/ServiceReadiness.test.js +0 -265
- package/tests/monitoring-e2e.test.js +0 -315
- package/tests/run-example.js +0 -257
- package/tests/unit/CookbookTestRunner.test.js +0 -353
- package/tests/unit/MockMQClient.test.js +0 -190
- package/tests/unit/MockRegistry.test.js +0 -233
- package/tests/unit/MockStorage.test.js +0 -257
- package/tests/unit/ServiceValidator.test.js +0 -429
- 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-
|
|
107
|
+
*Last updated: 2025-10-22*
|
package/TESTING_STRATEGY.md
CHANGED
|
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.
|
|
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
|
|
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
|
-
|
|
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.
|
|
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);
|
package/src/CookbookTestUtils.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/src/ServiceValidator.js
CHANGED
|
File without changes
|
package/src/TestOrchestrator.js
CHANGED
|
File without changes
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
|
-
const { ValidationProofCodec
|
|
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
|
-
|
|
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
|
-
//
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
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 (
|
|
110
|
-
console.log(
|
|
104
|
+
if (!verificationResult.valid) {
|
|
105
|
+
console.log(`[ValidationOrchestrator] Proof validation failed: ${verificationResult.reason}`);
|
|
111
106
|
return false;
|
|
112
107
|
}
|
|
113
108
|
|
|
114
|
-
//
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
481
|
+
console.error(`[ValidationOrchestrator] Errors: ${JSON.stringify(results.errors, null, 2)}`);
|
|
475
482
|
}
|
|
476
483
|
|
|
477
484
|
return results;
|
|
File without changes
|
package/src/helpers/README.md
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/src/index.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/src/mocks/MockStorage.js
CHANGED
|
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
|
-
});
|