Files
chinese-family-tree-2/comprehensive-api-test.js
T
freedakgmail e92884ae53 0.0.1.0
2025-11-23 03:07:39 +08:00

373 lines
11 KiB
JavaScript

#!/usr/bin/env node
const http = require('http');
class APITester {
constructor(baseUrl) {
this.baseUrl = baseUrl;
this.cookies = {};
this.results = [];
}
async request(path, options = {}) {
return new Promise((resolve, reject) => {
const url = new URL(path, this.baseUrl);
const opts = {
hostname: url.hostname,
port: url.port || 3000,
path: url.pathname + url.search,
method: options.method || 'GET',
headers: {
'Content-Type': 'application/json',
...options.headers
}
};
// 添加 cookies
const cookieStr = Object.entries(this.cookies)
.map(([k, v]) => `${k}=${v}`)
.join('; ');
if (cookieStr) {
opts.headers.Cookie = cookieStr;
}
const req = http.request(opts, (res) => {
let data = '';
res.on('data', chunk => data += chunk);
res.on('end', () => {
// 保存 cookies
if (res.headers['set-cookie']) {
res.headers['set-cookie'].forEach(cookie => {
const [nameValue] = cookie.split(';');
const [name, value] = nameValue.split('=');
if (name && value) {
this.cookies[name.trim()] = value.trim();
}
});
}
let parsedData;
try {
parsedData = JSON.parse(data);
} catch {
parsedData = data;
}
resolve({
status: res.statusCode,
data: parsedData,
headers: res.headers
});
});
});
req.on('error', reject);
if (options.body) {
req.write(JSON.stringify(options.body));
}
req.end();
});
}
log(emoji, title, status, message, data = null) {
const result = {
emoji,
title,
status,
message,
data
};
this.results.push(result);
console.log(`\n${emoji} ${title}`);
console.log(` 状态: ${status}`);
console.log(` ${message}`);
if (data) {
console.log(` 数据:`, JSON.stringify(data, null, 2).split('\n').map(l => ' ' + l).join('\n'));
}
}
async testRegister() {
try {
const email = `test-${Date.now()}@example.com`;
const response = await this.request('/api/auth/register', {
method: 'POST',
body: {
email,
password: 'password123',
name: '测试用户'
}
});
if (response.status === 200) {
this.log('✅', '用户注册', 'PASS', `成功注册用户: ${email}`, response.data);
this.testEmail = email;
return true;
} else {
this.log('❌', '用户注册', 'FAIL', `注册失败: ${response.status}`, response.data);
return false;
}
} catch (error) {
this.log('❌', '用户注册', 'ERROR', error.message);
return false;
}
}
async testGetCSRF() {
try {
const response = await this.request('/api/auth/csrf', {
method: 'GET'
});
if (response.status === 200 && response.data.csrfToken) {
this.csrfToken = response.data.csrfToken;
this.log('✅', '获取 CSRF Token', 'PASS', 'CSRF Token 获取成功');
return true;
} else {
this.log('❌', '获取 CSRF Token', 'FAIL', `状态码: ${response.status}`);
return false;
}
} catch (error) {
this.log('❌', '获取 CSRF Token', 'ERROR', error.message);
return false;
}
}
async testLogin() {
try {
const response = await this.request('/api/auth/callback/credentials', {
method: 'POST',
body: {
email: this.testEmail,
password: 'password123',
csrfToken: this.csrfToken,
callbackUrl: 'http://localhost:3000',
json: true
}
});
if (response.status === 200 || response.status === 302) {
this.log('✅', '用户登录', 'PASS', '登录成功,获取到 session');
return true;
} else {
this.log('❌', '用户登录', 'FAIL', `状态码: ${response.status}`, response.data);
return false;
}
} catch (error) {
this.log('❌', '用户登录', 'ERROR', error.message);
return false;
}
}
async testGetTrees() {
try {
const response = await this.request('/api/trees', {
method: 'GET'
});
if (response.status === 200) {
this.log('✅', '获取家族树列表', 'PASS', `成功获取 ${response.data.trees?.length || 0} 个家族树`, response.data);
return true;
} else if (response.status === 401) {
this.log('⚠️', '获取家族树列表', 'SKIP', '需要登录(权限保护正常)', response.data);
return false;
} else {
this.log('❌', '获取家族树列表', 'FAIL', `状态码: ${response.status}`, response.data);
return false;
}
} catch (error) {
this.log('❌', '获取家族树列表', 'ERROR', error.message);
return false;
}
}
async testCreateTree() {
try {
const response = await this.request('/api/trees', {
method: 'POST',
body: {
name: '测试家族树',
description: 'API 自动化测试创建'
}
});
if (response.status === 201) {
this.treeId = response.data.tree.id;
this.log('✅', '创建家族树', 'PASS', `成功创建家族树: ${this.treeId}`, response.data);
return true;
} else if (response.status === 401) {
this.log('⚠️', '创建家族树', 'SKIP', '需要登录(权限保护正常)', response.data);
return false;
} else {
this.log('❌', '创建家族树', 'FAIL', `状态码: ${response.status}`, response.data);
return false;
}
} catch (error) {
this.log('❌', '创建家族树', 'ERROR', error.message);
return false;
}
}
async testGetMembers() {
if (!this.treeId) {
this.log('⚠️', '获取成员列表', 'SKIP', '没有可用的家族树 ID');
return false;
}
try {
const response = await this.request(`/api/trees/${this.treeId}/members`, {
method: 'GET'
});
if (response.status === 200) {
this.log('✅', '获取成员列表', 'PASS', `成功获取 ${response.data.members?.length || 0} 个成员`, response.data);
return true;
} else if (response.status === 401 || response.status === 403) {
this.log('⚠️', '获取成员列表', 'SKIP', '需要权限', response.data);
return false;
} else {
this.log('❌', '获取成员列表', 'FAIL', `状态码: ${response.status}`, response.data);
return false;
}
} catch (error) {
this.log('❌', '获取成员列表', 'ERROR', error.message);
return false;
}
}
async testCreateMember() {
if (!this.treeId) {
this.log('⚠️', '创建成员', 'SKIP', '没有可用的家族树 ID');
return false;
}
try {
const response = await this.request(`/api/trees/${this.treeId}/members`, {
method: 'POST',
body: {
surname: '李',
givenName: '明',
fullName: '李明',
gender: 'MALE',
generation: 1,
birthDate: '1990-01-01'
}
});
if (response.status === 201) {
this.memberId = response.data.member.id;
this.log('✅', '创建成员', 'PASS', `成功创建成员: ${response.data.member.fullName}`, response.data);
return true;
} else if (response.status === 401 || response.status === 403) {
this.log('⚠️', '创建成员', 'SKIP', '需要权限', response.data);
return false;
} else {
this.log('❌', '创建成员', 'FAIL', `状态码: ${response.status}`, response.data);
return false;
}
} catch (error) {
this.log('❌', '创建成员', 'ERROR', error.message);
return false;
}
}
async testUnauthorizedAccess() {
// 清除 cookies 测试未授权访问
const savedCookies = { ...this.cookies };
this.cookies = {};
try {
const response = await this.request('/api/trees', {
method: 'GET'
});
if (response.status === 401) {
this.log('✅', '权限验证', 'PASS', '未登录正确返回 401', response.data);
this.cookies = savedCookies;
return true;
} else {
this.log('❌', '权限验证', 'FAIL', `应该返回 401,实际: ${response.status}`, response.data);
this.cookies = savedCookies;
return false;
}
} catch (error) {
this.log('❌', '权限验证', 'ERROR', error.message);
this.cookies = savedCookies;
return false;
}
}
printSummary() {
console.log('\n\n' + '='.repeat(60));
console.log('📊 测试总结');
console.log('='.repeat(60));
const passed = this.results.filter(r => r.status === 'PASS').length;
const failed = this.results.filter(r => r.status === 'FAIL').length;
const errors = this.results.filter(r => r.status === 'ERROR').length;
const skipped = this.results.filter(r => r.status === 'SKIP').length;
const total = this.results.length;
console.log(`\n总计: ${total} 个测试`);
console.log(`✅ 通过: ${passed}`);
console.log(`❌ 失败: ${failed}`);
console.log(`⚠️ 跳过: ${skipped}`);
console.log(`💥 错误: ${errors}`);
const successRate = total > 0 ? ((passed / (total - skipped)) * 100).toFixed(1) : 0;
console.log(`\n成功率: ${successRate}%`);
if (failed === 0 && errors === 0) {
console.log('\n🎉 所有测试通过!');
} else {
console.log('\n⚠️ 存在失败的测试,请检查上面的详细信息');
}
console.log('='.repeat(60) + '\n');
}
async runAllTests() {
console.log('🚀 开始全面 API 测试...');
console.log('目标: http://localhost:3000');
console.log('='.repeat(60));
// 1. 测试注册
await this.testRegister();
// 2. 测试权限验证(未登录)
await this.testUnauthorizedAccess();
// 3. 获取 CSRF Token
await this.testGetCSRF();
// 4. 测试登录
await this.testLogin();
// 5. 测试获取家族树列表
await this.testGetTrees();
// 6. 测试创建家族树
await this.testCreateTree();
// 7. 测试获取成员列表
await this.testGetMembers();
// 8. 测试创建成员
await this.testCreateMember();
// 9. 再次获取成员列表(验证创建成功)
if (this.memberId) {
await this.testGetMembers();
}
// 打印总结
this.printSummary();
}
}
// 运行测试
const tester = new APITester('http://localhost:3000');
tester.runAllTests().catch(console.error);