Files
AIclinicalresearch/backend/scripts/test-pkb-apis.ts
HaHafeng 5a17d096a7 feat(pkb): Complete PKB module frontend migration with V3 design
Summary:
- Implement PKB Dashboard and Workspace pages based on V3 prototype
- Add single-layer header with integrated Tab navigation
- Implement 3 work modes: Full Text, Deep Read, Batch Processing
- Integrate Ant Design X Chat component for AI conversations
- Create BatchModeComplete with template selection and document processing
- Add compact work mode selector with dropdown design

Backend:
- Migrate PKB controllers and services to /modules/pkb structure
- Register v2 API routes at /api/v2/pkb/knowledge
- Maintain dual API routes for backward compatibility

Technical details:
- Use Zustand for state management
- Handle SSE streaming responses for AI chat
- Support document selection for Deep Read mode
- Implement batch processing with progress tracking

Known issues:
- Batch processing API integration pending
- Knowledge assets page navigation needs optimization

Status: Frontend functional, pending refinement
2026-01-06 22:15:42 +08:00

441 lines
13 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 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<TestResult> {
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<TestResult> {
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<TestResult> {
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<TestResult> {
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<TestResult> {
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<TestResult> {
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,
};
}
}
// 测试7RAG检索对比v1和v2
async function testSearchKnowledgeBase(kbId: string): Promise<TestResult> {
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<TestResult> {
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<TestResult> {
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]);
// 测试7RAG检索
console.log('\n📋 阶段7RAG检索');
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);
});