feat: 个人记账与预算管理系统 MVP 初始版本

This commit is contained in:
2026-04-28 22:57:38 +08:00
commit d2b2e9887a
88 changed files with 29512 additions and 0 deletions
+314
View File
@@ -0,0 +1,314 @@
import http from 'http'
const BASE_URL = 'http://localhost:3001'
// 测试结果收集
const testResults = {
passed: 0,
failed: 0,
tests: []
}
// HTTP请求辅助函数
function request(method, path, data = null) {
return new Promise((resolve, reject) => {
const url = new URL(path, BASE_URL)
const options = {
method,
headers: {
'Content-Type': 'application/json'
}
}
const req = http.request(url, options, (res) => {
let body = ''
res.on('data', (chunk) => { body += chunk })
res.on('end', () => {
try {
const response = JSON.parse(body)
resolve({ status: res.statusCode, data: response })
} catch (e) {
resolve({ status: res.statusCode, data: body })
}
})
})
req.on('error', reject)
if (data) {
req.write(JSON.stringify(data))
}
req.end()
})
}
// 断言函数
function assert(condition, testName, message = '') {
const result = {
name: testName,
passed: condition,
message: message || (condition ? '通过' : '失败')
}
testResults.tests.push(result)
if (condition) {
testResults.passed++
console.log(`${testName}`)
} else {
testResults.failed++
console.log(`${testName}: ${message}`)
}
}
// 检查响应格式
function checkResponseFormat(response, testNamePrefix) {
assert(
response.data.hasOwnProperty('success'),
`${testNamePrefix} - 响应包含success字段`,
`响应缺少success字段,实际响应: ${JSON.stringify(response.data)}`
)
assert(
response.data.hasOwnProperty('data'),
`${testNamePrefix} - 响应包含data字段`,
`响应缺少data字段,实际响应: ${JSON.stringify(response.data)}`
)
assert(
response.data.hasOwnProperty('message'),
`${testNamePrefix} - 响应包含message字段`,
`响应缺少message字段,实际响应: ${JSON.stringify(response.data)}`
)
}
// 主测试流程
async function runTests() {
console.log('='.repeat(60))
console.log('个人理财系统 - API集成测试')
console.log('='.repeat(60))
try {
// 1. 健康检查
console.log('\n【1】健康检查测试')
console.log('-'.repeat(40))
const healthRes = await request('GET', '/health')
assert(healthRes.status === 200, '健康检查状态码200')
checkResponseFormat(healthRes, '健康检查')
assert(healthRes.data.success === true, '健康检查success为true')
// 2. 基础API信息
console.log('\n【2】API基础信息测试')
console.log('-'.repeat(40))
const apiRes = await request('GET', '/api')
assert(apiRes.status === 200, 'API信息状态码200')
checkResponseFormat(apiRes, 'API信息')
// 3. 用户接口测试
console.log('\n【3】用户接口测试')
console.log('-'.repeat(40))
// 创建测试用户
const createUserRes = await request('POST', '/api/users', {
name: '测试用户',
email: 'test' + Date.now() + '@example.com'
})
assert(createUserRes.status === 200, '创建用户状态码200')
checkResponseFormat(createUserRes, '创建用户')
assert(createUserRes.data.success === true, '创建用户success为true')
assert(createUserRes.data.data !== null, '创建用户返回数据不为null')
assert(createUserRes.data.data.id !== undefined, '创建用户返回ID')
const testUserId = createUserRes.data.data.id
// 获取用户列表
const getUsersRes = await request('GET', '/api/users')
assert(getUsersRes.status === 200, '获取用户列表状态码200')
checkResponseFormat(getUsersRes, '获取用户列表')
assert(Array.isArray(getUsersRes.data.data), '用户列表为数组')
// 4. 账户接口测试
console.log('\n【4】账户接口测试')
console.log('-'.repeat(40))
// 创建账户
const createAccountRes = await request('POST', '/api/accounts', {
userId: testUserId,
name: '测试账户',
type: 'cash',
balance: 1000
})
assert(createAccountRes.status === 200, '创建账户状态码200')
checkResponseFormat(createAccountRes, '创建账户')
assert(createAccountRes.data.success === true, '创建账户success为true')
assert(createAccountRes.data.data !== null, '创建账户返回数据不为null')
const testAccountId = createAccountRes.data.data.id
// 获取账户列表
const getAccountsRes = await request('GET', `/api/accounts?userId=${testUserId}`)
assert(getAccountsRes.status === 200, '获取账户列表状态码200')
checkResponseFormat(getAccountsRes, '获取账户列表')
assert(Array.isArray(getAccountsRes.data.data), '账户列表为数组')
// 获取单个账户
const getAccountRes = await request('GET', `/api/accounts/${testAccountId}`)
assert(getAccountRes.status === 200, '获取单个账户状态码200')
checkResponseFormat(getAccountRes, '获取单个账户')
assert(getAccountRes.data.data.id === testAccountId, '账户ID匹配')
// 更新账户
const updateAccountRes = await request('PUT', `/api/accounts/${testAccountId}`, {
name: '测试账户(已更新)',
balance: 2000
})
assert(updateAccountRes.status === 200, '更新账户状态码200')
checkResponseFormat(updateAccountRes, '更新账户')
assert(updateAccountRes.data.data.name === '测试账户(已更新)', '账户名称已更新')
// 5. 交易记录接口测试
console.log('\n【5】交易记录接口测试')
console.log('-'.repeat(40))
// 创建收入记录
const createIncomeRes = await request('POST', '/api/records', {
userId: testUserId,
accountId: testAccountId,
type: 'income',
amount: 500,
category: '工资',
description: '测试收入'
})
assert(createIncomeRes.status === 200, '创建收入记录状态码200')
checkResponseFormat(createIncomeRes, '创建收入记录')
assert(createIncomeRes.data.success === true, '创建收入记录success为true')
const testRecordId = createIncomeRes.data.data.id
// 创建支出记录
const createExpenseRes = await request('POST', '/api/records', {
userId: testUserId,
accountId: testAccountId,
type: 'expense',
amount: 200,
category: '餐饮',
description: '测试支出'
})
assert(createExpenseRes.status === 200, '创建支出记录状态码200')
checkResponseFormat(createExpenseRes, '创建支出记录')
// 获取记录列表
const getRecordsRes = await request('GET', `/api/records?userId=${testUserId}`)
assert(getRecordsRes.status === 200, '获取记录列表状态码200')
checkResponseFormat(getRecordsRes, '获取记录列表')
assert(Array.isArray(getRecordsRes.data.data), '记录列表为数组')
// 获取单个记录
const getRecordRes = await request('GET', `/api/records/${testRecordId}`)
assert(getRecordRes.status === 200, '获取单个记录状态码200')
checkResponseFormat(getRecordRes, '获取单个记录')
// 更新记录
const updateRecordRes = await request('PUT', `/api/records/${testRecordId}`, {
description: '测试收入(已更新)'
})
assert(updateRecordRes.status === 200, '更新记录状态码200')
checkResponseFormat(updateRecordRes, '更新记录')
// 6. 预算接口测试
console.log('\n【6】预算接口测试')
console.log('-'.repeat(40))
const currentMonth = new Date().toISOString().slice(0, 7)
// 创建预算
const createBudgetRes = await request('POST', '/api/budgets', {
userId: testUserId,
category: '餐饮',
amount: 1000,
month: currentMonth
})
assert(createBudgetRes.status === 200, '创建预算状态码200')
checkResponseFormat(createBudgetRes, '创建预算')
assert(createBudgetRes.data.success === true, '创建预算success为true')
const testBudgetId = createBudgetRes.data.data.id
// 获取预算列表
const getBudgetsRes = await request('GET', `/api/budgets?userId=${testUserId}`)
assert(getBudgetsRes.status === 200, '获取预算列表状态码200')
checkResponseFormat(getBudgetsRes, '获取预算列表')
assert(Array.isArray(getBudgetsRes.data.data), '预算列表为数组')
// 获取单个预算
const getBudgetRes = await request('GET', `/api/budgets/${testBudgetId}`)
assert(getBudgetRes.status === 200, '获取单个预算状态码200')
checkResponseFormat(getBudgetRes, '获取单个预算')
// 更新预算
const updateBudgetRes = await request('PUT', `/api/budgets/${testBudgetId}`, {
amount: 1500
})
assert(updateBudgetRes.status === 200, '更新预算状态码200')
checkResponseFormat(updateBudgetRes, '更新预算')
// 7. 统计接口测试
console.log('\n【7】统计接口测试')
console.log('-'.repeat(40))
// 月度统计
const monthlyStatsRes = await request('GET', `/api/statistics/monthly?userId=${testUserId}&month=${currentMonth}`)
assert(monthlyStatsRes.status === 200, '月度统计状态码200')
checkResponseFormat(monthlyStatsRes, '月度统计')
assert(monthlyStatsRes.data.data.totalIncome !== undefined, '月度统计包含总收入')
assert(monthlyStatsRes.data.data.totalExpense !== undefined, '月度统计包含总支出')
// 趋势统计
const trendRes = await request('GET', `/api/statistics/trend?userId=${testUserId}`)
assert(trendRes.status === 200, '趋势统计状态码200')
checkResponseFormat(trendRes, '趋势统计')
assert(Array.isArray(trendRes.data.data), '趋势统计返回数组')
// 8. 仪表盘接口测试
console.log('\n【8】仪表盘接口测试')
console.log('-'.repeat(40))
const dashboardRes = await request('GET', `/api/dashboard/summary?userId=${testUserId}`)
assert(dashboardRes.status === 200, '仪表盘状态码200')
checkResponseFormat(dashboardRes, '仪表盘')
assert(dashboardRes.data.data.totalBalance !== undefined, '仪表盘包含总余额')
assert(dashboardRes.data.data.monthIncome !== undefined, '仪表盘包含本月收入')
assert(dashboardRes.data.data.monthExpense !== undefined, '仪表盘包含本月支出')
// 9. 清理测试数据
console.log('\n【9】清理测试数据')
console.log('-'.repeat(40))
// 删除记录
await request('DELETE', `/api/records/${testRecordId}`)
// 删除预算
await request('DELETE', `/api/budgets/${testBudgetId}`)
// 删除账户
await request('DELETE', `/api/accounts/${testAccountId}`)
console.log('✅ 测试数据清理完成')
} catch (error) {
console.error('❌ 测试过程出错:', error)
}
// 输出测试报告
console.log('\n' + '='.repeat(60))
console.log('测试报告摘要')
console.log('='.repeat(60))
console.log(`总测试数: ${testResults.tests.length}`)
console.log(`✅ 通过: ${testResults.passed}`)
console.log(`❌ 失败: ${testResults.failed}`)
console.log(`通过率: ${((testResults.passed / testResults.tests.length) * 100).toFixed(1)}%`)
console.log('='.repeat(60))
return testResults
}
// 运行测试
runTests().then(results => {
process.exit(results.failed > 0 ? 1 : 0)
})