/**
* 跨平台兼容性测试套件
* 验证MCP工具在不同平台上的功能正确性和性能一致性
*/
const fs = require('fs').promises;
const path = require('path');
const os = require('os');
const { exec } = require('child_process');
const util = require('util');
const execAsync = util.promisify(exec);
// 导入要测试的模块
const platformUtils = require('../lib/platformUtils');
const { resolveUserPath, assertInHome, validatePathSafety } = require('../lib/pathUtils');
const { evaluate: evaluateCommand } = require('../lib/commandPolicy');
const { CrossPlatformPermissionManager } = require('../lib/crossPlatformPermissions');
/**
* 测试结果收集器
*/
class TestResultCollector {
constructor() {
this.results = [];
this.summary = {
total: 0,
passed: 0,
failed: 0,
skipped: 0,
startTime: null,
endTime: null
};
}
addResult(testName, category, status, details = {}) {
const result = {
testName,
category,
status, // 'passed', 'failed', 'skipped'
details,
timestamp: new Date().toISOString(),
platform: platformUtils.getPlatformInfo().platform
};
this.results.push(result);
this.summary.total++;
this.summary[status]++;
return result;
}
start() {
this.summary.startTime = Date.now();
}
finish() {
this.summary.endTime = Date.now();
this.summary.duration = this.summary.endTime - this.summary.startTime;
}
getReport() {
return {
summary: this.summary,
results: this.results,
platform: platformUtils.getPlatformInfo()
};
}
}
/**
* 跨平台测试套件
*/
class CrossPlatformTestSuite {
constructor() {
this.collector = new TestResultCollector();
this.tempDir = null;
this.testFiles = [];
}
/**
* 运行完整测试套件
*/
async runAllTests() {
console.log('开始跨平台兼容性测试...');
this.collector.start();
try {
// 设置测试环境
await this.setupTestEnvironment();
// 运行各个测试模块
await this.testPlatformDetection();
await this.testPathUtils();
await this.testCommandPolicy();
await this.testFilePermissions();
await this.testFileWatch();
// Linux特定测试
if (platformUtils.isLinux) {
await this.testSudoConfig();
}
// 性能测试
await this.performanceTests();
} finally {
// 清理测试环境
await this.cleanupTestEnvironment();
this.collector.finish();
}
return this.collector.getReport();
}
/**
* 设置测试环境
*/
async setupTestEnvironment() {
try {
this.tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'mcp-test-'));
console.log(`测试目录: ${this.tempDir}`);
// 创建测试文件和目录结构
const testStructure = [
'test-file.txt',
'test-dir/nested-file.txt',
'test-dir/sub-dir/deep-file.txt',
'special-chars-文件.txt'
];
for (const item of testStructure) {
const fullPath = path.join(this.tempDir, item);
await fs.mkdir(path.dirname(fullPath), { recursive: true });
if (path.extname(item)) {
await fs.writeFile(fullPath, `Test content for ${item}`);
this.testFiles.push(fullPath);
}
}
this.collector.addResult('环境设置', '基础设施', 'passed', {
tempDir: this.tempDir,
filesCreated: this.testFiles.length
});
} catch (error) {
this.collector.addResult('环境设置', '基础设施', 'failed', {
error: error.message
});
throw error;
}
}
/**
* 清理测试环境
*/
async cleanupTestEnvironment() {
if (this.tempDir) {
try {
await fs.rm(this.tempDir, { recursive: true, force: true });
this.collector.addResult('环境清理', '基础设施', 'passed');
} catch (error) {
this.collector.addResult('环境清理', '基础设施', 'failed', {
error: error.message
});
}
}
}
/**
* 测试平台检测功能
*/
async testPlatformDetection() {
try {
const platformInfo = platformUtils.getPlatformInfo();
// 基本信息测试
const hasRequiredFields = [
'platform', 'isWindows', 'isMacOS', 'isLinux', 'isUnix'
].every(field => platformInfo.hasOwnProperty(field));
this.collector.addResult('平台信息检测', '平台检测',
hasRequiredFields ? 'passed' : 'failed',
{ platformInfo }
);
// 路径配置测试
const pathConfig = platformUtils.getPathConfig();
const hasPathConfig = pathConfig.separator && pathConfig.maxLength;
this.collector.addResult('路径配置检测', '平台检测',
hasPathConfig ? 'passed' : 'failed',
{ pathConfig }
);
// 递归监控支持测试
const recursiveSupport = platformUtils.supportsRecursiveWatch();
const expectedSupport = platformUtils.isWindows || platformUtils.isMacOS;
this.collector.addResult('递归监控支持检测', '平台检测',
recursiveSupport === expectedSupport ? 'passed' : 'failed',
{ recursiveSupport, expectedSupport }
);
} catch (error) {
this.collector.addResult('平台检测模块', '平台检测', 'failed', {
error: error.message
});
}
}
/**
* 测试路径处理功能
*/
async testPathUtils() {
try {
// 使用家目录下的测试路径
const homeDir = os.homedir();
const testPath = path.join(homeDir, 'test-file.txt');
const resolved = resolveUserPath('test-file.txt'); // 相对路径,应该解析到家目录
this.collector.addResult('基本路径解析', '路径处理',
path.isAbsolute(resolved) ? 'passed' : 'failed',
{ input: 'test-file.txt', output: resolved }
);
// 家目录验证测试
try {
assertInHome(resolved);
this.collector.addResult('家目录验证', '路径处理', 'passed');
} catch (error) {
this.collector.addResult('家目录验证', '路径处理', 'failed', {
error: error.message
});
}
// 路径安全验证测试
const safetyResult = validatePathSafety('test-file.txt'); // 相对路径
this.collector.addResult('路径安全验证', '路径处理',
safetyResult.safe ? 'passed' : 'failed',
safetyResult
);
// Windows特定测试
if (platformUtils.isWindows) {
await this.testWindowsPathHandling();
}
// Unix特定测试
if (platformUtils.isUnix) {
await this.testUnixPathHandling();
}
} catch (error) {
this.collector.addResult('路径处理模块', '路径处理', 'failed', {
error: error.message
});
}
}
/**
* Windows路径处理测试
*/
async testWindowsPathHandling() {
try {
// UNC路径检测测试
const uncPath = '\\\\server\\share\\file.txt';
const isUNC = platformUtils.isUNCPath(uncPath);
this.collector.addResult('UNC路径检测', 'Windows路径',
isUNC ? 'passed' : 'failed',
{ path: uncPath, detected: isUNC }
);
// 长路径检测测试
const longPath = '\\\\?\\C:\\very\\long\\path\\file.txt';
const isLong = platformUtils.isLongPath(longPath);
this.collector.addResult('长路径检测', 'Windows路径',
isLong ? 'passed' : 'failed',
{ path: longPath, detected: isLong }
);
} catch (error) {
this.collector.addResult('Windows路径处理', 'Windows路径', 'failed', {
error: error.message
});
}
}
/**
* Unix路径处理测试
*/
async testUnixPathHandling() {
try {
// 隐藏文件检测(在家目录下创建)
const homeDir = os.homedir();
const hiddenFile = path.join(homeDir, '.hidden-file-test');
await fs.writeFile(hiddenFile, 'hidden content');
const pathInfo = require('../lib/pathUtils').getPathInfo('.hidden-file-test');
const isHidden = pathInfo.features && pathInfo.features.isHidden;
this.collector.addResult('隐藏文件检测', 'Unix路径',
isHidden ? 'passed' : 'failed',
{ path: '.hidden-file-test', detected: isHidden }
);
// 清理测试文件
try {
await fs.unlink(hiddenFile);
} catch (error) {
// 忽略清理错误
}
} catch (error) {
this.collector.addResult('Unix路径处理', 'Unix路径', 'failed', {
error: error.message
});
}
}
/**
* 测试命令策略功能
*/
async testCommandPolicy() {
try {
// 安全命令测试
const safeCommand = 'ls -la';
const safeResult = await evaluateCommand(safeCommand);
this.collector.addResult('安全命令检测', '命令策略',
safeResult.level === 'allow' ? 'passed' : 'failed',
{ command: safeCommand, result: safeResult }
);
// 危险命令测试
const dangerousCommand = 'rm -rf /';
const dangerResult = await evaluateCommand(dangerousCommand);
this.collector.addResult('危险命令检测', '命令策略',
dangerResult.level === 'deny' || dangerResult.level === 'warn' ? 'passed' : 'failed',
{ command: dangerousCommand, result: dangerResult }
);
// 平台特定命令测试
if (platformUtils.isWindows) {
await this.testWindowsCommands();
} else {
await this.testUnixCommands();
}
} catch (error) {
this.collector.addResult('命令策略模块', '命令策略', 'failed', {
error: error.message
});
}
}
/**
* Windows命令测试
*/
async testWindowsCommands() {
const windowsCommands = [
{ cmd: 'del /f /s /q temp', expected: 'warn' },
{ cmd: 'format C:', expected: 'deny' },
{ cmd: 'dir', expected: 'allow' }
];
for (const test of windowsCommands) {
try {
const result = await evaluateCommand(test.cmd);
const passed = result.level === test.expected;
this.collector.addResult(`Windows命令: ${test.cmd}`, 'Windows命令',
passed ? 'passed' : 'failed',
{ command: test.cmd, expected: test.expected, actual: result.level }
);
} catch (error) {
this.collector.addResult(`Windows命令: ${test.cmd}`, 'Windows命令', 'failed', {
error: error.message
});
}
}
}
/**
* Unix命令测试
*/
async testUnixCommands() {
const unixCommands = [
{ cmd: 'sudo systemctl stop nginx', expected: 'warn' },
{ cmd: 'chmod 777 file.txt', expected: 'warn' },
{ cmd: 'cat file.txt', expected: 'allow' }
];
for (const test of unixCommands) {
try {
const result = await evaluateCommand(test.cmd);
const passed = result.level === test.expected;
this.collector.addResult(`Unix命令: ${test.cmd}`, 'Unix命令',
passed ? 'passed' : 'failed',
{ command: test.cmd, expected: test.expected, actual: result.level }
);
} catch (error) {
this.collector.addResult(`Unix命令: ${test.cmd}`, 'Unix命令', 'failed', {
error: error.message
});
}
}
}
/**
* 测试文件权限功能
*/
async testFilePermissions() {
try {
const permissionManager = new CrossPlatformPermissionManager();
const testFile = this.testFiles[0];
// 获取权限测试
const permissions = await permissionManager.getPermissions(testFile);
this.collector.addResult('权限读取', '文件权限',
permissions && permissions.platform ? 'passed' : 'failed',
{ file: testFile, permissions }
);
// 设置权限测试
if (platformUtils.isUnix) {
try {
await permissionManager.setPermissions(testFile, 0o644);
this.collector.addResult('Unix权限设置', '文件权限', 'passed');
} catch (error) {
this.collector.addResult('Unix权限设置', '文件权限', 'failed', {
error: error.message
});
}
}
if (platformUtils.isWindows) {
try {
await permissionManager.setPermissions(testFile, { readonly: false });
this.collector.addResult('Windows权限设置', '文件权限', 'passed');
} catch (error) {
this.collector.addResult('Windows权限设置', '文件权限', 'failed', {
error: error.message
});
}
}
} catch (error) {
this.collector.addResult('文件权限模块', '文件权限', 'failed', {
error: error.message
});
}
}
/**
* 测试文件监控功能
*/
async testFileWatch() {
try {
// 递归监控支持测试
const FileWatchTool = require('../tools/fileWatch');
const mockValidator = { isPathAllowed: () => true };
const watcher = new FileWatchTool(mockValidator);
const recursiveSupport = watcher.supportsRecursiveWatch();
const expectedSupport = platformUtils.supportsRecursiveWatch();
this.collector.addResult('文件监控递归支持', '文件监控',
recursiveSupport === expectedSupport ? 'passed' : 'failed',
{ recursiveSupport, expectedSupport }
);
// 基本监控功能测试
// 注意:这里只测试接口,不进行实际监控以避免测试阻塞
this.collector.addResult('文件监控接口', '文件监控', 'passed', {
note: '接口可用,实际监控功能需要集成测试验证'
});
} catch (error) {
this.collector.addResult('文件监控模块', '文件监控', 'failed', {
error: error.message
});
}
}
/**
* 测试Sudo配置功能(仅Linux)
*/
async testSudoConfig() {
try {
const SudoConfigTool = require('../tools/sudoConfig');
const mockValidator = { isPathAllowed: () => true };
const sudoTool = new SudoConfigTool(mockValidator);
// 状态检查测试
if (sudoTool.configManager) {
const status = await sudoTool.configManager.checkSudoStatus();
this.collector.addResult('Sudo状态检查', 'Sudo配置',
status.available !== undefined ? 'passed' : 'failed',
{ status }
);
// 配置生成测试
try {
const config = sudoTool.configManager.generateSudoersRules('testuser', {
scope: 'limited'
});
this.collector.addResult('Sudo配置生成', 'Sudo配置',
config.includes('testuser') ? 'passed' : 'failed',
{ configLength: config.length }
);
} catch (error) {
this.collector.addResult('Sudo配置生成', 'Sudo配置', 'failed', {
error: error.message
});
}
} else {
this.collector.addResult('Sudo配置模块', 'Sudo配置', 'skipped', {
reason: 'Sudo配置管理器不可用'
});
}
} catch (error) {
this.collector.addResult('Sudo配置模块', 'Sudo配置', 'failed', {
error: error.message
});
}
}
/**
* 性能测试
*/
async performanceTests() {
try {
// 路径解析性能测试
const pathCount = 1000;
const startTime = Date.now();
for (let i = 0; i < pathCount; i++) {
resolveUserPath(`test-path-${i}.txt`);
}
const pathResolveTime = Date.now() - startTime;
this.collector.addResult('路径解析性能', '性能测试',
pathResolveTime < 1000 ? 'passed' : 'failed', // 应该在1秒内完成1000次解析
{
operations: pathCount,
duration: pathResolveTime,
opsPerSecond: Math.round(pathCount / (pathResolveTime / 1000))
}
);
// 命令评估性能测试
const commandCount = 500;
const cmdStartTime = Date.now();
for (let i = 0; i < commandCount; i++) {
await evaluateCommand(`test-command-${i}`);
}
const commandEvalTime = Date.now() - cmdStartTime;
this.collector.addResult('命令评估性能', '性能测试',
commandEvalTime < 2000 ? 'passed' : 'failed', // 应该在2秒内完成500次评估
{
operations: commandCount,
duration: commandEvalTime,
opsPerSecond: Math.round(commandCount / (commandEvalTime / 1000))
}
);
} catch (error) {
this.collector.addResult('性能测试', '性能测试', 'failed', {
error: error.message
});
}
}
}
module.exports = CrossPlatformTestSuite;