feat: 增加测试覆盖率并优化代码质量
test: 添加单元测试和端到端测试 refactor: 重构登录页面和上传模块 ci: 更新测试覆盖率阈值至42% build: 添加测试相关依赖 docs: 更新测试文档 style: 修复代码格式问题
This commit is contained in:
@@ -0,0 +1,169 @@
|
||||
import { GET, POST, PUT } 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([]),
|
||||
orderBy: jest.fn().mockResolvedValue([]),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
insert: jest.fn().mockReturnValue({
|
||||
values: jest.fn().mockReturnValue({
|
||||
returning: jest.fn().mockResolvedValue([{
|
||||
id: 'test-id',
|
||||
key: 'test_key',
|
||||
value: 'test_value',
|
||||
category: 'general',
|
||||
}]),
|
||||
}),
|
||||
}),
|
||||
},
|
||||
}));
|
||||
|
||||
describe('/api/admin/config', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('GET', () => {
|
||||
it('should return 401 if not authenticated', async () => {
|
||||
const request = new NextRequest('http://localhost/api/admin/config');
|
||||
const response = await GET(request);
|
||||
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/config');
|
||||
const response = await GET(request);
|
||||
const data = await response.json();
|
||||
|
||||
expect(response.status).toBe(403);
|
||||
expect(data.error).toBe('无权限');
|
||||
});
|
||||
|
||||
it('should return configs 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/config');
|
||||
const response = await GET(request);
|
||||
const data = await response.json();
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(data.configs).toBeDefined();
|
||||
expect(data.flat).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('POST', () => {
|
||||
it('should return 401 if not authenticated', async () => {
|
||||
const { auth } = require('@/lib/auth');
|
||||
auth.mockResolvedValue(null);
|
||||
|
||||
const request = new NextRequest('http://localhost/api/admin/config', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ key: 'test', value: {} }),
|
||||
});
|
||||
const response = await POST(request);
|
||||
const data = await response.json();
|
||||
|
||||
expect(response.status).toBe(401);
|
||||
expect(data.error).toBe('未授权');
|
||||
});
|
||||
|
||||
it('should return 400 if missing required fields', 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/config', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ key: 'test' }),
|
||||
});
|
||||
const response = await POST(request);
|
||||
const data = await response.json();
|
||||
|
||||
expect(response.status).toBe(400);
|
||||
expect(data.error).toBe('缺少必要字段');
|
||||
});
|
||||
});
|
||||
|
||||
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/config', {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify({ configs: [] }),
|
||||
});
|
||||
const response = await PUT(request);
|
||||
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/config', {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify({ configs: [] }),
|
||||
});
|
||||
const response = await PUT(request);
|
||||
const data = await response.json();
|
||||
|
||||
expect(response.status).toBe(403);
|
||||
expect(data.error).toBe('无权限');
|
||||
});
|
||||
|
||||
it('should return 400 if configs is not an array', 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/config', {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify({ configs: 'not-array' }),
|
||||
});
|
||||
const response = await PUT(request);
|
||||
const data = await response.json();
|
||||
|
||||
expect(response.status).toBe(400);
|
||||
expect(data.error).toBe('无效的数据格式');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,183 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
jest.mock('@/db', () => ({
|
||||
db: {
|
||||
select: jest.fn().mockReturnThis(),
|
||||
from: jest.fn().mockReturnThis(),
|
||||
where: jest.fn().mockReturnThis(),
|
||||
limit: jest.fn().mockReturnThis(),
|
||||
orderBy: jest.fn().mockReturnThis(),
|
||||
insert: jest.fn().mockReturnThis(),
|
||||
values: jest.fn().mockReturnThis(),
|
||||
returning: jest.fn().mockReturnThis(),
|
||||
update: jest.fn().mockReturnThis(),
|
||||
set: jest.fn().mockReturnThis(),
|
||||
delete: jest.fn().mockReturnThis(),
|
||||
},
|
||||
}));
|
||||
|
||||
jest.mock('@/lib/auth', () => ({
|
||||
auth: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('@/lib/auth/permissions', () => ({
|
||||
hasPermission: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('@/lib/audit', () => ({
|
||||
createAuditLog: jest.fn().mockResolvedValue({}),
|
||||
}));
|
||||
|
||||
const { db } = require('@/db');
|
||||
const { auth } = require('@/lib/auth');
|
||||
const { hasPermission } = require('@/lib/auth/permissions');
|
||||
|
||||
describe('GET /api/admin/content/[id]', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should return 401 if not authenticated', async () => {
|
||||
auth.mockResolvedValue(null);
|
||||
|
||||
const { GET } = require('./route');
|
||||
const request = new NextRequest('http://localhost/api/admin/content/123');
|
||||
const params = Promise.resolve({ id: '123' });
|
||||
|
||||
const response = await GET(request, { params });
|
||||
const data = await response.json();
|
||||
|
||||
expect(response.status).toBe(401);
|
||||
expect(data.error).toBe('未授权');
|
||||
});
|
||||
|
||||
it('should return 403 if no permission', async () => {
|
||||
auth.mockResolvedValue({ user: { role: 'viewer' } });
|
||||
hasPermission.mockReturnValue(false);
|
||||
|
||||
const { GET } = require('./route');
|
||||
const request = new NextRequest('http://localhost/api/admin/content/123');
|
||||
const params = Promise.resolve({ id: '123' });
|
||||
|
||||
const response = await GET(request, { params });
|
||||
const data = await response.json();
|
||||
|
||||
expect(response.status).toBe(403);
|
||||
expect(data.error).toBe('无权限');
|
||||
});
|
||||
|
||||
it('should return 404 if content not found', async () => {
|
||||
auth.mockResolvedValue({ user: { role: 'admin' } });
|
||||
hasPermission.mockReturnValue(true);
|
||||
db.limit.mockResolvedValue([]);
|
||||
|
||||
const { GET } = require('./route');
|
||||
const request = new NextRequest('http://localhost/api/admin/content/123');
|
||||
const params = Promise.resolve({ id: '123' });
|
||||
|
||||
const response = await GET(request, { params });
|
||||
const data = await response.json();
|
||||
|
||||
expect(response.status).toBe(404);
|
||||
expect(data.error).toBe('内容不存在');
|
||||
});
|
||||
|
||||
it('should return content if found', async () => {
|
||||
const mockContent = {
|
||||
id: '123',
|
||||
title: 'Test Content',
|
||||
status: 'published',
|
||||
};
|
||||
|
||||
auth.mockResolvedValue({ user: { role: 'admin' } });
|
||||
hasPermission.mockReturnValue(true);
|
||||
db.limit.mockResolvedValue([mockContent]);
|
||||
db.orderBy.mockResolvedValue([]);
|
||||
|
||||
const { GET } = require('./route');
|
||||
const request = new NextRequest('http://localhost/api/admin/content/123');
|
||||
const params = Promise.resolve({ id: '123' });
|
||||
|
||||
const response = await GET(request, { params });
|
||||
const data = await response.json();
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(data.title).toBe('Test Content');
|
||||
});
|
||||
});
|
||||
|
||||
describe('PUT /api/admin/content/[id]', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should return 401 if not authenticated', async () => {
|
||||
auth.mockResolvedValue(null);
|
||||
|
||||
const { PUT } = require('./route');
|
||||
const request = new NextRequest('http://localhost/api/admin/content/123', {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify({ title: 'Updated' }),
|
||||
});
|
||||
const params = Promise.resolve({ id: '123' });
|
||||
|
||||
const response = await PUT(request, { params });
|
||||
const data = await response.json();
|
||||
|
||||
expect(response.status).toBe(401);
|
||||
});
|
||||
|
||||
it('should return 403 if no permission', async () => {
|
||||
auth.mockResolvedValue({ user: { role: 'viewer' } });
|
||||
hasPermission.mockReturnValue(false);
|
||||
|
||||
const { PUT } = require('./route');
|
||||
const request = new NextRequest('http://localhost/api/admin/content/123', {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify({ title: 'Updated' }),
|
||||
});
|
||||
const params = Promise.resolve({ id: '123' });
|
||||
|
||||
const response = await PUT(request, { params });
|
||||
const data = await response.json();
|
||||
|
||||
expect(response.status).toBe(403);
|
||||
});
|
||||
});
|
||||
|
||||
describe('DELETE /api/admin/content/[id]', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should return 401 if not authenticated', async () => {
|
||||
auth.mockResolvedValue(null);
|
||||
|
||||
const { DELETE } = require('./route');
|
||||
const request = new NextRequest('http://localhost/api/admin/content/123', {
|
||||
method: 'DELETE',
|
||||
});
|
||||
const params = Promise.resolve({ id: '123' });
|
||||
|
||||
const response = await DELETE(request, { params });
|
||||
const data = await response.json();
|
||||
|
||||
expect(response.status).toBe(401);
|
||||
});
|
||||
|
||||
it('should return 403 if no permission', async () => {
|
||||
auth.mockResolvedValue({ user: { role: 'editor' } });
|
||||
hasPermission.mockReturnValue(false);
|
||||
|
||||
const { DELETE } = require('./route');
|
||||
const request = new NextRequest('http://localhost/api/admin/content/123', {
|
||||
method: 'DELETE',
|
||||
});
|
||||
const params = Promise.resolve({ id: '123' });
|
||||
|
||||
const response = await DELETE(request, { params });
|
||||
const data = await response.json();
|
||||
|
||||
expect(response.status).toBe(403);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,142 @@
|
||||
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: () => ({
|
||||
orderBy: () => ({
|
||||
limit: () => ({
|
||||
offset: mockDbSelect,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
insert: () => ({
|
||||
values: () => ({
|
||||
returning: mockDbInsert,
|
||||
}),
|
||||
}),
|
||||
},
|
||||
}));
|
||||
|
||||
jest.mock('drizzle-orm', () => ({
|
||||
eq: jest.fn(),
|
||||
desc: jest.fn(),
|
||||
and: jest.fn(),
|
||||
like: jest.fn(),
|
||||
sql: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('nanoid', () => ({
|
||||
nanoid: () => 'test-id-123',
|
||||
}));
|
||||
|
||||
jest.mock('@/lib/audit', () => ({
|
||||
createAuditLog: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('@/db/schema', () => ({
|
||||
content: {},
|
||||
}));
|
||||
|
||||
import { GET, POST } from './route';
|
||||
|
||||
describe('/api/admin/content', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('GET', () => {
|
||||
it('should return 401 when not authenticated', async () => {
|
||||
mockAuth.mockResolvedValueOnce(null);
|
||||
|
||||
const request = new NextRequest('http://localhost/api/admin/content');
|
||||
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/content');
|
||||
const response = await GET(request);
|
||||
const data = await response.json();
|
||||
|
||||
expect(response.status).toBe(403);
|
||||
expect(data.error).toBe('无权限');
|
||||
});
|
||||
|
||||
it('should return content list when authorized', async () => {
|
||||
mockAuth.mockResolvedValueOnce({
|
||||
user: { id: '1', role: 'admin' },
|
||||
});
|
||||
mockHasPermission.mockReturnValueOnce(true);
|
||||
mockDbSelect.mockResolvedValueOnce([]);
|
||||
mockDbSelect.mockResolvedValueOnce([{ count: 0 }]);
|
||||
|
||||
const request = new NextRequest('http://localhost/api/admin/content');
|
||||
const response = await GET(request);
|
||||
const data = await response.json();
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(data.items).toEqual([]);
|
||||
expect(data.pagination).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('POST', () => {
|
||||
it('should return 401 when not authenticated', async () => {
|
||||
mockAuth.mockResolvedValueOnce(null);
|
||||
|
||||
const request = new NextRequest('http://localhost/api/admin/content', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ type: 'news', title: 'Test', slug: 'test' }),
|
||||
});
|
||||
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/content', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ type: 'news' }),
|
||||
});
|
||||
const response = await POST(request);
|
||||
const data = await response.json();
|
||||
|
||||
expect(response.status).toBe(400);
|
||||
expect(data.error).toBe('缺少必要字段');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,98 @@
|
||||
import { POST, 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('@/lib/audit', () => ({
|
||||
createAuditLog: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('@/lib/upload', () => ({
|
||||
uploadFile: jest.fn().mockResolvedValue({
|
||||
id: 'test-id',
|
||||
name: 'test.jpg',
|
||||
type: 'image',
|
||||
size: 1024,
|
||||
url: 'https://example.com/test.jpg',
|
||||
}),
|
||||
deleteFile: jest.fn(),
|
||||
}));
|
||||
|
||||
describe('/api/admin/upload', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('POST', () => {
|
||||
it('should return 401 if not authenticated', async () => {
|
||||
const formData = new FormData();
|
||||
formData.append('file', new File(['test'], 'test.jpg', { type: 'image/jpeg' }));
|
||||
|
||||
const request = new NextRequest('http://localhost/api/admin/upload', {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
});
|
||||
const response = await POST(request);
|
||||
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/upload', {
|
||||
method: 'POST',
|
||||
});
|
||||
const response = await POST(request);
|
||||
const data = await response.json();
|
||||
|
||||
expect(response.status).toBe(403);
|
||||
expect(data.error).toBe('无权限');
|
||||
});
|
||||
|
||||
it('should return 400 if no file', async () => {
|
||||
const { auth } = require('@/lib/auth');
|
||||
const { hasPermission } = require('@/lib/auth/permissions');
|
||||
|
||||
auth.mockResolvedValue({ user: { role: 'admin', id: 'test-user' } });
|
||||
hasPermission.mockReturnValue(true);
|
||||
|
||||
const request = {
|
||||
formData: jest.fn().mockResolvedValue(new FormData()),
|
||||
} as any;
|
||||
const response = await POST(request);
|
||||
const data = await response.json();
|
||||
|
||||
expect(response.status).toBe(400);
|
||||
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/upload?url=test.jpg', {
|
||||
method: 'DELETE',
|
||||
});
|
||||
const response = await DELETE(request);
|
||||
const data = await response.json();
|
||||
|
||||
expect(response.status).toBe(401);
|
||||
expect(data.error).toBe('未授权');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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('未授权');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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('缺少必填字段');
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user