/** * P0 和 P1 功能综合测试脚本 * 测试音频下载、批量操作、搜索、编辑、播放模式等功能 */ const http = require('http'); const BASE_URL = 'http://localhost:3000'; // 测试结果统计 const results = { passed: 0, failed: 0, tests: [] }; // 测试函数 async function test(name, fn) { try { await fn(); results.passed++; results.tests.push({ name, status: '✅ PASSED' }); console.log(`✅ ${name}`); } catch (error) { results.failed++; results.tests.push({ name, status: '❌ FAILED', error: error.message }); console.log(`❌ ${name}: ${error.message}`); } } // HTTP 请求辅助函数 function request(method, path, body = null) { return new Promise((resolve, reject) => { const url = new URL(path, BASE_URL); const options = { hostname: url.hostname, port: url.port, path: url.pathname, method: method, headers: { 'Content-Type': 'application/json', }, }; const req = http.request(options, (res) => { let data = ''; res.on('data', (chunk) => { data += chunk; }); res.on('end', () => { try { const json = JSON.parse(data); if (res.statusCode >= 200 && res.statusCode < 300) { resolve(json); } else { reject(new Error(`HTTP ${res.statusCode}: ${json.message || data}`)); } } catch (e) { reject(new Error(`解析失败: ${data}`)); } }); }); req.on('error', reject); if (body) { req.write(JSON.stringify(body)); } req.end(); }); } // 主测试函数 async function runTests() { console.log('\n🚀 开始 P0 和 P1 功能测试\n'); console.log('=' .repeat(60)); // ========== P0-1: 音频下载功能 ========== console.log('\n📦 P0-1: 音频下载功能测试'); console.log('-'.repeat(60)); await test('P0-1-1: 获取音频下载信息', async () => { // 先获取一个音频记录 const history = await request('GET', '/api/history?page=1&pageSize=1'); if (history.list && history.list.length > 0) { const audioId = history.list[0].id; const downloadInfo = await request('GET', `/api/tts/download/${audioId}`); if (!downloadInfo.data || !downloadInfo.data.downloadUrl) { throw new Error('下载信息不完整'); } } }); await test('P0-1-2: 批量获取下载链接', async () => { const history = await request('GET', '/api/history?page=1&pageSize=3'); if (history.list && history.list.length > 0) { const audioIds = history.list.map(a => String(a.id)); const batchResult = await request('POST', '/api/tts/download/batch', { audioIds }); if (!batchResult.data || !batchResult.data.audios) { throw new Error('批量下载数据不完整'); } } }); // ========== P0-2: 批量操作增强 ========== console.log('\n📦 P0-2: 批量操作增强测试'); console.log('-'.repeat(60)); await test('P0-2-1: 获取书籍章节列表', async () => { const books = await request('GET', '/api/book-generator/langgraph/books'); if (books.books && books.books.length > 0) { const bookId = books.books[0].id; const bookDetail = await request('GET', `/api/book-generator/langgraph/books/${bookId}`); if (!bookDetail.book || !bookDetail.book.chapters) { throw new Error('书籍详情不完整'); } } }); // ========== P1-1: 章节内容编辑 ========== console.log('\n📦 P1-1: 章节内容编辑测试'); console.log('-'.repeat(60)); await test('P1-1-1: 章节内容保存接口存在', async () => { // 验证 API 路由是否存在(即使没有权限也应该返回错误而不是 404) try { await request('POST', '/api/book-generator/langgraph/books/1/chapters/1/content', { content: '测试内容' }); } catch (error) { // 如果返回 404 说明路由不存在 if (error.message.includes('404')) { throw new Error('章节内容保存 API 路由不存在'); } // 其他错误(如权限、参数错误)说明路由存在 } }); // ========== P1-2: 搜索功能 ========== console.log('\n📦 P1-2: 搜索功能测试'); console.log('-'.repeat(60)); await test('P1-2-1: 历史记录搜索功能', async () => { const result = await request('GET', '/api/history?page=1&pageSize=10&keyword=测试'); // 搜索应该正常返回(即使结果为空) if (!result.data || result.data.list === undefined) { throw new Error('搜索结果格式不正确'); } }); await test('P1-2-2: 清空搜索返回全部', async () => { const result = await request('GET', '/api/history?page=1&pageSize=5'); if (!result.data || !result.data.list || !Array.isArray(result.data.list)) { throw new Error('历史记录列表格式不正确'); } }); // ========== P1-3: 播放模式 ========== console.log('\n📦 P1-3: 播放模式测试'); console.log('-'.repeat(60)); await test('P1-3-1: 音频 Store 包含播放模式', async () => { // 这个需要在前端测试,后端只需验证音频相关接口正常 const voices = await request('GET', '/api/tts/voices'); if (!voices.data || !voices.data.voices || !Array.isArray(voices.data.voices)) { throw new Error('音色列表格式不正确'); } }); // ========== 综合测试 ========== console.log('\n📦 综合功能测试'); console.log('-'.repeat(60)); await test('综合-1: 书籍公开状态字段', async () => { const books = await request('GET', '/api/book-generator/langgraph/books'); if (books.books && books.books.length > 0) { const book = books.books[0]; if (book.isPublished === undefined) { throw new Error('书籍缺少 isPublished 字段'); } } }); await test('综合-2: 音频生成状态检查', async () => { const history = await request('GET', '/api/history?page=1&pageSize=1'); if (history.list && history.list.length > 0) { const audio = history.list[0]; if (!audio.status) { throw new Error('音频记录缺少状态字段'); } } }); // ========== 输出测试结果 ========== console.log('\n' + '='.repeat(60)); console.log('📊 测试结果统计'); console.log('='.repeat(60)); console.log(`✅ 通过: ${results.passed}`); console.log(`❌ 失败: ${results.failed}`); console.log(`📈 通过率: ${((results.passed / (results.passed + results.failed)) * 100).toFixed(1)}%`); console.log('='.repeat(60)); if (results.failed > 0) { console.log('\n❌ 失败的测试:'); results.tests .filter(t => t.status === '❌ FAILED') .forEach(t => { console.log(` - ${t.name}: ${t.error}`); }); } console.log('\n✨ 测试完成!\n'); // 如果有失败的测试,退出码为 1 process.exit(results.failed > 0 ? 1 : 0); } // 运行测试 runTests().catch(error => { console.error('测试执行失败:', error); process.exit(1); });