@tamyla/clodo-framework 4.0.13 ā 4.0.14
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/CHANGELOG.md +11 -0
- package/README.md +7 -0
- package/dist/cli/commands/create.js +2 -1
- package/dist/middleware/Composer.js +38 -0
- package/dist/middleware/Registry.js +14 -0
- package/dist/middleware/index.js +3 -0
- package/dist/middleware/shared/basicAuth.js +21 -0
- package/dist/middleware/shared/cors.js +28 -0
- package/dist/middleware/shared/index.js +3 -0
- package/dist/middleware/shared/logging.js +14 -0
- package/dist/service-management/GenerationEngine.js +13 -2
- package/dist/service-management/ServiceOrchestrator.js +6 -2
- package/dist/service-management/generators/code/ServiceMiddlewareGenerator.js +156 -10
- package/dist/service-management/generators/code/WorkerIndexGenerator.js +75 -9
- package/dist/simple-api.js +32 -1
- package/docs/MIDDLEWARE_MIGRATION_SUMMARY.md +121 -0
- package/package.json +4 -1
- package/scripts/DEPLOY_COMMAND_NEW.js +128 -0
- package/scripts/README-automated-testing-suite.md +356 -0
- package/scripts/README-test-clodo-deployment.md +157 -0
- package/scripts/README.md +50 -0
- package/scripts/analyze-imports.ps1 +104 -0
- package/scripts/analyze-mixed-code.js +163 -0
- package/scripts/analyze-mixed-rationale.js +149 -0
- package/scripts/automated-testing-suite.js +776 -0
- package/scripts/deployment/README.md +31 -0
- package/scripts/deployment/deploy-domain.ps1 +449 -0
- package/scripts/deployment/deploy-staging.js +120 -0
- package/scripts/deployment/validate-staging.js +166 -0
- package/scripts/diagnose-imports.js +362 -0
- package/scripts/framework-diagnostic.js +368 -0
- package/scripts/migration/migrate-middleware-legacy-to-contract.js +47 -0
- package/scripts/post-publish-test.js +663 -0
- package/scripts/scan-worker-issues.js +52 -0
- package/scripts/service-management/README.md +27 -0
- package/scripts/service-management/setup-interactive.ps1 +693 -0
- package/scripts/test-clodo-deployment.js +588 -0
- package/scripts/test-downstream-install.js +237 -0
- package/scripts/test-local-package.ps1 +126 -0
- package/scripts/test-local-package.sh +166 -0
- package/scripts/test-package.js +339 -0
- package/scripts/testing/README.md +49 -0
- package/scripts/testing/test-first.ps1 +0 -0
- package/scripts/testing/test-first50.ps1 +0 -0
- package/scripts/testing/test.ps1 +0 -0
- package/scripts/utilities/README.md +61 -0
- package/scripts/utilities/check-bin.js +8 -0
- package/scripts/utilities/check-bundle.js +23 -0
- package/scripts/utilities/check-dist-imports.js +65 -0
- package/scripts/utilities/check-import-paths.js +191 -0
- package/scripts/utilities/cleanup-cli.js +159 -0
- package/scripts/utilities/deployment-helpers.ps1 +199 -0
- package/scripts/utilities/fix-dist-imports.js +135 -0
- package/scripts/utilities/generate-secrets.js +159 -0
- package/scripts/utilities/safe-push.ps1 +51 -0
- package/scripts/utilities/setup-helpers.ps1 +206 -0
- package/scripts/utilities/test-packaged-artifact.js +92 -0
- package/scripts/utilities/validate-dist-imports.js +189 -0
- package/scripts/utilities/validate-schema.js +102 -0
- package/scripts/verify-exports.js +193 -0
- package/scripts/verify-worker-safety.js +73 -0
- package/types/middleware.d.ts +1 -0
|
@@ -0,0 +1,588 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Clodo Service Deployment Testing Script
|
|
5
|
+
*
|
|
6
|
+
* Comprehensive local testing script for clodo-service deployment process.
|
|
7
|
+
* Tests information collection, consolidation, and deployment execution phases
|
|
8
|
+
* without making actual Cloudflare API calls or deployments.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* node test-clodo-deployment.js [options]
|
|
12
|
+
*
|
|
13
|
+
* Options:
|
|
14
|
+
* --service-path <path> Path to service directory (default: current dir)
|
|
15
|
+
* --dry-run Simulate deployment without changes (default: true)
|
|
16
|
+
* --verbose Detailed logging output
|
|
17
|
+
* --mock-credentials Use mock credentials instead of prompting
|
|
18
|
+
* --test-phase <phase> Test specific phase: all, collection, consolidation, execution
|
|
19
|
+
*
|
|
20
|
+
* Testing Phases:
|
|
21
|
+
* 1. Information Collection - Service detection and manifest parsing
|
|
22
|
+
* 2. Consolidation - Credential gathering and configuration validation
|
|
23
|
+
* 3. Execution - Deployment simulation with detailed logging
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { existsSync, readFileSync, writeFileSync } from 'fs';
|
|
27
|
+
import { resolve, join, dirname } from 'path';
|
|
28
|
+
import { fileURLToPath } from 'url';
|
|
29
|
+
import chalk from 'chalk';
|
|
30
|
+
|
|
31
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
32
|
+
const __dirname = dirname(__filename);
|
|
33
|
+
|
|
34
|
+
// Mock credentials for testing
|
|
35
|
+
const MOCK_CREDENTIALS = {
|
|
36
|
+
token: 'mock-api-token-for-testing-purposes-only',
|
|
37
|
+
accountId: 'mock-account-id-123456789',
|
|
38
|
+
zoneId: 'mock-zone-id-987654321'
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// Mock service manifest template
|
|
42
|
+
const MOCK_MANIFEST = {
|
|
43
|
+
serviceName: 'test-clodo-service',
|
|
44
|
+
serviceType: 'data-service',
|
|
45
|
+
version: '1.0.0',
|
|
46
|
+
configuration: {
|
|
47
|
+
domain: 'test-service.example.com',
|
|
48
|
+
domainName: 'test-service.example.com',
|
|
49
|
+
environment: 'staging',
|
|
50
|
+
cloudflare: {
|
|
51
|
+
accountId: MOCK_CREDENTIALS.accountId,
|
|
52
|
+
zoneId: MOCK_CREDENTIALS.zoneId
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
metadata: {
|
|
56
|
+
created: new Date().toISOString(),
|
|
57
|
+
framework: 'clodo-framework',
|
|
58
|
+
version: '3.1.5'
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
class ClodoDeploymentTester {
|
|
63
|
+
constructor(options = {}) {
|
|
64
|
+
this.options = {
|
|
65
|
+
servicePath: options.servicePath || process.cwd(),
|
|
66
|
+
dryRun: options.dryRun !== false, // Default to true for safety
|
|
67
|
+
verbose: options.verbose || false,
|
|
68
|
+
mockCredentials: options.mockCredentials !== false, // Default to true
|
|
69
|
+
testPhase: options.testPhase || 'all',
|
|
70
|
+
...options
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
this.testResults = {
|
|
74
|
+
phases: {
|
|
75
|
+
collection: { status: 'pending', duration: 0, details: {} },
|
|
76
|
+
consolidation: { status: 'pending', duration: 0, details: {} },
|
|
77
|
+
execution: { status: 'pending', duration: 0, details: {} }
|
|
78
|
+
},
|
|
79
|
+
overall: { status: 'running', startTime: Date.now() }
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
this.serviceInfo = {};
|
|
83
|
+
this.credentials = {};
|
|
84
|
+
this.deploymentPlan = {};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
log(message, level = 'info') {
|
|
88
|
+
const timestamp = new Date().toISOString().substr(11, 8);
|
|
89
|
+
const prefix = `[${timestamp}]`;
|
|
90
|
+
|
|
91
|
+
switch (level) {
|
|
92
|
+
case 'error':
|
|
93
|
+
console.error(chalk.red(`${prefix} ā ${message}`));
|
|
94
|
+
break;
|
|
95
|
+
case 'warning':
|
|
96
|
+
console.warn(chalk.yellow(`${prefix} ā ļø ${message}`));
|
|
97
|
+
break;
|
|
98
|
+
case 'success':
|
|
99
|
+
console.log(chalk.green(`${prefix} ā
${message}`));
|
|
100
|
+
break;
|
|
101
|
+
case 'phase':
|
|
102
|
+
console.log(chalk.cyan(`${prefix} š ${message}`));
|
|
103
|
+
break;
|
|
104
|
+
case 'step':
|
|
105
|
+
console.log(chalk.blue(`${prefix} š ${message}`));
|
|
106
|
+
break;
|
|
107
|
+
default:
|
|
108
|
+
if (this.options.verbose) {
|
|
109
|
+
console.log(chalk.gray(`${prefix} ā¹ļø ${message}`));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async run() {
|
|
115
|
+
this.log('Starting Clodo Service Deployment Testing', 'phase');
|
|
116
|
+
this.log(`Test Phase: ${this.options.testPhase}`, 'step');
|
|
117
|
+
this.log(`Service Path: ${this.options.servicePath}`, 'step');
|
|
118
|
+
this.log(`Dry Run: ${this.options.dryRun}`, 'step');
|
|
119
|
+
this.log('');
|
|
120
|
+
|
|
121
|
+
try {
|
|
122
|
+
// Phase 1: Information Collection
|
|
123
|
+
if (this.shouldRunPhase('collection')) {
|
|
124
|
+
await this.runInformationCollection();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Phase 2: Consolidation
|
|
128
|
+
if (this.shouldRunPhase('consolidation')) {
|
|
129
|
+
await this.runConsolidation();
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Phase 3: Execution
|
|
133
|
+
if (this.shouldRunPhase('execution')) {
|
|
134
|
+
await this.runExecution();
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Final Results
|
|
138
|
+
this.displayFinalResults();
|
|
139
|
+
|
|
140
|
+
} catch (error) {
|
|
141
|
+
this.log(`Test failed: ${error.message}`, 'error');
|
|
142
|
+
if (this.options.verbose) {
|
|
143
|
+
console.error(chalk.gray(error.stack));
|
|
144
|
+
}
|
|
145
|
+
this.testResults.overall.status = 'failed';
|
|
146
|
+
process.exit(1);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
shouldRunPhase(phase) {
|
|
151
|
+
return this.options.testPhase === 'all' || this.options.testPhase === phase;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async runInformationCollection() {
|
|
155
|
+
const phaseStart = Date.now();
|
|
156
|
+
this.log('Phase 1: Information Collection', 'phase');
|
|
157
|
+
this.testResults.phases.collection.status = 'running';
|
|
158
|
+
|
|
159
|
+
try {
|
|
160
|
+
// Step 1: Service Detection
|
|
161
|
+
this.log('Step 1: Detecting service project...', 'step');
|
|
162
|
+
const manifestPath = join(this.options.servicePath, 'clodo-service-manifest.json');
|
|
163
|
+
|
|
164
|
+
let manifest;
|
|
165
|
+
if (existsSync(manifestPath)) {
|
|
166
|
+
this.log('Found existing service manifest', 'success');
|
|
167
|
+
manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
|
|
168
|
+
} else {
|
|
169
|
+
this.log('No manifest found, creating mock service for testing', 'warning');
|
|
170
|
+
// Create mock manifest for testing
|
|
171
|
+
writeFileSync(manifestPath, JSON.stringify(MOCK_MANIFEST, null, 2));
|
|
172
|
+
manifest = MOCK_MANIFEST;
|
|
173
|
+
this.log('Created mock service manifest', 'success');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Step 2: Parse Service Information
|
|
177
|
+
this.log('Step 2: Parsing service information...', 'step');
|
|
178
|
+
this.serviceInfo = {
|
|
179
|
+
name: manifest.serviceName,
|
|
180
|
+
type: manifest.serviceType,
|
|
181
|
+
version: manifest.version,
|
|
182
|
+
domain: manifest.configuration?.domain || manifest.configuration?.domainName,
|
|
183
|
+
environment: manifest.configuration?.environment,
|
|
184
|
+
path: this.options.servicePath
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
// Validate required information
|
|
188
|
+
if (!this.serviceInfo.name) {
|
|
189
|
+
throw new Error('Service name not found in manifest');
|
|
190
|
+
}
|
|
191
|
+
if (!this.serviceInfo.domain) {
|
|
192
|
+
throw new Error('Domain not configured in manifest');
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
this.log(`Service Name: ${this.serviceInfo.name}`, 'step');
|
|
196
|
+
this.log(`Service Type: ${this.serviceInfo.type}`, 'step');
|
|
197
|
+
this.log(`Domain: ${this.serviceInfo.domain}`, 'step');
|
|
198
|
+
this.log(`Environment: ${this.serviceInfo.environment}`, 'step');
|
|
199
|
+
|
|
200
|
+
// Step 3: Validate Project Structure
|
|
201
|
+
this.log('Step 3: Validating project structure...', 'step');
|
|
202
|
+
const requiredFiles = ['package.json', 'wrangler.toml'];
|
|
203
|
+
const missingFiles = requiredFiles.filter(file => !existsSync(join(this.options.servicePath, file)));
|
|
204
|
+
|
|
205
|
+
if (missingFiles.length > 0) {
|
|
206
|
+
this.log(`Missing files: ${missingFiles.join(', ')}`, 'warning');
|
|
207
|
+
this.log('This may cause deployment issues in real scenarios', 'warning');
|
|
208
|
+
} else {
|
|
209
|
+
this.log('Project structure validation passed', 'success');
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
this.testResults.phases.collection.status = 'completed';
|
|
213
|
+
this.testResults.phases.collection.duration = Date.now() - phaseStart;
|
|
214
|
+
this.testResults.phases.collection.details = {
|
|
215
|
+
serviceInfo: this.serviceInfo,
|
|
216
|
+
manifestFound: existsSync(manifestPath),
|
|
217
|
+
missingFiles: missingFiles
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
this.log(`Information collection completed in ${this.testResults.phases.collection.duration}ms`, 'success');
|
|
221
|
+
this.log('');
|
|
222
|
+
|
|
223
|
+
} catch (error) {
|
|
224
|
+
this.testResults.phases.collection.status = 'failed';
|
|
225
|
+
this.testResults.phases.collection.details.error = error.message;
|
|
226
|
+
throw error;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
async runConsolidation() {
|
|
231
|
+
const phaseStart = Date.now();
|
|
232
|
+
this.log('Phase 2: Information Consolidation', 'phase');
|
|
233
|
+
this.testResults.phases.consolidation.status = 'running';
|
|
234
|
+
|
|
235
|
+
try {
|
|
236
|
+
// Step 1: Credential Gathering Strategy
|
|
237
|
+
this.log('Step 1: Analyzing credential gathering strategy...', 'step');
|
|
238
|
+
|
|
239
|
+
const credentialSources = {
|
|
240
|
+
flags: { token: false, accountId: false, zoneId: false },
|
|
241
|
+
environment: { token: false, accountId: false, zoneId: false },
|
|
242
|
+
mock: { token: false, accountId: false, zoneId: false }
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
// Check environment variables
|
|
246
|
+
credentialSources.environment.token = !!process.env.CLOUDFLARE_API_TOKEN;
|
|
247
|
+
credentialSources.environment.accountId = !!process.env.CLOUDFLARE_ACCOUNT_ID;
|
|
248
|
+
credentialSources.environment.zoneId = !!process.env.CLOUDFLARE_ZONE_ID;
|
|
249
|
+
|
|
250
|
+
// Use mock credentials if enabled
|
|
251
|
+
if (this.options.mockCredentials) {
|
|
252
|
+
this.credentials = { ...MOCK_CREDENTIALS };
|
|
253
|
+
credentialSources.mock = { token: true, accountId: true, zoneId: true };
|
|
254
|
+
this.log('Using mock credentials for testing', 'step');
|
|
255
|
+
} else {
|
|
256
|
+
// In real scenarios, would check command line flags here
|
|
257
|
+
this.log('Mock credentials disabled - would require real credentials', 'warning');
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
this.log('Credential Sources:', 'step');
|
|
261
|
+
this.log(` Environment Variables: ${credentialSources.environment.token && credentialSources.environment.accountId && credentialSources.environment.zoneId ? 'ā
' : 'ā'}`, 'step');
|
|
262
|
+
this.log(` Mock Credentials: ${credentialSources.mock.token ? 'ā
' : 'ā'}`, 'step');
|
|
263
|
+
|
|
264
|
+
// Step 2: Configuration Consolidation
|
|
265
|
+
this.log('Step 2: Consolidating deployment configuration...', 'step');
|
|
266
|
+
|
|
267
|
+
this.deploymentPlan = {
|
|
268
|
+
service: this.serviceInfo,
|
|
269
|
+
credentials: {
|
|
270
|
+
token: this.credentials.token ? `${this.credentials.token.substring(0, 8)}...` : 'NOT SET',
|
|
271
|
+
accountId: this.credentials.accountId ? `${this.credentials.accountId.substring(0, 8)}...` : 'NOT SET',
|
|
272
|
+
zoneId: this.credentials.zoneId ? `${this.credentials.zoneId.substring(0, 8)}...` : 'NOT SET'
|
|
273
|
+
},
|
|
274
|
+
deployment: {
|
|
275
|
+
domain: this.serviceInfo.domain,
|
|
276
|
+
environment: this.serviceInfo.environment,
|
|
277
|
+
dryRun: this.options.dryRun,
|
|
278
|
+
type: 'simulated'
|
|
279
|
+
},
|
|
280
|
+
metadata: {
|
|
281
|
+
deploymentId: `test-${Date.now()}`,
|
|
282
|
+
timestamp: new Date().toISOString(),
|
|
283
|
+
tester: 'clodo-deployment-tester'
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
// Step 3: Validation Checks
|
|
288
|
+
this.log('Step 3: Running validation checks...', 'step');
|
|
289
|
+
|
|
290
|
+
const validations = {
|
|
291
|
+
serviceInfo: !!this.serviceInfo.name && !!this.serviceInfo.domain,
|
|
292
|
+
credentials: !!(this.credentials.token && this.credentials.accountId && this.credentials.zoneId),
|
|
293
|
+
configuration: !!this.deploymentPlan.deployment.domain,
|
|
294
|
+
environment: true // Mock validation
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
const failedValidations = Object.entries(validations)
|
|
298
|
+
.filter(([key, passed]) => !passed)
|
|
299
|
+
.map(([key]) => key);
|
|
300
|
+
|
|
301
|
+
if (failedValidations.length > 0) {
|
|
302
|
+
throw new Error(`Validation failed for: ${failedValidations.join(', ')}`);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
this.log('All validations passed', 'success');
|
|
306
|
+
|
|
307
|
+
// Display consolidated information
|
|
308
|
+
this.log('Consolidated Deployment Plan:', 'step');
|
|
309
|
+
this.log(` Service: ${this.deploymentPlan.service.name} (${this.deploymentPlan.service.type})`, 'step');
|
|
310
|
+
this.log(` Domain: ${this.deploymentPlan.deployment.domain}`, 'step');
|
|
311
|
+
this.log(` Environment: ${this.deploymentPlan.deployment.environment}`, 'step');
|
|
312
|
+
this.log(` Dry Run: ${this.deploymentPlan.deployment.dryRun}`, 'step');
|
|
313
|
+
this.log(` Credentials: ${this.deploymentPlan.credentials.token !== 'NOT SET' ? 'ā
Configured' : 'ā Missing'}`, 'step');
|
|
314
|
+
|
|
315
|
+
this.testResults.phases.consolidation.status = 'completed';
|
|
316
|
+
this.testResults.phases.consolidation.duration = Date.now() - phaseStart;
|
|
317
|
+
this.testResults.phases.consolidation.details = {
|
|
318
|
+
credentialSources,
|
|
319
|
+
deploymentPlan: this.deploymentPlan,
|
|
320
|
+
validations
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
this.log(`Information consolidation completed in ${this.testResults.phases.consolidation.duration}ms`, 'success');
|
|
324
|
+
this.log('');
|
|
325
|
+
|
|
326
|
+
} catch (error) {
|
|
327
|
+
this.testResults.phases.consolidation.status = 'failed';
|
|
328
|
+
this.testResults.phases.consolidation.details.error = error.message;
|
|
329
|
+
throw error;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
async runExecution() {
|
|
334
|
+
const phaseStart = Date.now();
|
|
335
|
+
this.log('Phase 3: Deployment Execution Simulation', 'phase');
|
|
336
|
+
this.testResults.phases.execution.status = 'running';
|
|
337
|
+
|
|
338
|
+
try {
|
|
339
|
+
// Step 1: Pre-deployment Checks
|
|
340
|
+
this.log('Step 1: Running pre-deployment checks...', 'step');
|
|
341
|
+
|
|
342
|
+
const preChecks = {
|
|
343
|
+
dryRunMode: this.options.dryRun,
|
|
344
|
+
credentialsValid: !!(this.credentials.token && this.credentials.accountId && this.credentials.zoneId),
|
|
345
|
+
serviceReady: !!this.serviceInfo.name,
|
|
346
|
+
domainConfigured: !!this.serviceInfo.domain
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
this.log('Pre-deployment Status:', 'step');
|
|
350
|
+
Object.entries(preChecks).forEach(([check, status]) => {
|
|
351
|
+
this.log(` ${check}: ${status ? 'ā
' : 'ā'}`, 'step');
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
// Step 2: Simulate Deployment Phases
|
|
355
|
+
this.log('Step 2: Simulating deployment phases...', 'step');
|
|
356
|
+
|
|
357
|
+
const deploymentPhases = [
|
|
358
|
+
{ name: 'Initialization', duration: 500, status: 'success' },
|
|
359
|
+
{ name: 'Configuration Validation', duration: 300, status: 'success' },
|
|
360
|
+
{ name: 'Environment Setup', duration: 800, status: 'success' },
|
|
361
|
+
{ name: 'Cloudflare API Calls', duration: 1200, status: 'success' },
|
|
362
|
+
{ name: 'Resource Deployment', duration: 1500, status: 'success' },
|
|
363
|
+
{ name: 'DNS Configuration', duration: 600, status: 'success' },
|
|
364
|
+
{ name: 'Health Checks', duration: 400, status: 'success' },
|
|
365
|
+
{ name: 'Final Validation', duration: 300, status: 'success' }
|
|
366
|
+
];
|
|
367
|
+
|
|
368
|
+
for (const phase of deploymentPhases) {
|
|
369
|
+
this.log(` Executing: ${phase.name}...`, 'step');
|
|
370
|
+
await this.delay(phase.duration);
|
|
371
|
+
|
|
372
|
+
if (phase.status === 'success') {
|
|
373
|
+
this.log(` ā
${phase.name} completed`, 'success');
|
|
374
|
+
} else {
|
|
375
|
+
this.log(` ā ${phase.name} failed`, 'error');
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// Step 3: Generate Mock Results
|
|
380
|
+
this.log('Step 3: Generating deployment results...', 'step');
|
|
381
|
+
|
|
382
|
+
const mockResults = {
|
|
383
|
+
success: true,
|
|
384
|
+
deploymentId: this.deploymentPlan.metadata.deploymentId,
|
|
385
|
+
url: `https://${this.serviceInfo.domain}`,
|
|
386
|
+
workerId: `worker-${Math.random().toString(36).substr(2, 9)}`,
|
|
387
|
+
status: 'Deployed successfully',
|
|
388
|
+
timestamp: new Date().toISOString(),
|
|
389
|
+
details: {
|
|
390
|
+
service: this.serviceInfo.name,
|
|
391
|
+
type: this.serviceInfo.type,
|
|
392
|
+
domain: this.serviceInfo.domain,
|
|
393
|
+
environment: this.serviceInfo.environment,
|
|
394
|
+
dryRun: this.options.dryRun
|
|
395
|
+
}
|
|
396
|
+
};
|
|
397
|
+
|
|
398
|
+
// Step 4: Post-deployment Validation
|
|
399
|
+
this.log('Step 4: Running post-deployment validation...', 'step');
|
|
400
|
+
|
|
401
|
+
const postChecks = {
|
|
402
|
+
urlAccessible: true, // Mock
|
|
403
|
+
workerRunning: true, // Mock
|
|
404
|
+
dnsConfigured: true, // Mock
|
|
405
|
+
healthChecks: true // Mock
|
|
406
|
+
};
|
|
407
|
+
|
|
408
|
+
this.log('Post-deployment Status:', 'step');
|
|
409
|
+
Object.entries(postChecks).forEach(([check, status]) => {
|
|
410
|
+
this.log(` ${check}: ${status ? 'ā
' : 'ā'}`, 'step');
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
this.testResults.phases.execution.status = 'completed';
|
|
414
|
+
this.testResults.phases.execution.duration = Date.now() - phaseStart;
|
|
415
|
+
this.testResults.phases.execution.details = {
|
|
416
|
+
phases: deploymentPhases,
|
|
417
|
+
results: mockResults,
|
|
418
|
+
postChecks
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
this.log(`Deployment execution simulation completed in ${this.testResults.phases.execution.duration}ms`, 'success');
|
|
422
|
+
this.log('');
|
|
423
|
+
|
|
424
|
+
} catch (error) {
|
|
425
|
+
this.testResults.phases.execution.status = 'failed';
|
|
426
|
+
this.testResults.phases.execution.details.error = error.message;
|
|
427
|
+
throw error;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
displayFinalResults() {
|
|
432
|
+
this.log('š Clodo Service Deployment Testing Complete', 'phase');
|
|
433
|
+
this.log('');
|
|
434
|
+
|
|
435
|
+
// Overall Summary
|
|
436
|
+
const totalDuration = Date.now() - this.testResults.overall.startTime;
|
|
437
|
+
this.testResults.overall.status = 'completed';
|
|
438
|
+
|
|
439
|
+
this.log('š Test Summary:', 'step');
|
|
440
|
+
this.log(` Total Duration: ${totalDuration}ms`, 'step');
|
|
441
|
+
this.log(` Phases Tested: ${this.options.testPhase}`, 'step');
|
|
442
|
+
this.log(` Dry Run: ${this.options.dryRun}`, 'step');
|
|
443
|
+
this.log('');
|
|
444
|
+
|
|
445
|
+
// Phase Results
|
|
446
|
+
this.log('š Phase Results:', 'step');
|
|
447
|
+
Object.entries(this.testResults.phases).forEach(([phase, result]) => {
|
|
448
|
+
if (result.status !== 'pending') {
|
|
449
|
+
const statusIcon = result.status === 'completed' ? 'ā
' : 'ā';
|
|
450
|
+
const duration = result.duration > 0 ? ` (${result.duration}ms)` : '';
|
|
451
|
+
this.log(` ${phase}: ${statusIcon} ${result.status}${duration}`, 'step');
|
|
452
|
+
}
|
|
453
|
+
});
|
|
454
|
+
this.log('');
|
|
455
|
+
|
|
456
|
+
// Service Information
|
|
457
|
+
if (Object.keys(this.serviceInfo).length > 0) {
|
|
458
|
+
this.log('šļø Service Information:', 'step');
|
|
459
|
+
this.log(` Name: ${this.serviceInfo.name}`, 'step');
|
|
460
|
+
this.log(` Type: ${this.serviceInfo.type}`, 'step');
|
|
461
|
+
this.log(` Domain: ${this.serviceInfo.domain}`, 'step');
|
|
462
|
+
this.log(` Environment: ${this.serviceInfo.environment}`, 'step');
|
|
463
|
+
this.log('');
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// Deployment Plan
|
|
467
|
+
if (Object.keys(this.deploymentPlan).length > 0) {
|
|
468
|
+
this.log('š Deployment Plan:', 'step');
|
|
469
|
+
this.log(` Service: ${this.deploymentPlan.service?.name} (${this.deploymentPlan.service?.type})`, 'step');
|
|
470
|
+
this.log(` Domain: ${this.deploymentPlan.deployment?.domain}`, 'step');
|
|
471
|
+
this.log(` Environment: ${this.deploymentPlan.deployment?.environment}`, 'step');
|
|
472
|
+
this.log(` Dry Run: ${this.deploymentPlan.deployment?.dryRun}`, 'step');
|
|
473
|
+
this.log(` Credentials: ${this.deploymentPlan.credentials?.token !== 'NOT SET' ? 'Configured' : 'Missing'}`, 'step');
|
|
474
|
+
this.log('');
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// Recommendations
|
|
478
|
+
this.log('š” Recommendations:', 'step');
|
|
479
|
+
if (this.options.dryRun) {
|
|
480
|
+
this.log(' ⢠Test completed in dry-run mode - safe for development', 'step');
|
|
481
|
+
}
|
|
482
|
+
if (this.options.mockCredentials) {
|
|
483
|
+
this.log(' ⢠Mock credentials used - test real deployment with actual credentials', 'step');
|
|
484
|
+
}
|
|
485
|
+
this.log(' ⢠Review test output for any warnings or validation issues', 'step');
|
|
486
|
+
this.log(' ⢠Use --verbose flag for detailed logging in future tests', 'step');
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
delay(ms) {
|
|
490
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// CLI Interface
|
|
495
|
+
function parseArgs() {
|
|
496
|
+
const args = process.argv.slice(2);
|
|
497
|
+
const options = {};
|
|
498
|
+
|
|
499
|
+
for (let i = 0; i < args.length; i++) {
|
|
500
|
+
const arg = args[i];
|
|
501
|
+
switch (arg) {
|
|
502
|
+
case '--service-path':
|
|
503
|
+
options.servicePath = args[++i];
|
|
504
|
+
break;
|
|
505
|
+
case '--dry-run':
|
|
506
|
+
options.dryRun = true;
|
|
507
|
+
break;
|
|
508
|
+
case '--no-dry-run':
|
|
509
|
+
options.dryRun = false;
|
|
510
|
+
break;
|
|
511
|
+
case '--verbose':
|
|
512
|
+
options.verbose = true;
|
|
513
|
+
break;
|
|
514
|
+
case '--mock-credentials':
|
|
515
|
+
options.mockCredentials = true;
|
|
516
|
+
break;
|
|
517
|
+
case '--no-mock-credentials':
|
|
518
|
+
options.mockCredentials = false;
|
|
519
|
+
break;
|
|
520
|
+
case '--test-phase':
|
|
521
|
+
options.testPhase = args[++i];
|
|
522
|
+
break;
|
|
523
|
+
case '--help':
|
|
524
|
+
showHelp();
|
|
525
|
+
process.exit(0);
|
|
526
|
+
break;
|
|
527
|
+
default:
|
|
528
|
+
if (arg.startsWith('--')) {
|
|
529
|
+
console.error(`Unknown option: ${arg}`);
|
|
530
|
+
showHelp();
|
|
531
|
+
process.exit(1);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
return options;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
function showHelp() {
|
|
540
|
+
console.log(`
|
|
541
|
+
Clodo Service Deployment Testing Script
|
|
542
|
+
|
|
543
|
+
Usage:
|
|
544
|
+
node test-clodo-deployment.js [options]
|
|
545
|
+
|
|
546
|
+
Options:
|
|
547
|
+
--service-path <path> Path to service directory (default: current dir)
|
|
548
|
+
--dry-run Simulate deployment without changes (default: true)
|
|
549
|
+
--no-dry-run Actually perform deployment (use with caution)
|
|
550
|
+
--verbose Detailed logging output
|
|
551
|
+
--mock-credentials Use mock credentials instead of prompting (default: true)
|
|
552
|
+
--no-mock-credentials Require real credentials
|
|
553
|
+
--test-phase <phase> Test specific phase: all, collection, consolidation, execution
|
|
554
|
+
--help Show this help message
|
|
555
|
+
|
|
556
|
+
Examples:
|
|
557
|
+
# Test all phases with mock data
|
|
558
|
+
node test-clodo-deployment.js
|
|
559
|
+
|
|
560
|
+
# Test only information collection
|
|
561
|
+
node test-clodo-deployment.js --test-phase collection --verbose
|
|
562
|
+
|
|
563
|
+
# Test with real credentials (dangerous!)
|
|
564
|
+
node test-clodo-deployment.js --no-mock-credentials --no-dry-run
|
|
565
|
+
`);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// Main execution
|
|
569
|
+
async function main() {
|
|
570
|
+
try {
|
|
571
|
+
const options = parseArgs();
|
|
572
|
+
const tester = new ClodoDeploymentTester(options);
|
|
573
|
+
await tester.run();
|
|
574
|
+
} catch (error) {
|
|
575
|
+
console.error(chalk.red(`\nā Test script failed: ${error.message}`));
|
|
576
|
+
if (process.env.DEBUG) {
|
|
577
|
+
console.error(chalk.gray(error.stack));
|
|
578
|
+
}
|
|
579
|
+
process.exit(1);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
// Run if called directly
|
|
584
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
585
|
+
main();
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
export { ClodoDeploymentTester };
|