| import fs from 'fs'; |
| import path from 'path'; |
| import app from './src/index'; |
|
|
| const PORT = 3000; |
| const BASE_URL = `http://localhost:${PORT}`; |
|
|
| |
| const testCases = [ |
| { |
| case_id: 'TC-01', |
| sample_type: '明显漏洞', |
| materials: { |
| current_policy_text: '我们收集您的姓名和手机号。我们会申请设备权限。我们会共享数据给第三方。', |
| prd_text: '包含人脸识别功能,需要获取身份证正反面,并在开户流程中使用相机进行活体检测。', |
| permission_items: [ |
| { name: '相机权限', purpose: '活体检测与身份证识别', trigger_page: '开户流程', required: true } |
| ], |
| sdk_items: [ |
| { name: '极光推送SDK', vendor: '极光', purpose: '消息推送', data_items: ['设备标识符', '网络状态'], privacy_url: 'https://jiguang.cn/privacy' } |
| ] |
| }, |
| peer_updates: ['同业A细化了权限说明'], |
| regulatory_updates: ['监管强调最小必要原则'] |
| }, |
| { |
| case_id: 'TC-02', |
| sample_type: '仅缺权限用途', |
| materials: { |
| current_policy_text: '我们收集您的姓名和手机号。我们在开户时申请相机权限。接入了极光推送SDK用于消息推送。', |
| prd_text: '开户流程中使用相机进行活体检测。', |
| permission_items: [ |
| { name: '相机权限', purpose: '活体检测与身份证识别', trigger_page: '开户流程', required: true } |
| ], |
| sdk_items: [ |
| { name: '极光推送SDK', vendor: '极光', purpose: '消息推送', data_items: ['设备标识符'], privacy_url: 'https://jiguang.cn/privacy' } |
| ] |
| }, |
| peer_updates: [], |
| regulatory_updates: [] |
| }, |
| { |
| case_id: 'TC-03', |
| sample_type: 'SDK缺失', |
| materials: { |
| current_policy_text: '我们在开户时申请相机权限用于活体检测。我们绝不与第三方共享数据。', |
| prd_text: '接入了极光推送SDK用于消息推送。', |
| permission_items: [ |
| { name: '相机权限', purpose: '活体检测', trigger_page: '开户流程', required: true } |
| ], |
| sdk_items: [ |
| { name: '极光推送SDK', vendor: '极光', purpose: '消息推送', data_items: ['设备标识符'], privacy_url: 'https://jiguang.cn/privacy' } |
| ] |
| }, |
| peer_updates: [], |
| regulatory_updates: [] |
| }, |
| { |
| case_id: 'TC-04', |
| sample_type: '基本合规', |
| materials: { |
| current_policy_text: '我们在开户流程申请相机权限,用于身份证识别与活体检测。我们接入极光推送SDK(收集设备标识符)用于消息推送。', |
| prd_text: '开户活体检测,极光消息推送。', |
| permission_items: [ |
| { name: '相机权限', purpose: '活体检测与身份证识别', trigger_page: '开户流程', required: true } |
| ], |
| sdk_items: [ |
| { name: '极光推送SDK', vendor: '极光', purpose: '消息推送', data_items: ['设备标识符'], privacy_url: 'https://jiguang.cn/privacy' } |
| ] |
| }, |
| peer_updates: [], |
| regulatory_updates: [] |
| }, |
| { |
| case_id: 'TC-05', |
| sample_type: '缺 PRD', |
| materials: { |
| current_policy_text: '基本条款...', |
| prd_text: '', |
| permission_items: [], |
| sdk_items: [] |
| }, |
| peer_updates: [], |
| regulatory_updates: [] |
| }, |
| { |
| case_id: 'TC-06', |
| sample_type: '缺 权限清单', |
| materials: { |
| current_policy_text: '基本条款...', |
| prd_text: '基本说明...', |
| permission_items: null, |
| sdk_items: [] |
| }, |
| peer_updates: [], |
| regulatory_updates: [] |
| }, |
| { |
| case_id: 'TC-07', |
| sample_type: '缺 SDK清单', |
| materials: { |
| current_policy_text: '基本条款...', |
| prd_text: '基本说明...', |
| permission_items: [], |
| sdk_items: null |
| }, |
| peer_updates: [], |
| regulatory_updates: [] |
| }, |
| { |
| case_id: 'TC-08', |
| sample_type: '权限超范围收集', |
| materials: { |
| current_policy_text: '我们申请相机权限用于活体检测。我们还会申请通讯录权限用于推荐好友。', |
| prd_text: '仅开户需要相机,无推荐好友功能。', |
| permission_items: [ |
| { name: '相机权限', purpose: '活体检测', trigger_page: '开户流程', required: true } |
| ], |
| sdk_items: [] |
| }, |
| peer_updates: [], |
| regulatory_updates: ['严禁超范围收集无关权限'] |
| }, |
| { |
| case_id: 'TC-09', |
| sample_type: 'SDK超范围收集', |
| materials: { |
| current_policy_text: '极光SDK用于推送,仅收集设备标识。', |
| prd_text: '极光SDK实际还偷偷收集了用户地理位置。', |
| permission_items: [], |
| sdk_items: [ |
| { name: '极光推送SDK', vendor: '极光', purpose: '消息推送', data_items: ['设备标识符', '精准地理位置'], privacy_url: 'https://jiguang.cn/privacy' } |
| ] |
| }, |
| peer_updates: [], |
| regulatory_updates: [] |
| }, |
| { |
| case_id: 'TC-10', |
| sample_type: '缺少注销账号条款', |
| materials: { |
| current_policy_text: '欢迎使用。收集信息... 完毕。', |
| prd_text: 'APP包含用户注册和注销功能。', |
| permission_items: [], |
| sdk_items: [] |
| }, |
| peer_updates: [], |
| regulatory_updates: ['工信部要求必须提供便捷的账号注销方式并在协议中说明'] |
| }, |
| { |
| case_id: 'TC-11', |
| sample_type: '缺少未成年人保护条款', |
| materials: { |
| current_policy_text: '我们面向所有用户提供服务。', |
| prd_text: 'APP可能有不满14周岁用户注册。', |
| permission_items: [], |
| sdk_items: [] |
| }, |
| peer_updates: [], |
| regulatory_updates: ['必须设立专门的未成年人个人信息保护规则'] |
| }, |
| { |
| case_id: 'TC-12', |
| sample_type: '个性化推荐未提供关闭选项', |
| materials: { |
| current_policy_text: '我们会根据您的浏览记录进行千人千面的内容推荐。', |
| prd_text: '首页包含基于用户画像的个性化推荐信息流。', |
| permission_items: [], |
| sdk_items: [] |
| }, |
| peer_updates: [], |
| regulatory_updates: ['算法推荐必须提供便捷的关闭选项'] |
| } |
| ]; |
|
|
| |
| async function post(url: string, data: any) { |
| const start = Date.now(); |
| const res = await fetch(url, { |
| method: 'POST', |
| headers: { 'Content-Type': 'application/json' }, |
| body: JSON.stringify(data) |
| }); |
| const latency = Date.now() - start; |
| const json = await res.json(); |
| if (!res.ok) { |
| throw { status: res.status, latency, error: json }; |
| } |
| return { data: json, latency }; |
| } |
|
|
| |
| async function runRegression() { |
| console.log('Starting Agent Services for Regression Test...'); |
| const server = app.listen(PORT, () => { |
| console.log(`Server listening on port ${PORT}`); |
| }); |
|
|
| const report: any[] = []; |
| const logStream = fs.createWriteStream(path.join(__dirname, 'regression_report.log'), { flags: 'w' }); |
|
|
| for (const tc of testCases) { |
| console.log(`\n====================================`); |
| console.log(`Executing ${tc.case_id}: ${tc.sample_type}`); |
| logStream.write(`\n====================================\n`); |
| logStream.write(`Executing ${tc.case_id}: ${tc.sample_type}\n`); |
|
|
| const result = { |
| case_id: tc.case_id, |
| sample_type: tc.sample_type, |
| e2e_latency_ms: 0, |
| each_agent_latency_ms: { |
| agent1: 0, |
| agent2: 0, |
| agent3: 0, |
| agent4: 0 |
| }, |
| actual_recommendation: '', |
| error: null as any |
| }; |
|
|
| const startE2E = Date.now(); |
|
|
| try { |
| |
| const baseContext = { |
| case_id: tc.case_id, |
| customer_id: 'CUST-TEST', |
| customer_name: '测试银行', |
| app_name: '测试银行APP', |
| business_line: 'retail', |
| language: 'zh-CN', |
| materials: tc.materials, |
| external_context: { |
| peer_updates: tc.peer_updates, |
| regulatory_updates: tc.regulatory_updates |
| }, |
| metadata: { |
| submitted_at: new Date().toISOString(), |
| source: 'regression-test' |
| } |
| }; |
|
|
| |
| console.log(` -> [Agent 1] Peer Reg Summary...`); |
| const a1Res = await post(`${BASE_URL}/api/agents/peer-reg-summary`, { |
| peer_updates: tc.peer_updates, |
| regulatory_updates: tc.regulatory_updates, |
| business_line: 'retail', |
| app_name: '测试银行APP' |
| }); |
| result.each_agent_latency_ms.agent1 = a1Res.latency; |
|
|
| |
| console.log(` -> [Agent 2] Policy Rewrite...`); |
| const a2Res = await post(`${BASE_URL}/api/agents/policy-rewrite`, { |
| case_context: baseContext, |
| peer_reg_summary: a1Res.data |
| }); |
| result.each_agent_latency_ms.agent2 = a2Res.latency; |
|
|
| |
| console.log(` -> [Agent 3] Compliance Check...`); |
| const a3Res = await post(`${BASE_URL}/api/agents/compliance-check`, { |
| case_context: baseContext, |
| gap_analysis: a2Res.data |
| }); |
| result.each_agent_latency_ms.agent3 = a3Res.latency; |
|
|
| |
| console.log(` -> [Agent 4] Legal Pack...`); |
| const a4Res = await post(`${BASE_URL}/api/agents/legal-pack`, { |
| case_context: baseContext, |
| peer_reg_summary: a1Res.data, |
| gap_analysis: a2Res.data, |
| compliance_check: a3Res.data |
| }); |
| result.each_agent_latency_ms.agent4 = a4Res.latency; |
|
|
| result.actual_recommendation = a4Res.data.review_recommendation || 'unknown'; |
| console.log(` => Recommendation: ${result.actual_recommendation}`); |
|
|
| } catch (err: any) { |
| console.log(` => Error caught:`, err.status || err.message || err); |
| result.error = err.error || err.message || err; |
| if (err.error && (err.error.error_code === 'need_more_material' || (err.error.error && err.error.error.code === 4001))) { |
| result.actual_recommendation = 'need_more_material (4001)'; |
| } else if (err.error && err.error.error && err.error.error.code === 5001) { |
| result.actual_recommendation = 'LLM_ERROR (5001)'; |
| } else { |
| result.actual_recommendation = 'ERROR'; |
| } |
| } |
|
|
| result.e2e_latency_ms = Date.now() - startE2E; |
| |
| |
| logStream.write(JSON.stringify(result, null, 2) + '\n'); |
| report.push(result); |
| } |
|
|
| |
| console.log(`\n\n====== Regression Test Report ======`); |
| console.table(report.map(r => ({ |
| CaseID: r.case_id, |
| Type: r.sample_type, |
| E2E_ms: r.e2e_latency_ms, |
| A1_ms: r.each_agent_latency_ms.agent1, |
| A2_ms: r.each_agent_latency_ms.agent2, |
| A3_ms: r.each_agent_latency_ms.agent3, |
| A4_ms: r.each_agent_latency_ms.agent4, |
| Recommendation: r.actual_recommendation |
| }))); |
|
|
| logStream.write(`\n\n====== Summary ======\n`); |
| logStream.write(JSON.stringify(report, null, 2)); |
| logStream.end(); |
|
|
| console.log(`\nRegression test complete. Report saved to regression_report.log`); |
| server.close(); |
| process.exit(0); |
| } |
|
|
| runRegression().catch(console.error); |
|
|