| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237 |
- #!/usr/bin/env node
- /**
- * 完整语音集成测试报告
- * 验证培训包的语音克隆能力是否完整可用
- */
- const path = require('path');
- const fs = require('fs');
- const https = require('https');
- const TRAINING_DIR = path.resolve('D:', 'qiwei-training');
- console.log('🧪 完整语音集成测试报告');
- console.log('='.repeat(70));
- console.log(`测试目标: ${TRAINING_DIR}`);
- console.log(`测试时间: ${new Date().toLocaleString('zh-CN')}\n`);
- let passCount = 0;
- let failCount = 0;
- function reportTest(name, passed, details = '') {
- if (passed) {
- console.log(`✅ [通过] ${name}`);
- if (details) console.log(` ${details}`);
- passCount++;
- } else {
- console.log(`❌ [失败] ${name}`);
- if (details) console.log(` ${details}`);
- failCount++;
- }
- }
- // ============================================================
- // 测试 1: 培训包结构
- // ============================================================
- console.log('【第 1 组】培训包结构验证\n');
- const exePath = path.join(TRAINING_DIR, 'qiwei-workbench.exe');
- const exeExists = fs.existsSync(exePath);
- const exeSize = exeExists ? (fs.statSync(exePath).size / (1024 * 1024)).toFixed(1) : 0;
- reportTest(
- 'exe 文件存在且大小合理',
- exeExists && parseFloat(exeSize) > 90,
- `大小: ${exeSize} MB`
- );
- const envPath = path.join(TRAINING_DIR, '.env.local');
- const envExists = fs.existsSync(envPath);
- reportTest('配置文件存在', envExists, envPath);
- const readmePath = path.join(TRAINING_DIR, 'README.md');
- const readmeExists = fs.existsSync(readmePath);
- reportTest('用户手册存在', readmeExists, readmePath);
- // ============================================================
- // 测试 2: Token 配置
- // ============================================================
- console.log('\n【第 2 组】认证配置验证\n');
- if (envExists) {
- const envContent = fs.readFileSync(envPath, 'utf8');
- const tokenMatch = envContent.match(/^QIWEI_AUTH_TOKEN=(.+)$/m);
- const hasToken = tokenMatch && tokenMatch[1].trim().length > 0;
- reportTest(
- 'Token 已配置',
- hasToken,
- hasToken ? '已检测到凭据(内容已隐藏)' : '未找到有效 Token'
- );
- if (hasToken) {
- const token = tokenMatch[1].trim();
- const validFormat = token.startsWith('sk-') || token.startsWith('r:');
- reportTest('Token 格式正确', validFormat, validFormat ? '格式符合要求(内容已隐藏)' : '格式不符合要求');
- }
- }
- // ============================================================
- // 测试 3: README 语音文档
- // ============================================================
- console.log('\n【第 3 组】用户文档验证\n');
- if (readmeExists) {
- const readme = fs.readFileSync(readmePath, 'utf8');
- const hasVoiceSection = readme.includes('## 语音克隆功能');
- reportTest('包含语音克隆章节', hasVoiceSection);
- const hasReferenceAudio = readme.includes('录制参考音频') && readme.includes('5-30 秒');
- reportTest('说明参考音频要求', hasReferenceAudio);
- const hasBilling = readme.includes('3元/万字符') || readme.includes('计费说明');
- reportTest('说明计费信息', hasBilling);
- const hasSILK = readme.includes('SILK');
- reportTest('说明 SILK 编码', hasSILK);
- }
- // ============================================================
- // 测试 4: 源码模块完整性
- // ============================================================
- console.log('\n【第 4 组】源码模块验证\n');
- const voiceServicePath = path.resolve(__dirname, '..', 'mcp/src/core/voice-clone-service.js');
- const voiceServiceExists = fs.existsSync(voiceServicePath);
- reportTest('语音服务模块存在', voiceServiceExists, voiceServicePath);
- if (voiceServiceExists) {
- const voiceService = fs.readFileSync(voiceServicePath, 'utf8');
- const hasClass = voiceService.includes('class VoiceCloneService');
- reportTest('VoiceCloneService 类定义', hasClass);
- const hasSynthesize = voiceService.includes('async synthesize');
- reportTest('synthesize 方法定义', hasSynthesize);
- const hasEncodeSilk = voiceService.includes('async encodeSilk');
- reportTest('encodeSilk 方法定义', hasEncodeSilk);
- const hasEndpoint = voiceService.includes('server.fmode.cn/api/voice/indextts2');
- reportTest('Fmode 后端端点配置', hasEndpoint);
- const hasWxVoice = voiceService.includes('@binsee/wx-voice');
- reportTest('wx-voice 依赖引用', hasWxVoice);
- const hasFfmpeg = voiceService.includes('ffmpeg');
- reportTest('ffmpeg 依赖引用', hasFfmpeg);
- }
- // ============================================================
- // 测试 5: package.json 依赖声明
- // ============================================================
- console.log('\n【第 5 组】依赖声明验证\n');
- const packageJsonPath = path.resolve(__dirname, '..', 'package.json');
- const packageJson = require(packageJsonPath);
- const allDeps = {
- ...packageJson.dependencies,
- ...packageJson.optionalDependencies
- };
- const voiceDeps = [
- '@ffmpeg-installer/ffmpeg',
- '@ffprobe-installer/ffprobe',
- '@binsee/wx-voice'
- ];
- voiceDeps.forEach(dep => {
- const declared = !!allDeps[dep];
- reportTest(`依赖 ${dep}`, declared, declared ? `版本: ${allDeps[dep]}` : '未声明');
- });
- // ============================================================
- // 测试 6: 运行时加载
- // ============================================================
- console.log('\n【第 6 组】运行时加载验证\n');
- try {
- const { VoiceCloneService } = require('../mcp/src/core/voice-clone-service.js');
- reportTest('VoiceCloneService 类加载', true);
- const mockConfig = {
- config: { endpoint: 'https://server.fmode.cn/api/voice/indextts2', authToken: 'test' },
- qiwei: { uid: 'test', guid: 'test' }
- };
- const service = new VoiceCloneService(mockConfig);
- reportTest('VoiceCloneService 实例化', true, `端点: ${service.config.endpoint}`);
- } catch (err) {
- reportTest('VoiceCloneService 加载/实例化', false, err.message);
- }
- try {
- const ffmpeg = require('@ffmpeg-installer/ffmpeg');
- const ffmpegExists = fs.existsSync(ffmpeg.path);
- reportTest('ffmpeg 二进制可访问', ffmpegExists, `路径: ${ffmpeg.path}`);
- } catch (err) {
- reportTest('ffmpeg 加载', false, err.message);
- }
- try {
- const ffprobe = require('@ffprobe-installer/ffprobe');
- const ffprobeExists = fs.existsSync(ffprobe.path);
- reportTest('ffprobe 二进制可访问', ffprobeExists, `路径: ${ffprobe.path}`);
- } catch (err) {
- reportTest('ffprobe 加载', false, err.message);
- }
- try {
- const WxVoice = require('@binsee/wx-voice');
- const hasEncode = typeof WxVoice.WxVoice === 'function';
- reportTest('wx-voice 模块可用', hasEncode);
- } catch (err) {
- reportTest('wx-voice 加载', false, err.message);
- }
- // ============================================================
- // 测试 7: exe 启动检查
- // ============================================================
- console.log('\n【第 7 组】exe 启动检查\n');
- const { execSync } = require('child_process');
- try {
- const helpOutput = execSync(
- `"${exePath}" --help`,
- { encoding: 'utf8', timeout: 5000 }
- );
- const canRun = helpOutput.includes('企微培训工作台');
- reportTest('exe 可以正常启动', canRun, '命令行参数解析正常');
- } catch (err) {
- reportTest('exe 启动测试', false, err.message);
- }
- // ============================================================
- // 汇总报告
- // ============================================================
- console.log('\n' + '='.repeat(70));
- console.log('📊 测试汇总');
- console.log('='.repeat(70));
- console.log(`✅ 通过: ${passCount} 项`);
- console.log(`❌ 失败: ${failCount} 项`);
- console.log(`📈 通过率: ${((passCount / (passCount + failCount)) * 100).toFixed(1)}%`);
- if (failCount === 0) {
- console.log('\n🎉 所有测试通过!培训包语音克隆功能完整可用。');
- console.log('\n📝 下一步建议:');
- console.log(' 1. 准备 5-30 秒清晰的参考录音(WAV/MP3 格式)');
- console.log(' 2. 启动工作台: D:\\qiwei-training\\qiwei-workbench.exe');
- console.log(' 3. 在工作台上传参考音频完成初始化');
- console.log(' 4. 输入测试文本并发送到测试联系人验证效果');
- console.log(' 5. 验证通过后可打包分发或部署到培训机');
- process.exit(0);
- } else {
- console.log('\n⚠️ 部分测试未通过,请检查上述失败项。');
- process.exit(1);
- }
|