UNPKG

@re-shell/cli

Version:

Full-stack development platform uniting microservices and microfrontends. Build complete applications with .NET (ASP.NET Core Web API, Minimal API), Java (Spring Boot, Quarkus, Micronaut, Vert.x), Rust (Actix-Web, Warp, Rocket, Axum), Python (FastAPI, Dja

616 lines (615 loc) 22.5 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.CrossPlatformTesting = void 0; exports.createPlatformTest = createPlatformTest; exports.createPlatformTarget = createPlatformTarget; exports.runCrossPlatformTests = runCrossPlatformTests; const events_1 = require("events"); const fs = __importStar(require("fs-extra")); const path = __importStar(require("path")); const os = __importStar(require("os")); const child_process_1 = require("child_process"); class CrossPlatformTesting extends events_1.EventEmitter { constructor(config) { super(); this.results = []; this.config = { dockerEnabled: false, vmEnabled: false, ciEnabled: false, localOnly: true, parallel: false, generateReport: true, failFast: false, ...config }; this.currentPlatform = this.detectCurrentPlatform(); this.dockerConfig = this.initializeDockerConfig(); } async run() { this.emit('test:start', { platforms: this.config.platforms.length, tests: this.config.tests.length }); const startTime = Date.now(); try { // Filter enabled platforms const platforms = this.getEnabledPlatforms(); // Run tests on each platform if (this.config.parallel && platforms.length > 1) { await this.runParallel(platforms); } else { await this.runSequential(platforms); } // Generate report const report = this.generateReport(Date.now() - startTime); if (this.config.generateReport) { await this.saveReport(report); } this.emit('test:complete', report); return report; } catch (error) { this.emit('test:error', error); throw error; } } async runSequential(platforms) { for (const platform of platforms) { if (this.config.failFast && this.hasFailures()) { break; } await this.runPlatformTests(platform); } } async runParallel(platforms) { const promises = platforms.map(platform => this.runPlatformTests(platform)); await Promise.all(promises); } async runPlatformTests(platform) { this.emit('platform:start', platform); const platformInfo = await this.getPlatformInfo(platform); for (const test of this.config.tests) { // Check if test should run on this platform if (!this.shouldRunTest(test, platform)) { continue; } const result = await this.runTest(test, platform, platformInfo); this.results.push(result); if (this.config.failFast && !result.success) { throw new Error(`Test failed on ${platform.os}: ${test.name}`); } } this.emit('platform:complete', platform); } async runTest(test, platform, platformInfo) { this.emit('test:run', { test: test.name, platform: platform.os }); const result = { platform: platformInfo, test: test.name, success: false, duration: 0 }; const startTime = Date.now(); try { // Execute test based on platform environment let output; if (this.isCurrentPlatform(platform)) { output = await this.runLocalTest(test); } else if (this.config.dockerEnabled && platform.dockerImage) { output = await this.runDockerTest(test, platform); } else if (this.config.vmEnabled && platform.vmImage) { output = await this.runVMTest(test, platform); } else if (this.config.ciEnabled) { output = await this.runCITest(test, platform); } else { throw new Error(`Cannot test on ${platform.os} - no execution environment available`); } result.output = output; // Check expected output if (test.expectedOutput) { if (typeof test.expectedOutput === 'string') { result.success = output.includes(test.expectedOutput); } else { result.success = test.expectedOutput.test(output); } } else { result.success = true; } // Run validators if (test.validators) { result.validators = await this.runValidators(test.validators, platform); result.success = result.success && result.validators.every(v => v.success); } } catch (error) { result.error = error.message; result.success = false; // Check if error is expected if (test.expectedExitCode !== undefined && error.status === test.expectedExitCode) { result.success = true; } } result.duration = Date.now() - startTime; this.emit('test:result', result); return result; } async runLocalTest(test) { const command = test.command; const args = test.args?.join(' ') || ''; const fullCommand = `${command} ${args}`.trim(); const options = { cwd: test.cwd || process.cwd(), env: { ...process.env, ...test.env }, timeout: test.timeout || 30000, encoding: 'utf-8', shell: true }; return (0, child_process_1.execSync)(fullCommand, options); } async runDockerTest(test, platform) { const image = platform.dockerImage; const command = test.command; const args = test.args || []; // Build docker command const dockerArgs = [ 'run', '--rm', '-v', `${process.cwd()}:/workspace`, '-w', '/workspace' ]; // Add environment variables if (test.env) { Object.entries(test.env).forEach(([key, value]) => { dockerArgs.push('-e', `${key}=${value}`); }); } dockerArgs.push(image, command, ...args); const result = (0, child_process_1.execSync)(`docker ${dockerArgs.join(' ')}`, { encoding: 'utf-8', timeout: test.timeout || 60000 }); return result; } async runVMTest(test, platform) { // VM test implementation would use vagrant or similar throw new Error('VM testing not implemented'); } async runCITest(test, platform) { // CI test implementation would use GitHub Actions or similar throw new Error('CI testing not implemented'); } async runValidators(validators, platform) { const results = []; for (const validator of validators) { // Skip if validator is platform-specific if (validator.platform && validator.platform !== platform.os) { continue; } const result = await this.runValidator(validator, platform); results.push(result); } return results; } async runValidator(validator, platform) { const result = { validator: `${validator.type}:${validator.target}`, success: false }; try { switch (validator.type) { case 'file': await this.validateFile(validator, result); break; case 'directory': await this.validateDirectory(validator, result); break; case 'command': await this.validateCommand(validator, result); break; case 'custom': await this.validateCustom(validator, result); break; } } catch (error) { result.error = error.message; } return result; } async validateFile(validator, result) { const exists = await fs.pathExists(validator.target); switch (validator.condition) { case 'exists': result.success = exists; result.actual = exists; result.expected = true; break; case 'contains': if (exists) { const content = await fs.readFile(validator.target, 'utf-8'); result.success = content.includes(validator.value); result.actual = content.substring(0, 100); result.expected = `contains "${validator.value}"`; } break; case 'matches': if (exists) { const content = await fs.readFile(validator.target, 'utf-8'); const regex = new RegExp(validator.value); result.success = regex.test(content); result.actual = content.substring(0, 100); result.expected = `matches ${regex}`; } break; } } async validateDirectory(validator, result) { const exists = await fs.pathExists(validator.target); switch (validator.condition) { case 'exists': result.success = exists; result.actual = exists; result.expected = true; break; case 'contains': if (exists) { const files = await fs.readdir(validator.target); result.success = files.includes(validator.value); result.actual = files; result.expected = `contains "${validator.value}"`; } break; } } async validateCommand(validator, result) { try { const output = (0, child_process_1.execSync)(validator.target, { encoding: 'utf-8' }); result.success = true; result.actual = output.trim(); } catch (error) { result.success = false; result.error = error.message; } } async validateCustom(validator, result) { // Custom validation would be implemented by extending this class result.success = true; } detectCurrentPlatform() { return { os: os.platform(), arch: os.arch(), version: os.release(), nodeVersion: process.version, npmVersion: this.getNpmVersion(), environment: 'local' }; } getNpmVersion() { try { return (0, child_process_1.execSync)('npm --version', { encoding: 'utf-8' }).trim(); } catch { return 'unknown'; } } initializeDockerConfig() { return { baseImages: new Map([ ['win32', 'mcr.microsoft.com/windows/servercore:ltsc2022'], ['darwin', 'sickcodes/docker-osx:latest'], ['linux', 'node:18-alpine'] ]), buildCache: true, cleanup: true, network: 'bridge' }; } getEnabledPlatforms() { return this.config.platforms.filter(p => p.enabled !== false); } isCurrentPlatform(platform) { return platform.os === this.currentPlatform.os && (!platform.arch || platform.arch === this.currentPlatform.arch); } shouldRunTest(test, platform) { // Check if test is platform-specific if (test.platforms && !test.platforms.includes(platform.os)) { return false; } // Check if test should be skipped on this platform if (test.skip && test.skip.includes(platform.os)) { return false; } return true; } hasFailures() { return this.results.some(r => !r.success); } async getPlatformInfo(platform) { let environment = 'local'; if (!this.isCurrentPlatform(platform)) { if (this.config.dockerEnabled && platform.dockerImage) { environment = 'docker'; } else if (this.config.vmEnabled && platform.vmImage) { environment = 'vm'; } else if (this.config.ciEnabled) { environment = 'ci'; } } return { os: platform.os, arch: platform.arch || 'x64', version: platform.version || 'latest', nodeVersion: platform.nodeVersion || process.version, environment }; } generateReport(duration) { const platforms = this.getUniquePlatforms(); const tests = this.getUniqueTests(); const matrix = this.buildCompatibilityMatrix(tests, platforms); const issues = this.identifyIssues(); const recommendations = this.generateRecommendations(issues); const summary = { totalTests: tests.length, totalPlatforms: platforms.length, passed: this.results.filter(r => r.success).length, failed: this.results.filter(r => !r.success).length, skipped: 0, duration: duration / 1000, compatibility: this.calculateCompatibility(matrix) }; return { summary, platforms, results: this.results, compatibility: matrix, issues, recommendations, timestamp: new Date() }; } getUniquePlatforms() { const platforms = new Map(); for (const result of this.results) { const key = `${result.platform.os}-${result.platform.arch}`; platforms.set(key, result.platform); } return Array.from(platforms.values()); } getUniqueTests() { return [...new Set(this.results.map(r => r.test))]; } buildCompatibilityMatrix(tests, platforms) { const matrix = []; for (const test of tests) { const row = []; for (const platform of platforms) { const result = this.results.find(r => r.test === test && r.platform.os === platform.os && r.platform.arch === platform.arch); row.push(result?.success || false); } matrix.push(row); } return { tests, platforms: platforms.map(p => `${p.os}-${p.arch}`), results: matrix }; } identifyIssues() { const issues = []; for (const result of this.results) { if (!result.success) { issues.push({ test: result.test, platform: `${result.platform.os}-${result.platform.arch}`, type: 'failure', description: result.error || 'Test failed', suggestion: this.getSuggestion(result) }); } // Check for platform-specific warnings if (result.validators) { for (const validator of result.validators) { if (!validator.success && validator.error?.includes('warning')) { issues.push({ test: result.test, platform: `${result.platform.os}-${result.platform.arch}`, type: 'warning', description: validator.error }); } } } } return issues; } getSuggestion(result) { const error = result.error?.toLowerCase() || ''; if (error.includes('permission denied')) { return 'Check file permissions or run with elevated privileges'; } if (error.includes('command not found')) { return 'Ensure required dependencies are installed'; } if (error.includes('timeout')) { return 'Increase timeout or optimize test performance'; } if (result.platform.os === 'win32' && error.includes('path')) { return 'Use cross-platform path handling (path.join)'; } return undefined; } calculateCompatibility(matrix) { let total = 0; let passed = 0; for (const row of matrix.results) { for (const result of row) { total++; if (result) passed++; } } return total > 0 ? (passed / total) * 100 : 0; } generateRecommendations(issues) { const recommendations = []; // Platform-specific issues const platformIssues = new Map(); for (const issue of issues) { const count = platformIssues.get(issue.platform) || 0; platformIssues.set(issue.platform, count + 1); } for (const [platform, count] of platformIssues) { if (count > 3) { recommendations.push(`Consider platform-specific implementation for ${platform}`); } } // Common issues const pathIssues = issues.filter(i => i.description.toLowerCase().includes('path')); if (pathIssues.length > 0) { recommendations.push('Use path.join() and path.resolve() for cross-platform paths'); } const permissionIssues = issues.filter(i => i.description.toLowerCase().includes('permission')); if (permissionIssues.length > 0) { recommendations.push('Review file permissions and consider platform-specific handling'); } return recommendations; } async saveReport(report) { const reportPath = path.join(process.cwd(), 'cross-platform-report.json'); await fs.writeJson(reportPath, report, { spaces: 2 }); // Also save a markdown report const mdPath = reportPath.replace('.json', '.md'); await fs.writeFile(mdPath, this.formatMarkdownReport(report)); this.emit('report:saved', { json: reportPath, md: mdPath }); } formatMarkdownReport(report) { const lines = [ '# Cross-Platform Compatibility Report', '', `**Date:** ${report.timestamp.toISOString()}`, `**Compatibility:** ${report.summary.compatibility.toFixed(1)}%`, '', '## Summary', '', `- **Total Tests:** ${report.summary.totalTests}`, `- **Total Platforms:** ${report.summary.totalPlatforms}`, `- **Passed:** ${report.summary.passed}`, `- **Failed:** ${report.summary.failed}`, '', '## Compatibility Matrix', '', this.formatMatrix(report.compatibility), '', '## Issues', '' ]; if (report.issues.length > 0) { for (const issue of report.issues) { lines.push(`### ${issue.test} on ${issue.platform}`); lines.push(`- **Type:** ${issue.type}`); lines.push(`- **Description:** ${issue.description}`); if (issue.suggestion) { lines.push(`- **Suggestion:** ${issue.suggestion}`); } lines.push(''); } } else { lines.push('No compatibility issues found! 🎉'); lines.push(''); } if (report.recommendations.length > 0) { lines.push('## Recommendations'); lines.push(''); for (const rec of report.recommendations) { lines.push(`- ${rec}`); } } return lines.join('\n'); } formatMatrix(matrix) { const lines = []; // Header lines.push(`| Test | ${matrix.platforms.join(' | ')} |`); lines.push(`|------|${matrix.platforms.map(() => '------').join('|')}|`); // Rows for (let i = 0; i < matrix.tests.length; i++) { const cells = [matrix.tests[i]]; for (let j = 0; j < matrix.platforms.length; j++) { cells.push(matrix.results[i][j] ? '✅' : '❌'); } lines.push(`| ${cells.join(' | ')} |`); } return lines.join('\n'); } } exports.CrossPlatformTesting = CrossPlatformTesting; // Export utility functions function createPlatformTest(name, command, options) { return { name, command, ...options }; } function createPlatformTarget(os, options) { return { os, enabled: true, ...options }; } async function runCrossPlatformTests(config) { const tester = new CrossPlatformTesting(config); return tester.run(); }