Files
AIclinicalresearch/backend/scripts/test-pkb-apis-simple.ts
HaHafeng 2481b786d8 deploy: Complete 0126-27 deployment - database upgrade, services update, code recovery
Major Changes:
- Database: Install pg_bigm/pgvector plugins, create test database
- Python service: v1.0 -> v1.1, add pymupdf4llm/openpyxl/pypandoc
- Node.js backend: v1.3 -> v1.7, fix pino-pretty and ES Module imports
- Frontend: v1.2 -> v1.3, skip TypeScript check for deployment
- Code recovery: Restore empty files from local backup

Technical Fixes:
- Fix pino-pretty error in production (conditional loading)
- Fix ES Module import paths (add .js extensions)
- Fix OSSAdapter TypeScript errors
- Update Prisma Schema (63 models, 16 schemas)
- Update environment variables (DATABASE_URL, EXTRACTION_SERVICE_URL, OSS)
- Remove deprecated variables (REDIS_URL, DIFY_API_URL, DIFY_API_KEY)

Documentation:
- Create 0126 deployment folder with 8 documents
- Update database development standards v2.0
- Update SAE deployment status records

Deployment Status:
- PostgreSQL: ai_clinical_research_test with plugins
- Python: v1.1 @ 172.17.173.84:8000
- Backend: v1.7 @ 172.17.173.89:3001
- Frontend: v1.3 @ 172.17.173.90:80

Tested: All services running successfully on SAE
2026-01-27 08:13:27 +08:00

358 lines
10 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简化测试脚<E8AF95>?
* 测试现有知识库的各项功能
*/
import axios from 'axios';
const BASE_URL = 'http://localhost:3000';
interface TestResult {
name: string;
status: 'pass' | 'fail';
message: string;
duration?: number;
}
const results: TestResult[] = [];
function printResult(result: TestResult) {
const icon = result.status === 'pass' ? '<27>? : '<EFBFBD>?;
console.log(`${icon} ${result.name} ${result.duration ? `(${result.duration}ms)` : ''}`);
console.log(` ${result.message}`);
}
// 测试1健康检<E5BAB7>?
async function testHealthCheck(): Promise<TestResult> {
const startTime = Date.now();
try {
const response = await axios.get(`${BASE_URL}/api/v1/pkb/health`);
const duration = Date.now() - startTime;
if (response.data.status === 'ok') {
return {
name: '健康检查v2<76>?,
status: 'pass',
message: `知识库数: ${response.data.database.knowledgeBases}, schema: ${response.data.database.schema}`,
duration,
};
} else {
return {
name: 'v2<EFBFBD>?,
status: 'fail',
message: '返回状态异<E68081>?,
duration,
};
}
} catch (error: any) {
return {
name: 'v2<EFBFBD>?,
status: 'fail',
message: error.message,
duration: Date.now() - startTime,
};
}
}
// 测试2获取知识库列表v1 vs v2<76>?
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/v1/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<76>?,
status: 'pass',
message: `v1: ${v1Count}<7D>?(${v1Duration}ms), v2: ${v2Count}<7D>?(${v2Duration}ms) ✅`,
duration: v1Duration + v2Duration,
};
} else {
return {
name: 'v1 vs v2<EFBFBD>?,
status: 'fail',
message: `数量不一致v1: ${v1Count}, v2: ${v2Count}`,
duration: v1Duration + v2Duration,
};
}
} catch (error: any) {
return {
name: '获取知识库列表v1 vs v2<76>?,
status: 'fail',
message: error.message,
};
}
}
// 测试3获取知识库详情v1 vs v2<76>?
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/v1/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<EFBFBD>?,
status: 'pass',
message: `名称一<EFBFBD>? "${v1Name}", v1: ${v1Duration}ms, v2: ${v2Duration}ms ✅`,
duration: v1Duration + v2Duration,
};
} else {
return {
name: '获取知识库详情v1 vs v2<76>?,
status: 'fail',
message: `名称不一致v1: "${v1Name}", v2: "${v2Name}"`,
duration: v1Duration + v2Duration,
};
}
} catch (error: any) {
return {
name: 'v1 vs v2<EFBFBD>?,
status: 'fail',
message: error.message,
};
}
}
// 测试4获取知识库统计v1 vs v2<76>?
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/v1/pkb/knowledge/knowledge-bases/${kbId}/stats`);
const v1Docs = v1Response.data.data.totalDocuments;
const v2Docs = v2Response.data.data.totalDocuments;
if (v1Docs === v2Docs) {
return {
name: '获取知识库统计v1 vs v2<76>?,
status: 'pass',
message: `文档数一<E695B0>? ${v1Docs}<7D>?✅`,
};
} else {
return {
name: 'v1 vs v2<EFBFBD>?,
status: 'fail',
message: `文档数不一致v1: ${v1Docs}, v2: ${v2Docs}`,
};
}
} catch (error: any) {
return {
name: '获取知识库统计v1 vs v2<76>?,
status: 'fail',
message: error.message,
};
}
}
// 测试5RAG检索v1 vs v2<76>?
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: 3 },
});
const v2Response = await axios.get(`${BASE_URL}/api/v1/pkb/knowledge/knowledge-bases/${kbId}/search`, {
params: { query, top_k: 3 },
});
const v1Count = v1Response.data.data?.records?.length || 0;
const v2Count = v2Response.data.data?.records?.length || 0;
return {
name: 'RAG检索v1 vs v2<EFBFBD>?,
status: 'pass',
message: `v1返回${v1Count}<EFBFBD>? v2返回${v2Count}<EFBFBD>?✅`,
};
} catch (error: any) {
return {
name: 'RAG检索v1 vs v2<76>?,
status: 'fail',
message: error.message,
};
}
}
// 测试6文档选择全文阅读模式
async function testDocumentSelection(kbId: string): Promise<TestResult> {
try {
const v1Response = await axios.get(`${BASE_URL}/api/v1/knowledge-bases/${kbId}/document-selection`, {
params: { max_files: 5, max_tokens: 100000 },
});
const v2Response = await axios.get(`${BASE_URL}/api/v1/pkb/knowledge/knowledge-bases/${kbId}/document-selection`, {
params: { max_files: 5, max_tokens: 100000 },
});
const v1Docs = v1Response.data.data?.selectedDocuments?.length || 0;
const v2Docs = v2Response.data.data?.selectedDocuments?.length || 0;
return {
name: '-v1 vs v2<EFBFBD>?,
status: 'pass',
message: `v1选择${v1Docs}个文<EFBFBD>? v2选择${v2Docs}个文<EFBFBD>?✅`,
};
} catch (error: any) {
return {
name: '文档选择-全文阅读模式v1 vs v2<76>?,
status: 'fail',
message: error.message,
};
}
}
// 测试7批处理模板
async function testBatchTemplates(): Promise<TestResult> {
try {
const v1Response = await axios.get(`${BASE_URL}/api/v1/batch/templates`);
const v2Response = await axios.get(`${BASE_URL}/api/v1/pkb/batch-tasks/batch/templates`);
const v1Count = v1Response.data.data?.length || 0;
const v2Count = v2Response.data.data?.length || 0;
return {
name: 'v1 vs v2<EFBFBD>?,
status: 'pass',
message: `v1: ${v1Count}个模<EFBFBD>? v2: ${v2Count}个模<EFBFBD>?✅`,
};
} catch (error: any) {
return {
name: '批处理模板v1 vs v2<76>?,
status: 'fail',
message: error.message,
};
}
}
// 主测试函<E8AF95>?
async function runTests() {
console.log('🚀 PKB API测试开<EFBFBD>?..\n');
console.log('='.repeat(80));
// 测试1健康检<E5BAB7>?
console.log('\n📋 1<EFBFBD>?);
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]);
// 获取第一个知识库ID用于后续测试
const kbListResponse = await axios.get(`${BASE_URL}/api/v1/pkb/knowledge/knowledge-bases`);
const firstKb = kbListResponse.data.data?.[0];
if (!firstKb) {
console.log('\n<>?没有可用的知识库后续测试跳<E8AF95>?);
return;
}
const kbId = firstKb.id;
console.log(`\n使用知识<E79FA5>? ${firstKb.name} (ID: ${kbId})`);
// 测试3获取知识库详情
console.log('\n📋 3');
console.log('-'.repeat(80));
results.push(await testGetKnowledgeBaseById(kbId));
printResult(results[results.length - 1]);
// 测试4知识库统计
console.log('\n📋 4');
console.log('-'.repeat(80));
results.push(await testGetKnowledgeBaseStats(kbId));
printResult(results[results.length - 1]);
// 测试5RAG检<47>?
console.log('\n📋 5RAG检<EFBFBD>?);
console.log('-'.repeat(80));
results.push(await testSearchKnowledgeBase(kbId));
printResult(results[results.length - 1]);
// 测试6文档选择
console.log('\n📋 测试6文档选择全文阅读');
console.log('-'.repeat(80));
results.push(await testDocumentSelection(kbId));
printResult(results[results.length - 1]);
// 测试7批处理模板
console.log('\n📋 测试7批处理模板');
console.log('-'.repeat(80));
results.push(await testBatchTemplates());
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 totalDuration = results.reduce((sum, r) => sum + (r.duration || 0), 0);
console.log(`\n总计: ${results.length}个测试`);
console.log(`<EFBFBD>?通过: ${passCount}`);
console.log(`<EFBFBD>?失败: ${failCount}`);
console.log(`⏱️ 总耗时: ${totalDuration}ms`);
if (failCount === 0) {
console.log('\n🎉 所有测试通过v1和v2功能完全一致');
} else {
console.log('\n⚠ 部分测试失败,请查看详情');
}
}
runTests().catch(error => {
console.error('<27>?测试执行失败:', error);
process.exit(1);
});