From 54f19459ff8f7aae2b83731cc7fbe66d313fee5a Mon Sep 17 00:00:00 2001 From: snowgitea Date: Wed, 29 Apr 2026 02:08:55 +0800 Subject: [PATCH] chore: remove backend/test-sort-regression.js --- backend/test-sort-regression.js | 391 -------------------------------- 1 file changed, 391 deletions(-) delete mode 100644 backend/test-sort-regression.js diff --git a/backend/test-sort-regression.js b/backend/test-sort-regression.js deleted file mode 100644 index ad1f8fe..0000000 --- a/backend/test-sort-regression.js +++ /dev/null @@ -1,391 +0,0 @@ -/** - * 回归测试脚本:账单倒序排序功能完整验证 - * - * 测试场景: - * 1. 添加5条支出记录(餐饮、交通、购物、娱乐、医疗),每条间隔1秒 - * 2. 添加5条收入记录(工资、奖金、投资、兼职、理财),每条间隔1秒 - * 3. 验证首页最近5条记录按createdAt倒序 - * 4. 验证记账页面所有记录按createdAt倒序 - * 5. 验证筛选后仍保持倒序 - * 6. 同一秒内添加2条记录,验证排序稳定性 - * 7. 删除中间记录后验证剩余记录排序 - * 8. 跨天记录验证排序 - * - * 测试环境: - * - 后端: http://localhost:3001 - * - 用户ID: 1 - * - 账户ID: 3 (招商银行) - */ - -import http from 'http'; -import fs from 'fs'; -import path from 'path'; - -const BASE_URL = 'http://localhost:3001'; -const USER_ID = 1; -const ACCOUNT_ID = 3; -const SCREENSHOT_DIR = 'd:\\Users\\kaifa\\Trae_cn260425\\test-screenshots\\sort-fix-regression'; - -// 测试结果收集 -const testResults = []; -let testCounter = 0; - -function recordTest(name, status, details = '') { - testCounter++; - testResults.push({ - id: testCounter, - name, - status, - details, - timestamp: new Date().toISOString() - }); - const icon = status === 'PASS' ? '✅' : status === 'FAIL' ? '❌' : '⚠️'; - console.log(` ${icon} [TC-${testCounter}] ${name}: ${status}${details ? ' - ' + details : ''}`); -} - -function apiRequest(method, urlPath, body = null) { - return new Promise((resolve, reject) => { - const url = new URL(urlPath, BASE_URL); - const options = { - hostname: url.hostname, - port: url.port, - path: url.pathname + url.search, - method, - headers: { 'Content-Type': 'application/json' } - }; - - const req = http.request(options, (res) => { - let data = ''; - res.on('data', (chunk) => { data += chunk; }); - res.on('end', () => { - try { - resolve({ status: res.statusCode, body: JSON.parse(data) }); - } catch (e) { - resolve({ status: res.statusCode, body: data }); - } - }); - }); - - req.on('error', reject); - if (body) req.write(JSON.stringify(body)); - req.end(); - }); -} - -function sleep(ms) { - return new Promise(resolve => setTimeout(resolve, ms)); -} - -function logSeparator(title) { - console.log(`\n${'='.repeat(70)}`); - if (title) console.log(` ${title}`); - console.log('='.repeat(70)); -} - -async function createRecord(type, category, amount, description, date) { - const result = await apiRequest('POST', '/api/records', { - userId: USER_ID, - accountId: ACCOUNT_ID, - type, - amount, - category, - description, - date: date || new Date().toISOString().split('T')[0] - }); - return result.body.data; -} - -async function getRecords(filterType = null) { - let url = `/api/records?userId=${USER_ID}`; - if (filterType) url += `&type=${filterType}`; - const result = await apiRequest('GET', url); - return result.body.data || []; -} - -async function deleteRecord(id) { - const result = await apiRequest('DELETE', `/api/records/${id}`); - return result.status === 200; -} - -// ==================== 测试执行 ==================== - -async function runTests() { - // 创建截图目录 - if (!fs.existsSync(SCREENSHOT_DIR)) { - fs.mkdirSync(SCREENSHOT_DIR, { recursive: true }); - } - - logSeparator('🚀 账单倒序排序功能 - 完整回归测试'); - console.log(` 测试时间: ${new Date().toLocaleString('zh-CN')}`); - console.log(` 测试环境: ${BASE_URL}`); - console.log(` 用户ID: ${USER_ID} | 账户ID: ${ACCOUNT_ID}`); - - // ---- 测试1: 添加5条支出记录 ---- - logSeparator('📝 测试1: 添加5条支出记录(每条间隔1秒)'); - - const expenseRecords = [ - { category: '餐饮', amount: 35, description: '午餐外卖' }, - { category: '交通', amount: 15, description: '地铁通勤' }, - { category: '购物', amount: 199, description: '日用品采购' }, - { category: '娱乐', amount: 68, description: '电影票' }, - { category: '医疗', amount: 120, description: '感冒药' }, - ]; - - const createdExpenseIds = []; - for (const exp of expenseRecords) { - const record = await createRecord('expense', exp.category, exp.amount, exp.description); - createdExpenseIds.push(record.id); - console.log(` ➕ 支出: ${exp.category} ¥${exp.amount} | ID:${record.id} | createdAt:${new Date(record.createdAt).toLocaleString('zh-CN')}`); - await sleep(1100); // 间隔1.1秒确保createdAt不同 - } - recordTest('添加5条支出记录', 'PASS', `IDs: ${createdExpenseIds.join(', ')}`); - - // ---- 测试2: 添加5条收入记录 ---- - logSeparator('📝 测试2: 添加5条收入记录(每条间隔1秒)'); - - const incomeRecords = [ - { category: '工资', amount: 12000, description: '4月工资' }, - { category: '奖金', amount: 2000, description: '季度奖金' }, - { category: '投资', amount: 500, description: '基金收益' }, - { category: '兼职', amount: 800, description: '技术咨询' }, - { category: '理财', amount: 300, description: '银行理财到期' }, - ]; - - const createdIncomeIds = []; - for (const inc of incomeRecords) { - const record = await createRecord('income', inc.category, inc.amount, inc.description); - createdIncomeIds.push(record.id); - console.log(` ➕ 收入: ${inc.category} ¥${inc.amount} | ID:${record.id} | createdAt:${new Date(record.createdAt).toLocaleString('zh-CN')}`); - await sleep(1100); - } - recordTest('添加5条收入记录', 'PASS', `IDs: ${createdIncomeIds.join(', ')}`); - - // ---- 测试3: 验证API返回按createdAt倒序 ---- - logSeparator('🔍 测试3: 验证API返回按createdAt倒序'); - - const allRecords = await getRecords(); - console.log(` 📊 总记录数: ${allRecords.length}`); - console.log(` 📋 前10条记录:`); - allRecords.slice(0, 10).forEach((r, i) => { - const isNew = createdExpenseIds.includes(r.id) || createdIncomeIds.includes(r.id) ? ' ⬅️ 新' : ''; - console.log(` [${i+1}] ${r.type==='income'?'收入':'支出'} ${r.category} ¥${r.amount} | createdAt: ${new Date(r.createdAt).toLocaleString('zh-CN')}${isNew}`); - }); - - // 检查createdAt是否严格降序 - let sortedCorrectly = true; - for (let i = 0; i < allRecords.length - 1; i++) { - if (new Date(allRecords[i].createdAt).getTime() < new Date(allRecords[i+1].createdAt).getTime()) { - sortedCorrectly = false; - console.log(` ❌ 排序异常: [${i+1}] ${new Date(allRecords[i].createdAt).toISOString()} < [${i+2}] ${new Date(allRecords[i+1].createdAt).toISOString()}`); - break; - } - } - recordTest('API按createdAt倒序', sortedCorrectly ? 'PASS' : 'FAIL', `共${allRecords.length}条记录`); - - // 验证最新创建的记录在第一位 - const lastCreatedId = createdIncomeIds[createdIncomeIds.length - 1]; // 最后一条收入 - const firstRecord = allRecords[0]; - recordTest('最新记录在API第一位', firstRecord.id === lastCreatedId ? 'PASS' : 'FAIL', - `预期ID:${lastCreatedId}, 实际ID:${firstRecord.id}`); - - // ---- 测试4: 验证首页最近5条记录 ---- - logSeparator('🔍 测试4: 验证首页最近5条记录(前5条)'); - - const recent5 = allRecords.slice(0, 5); - console.log(` 📋 最近5条记录:`); - recent5.forEach((r, i) => { - console.log(` [${i+1}] ${r.type==='income'?'收入':'支出'} ${r.category} ¥${r.amount} | ${new Date(r.createdAt).toLocaleTimeString('zh-CN')}`); - }); - - // 验证前5条也是按createdAt降序 - let recent5Sorted = true; - for (let i = 0; i < recent5.length - 1; i++) { - if (new Date(recent5[i].createdAt).getTime() < new Date(recent5[i+1].createdAt).getTime()) { - recent5Sorted = false; - break; - } - } - recordTest('首页最近5条按createdAt倒序', recent5Sorted ? 'PASS' : 'FAIL'); - - // ---- 测试5: 支出筛选后排序验证 ---- - logSeparator('🔍 测试5: 支出筛选后排序验证'); - - const expenseFiltered = await getRecords('expense'); - console.log(` 📊 支出记录数: ${expenseFiltered.length}`); - - let expenseSorted = true; - for (let i = 0; i < expenseFiltered.length - 1; i++) { - if (new Date(expenseFiltered[i].createdAt).getTime() < new Date(expenseFiltered[i+1].createdAt).getTime()) { - expenseSorted = false; - break; - } - } - - console.log(` 📋 前5条支出:`); - expenseFiltered.slice(0, 5).forEach((r, i) => { - console.log(` [${i+1}] ${r.category} ¥${r.amount} | ${new Date(r.createdAt).toLocaleTimeString('zh-CN')}`); - }); - recordTest('支出筛选后按createdAt倒序', expenseSorted ? 'PASS' : 'FAIL'); - - // ---- 测试6: 收入筛选后排序验证 ---- - logSeparator('🔍 测试6: 收入筛选后排序验证'); - - const incomeFiltered = await getRecords('income'); - console.log(` 📊 收入记录数: ${incomeFiltered.length}`); - - let incomeSorted = true; - for (let i = 0; i < incomeFiltered.length - 1; i++) { - if (new Date(incomeFiltered[i].createdAt).getTime() < new Date(incomeFiltered[i+1].createdAt).getTime()) { - incomeSorted = false; - break; - } - } - - console.log(` 📋 前5条收入:`); - incomeFiltered.slice(0, 5).forEach((r, i) => { - console.log(` [${i+1}] ${r.category} ¥${r.amount} | ${new Date(r.createdAt).toLocaleTimeString('zh-CN')}`); - }); - recordTest('收入筛选后按createdAt倒序', incomeSorted ? 'PASS' : 'FAIL'); - - // ---- 测试7: 同一秒内添加2条记录 ---- - logSeparator('🔍 测试7: 同一秒内添加2条记录(排序稳定性)'); - - const sameTime1 = await createRecord('expense', '餐饮', 10, '同时记录1'); - const sameTime2 = await createRecord('expense', '交通', 20, '同时记录2'); - console.log(` ➕ 记录1: ID:${sameTime1.id} | createdAt:${new Date(sameTime1.createdAt).toISOString()}`); - console.log(` ➕ 记录2: ID:${sameTime2.id} | createdAt:${new Date(sameTime2.createdAt).toISOString()}`); - - const recordsAfterSameTime = await getRecords('expense'); - const idx1 = recordsAfterSameTime.findIndex(r => r.id === sameTime1.id); - const idx2 = recordsAfterSameTime.findIndex(r => r.id === sameTime2.id); - console.log(` 记录1位置: 第${idx1+1}位 | 记录2位置: 第${idx2+1}位`); - - // SQLite的createdAt由数据库自动生成,即使同一秒也应该有微小差异或保持插入顺序 - // 只要不出现排序混乱即可 - recordTest('同秒记录排序稳定性', 'PASS', `记录1位置:${idx1+1}, 记录2位置:${idx2+1} (同秒允许顺序不定)`); - - // ---- 测试8: 删除中间记录后排序验证 ---- - logSeparator('🔍 测试8: 删除中间记录后排序验证'); - - // 删除第3条创建的支出记录(购物) - const deleteTargetId = createdExpenseIds[2]; // 购物记录 - const beforeDelete = await getRecords(); - const deleteTargetIndex = beforeDelete.findIndex(r => r.id === deleteTargetId); - console.log(` 🗑️ 删除记录: ID:${deleteTargetId} (${beforeDelete[deleteTargetIndex]?.category} ¥${beforeDelete[deleteTargetIndex]?.amount})`); - - const deleteSuccess = await deleteRecord(deleteTargetId); - recordTest('删除记录', deleteSuccess ? 'PASS' : 'FAIL', `ID:${deleteTargetId}`); - - const afterDelete = await getRecords(); - let afterDeleteSorted = true; - for (let i = 0; i < afterDelete.length - 1; i++) { - if (new Date(afterDelete[i].createdAt).getTime() < new Date(afterDelete[i+1].createdAt).getTime()) { - afterDeleteSorted = false; - console.log(` ❌ 排序异常: [${i+1}] < [${i+2}]`); - break; - } - } - console.log(` 📊 删除后记录数: ${afterDelete.length} (原${beforeDelete.length}条)`); - recordTest('删除后剩余记录排序正确', afterDeleteSorted ? 'PASS' : 'FAIL'); - - // ---- 测试9: 跨天记录排序验证 ---- - logSeparator('🔍 测试9: 跨天记录排序验证'); - - // 创建昨天的记录 - const yesterday = new Date(); - yesterday.setDate(yesterday.getDate() - 1); - const yesterdayStr = yesterday.toISOString().split('T')[0]; - - const yesterdayRecord = await createRecord('expense', '餐饮', 50, '昨天晚餐', yesterdayStr); - console.log(` ➕ 昨天记录: ${yesterdayRecord.category} ¥${yesterdayRecord.amount} | date:${yesterdayStr} | createdAt:${new Date(yesterdayRecord.createdAt).toLocaleString('zh-CN')}`); - - const recordsWithYesterday = await getRecords(); - const yesterdayIdx = recordsWithYesterday.findIndex(r => r.id === yesterdayRecord.id); - console.log(` 昨天记录位置: 第${yesterdayIdx+1}位 (共${recordsWithYesterday.length}条)`); - - // 昨天的记录应该排在今天的记录后面 - const todayRecords = recordsWithYesterday.filter(r => { - const createdDate = new Date(r.createdAt).toLocaleDateString('zh-CN'); - const todayDate = new Date().toLocaleDateString('zh-CN'); - return createdDate === todayDate; - }); - - const yesterdayRecordsAfter = recordsWithYesterday.filter(r => { - const createdDate = new Date(r.createdAt).toLocaleDateString('zh-CN'); - const todayDate = new Date().toLocaleDateString('zh-CN'); - return createdDate !== todayDate; - }); - - let crossDaySorted = true; - // 验证所有今天的记录都在昨天的记录前面 - if (todayRecords.length > 0 && yesterdayRecordsAfter.length > 0) { - const lastToday = new Date(todayRecords[todayRecords.length - 1].createdAt).getTime(); - const firstYesterday = new Date(yesterdayRecordsAfter[0].createdAt).getTime(); - if (lastToday < firstYesterday) { - crossDaySorted = false; - } - } - recordTest('跨天记录排序正确', crossDaySorted ? 'PASS' : 'FAIL', - `今天:${todayRecords.length}条 | 昨天:${yesterdayRecordsAfter.length}条`); - - // ---- 清理测试数据 ---- - logSeparator('🧹 清理测试数据'); - - const allCreatedIds = [...createdExpenseIds.slice(0, 2), ...createdExpenseIds.slice(3), ...createdIncomeIds, sameTime1.id, sameTime2.id, yesterdayRecord.id]; - // 注意: createdExpenseIds[2] (购物) 已经删除了 - - for (const id of allCreatedIds) { - await deleteRecord(id); - } - console.log(` ✅ 已清理 ${allCreatedIds.length} 条测试记录`); - - // ---- 生成测试报告 ---- - logSeparator('📊 测试报告'); - - const passCount = testResults.filter(r => r.status === 'PASS').length; - const failCount = testResults.filter(r => r.status === 'FAIL').length; - const totalCount = testResults.length; - - console.log(`\n 总测试用例: ${totalCount}`); - console.log(` ✅ 通过: ${passCount}`); - console.log(` ❌ 失败: ${failCount}`); - console.log(` 通过率: ${((passCount / totalCount) * 100).toFixed(1)}%`); - - console.log(`\n 详细结果:`); - testResults.forEach(r => { - const icon = r.status === 'PASS' ? '✅' : '❌'; - console.log(` ${icon} [TC-${r.id}] ${r.name}${r.details ? ': ' + r.details : ''}`); - }); - - // 保存测试报告到文件 - const reportPath = path.join(SCREENSHOT_DIR, 'test-report.json'); - const reportData = { - testDate: new Date().toISOString(), - environment: { - backendUrl: BASE_URL, - userId: USER_ID, - accountId: ACCOUNT_ID - }, - summary: { - total: totalCount, - passed: passCount, - failed: failCount, - passRate: `${((passCount / totalCount) * 100).toFixed(1)}%` - }, - testCases: testResults - }; - - fs.writeFileSync(reportPath, JSON.stringify(reportData, null, 2)); - console.log(`\n 📁 测试报告已保存: ${reportPath}`); - - if (failCount > 0) { - console.log(`\n ⚠️ 【高危】存在${failCount}个失败用例,建议修复后重新测试`); - } else { - console.log(`\n 🎉 所有测试用例通过!账单倒序排序功能修复验证成功`); - } -} - -runTests().catch(err => { - console.error('测试执行失败:', err); - process.exit(1); -});