@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
JavaScript
;
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();
}