/** * PKB模块API自动化测试脚本 * * 功能: * 1. 测试所有PKB API端点(v1和v2) * 2. 对比v1和v2的返回结果 * 3. 验证数据一致性 * 4. 性能对比 * 5. 边界条件测试 * * 运行方式: * npx tsx scripts/test-pkb-apis.ts */ import axios, { AxiosError } from 'axios'; const BASE_URL = 'http://localhost:3000'; const TEST_KB_NAME = `测试知识库-${Date.now()}`; interface TestResult { name: string; status: 'pass' | 'fail' | 'skip'; message: string; duration?: number; v1Response?: any; v2Response?: any; } const results: TestResult[] = []; let testKbId: string | null = null; // 工具函数:比较两个响应是否一致 function compareResponses(v1: any, v2: any): boolean { return JSON.stringify(v1) === JSON.stringify(v2); } // 工具函数:打印测试结果 function printResult(result: TestResult) { const icon = result.status === 'pass' ? '✅' : result.status === 'fail' ? '❌' : '⏭️'; console.log(`${icon} ${result.name} ${result.duration ? `(${result.duration}ms)` : ''}`); if (result.message) { console.log(` ${result.message}`); } } // 测试1:健康检查 async function testHealthCheck(): Promise { const startTime = Date.now(); try { const response = await axios.get(`${BASE_URL}/api/v2/pkb/health`); const duration = Date.now() - startTime; if (response.data.status === 'ok' && response.data.module === 'pkb' && response.data.version === 'v2') { return { name: '健康检查', status: 'pass', message: `状态: ${response.data.status}, 知识库数: ${response.data.database.knowledgeBases}`, duration, }; } else { return { name: '健康检查', status: 'fail', message: '返回数据格式不正确', duration, }; } } catch (error: any) { return { name: '健康检查', status: 'fail', message: error.message, duration: Date.now() - startTime, }; } } // 测试2:获取知识库列表(对比v1和v2) async function testGetKnowledgeBases(): Promise { try { const startV1 = Date.now(); const v1Response = await axios.get(`${BASE_URL}/api/v1/knowledge-bases`); const v1Duration = Date.now() - startV1; const startV2 = Date.now(); const v2Response = await axios.get(`${BASE_URL}/api/v2/pkb/knowledge/knowledge-bases`); const v2Duration = Date.now() - startV2; const v1Count = v1Response.data.data?.length || 0; const v2Count = v2Response.data.data?.length || 0; if (v1Count === v2Count) { return { name: '获取知识库列表(v1 vs v2)', status: 'pass', message: `v1: ${v1Count}个 (${v1Duration}ms), v2: ${v2Count}个 (${v2Duration}ms), 数据一致✅`, duration: v1Duration + v2Duration, v1Response: v1Response.data, v2Response: v2Response.data, }; } else { return { name: '获取知识库列表(v1 vs v2)', status: 'fail', message: `数量不一致!v1: ${v1Count}个, v2: ${v2Count}个`, duration: v1Duration + v2Duration, }; } } catch (error: any) { return { name: '获取知识库列表(v1 vs v2)', status: 'fail', message: error.message, }; } } // 测试3:创建知识库(v2) async function testCreateKnowledgeBase(): Promise { const startTime = Date.now(); try { const response = await axios.post(`${BASE_URL}/api/v2/pkb/knowledge/knowledge-bases`, { name: TEST_KB_NAME, description: '这是一个自动化测试创建的知识库', }, { headers: { 'Content-Type': 'application/json' } }); const duration = Date.now() - startTime; if (response.data.success && response.data.data.id) { testKbId = response.data.data.id; return { name: '创建知识库(v2)', status: 'pass', message: `成功创建,ID: ${testKbId}`, duration, }; } else { return { name: '创建知识库(v2)', status: 'fail', message: '创建失败或返回格式不正确', duration, }; } } catch (error: any) { const errorDetail = error.response?.data ? JSON.stringify(error.response.data) : (error.response?.data?.message || error.message); return { name: '创建知识库(v2)', status: 'fail', message: errorDetail, duration: Date.now() - startTime, }; } } // 测试4:获取知识库详情(对比v1和v2) async function testGetKnowledgeBaseById(kbId: string): Promise { try { const startV1 = Date.now(); const v1Response = await axios.get(`${BASE_URL}/api/v1/knowledge-bases/${kbId}`); const v1Duration = Date.now() - startV1; const startV2 = Date.now(); const v2Response = await axios.get(`${BASE_URL}/api/v2/pkb/knowledge/knowledge-bases/${kbId}`); const v2Duration = Date.now() - startV2; const v1Name = v1Response.data.data?.name; const v2Name = v2Response.data.data?.name; if (v1Name === v2Name) { return { name: '获取知识库详情(v1 vs v2)', status: 'pass', message: `v1: ${v1Duration}ms, v2: ${v2Duration}ms, 名称一致: "${v1Name}"✅`, duration: v1Duration + v2Duration, }; } else { return { name: '获取知识库详情(v1 vs v2)', status: 'fail', message: `名称不一致!v1: "${v1Name}", v2: "${v2Name}"`, duration: v1Duration + v2Duration, }; } } catch (error: any) { return { name: '获取知识库详情(v1 vs v2)', status: 'fail', message: error.message, }; } } // 测试5:更新知识库(v2) async function testUpdateKnowledgeBase(kbId: string): Promise { const startTime = Date.now(); try { const response = await axios.put(`${BASE_URL}/api/v2/pkb/knowledge/knowledge-bases/${kbId}`, { name: `${TEST_KB_NAME}-已更新`, description: '描述已更新', }); const duration = Date.now() - startTime; if (response.data.success) { return { name: '更新知识库(v2)', status: 'pass', message: '更新成功', duration, }; } else { return { name: '更新知识库(v2)', status: 'fail', message: '更新失败', duration, }; } } catch (error: any) { return { name: '更新知识库(v2)', status: 'fail', message: error.response?.data?.message || error.message, duration: Date.now() - startTime, }; } } // 测试6:获取知识库统计(对比v1和v2) async function testGetKnowledgeBaseStats(kbId: string): Promise { try { const v1Response = await axios.get(`${BASE_URL}/api/v1/knowledge-bases/${kbId}/stats`); const v2Response = await axios.get(`${BASE_URL}/api/v2/pkb/knowledge/knowledge-bases/${kbId}/stats`); const v1Stats = v1Response.data.data; const v2Stats = v2Response.data.data; if (v1Stats.totalDocuments === v2Stats.totalDocuments) { return { name: '获取知识库统计(v1 vs v2)', status: 'pass', message: `文档数一致: ${v1Stats.totalDocuments}个✅`, }; } else { return { name: '获取知识库统计(v1 vs v2)', status: 'fail', message: `文档数不一致!v1: ${v1Stats.totalDocuments}, v2: ${v2Stats.totalDocuments}`, }; } } catch (error: any) { return { name: '获取知识库统计(v1 vs v2)', status: 'fail', message: error.message, }; } } // 测试7:RAG检索(对比v1和v2) async function testSearchKnowledgeBase(kbId: string): Promise { try { const query = '测试查询'; const v1Response = await axios.get(`${BASE_URL}/api/v1/knowledge-bases/${kbId}/search`, { params: { query, top_k: 5 }, }); const v2Response = await axios.get(`${BASE_URL}/api/v2/pkb/knowledge/knowledge-bases/${kbId}/search`, { params: { query, top_k: 5 }, }); return { name: 'RAG检索(v1 vs v2)', status: 'pass', message: '检索成功,两个版本都返回了结果', }; } catch (error: any) { return { name: 'RAG检索(v1 vs v2)', status: 'fail', message: error.message, }; } } // 测试8:边界条件 - 不存在的知识库 async function testNotFoundKnowledgeBase(): Promise { try { await axios.get(`${BASE_URL}/api/v2/pkb/knowledge/knowledge-bases/00000000-0000-0000-0000-000000000000`); return { name: '边界测试:不存在的知识库', status: 'fail', message: '应该返回404错误,但没有', }; } catch (error: any) { if (error.response?.status === 404 || error.response?.status === 500) { return { name: '边界测试:不存在的知识库', status: 'pass', message: `正确返回错误状态: ${error.response.status}✅`, }; } else { return { name: '边界测试:不存在的知识库', status: 'fail', message: `意外的状态码: ${error.response?.status}`, }; } } } // 测试9:清理 - 删除测试知识库 async function testDeleteKnowledgeBase(kbId: string): Promise { const startTime = Date.now(); try { const response = await axios.delete(`${BASE_URL}/api/v2/pkb/knowledge/knowledge-bases/${kbId}`); const duration = Date.now() - startTime; if (response.data.success) { return { name: '删除知识库(v2)', status: 'pass', message: '删除成功', duration, }; } else { return { name: '删除知识库(v2)', status: 'fail', message: '删除失败', duration, }; } } catch (error: any) { return { name: '删除知识库(v2)', status: 'fail', message: error.response?.data?.message || error.message, duration: Date.now() - startTime, }; } } // 主测试函数 async function runTests() { console.log('🚀 开始PKB API自动化测试...\n'); console.log('='.repeat(80)); // 测试1:健康检查 console.log('\n📋 阶段1:健康检查'); console.log('-'.repeat(80)); results.push(await testHealthCheck()); printResult(results[results.length - 1]); // 测试2:获取知识库列表 console.log('\n📋 阶段2:知识库列表'); console.log('-'.repeat(80)); results.push(await testGetKnowledgeBases()); printResult(results[results.length - 1]); // 测试3:创建知识库 console.log('\n📋 阶段3:创建知识库'); console.log('-'.repeat(80)); results.push(await testCreateKnowledgeBase()); printResult(results[results.length - 1]); if (!testKbId) { console.log('\n❌ 无法获取测试知识库ID,后续测试跳过'); return; } // 测试4:获取知识库详情 console.log('\n📋 阶段4:知识库详情'); console.log('-'.repeat(80)); results.push(await testGetKnowledgeBaseById(testKbId)); printResult(results[results.length - 1]); // 测试5:更新知识库 console.log('\n📋 阶段5:更新知识库'); console.log('-'.repeat(80)); results.push(await testUpdateKnowledgeBase(testKbId)); printResult(results[results.length - 1]); // 测试6:获取统计信息 console.log('\n📋 阶段6:知识库统计'); console.log('-'.repeat(80)); results.push(await testGetKnowledgeBaseStats(testKbId)); printResult(results[results.length - 1]); // 测试7:RAG检索 console.log('\n📋 阶段7:RAG检索'); console.log('-'.repeat(80)); results.push(await testSearchKnowledgeBase(testKbId)); printResult(results[results.length - 1]); // 测试8:边界条件 console.log('\n📋 阶段8:边界条件测试'); console.log('-'.repeat(80)); results.push(await testNotFoundKnowledgeBase()); printResult(results[results.length - 1]); // 测试9:清理 console.log('\n📋 阶段9:清理测试数据'); console.log('-'.repeat(80)); results.push(await testDeleteKnowledgeBase(testKbId)); printResult(results[results.length - 1]); // 总结 console.log('\n' + '='.repeat(80)); console.log('📊 测试总结'); console.log('='.repeat(80)); const passCount = results.filter(r => r.status === 'pass').length; const failCount = results.filter(r => r.status === 'fail').length; const skipCount = results.filter(r => r.status === 'skip').length; const totalDuration = results.reduce((sum, r) => sum + (r.duration || 0), 0); console.log(`\n总计: ${results.length}个测试`); console.log(`✅ 通过: ${passCount}个`); console.log(`❌ 失败: ${failCount}个`); console.log(`⏭️ 跳过: ${skipCount}个`); console.log(`⏱️ 总耗时: ${totalDuration}ms`); if (failCount === 0) { console.log('\n🎉 所有测试通过!'); } else { console.log('\n⚠️ 部分测试失败,请查看详情'); } } // 执行测试 runTests().catch(error => { console.error('❌ 测试执行失败:', error); process.exit(1); });