@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.
Files changed (37) hide show
  1. package/README.md +78 -0
  2. package/TESTING_STRATEGY.md +92 -0
  3. package/docs/DESIGN.md +134 -0
  4. package/examples/service-wrapper-usage.js +250 -0
  5. package/examples/three-tier-testing.js +144 -0
  6. package/jest.config.js +23 -0
  7. package/onlineapps-conn-e2e-testing-1.0.0.tgz +0 -0
  8. package/package.json +43 -0
  9. package/src/CookbookTestRunner.js +434 -0
  10. package/src/CookbookTestUtils.js +237 -0
  11. package/src/ServiceReadinessValidator.js +430 -0
  12. package/src/ServiceTestHarness.js +256 -0
  13. package/src/ServiceValidator.js +387 -0
  14. package/src/TestOrchestrator.js +727 -0
  15. package/src/ValidationOrchestrator.js +506 -0
  16. package/src/WorkflowTestRunner.js +396 -0
  17. package/src/helpers/README.md +235 -0
  18. package/src/helpers/createPreValidationTests.js +321 -0
  19. package/src/helpers/createServiceReadinessTests.js +245 -0
  20. package/src/index.js +62 -0
  21. package/src/mocks/MockMQClient.js +176 -0
  22. package/src/mocks/MockRegistry.js +164 -0
  23. package/src/mocks/MockStorage.js +186 -0
  24. package/src/validators/ServiceStructureValidator.js +487 -0
  25. package/src/validators/ValidationProofGenerator.js +79 -0
  26. package/test-mq-flow.js +72 -0
  27. package/test-orchestrator.js +95 -0
  28. package/tests/component/testing-framework-integration.test.js +313 -0
  29. package/tests/integration/ServiceReadiness.test.js +265 -0
  30. package/tests/monitoring-e2e.test.js +315 -0
  31. package/tests/run-example.js +257 -0
  32. package/tests/unit/CookbookTestRunner.test.js +353 -0
  33. package/tests/unit/MockMQClient.test.js +190 -0
  34. package/tests/unit/MockRegistry.test.js +233 -0
  35. package/tests/unit/MockStorage.test.js +257 -0
  36. package/tests/unit/ServiceValidator.test.js +429 -0
  37. package/tests/unit/WorkflowTestRunner.test.js +546 -0
@@ -0,0 +1,176 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * MockMQClient - Simulates RabbitMQ for testing
5
+ * Provides in-memory message queue simulation
6
+ */
7
+ class MockMQClient {
8
+ constructor() {
9
+ this.queues = {};
10
+ this.consumers = {};
11
+ this.isConnected = false;
12
+ this.publishedMessages = [];
13
+ this.acknowledgedMessages = [];
14
+ this.rejectedMessages = [];
15
+ }
16
+
17
+ /**
18
+ * Simulate connection
19
+ */
20
+ async connect() {
21
+ this.isConnected = true;
22
+ return Promise.resolve();
23
+ }
24
+
25
+ /**
26
+ * Simulate disconnection
27
+ */
28
+ async disconnect() {
29
+ this.isConnected = false;
30
+ this.consumers = {};
31
+ return Promise.resolve();
32
+ }
33
+
34
+ /**
35
+ * Publish message to queue
36
+ */
37
+ async publish(queue, message, options = {}) {
38
+ if (!this.isConnected) {
39
+ throw new Error('Not connected to MQ');
40
+ }
41
+
42
+ if (!this.queues[queue]) {
43
+ this.queues[queue] = [];
44
+ }
45
+
46
+ const messageWrapper = {
47
+ content: Buffer.from(JSON.stringify(message)),
48
+ fields: { routingKey: queue },
49
+ properties: options,
50
+ timestamp: Date.now()
51
+ };
52
+
53
+ this.queues[queue].push(messageWrapper);
54
+ this.publishedMessages.push({ queue, message, options, timestamp: Date.now() });
55
+
56
+ // Trigger consumers if any
57
+ if (this.consumers[queue]) {
58
+ const consumer = this.consumers[queue];
59
+ setImmediate(() => {
60
+ consumer.callback(messageWrapper);
61
+ });
62
+ }
63
+
64
+ return Promise.resolve();
65
+ }
66
+
67
+ /**
68
+ * Consume messages from queue
69
+ */
70
+ async consume(queue, callback, options = {}) {
71
+ if (!this.isConnected) {
72
+ throw new Error('Not connected to MQ');
73
+ }
74
+
75
+ this.consumers[queue] = {
76
+ callback,
77
+ options
78
+ };
79
+
80
+ // Process existing messages in queue
81
+ if (this.queues[queue]) {
82
+ const messages = [...this.queues[queue]];
83
+ this.queues[queue] = [];
84
+
85
+ messages.forEach(msg => {
86
+ setImmediate(() => callback(msg));
87
+ });
88
+ }
89
+
90
+ return Promise.resolve();
91
+ }
92
+
93
+ /**
94
+ * Acknowledge message
95
+ */
96
+ async ack(message) {
97
+ this.acknowledgedMessages.push({
98
+ message,
99
+ timestamp: Date.now()
100
+ });
101
+ return Promise.resolve();
102
+ }
103
+
104
+ /**
105
+ * Reject message
106
+ */
107
+ async nack(message, allUpTo = false, requeue = false) {
108
+ this.rejectedMessages.push({
109
+ message,
110
+ allUpTo,
111
+ requeue,
112
+ timestamp: Date.now()
113
+ });
114
+
115
+ if (requeue && message.fields) {
116
+ const queue = message.fields.routingKey;
117
+ if (this.queues[queue]) {
118
+ this.queues[queue].push(message);
119
+ }
120
+ }
121
+
122
+ return Promise.resolve();
123
+ }
124
+
125
+ /**
126
+ * Get messages from queue (for testing)
127
+ */
128
+ getMessages(queue) {
129
+ return (this.queues[queue] || []).map(msg =>
130
+ JSON.parse(msg.content.toString())
131
+ );
132
+ }
133
+
134
+ /**
135
+ * Get all published messages (for testing)
136
+ */
137
+ getPublishedMessages(queue = null) {
138
+ if (queue) {
139
+ return this.publishedMessages.filter(m => m.queue === queue);
140
+ }
141
+ return this.publishedMessages;
142
+ }
143
+
144
+ /**
145
+ * Clear all data (for testing)
146
+ */
147
+ clear() {
148
+ this.queues = {};
149
+ this.publishedMessages = [];
150
+ this.acknowledgedMessages = [];
151
+ this.rejectedMessages = [];
152
+ }
153
+
154
+ /**
155
+ * Get statistics (for testing)
156
+ */
157
+ getStats() {
158
+ return {
159
+ queues: Object.keys(this.queues),
160
+ messageCount: Object.values(this.queues).reduce((sum, q) => sum + q.length, 0),
161
+ published: this.publishedMessages.length,
162
+ acknowledged: this.acknowledgedMessages.length,
163
+ rejected: this.rejectedMessages.length,
164
+ consumers: Object.keys(this.consumers)
165
+ };
166
+ }
167
+
168
+ /**
169
+ * Simulate message processing delay
170
+ */
171
+ async simulateDelay(ms) {
172
+ return new Promise(resolve => setTimeout(resolve, ms));
173
+ }
174
+ }
175
+
176
+ module.exports = MockMQClient;
@@ -0,0 +1,164 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * MockRegistry - Simulates service registry for testing
5
+ */
6
+ class MockRegistry {
7
+ constructor() {
8
+ this.services = {};
9
+ this.certificates = {};
10
+ this.heartbeats = {};
11
+ this.validationResults = {};
12
+ }
13
+
14
+ /**
15
+ * Register a service
16
+ */
17
+ async register(serviceData) {
18
+ const { name, version, url, healthCheck, openapi } = serviceData;
19
+
20
+ if (!name) {
21
+ throw new Error('Service name is required');
22
+ }
23
+
24
+ this.services[name] = {
25
+ name,
26
+ version: version || '1.0.0',
27
+ url: url || `http://localhost:3000`,
28
+ healthCheck: healthCheck || '/health',
29
+ openapi: openapi || {},
30
+ registeredAt: Date.now(),
31
+ status: 'active'
32
+ };
33
+
34
+ // Issue mock certificate
35
+ this.certificates[name] = {
36
+ serviceId: name,
37
+ issuedAt: Date.now(),
38
+ expiresAt: Date.now() + 86400000, // 24 hours
39
+ fingerprint: `mock-cert-${name}-${Date.now()}`
40
+ };
41
+
42
+ return {
43
+ success: true,
44
+ certificate: this.certificates[name]
45
+ };
46
+ }
47
+
48
+ /**
49
+ * Unregister a service
50
+ */
51
+ async unregister(serviceName) {
52
+ delete this.services[serviceName];
53
+ delete this.certificates[serviceName];
54
+ delete this.heartbeats[serviceName];
55
+ return { success: true };
56
+ }
57
+
58
+ /**
59
+ * Send heartbeat
60
+ */
61
+ async heartbeat(serviceName) {
62
+ if (!this.services[serviceName]) {
63
+ throw new Error(`Service ${serviceName} not registered`);
64
+ }
65
+
66
+ this.heartbeats[serviceName] = {
67
+ timestamp: Date.now(),
68
+ status: 'healthy'
69
+ };
70
+
71
+ return { success: true };
72
+ }
73
+
74
+ /**
75
+ * Get service by name
76
+ */
77
+ getService(serviceName) {
78
+ return this.services[serviceName] || null;
79
+ }
80
+
81
+ /**
82
+ * Get all services
83
+ */
84
+ getServices() {
85
+ return Object.values(this.services);
86
+ }
87
+
88
+ /**
89
+ * Get certificate for service
90
+ */
91
+ getCertificate(serviceName) {
92
+ return this.certificates[serviceName] || null;
93
+ }
94
+
95
+ /**
96
+ * Simulate validation
97
+ */
98
+ async validate(serviceName, validationData) {
99
+ this.validationResults[serviceName] = {
100
+ ...validationData,
101
+ timestamp: Date.now(),
102
+ passed: true
103
+ };
104
+
105
+ return {
106
+ success: true,
107
+ token: `validation-token-${serviceName}-${Date.now()}`
108
+ };
109
+ }
110
+
111
+ /**
112
+ * Get validation results
113
+ */
114
+ getValidationResults(serviceName) {
115
+ return this.validationResults[serviceName] || null;
116
+ }
117
+
118
+ /**
119
+ * Clear all data
120
+ */
121
+ clear() {
122
+ this.services = {};
123
+ this.certificates = {};
124
+ this.heartbeats = {};
125
+ this.validationResults = {};
126
+ }
127
+
128
+ /**
129
+ * Get registry stats
130
+ */
131
+ getStats() {
132
+ return {
133
+ serviceCount: Object.keys(this.services).length,
134
+ activeServices: Object.values(this.services).filter(s => s.status === 'active').length,
135
+ certificates: Object.keys(this.certificates).length,
136
+ recentHeartbeats: Object.values(this.heartbeats).filter(
137
+ h => Date.now() - h.timestamp < 60000
138
+ ).length
139
+ };
140
+ }
141
+
142
+ /**
143
+ * Simulate service discovery
144
+ */
145
+ discover(query = {}) {
146
+ let results = Object.values(this.services);
147
+
148
+ if (query.name) {
149
+ results = results.filter(s => s.name.includes(query.name));
150
+ }
151
+
152
+ if (query.version) {
153
+ results = results.filter(s => s.version === query.version);
154
+ }
155
+
156
+ if (query.status) {
157
+ results = results.filter(s => s.status === query.status);
158
+ }
159
+
160
+ return results;
161
+ }
162
+ }
163
+
164
+ module.exports = MockRegistry;
@@ -0,0 +1,186 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * MockStorage - Simulates MinIO/S3 storage for testing
5
+ */
6
+ class MockStorage {
7
+ constructor() {
8
+ this.buckets = {};
9
+ this.metadata = {};
10
+ }
11
+
12
+ /**
13
+ * Create bucket
14
+ */
15
+ async createBucket(bucketName) {
16
+ if (!this.buckets[bucketName]) {
17
+ this.buckets[bucketName] = {};
18
+ }
19
+ return { success: true };
20
+ }
21
+
22
+ /**
23
+ * Put object
24
+ */
25
+ async put(bucket, key, content, options = {}) {
26
+ if (!this.buckets[bucket]) {
27
+ await this.createBucket(bucket);
28
+ }
29
+
30
+ const data = typeof content === 'object' ? JSON.stringify(content) : content;
31
+
32
+ this.buckets[bucket][key] = {
33
+ content: data,
34
+ contentType: options.contentType || 'application/json',
35
+ metadata: options.metadata || {},
36
+ timestamp: Date.now(),
37
+ size: data.length
38
+ };
39
+
40
+ // Store metadata separately for querying
41
+ if (!this.metadata[bucket]) {
42
+ this.metadata[bucket] = {};
43
+ }
44
+ this.metadata[bucket][key] = {
45
+ size: data.length,
46
+ lastModified: Date.now(),
47
+ contentType: options.contentType || 'application/json'
48
+ };
49
+
50
+ return {
51
+ success: true,
52
+ etag: `mock-etag-${Date.now()}`,
53
+ location: `${bucket}/${key}`
54
+ };
55
+ }
56
+
57
+ /**
58
+ * Get object
59
+ */
60
+ async get(bucket, key) {
61
+ if (!this.buckets[bucket] || !this.buckets[bucket][key]) {
62
+ throw new Error(`Object not found: ${bucket}/${key}`);
63
+ }
64
+
65
+ const obj = this.buckets[bucket][key];
66
+ let content = obj.content;
67
+
68
+ // Try to parse JSON if content type suggests it
69
+ if (obj.contentType === 'application/json' && typeof content === 'string') {
70
+ try {
71
+ content = JSON.parse(content);
72
+ } catch (e) {
73
+ // Keep as string if parsing fails
74
+ }
75
+ }
76
+
77
+ return {
78
+ content,
79
+ metadata: obj.metadata,
80
+ contentType: obj.contentType,
81
+ size: obj.size,
82
+ lastModified: obj.timestamp
83
+ };
84
+ }
85
+
86
+ /**
87
+ * Delete object
88
+ */
89
+ async delete(bucket, key) {
90
+ if (this.buckets[bucket]) {
91
+ delete this.buckets[bucket][key];
92
+ if (this.metadata[bucket]) {
93
+ delete this.metadata[bucket][key];
94
+ }
95
+ }
96
+ return { success: true };
97
+ }
98
+
99
+ /**
100
+ * List objects in bucket
101
+ */
102
+ async list(bucket, prefix = '', limit = 1000) {
103
+ if (!this.buckets[bucket]) {
104
+ return { objects: [] };
105
+ }
106
+
107
+ const objects = Object.keys(this.buckets[bucket])
108
+ .filter(key => key.startsWith(prefix))
109
+ .slice(0, limit)
110
+ .map(key => ({
111
+ key,
112
+ size: this.buckets[bucket][key].size,
113
+ lastModified: this.buckets[bucket][key].timestamp
114
+ }));
115
+
116
+ return { objects };
117
+ }
118
+
119
+ /**
120
+ * Check if object exists
121
+ */
122
+ async exists(bucket, key) {
123
+ return !!(this.buckets[bucket] && this.buckets[bucket][key]);
124
+ }
125
+
126
+ /**
127
+ * Get object metadata
128
+ */
129
+ async getMetadata(bucket, key) {
130
+ if (!this.metadata[bucket] || !this.metadata[bucket][key]) {
131
+ throw new Error(`Metadata not found: ${bucket}/${key}`);
132
+ }
133
+ return this.metadata[bucket][key];
134
+ }
135
+
136
+ /**
137
+ * Generate presigned URL (mock)
138
+ */
139
+ async getPresignedUrl(bucket, key, expiresIn = 3600) {
140
+ return {
141
+ url: `http://mock-storage/${bucket}/${key}?token=mock-token-${Date.now()}`,
142
+ expiresAt: Date.now() + (expiresIn * 1000)
143
+ };
144
+ }
145
+
146
+ /**
147
+ * Clear all data
148
+ */
149
+ clear() {
150
+ this.buckets = {};
151
+ this.metadata = {};
152
+ }
153
+
154
+ /**
155
+ * Get storage stats
156
+ */
157
+ getStats() {
158
+ const stats = {
159
+ bucketCount: Object.keys(this.buckets).length,
160
+ totalObjects: 0,
161
+ totalSize: 0
162
+ };
163
+
164
+ Object.values(this.buckets).forEach(bucket => {
165
+ Object.values(bucket).forEach(obj => {
166
+ stats.totalObjects++;
167
+ stats.totalSize += obj.size;
168
+ });
169
+ });
170
+
171
+ return stats;
172
+ }
173
+
174
+ /**
175
+ * Copy object
176
+ */
177
+ async copy(sourceBucket, sourceKey, destBucket, destKey) {
178
+ const source = await this.get(sourceBucket, sourceKey);
179
+ return await this.put(destBucket, destKey, source.content, {
180
+ contentType: source.contentType,
181
+ metadata: source.metadata
182
+ });
183
+ }
184
+ }
185
+
186
+ module.exports = MockStorage;