feat: Initial commit of PDF Tools project
This commit is contained in:
308
server/tests/api.test.js
Normal file
308
server/tests/api.test.js
Normal file
@@ -0,0 +1,308 @@
|
||||
const request = require('supertest');
|
||||
const app = require('../index');
|
||||
|
||||
describe('PDF转换工具 API 测试', () => {
|
||||
|
||||
describe('系统健康检查', () => {
|
||||
test('GET /health 应该返回系统健康状态', async () => {
|
||||
const response = await request(app)
|
||||
.get('/health')
|
||||
.expect(200);
|
||||
|
||||
expect(response.body).toHaveProperty('status', 'OK');
|
||||
expect(response.body).toHaveProperty('timestamp');
|
||||
expect(response.body).toHaveProperty('uptime');
|
||||
expect(response.body).toHaveProperty('memory');
|
||||
});
|
||||
});
|
||||
|
||||
describe('系统信息 API', () => {
|
||||
test('GET /api/system/health 应该返回详细健康信息', async () => {
|
||||
const response = await request(app)
|
||||
.get('/api/system/health')
|
||||
.expect(200);
|
||||
|
||||
expect(response.body).toHaveProperty('status');
|
||||
expect(response.body).toHaveProperty('timestamp');
|
||||
expect(response.body).toHaveProperty('system');
|
||||
});
|
||||
|
||||
test('GET /api/system/stats 应该返回系统统计信息', async () => {
|
||||
const response = await request(app)
|
||||
.get('/api/system/stats')
|
||||
.expect(200);
|
||||
|
||||
expect(response.body.success).toBe(true);
|
||||
expect(response.body.data).toHaveProperty('files');
|
||||
expect(response.body.data).toHaveProperty('conversions');
|
||||
expect(response.body.data).toHaveProperty('users');
|
||||
});
|
||||
|
||||
test('GET /api/system/formats 应该返回支持的格式信息', async () => {
|
||||
const response = await request(app)
|
||||
.get('/api/system/formats')
|
||||
.expect(200);
|
||||
|
||||
expect(response.body.success).toBe(true);
|
||||
expect(response.body.data).toHaveProperty('input');
|
||||
expect(response.body.data).toHaveProperty('output');
|
||||
expect(Array.isArray(response.body.data.input)).toBe(true);
|
||||
expect(Array.isArray(response.body.data.output)).toBe(true);
|
||||
});
|
||||
|
||||
test('GET /api/system/config 应该返回系统配置信息', async () => {
|
||||
const response = await request(app)
|
||||
.get('/api/system/config')
|
||||
.expect(200);
|
||||
|
||||
expect(response.body.success).toBe(true);
|
||||
expect(response.body.data).toHaveProperty('upload');
|
||||
expect(response.body.data).toHaveProperty('conversion');
|
||||
expect(response.body.data).toHaveProperty('security');
|
||||
});
|
||||
});
|
||||
|
||||
describe('用户认证 API', () => {
|
||||
const testUser = {
|
||||
email: 'test@example.com',
|
||||
username: 'testuser',
|
||||
password: 'password123'
|
||||
};
|
||||
|
||||
test('POST /api/users/register 应该成功注册新用户', async () => {
|
||||
const response = await request(app)
|
||||
.post('/api/users/register')
|
||||
.send(testUser)
|
||||
.expect(201);
|
||||
|
||||
expect(response.body.success).toBe(true);
|
||||
expect(response.body.data).toHaveProperty('user');
|
||||
expect(response.body.data).toHaveProperty('token');
|
||||
expect(response.body.data.user.email).toBe(testUser.email);
|
||||
});
|
||||
|
||||
test('POST /api/users/register 应该拒绝重复邮箱', async () => {
|
||||
// 先注册一次
|
||||
await request(app)
|
||||
.post('/api/users/register')
|
||||
.send(testUser);
|
||||
|
||||
// 再次注册相同邮箱
|
||||
const response = await request(app)
|
||||
.post('/api/users/register')
|
||||
.send(testUser)
|
||||
.expect(400);
|
||||
|
||||
expect(response.body.success).toBe(false);
|
||||
expect(response.body.message).toContain('邮箱已被注册');
|
||||
});
|
||||
|
||||
test('POST /api/users/login 应该成功登录', async () => {
|
||||
// 先注册用户
|
||||
await request(app)
|
||||
.post('/api/users/register')
|
||||
.send(testUser);
|
||||
|
||||
// 登录
|
||||
const response = await request(app)
|
||||
.post('/api/users/login')
|
||||
.send({
|
||||
email: testUser.email,
|
||||
password: testUser.password
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
expect(response.body.success).toBe(true);
|
||||
expect(response.body.data).toHaveProperty('user');
|
||||
expect(response.body.data).toHaveProperty('token');
|
||||
});
|
||||
|
||||
test('POST /api/users/login 应该拒绝错误密码', async () => {
|
||||
// 先注册用户
|
||||
await request(app)
|
||||
.post('/api/users/register')
|
||||
.send(testUser);
|
||||
|
||||
// 使用错误密码登录
|
||||
const response = await request(app)
|
||||
.post('/api/users/login')
|
||||
.send({
|
||||
email: testUser.email,
|
||||
password: 'wrongpassword'
|
||||
})
|
||||
.expect(401);
|
||||
|
||||
expect(response.body.success).toBe(false);
|
||||
expect(response.body.message).toContain('邮箱或密码错误');
|
||||
});
|
||||
});
|
||||
|
||||
describe('转换任务 API', () => {
|
||||
test('POST /api/convert/start 应该创建转换任务', async () => {
|
||||
const conversionData = {
|
||||
fileId: 'test-file-id',
|
||||
outputFormat: 'docx',
|
||||
options: {
|
||||
preserveLayout: true,
|
||||
includeImages: true
|
||||
}
|
||||
};
|
||||
|
||||
const response = await request(app)
|
||||
.post('/api/convert/start')
|
||||
.send(conversionData)
|
||||
.expect(200);
|
||||
|
||||
expect(response.body.success).toBe(true);
|
||||
expect(response.body.data).toHaveProperty('taskId');
|
||||
expect(response.body.data).toHaveProperty('status', 'pending');
|
||||
expect(response.body.data).toHaveProperty('progress', 0);
|
||||
});
|
||||
|
||||
test('POST /api/convert/start 应该验证必要参数', async () => {
|
||||
const response = await request(app)
|
||||
.post('/api/convert/start')
|
||||
.send({})
|
||||
.expect(400);
|
||||
|
||||
expect(response.body.success).toBe(false);
|
||||
expect(response.body.message).toContain('缺少必要参数');
|
||||
});
|
||||
|
||||
test('POST /api/convert/start 应该验证输出格式', async () => {
|
||||
const response = await request(app)
|
||||
.post('/api/convert/start')
|
||||
.send({
|
||||
fileId: 'test-file-id',
|
||||
outputFormat: 'invalid-format'
|
||||
})
|
||||
.expect(400);
|
||||
|
||||
expect(response.body.success).toBe(false);
|
||||
expect(response.body.message).toContain('不支持的输出格式');
|
||||
});
|
||||
|
||||
test('GET /api/convert/status/:taskId 应该返回任务状态', async () => {
|
||||
// 先创建任务
|
||||
const createResponse = await request(app)
|
||||
.post('/api/convert/start')
|
||||
.send({
|
||||
fileId: 'test-file-id',
|
||||
outputFormat: 'docx'
|
||||
});
|
||||
|
||||
const taskId = createResponse.body.data.taskId;
|
||||
|
||||
// 查询状态
|
||||
const response = await request(app)
|
||||
.get(`/api/convert/status/${taskId}`)
|
||||
.expect(200);
|
||||
|
||||
expect(response.body.success).toBe(true);
|
||||
expect(response.body.data).toHaveProperty('taskId', taskId);
|
||||
expect(response.body.data).toHaveProperty('status');
|
||||
expect(response.body.data).toHaveProperty('progress');
|
||||
});
|
||||
|
||||
test('GET /api/convert/status/:taskId 应该处理不存在的任务', async () => {
|
||||
const response = await request(app)
|
||||
.get('/api/convert/status/non-existent-task')
|
||||
.expect(404);
|
||||
|
||||
expect(response.body.success).toBe(false);
|
||||
expect(response.body.message).toContain('转换任务未找到');
|
||||
});
|
||||
});
|
||||
|
||||
describe('文件管理 API', () => {
|
||||
test('GET /api/files/:fileId 应该返回文件信息', async () => {
|
||||
const response = await request(app)
|
||||
.get('/api/files/test-file-id')
|
||||
.expect(200);
|
||||
|
||||
expect(response.body.success).toBe(true);
|
||||
expect(response.body.data).toHaveProperty('fileId');
|
||||
expect(response.body.data).toHaveProperty('originalName');
|
||||
expect(response.body.data).toHaveProperty('fileSize');
|
||||
});
|
||||
|
||||
test('DELETE /api/files/:fileId 应该删除文件', async () => {
|
||||
const response = await request(app)
|
||||
.delete('/api/files/test-file-id')
|
||||
.expect(200);
|
||||
|
||||
expect(response.body.success).toBe(true);
|
||||
expect(response.body.message).toContain('文件删除成功');
|
||||
});
|
||||
});
|
||||
|
||||
describe('错误处理', () => {
|
||||
test('不存在的路由应该返回404', async () => {
|
||||
const response = await request(app)
|
||||
.get('/api/non-existent-route')
|
||||
.expect(404);
|
||||
|
||||
expect(response.body.success).toBe(false);
|
||||
expect(response.body.message).toContain('接口不存在');
|
||||
});
|
||||
|
||||
test('无效的JSON应该返回400', async () => {
|
||||
const response = await request(app)
|
||||
.post('/api/convert/start')
|
||||
.send('invalid json')
|
||||
.set('Content-Type', 'application/json')
|
||||
.expect(400);
|
||||
});
|
||||
});
|
||||
|
||||
describe('请求限流', () => {
|
||||
test('短时间内大量请求应该被限制', async () => {
|
||||
const requests = [];
|
||||
|
||||
// 发送多个并发请求
|
||||
for (let i = 0; i < 20; i++) {
|
||||
requests.push(
|
||||
request(app)
|
||||
.get('/api/system/health')
|
||||
);
|
||||
}
|
||||
|
||||
const responses = await Promise.all(requests);
|
||||
|
||||
// 应该有一些请求被限制
|
||||
const rateLimitedResponses = responses.filter(res => res.status === 429);
|
||||
expect(rateLimitedResponses.length).toBeGreaterThan(0);
|
||||
}, 10000);
|
||||
});
|
||||
});
|
||||
|
||||
// 测试工具函数
|
||||
describe('工具函数测试', () => {
|
||||
test('应该正确格式化文件大小', () => {
|
||||
const formatBytes = require('../utils/formatBytes');
|
||||
|
||||
expect(formatBytes(0)).toBe('0 Bytes');
|
||||
expect(formatBytes(1024)).toBe('1.0 KB');
|
||||
expect(formatBytes(1048576)).toBe('1.0 MB');
|
||||
expect(formatBytes(1073741824)).toBe('1.0 GB');
|
||||
});
|
||||
|
||||
test('应该正确验证邮箱格式', () => {
|
||||
const validateEmail = require('../utils/validateEmail');
|
||||
|
||||
expect(validateEmail('test@example.com')).toBe(true);
|
||||
expect(validateEmail('invalid-email')).toBe(false);
|
||||
expect(validateEmail('test@')).toBe(false);
|
||||
expect(validateEmail('@example.com')).toBe(false);
|
||||
});
|
||||
|
||||
test('应该正确生成UUID', () => {
|
||||
const { v4: uuidv4 } = require('uuid');
|
||||
|
||||
const uuid1 = uuidv4();
|
||||
const uuid2 = uuidv4();
|
||||
|
||||
expect(uuid1).not.toBe(uuid2);
|
||||
expect(uuid1).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user