feat: 增加测试覆盖率并优化代码质量

test: 添加单元测试和端到端测试
refactor: 重构登录页面和上传模块
ci: 更新测试覆盖率阈值至42%
build: 添加测试相关依赖
docs: 更新测试文档
style: 修复代码格式问题
This commit is contained in:
张翔
2026-03-11 11:14:37 +08:00
parent 8fd7ed84ed
commit b207bfa7af
58 changed files with 14494 additions and 655 deletions
+121
View File
@@ -0,0 +1,121 @@
import { GET, PUT, DELETE } from './route';
import { NextRequest } from 'next/server';
jest.mock('@/lib/auth', () => ({
auth: jest.fn(),
}));
jest.mock('@/lib/auth/permissions', () => ({
hasPermission: jest.fn(),
}));
jest.mock('@/db', () => ({
db: {
select: jest.fn().mockReturnValue({
from: jest.fn().mockReturnValue({
where: jest.fn().mockReturnValue({
limit: jest.fn().mockResolvedValue([{
id: 'test-user-id',
email: 'test@example.com',
name: 'Test User',
role: 'admin',
}]),
}),
}),
}),
update: jest.fn().mockReturnValue({
set: jest.fn().mockReturnValue({
where: jest.fn().mockReturnValue({
returning: jest.fn().mockResolvedValue([{
id: 'test-user-id',
email: 'updated@example.com',
name: 'Updated User',
}]),
}),
}),
}),
delete: jest.fn().mockReturnValue({
where: jest.fn().mockResolvedValue(undefined),
}),
},
}));
describe('/api/admin/users/[id]', () => {
beforeEach(() => {
jest.clearAllMocks();
});
describe('GET', () => {
it('should return 401 if not authenticated', async () => {
const request = new NextRequest('http://localhost/api/admin/users/test-id');
const response = await GET(request, { params: Promise.resolve({ id: 'test-id' }) });
const data = await response.json();
expect(response.status).toBe(401);
expect(data.error).toBe('未授权');
});
it('should return 403 if no permission', async () => {
const { auth } = require('@/lib/auth');
const { hasPermission } = require('@/lib/auth/permissions');
auth.mockResolvedValue({ user: { role: 'viewer' } });
hasPermission.mockReturnValue(false);
const request = new NextRequest('http://localhost/api/admin/users/test-id');
const response = await GET(request, { params: Promise.resolve({ id: 'test-id' }) });
const data = await response.json();
expect(response.status).toBe(403);
expect(data.error).toBe('无权限');
});
it('should return user if authenticated and has permission', async () => {
const { auth } = require('@/lib/auth');
const { hasPermission } = require('@/lib/auth/permissions');
auth.mockResolvedValue({ user: { role: 'admin' } });
hasPermission.mockReturnValue(true);
const request = new NextRequest('http://localhost/api/admin/users/test-id');
const response = await GET(request, { params: Promise.resolve({ id: 'test-id' }) });
const data = await response.json();
expect(response.status).toBe(200);
expect(data.user).toBeDefined();
});
});
describe('PUT', () => {
it('should return 401 if not authenticated', async () => {
const { auth } = require('@/lib/auth');
auth.mockResolvedValue(null);
const request = new NextRequest('http://localhost/api/admin/users/test-id', {
method: 'PUT',
body: JSON.stringify({ name: 'Updated User' }),
});
const response = await PUT(request, { params: Promise.resolve({ id: 'test-id' }) });
const data = await response.json();
expect(response.status).toBe(401);
expect(data.error).toBe('未授权');
});
});
describe('DELETE', () => {
it('should return 401 if not authenticated', async () => {
const { auth } = require('@/lib/auth');
auth.mockResolvedValue(null);
const request = new NextRequest('http://localhost/api/admin/users/test-id', {
method: 'DELETE',
});
const response = await DELETE(request, { params: Promise.resolve({ id: 'test-id' }) });
const data = await response.json();
expect(response.status).toBe(401);
expect(data.error).toBe('未授权');
});
});
});
+135
View File
@@ -0,0 +1,135 @@
import { describe, it, expect, jest, beforeAll, beforeEach } from '@jest/globals';
import { NextRequest } from 'next/server';
import '@testing-library/jest-dom';
const mockAuth = jest.fn();
const mockHasPermission = jest.fn();
const mockDbSelect = jest.fn();
const mockDbInsert = jest.fn();
jest.mock('@/lib/auth', () => ({
auth: mockAuth,
}));
jest.mock('@/lib/auth/permissions', () => ({
hasPermission: mockHasPermission,
}));
jest.mock('@/db', () => ({
db: {
select: () => ({
from: () => ({
where: () => ({
limit: mockDbSelect,
}),
orderBy: mockDbSelect,
}),
}),
insert: () => ({
values: () => ({
returning: mockDbInsert,
}),
}),
},
}));
jest.mock('drizzle-orm', () => ({
eq: jest.fn(),
}));
jest.mock('nanoid', () => ({
nanoid: () => 'test-id-123',
}));
jest.mock('bcryptjs', () => ({
hash: jest.fn().mockResolvedValue('hashed-password'),
}));
jest.mock('@/db/schema', () => ({
users: {},
}));
import { GET, POST } from './route';
describe('/api/admin/users', () => {
beforeEach(() => {
jest.clearAllMocks();
});
describe('GET', () => {
it('should return 401 when not authenticated', async () => {
mockAuth.mockResolvedValueOnce(null);
const request = new NextRequest('http://localhost/api/admin/users');
const response = await GET(request);
const data = await response.json();
expect(response.status).toBe(401);
expect(data.error).toBe('未授权');
});
it('should return 403 when user lacks permission', async () => {
mockAuth.mockResolvedValueOnce({
user: { id: '1', role: 'viewer' },
});
mockHasPermission.mockReturnValueOnce(false);
const request = new NextRequest('http://localhost/api/admin/users');
const response = await GET(request);
const data = await response.json();
expect(response.status).toBe(403);
expect(data.error).toBe('无权限');
});
it('should return users list when authorized', async () => {
mockAuth.mockResolvedValueOnce({
user: { id: '1', role: 'admin' },
});
mockHasPermission.mockReturnValueOnce(true);
mockDbSelect.mockResolvedValueOnce([
{ id: '1', email: 'admin@example.com', name: 'Admin', role: 'admin' },
]);
const request = new NextRequest('http://localhost/api/admin/users');
const response = await GET(request);
const data = await response.json();
expect(response.status).toBe(200);
expect(data.users).toBeDefined();
});
});
describe('POST', () => {
it('should return 401 when not authenticated', async () => {
mockAuth.mockResolvedValueOnce(null);
const request = new NextRequest('http://localhost/api/admin/users', {
method: 'POST',
body: JSON.stringify({ email: 'test@example.com', name: 'Test', password: 'password', role: 'viewer' }),
});
const response = await POST(request);
const data = await response.json();
expect(response.status).toBe(401);
expect(data.error).toBe('未授权');
});
it('should return 400 when missing required fields', async () => {
mockAuth.mockResolvedValueOnce({
user: { id: '1', role: 'admin' },
});
mockHasPermission.mockReturnValueOnce(true);
const request = new NextRequest('http://localhost/api/admin/users', {
method: 'POST',
body: JSON.stringify({ email: 'test@example.com' }),
});
const response = await POST(request);
const data = await response.json();
expect(response.status).toBe(400);
expect(data.error).toBe('缺少必填字段');
});
});
});