parent
84099dfc58
commit
0e176c9883
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,393 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
import axios from 'axios'
|
||||
import * as forumApi from '../forum'
|
||||
|
||||
// Mock axios
|
||||
vi.mock('axios')
|
||||
const mockedAxios = vi.mocked(axios)
|
||||
|
||||
describe('Forum API', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('getPosts', () => {
|
||||
it('should fetch posts successfully', async () => {
|
||||
const mockResponse = {
|
||||
data: {
|
||||
code: 200,
|
||||
success: true,
|
||||
data: {
|
||||
total: 10,
|
||||
list: [
|
||||
{
|
||||
id: 1,
|
||||
title: '测试帖子',
|
||||
content: '测试内容',
|
||||
categoryId: 1,
|
||||
userId: 1,
|
||||
likeCount: 5,
|
||||
viewCount: 100,
|
||||
commentCount: 3,
|
||||
createdAt: '2024-01-01T10:00:00'
|
||||
}
|
||||
],
|
||||
pages: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mockedAxios.get.mockResolvedValue(mockResponse)
|
||||
|
||||
const result = await forumApi.getPosts({
|
||||
categoryId: 1,
|
||||
keyword: '测试',
|
||||
page: 1,
|
||||
size: 10,
|
||||
sort: 'latest'
|
||||
})
|
||||
|
||||
expect(mockedAxios.get).toHaveBeenCalledWith('/posts', {
|
||||
params: {
|
||||
categoryId: 1,
|
||||
keyword: '测试',
|
||||
page: 1,
|
||||
size: 10,
|
||||
sort: 'latest'
|
||||
}
|
||||
})
|
||||
expect(result.data.data.list).toHaveLength(1)
|
||||
expect(result.data.data.list[0].title).toBe('测试帖子')
|
||||
})
|
||||
|
||||
it('should handle empty parameters', async () => {
|
||||
const mockResponse = {
|
||||
data: {
|
||||
code: 200,
|
||||
success: true,
|
||||
data: { total: 0, list: [], pages: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
mockedAxios.get.mockResolvedValue(mockResponse)
|
||||
|
||||
await forumApi.getPosts({})
|
||||
|
||||
expect(mockedAxios.get).toHaveBeenCalledWith('/posts', {
|
||||
params: {}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('getPostDetail', () => {
|
||||
it('should fetch post detail successfully', async () => {
|
||||
const mockPost = {
|
||||
id: 1,
|
||||
title: '测试帖子详情',
|
||||
content: '详细内容',
|
||||
categoryId: 1,
|
||||
userId: 1,
|
||||
author: {
|
||||
id: 1,
|
||||
nickname: '作者',
|
||||
avatar: 'avatar.jpg'
|
||||
},
|
||||
category: {
|
||||
id: 1,
|
||||
name: '学习讨论'
|
||||
},
|
||||
likeCount: 10,
|
||||
viewCount: 200,
|
||||
commentCount: 5,
|
||||
isLiked: false,
|
||||
createdAt: '2024-01-01T10:00:00'
|
||||
}
|
||||
|
||||
const mockResponse = {
|
||||
data: {
|
||||
code: 200,
|
||||
success: true,
|
||||
data: mockPost
|
||||
}
|
||||
}
|
||||
|
||||
mockedAxios.get.mockResolvedValue(mockResponse)
|
||||
|
||||
const result = await forumApi.getPostDetail(1)
|
||||
|
||||
expect(mockedAxios.get).toHaveBeenCalledWith('/posts/1')
|
||||
expect(result.data.data.title).toBe('测试帖子详情')
|
||||
expect(result.data.data.author.nickname).toBe('作者')
|
||||
})
|
||||
|
||||
it('should handle post not found', async () => {
|
||||
const mockResponse = {
|
||||
data: {
|
||||
code: 404,
|
||||
success: false,
|
||||
message: '帖子不存在'
|
||||
}
|
||||
}
|
||||
|
||||
mockedAxios.get.mockResolvedValue(mockResponse)
|
||||
|
||||
const result = await forumApi.getPostDetail(999)
|
||||
|
||||
expect(result.data.success).toBe(false)
|
||||
expect(result.data.message).toBe('帖子不存在')
|
||||
})
|
||||
})
|
||||
|
||||
describe('createPost', () => {
|
||||
it('should create post successfully', async () => {
|
||||
const postData = {
|
||||
title: '新帖子',
|
||||
content: '新帖子内容',
|
||||
categoryId: 1
|
||||
}
|
||||
|
||||
const mockResponse = {
|
||||
data: {
|
||||
code: 200,
|
||||
success: true,
|
||||
data: { postId: 123 },
|
||||
message: '帖子发布成功'
|
||||
}
|
||||
}
|
||||
|
||||
mockedAxios.post.mockResolvedValue(mockResponse)
|
||||
|
||||
const result = await forumApi.createPost(postData)
|
||||
|
||||
expect(mockedAxios.post).toHaveBeenCalledWith('/posts', postData)
|
||||
expect(result.data.data.postId).toBe(123)
|
||||
expect(result.data.message).toBe('帖子发布成功')
|
||||
})
|
||||
|
||||
it('should handle validation errors', async () => {
|
||||
const invalidPostData = {
|
||||
title: '',
|
||||
content: '内容',
|
||||
categoryId: 1
|
||||
}
|
||||
|
||||
const mockResponse = {
|
||||
data: {
|
||||
code: 400,
|
||||
success: false,
|
||||
message: '标题不能为空'
|
||||
}
|
||||
}
|
||||
|
||||
mockedAxios.post.mockResolvedValue(mockResponse)
|
||||
|
||||
const result = await forumApi.createPost(invalidPostData)
|
||||
|
||||
expect(result.data.success).toBe(false)
|
||||
expect(result.data.message).toBe('标题不能为空')
|
||||
})
|
||||
})
|
||||
|
||||
describe('updatePost', () => {
|
||||
it('should update post successfully', async () => {
|
||||
const updateData = {
|
||||
title: '更新后的标题',
|
||||
content: '更新后的内容',
|
||||
categoryId: 2
|
||||
}
|
||||
|
||||
const mockResponse = {
|
||||
data: {
|
||||
code: 200,
|
||||
success: true,
|
||||
message: '帖子更新成功'
|
||||
}
|
||||
}
|
||||
|
||||
mockedAxios.put.mockResolvedValue(mockResponse)
|
||||
|
||||
const result = await forumApi.updatePost(1, updateData)
|
||||
|
||||
expect(mockedAxios.put).toHaveBeenCalledWith('/posts/1', updateData)
|
||||
expect(result.data.message).toBe('帖子更新成功')
|
||||
})
|
||||
})
|
||||
|
||||
describe('deletePost', () => {
|
||||
it('should delete post successfully', async () => {
|
||||
const mockResponse = {
|
||||
data: {
|
||||
code: 200,
|
||||
success: true,
|
||||
message: '帖子删除成功'
|
||||
}
|
||||
}
|
||||
|
||||
mockedAxios.delete.mockResolvedValue(mockResponse)
|
||||
|
||||
const result = await forumApi.deletePost(1)
|
||||
|
||||
expect(mockedAxios.delete).toHaveBeenCalledWith('/posts/1')
|
||||
expect(result.data.message).toBe('帖子删除成功')
|
||||
})
|
||||
})
|
||||
|
||||
describe('likePost', () => {
|
||||
it('should like post successfully', async () => {
|
||||
const mockResponse = {
|
||||
data: {
|
||||
code: 200,
|
||||
success: true,
|
||||
message: '点赞成功'
|
||||
}
|
||||
}
|
||||
|
||||
mockedAxios.post.mockResolvedValue(mockResponse)
|
||||
|
||||
const result = await forumApi.likePost(1)
|
||||
|
||||
expect(mockedAxios.post).toHaveBeenCalledWith('/posts/1/like')
|
||||
expect(result.data.message).toBe('点赞成功')
|
||||
})
|
||||
})
|
||||
|
||||
describe('getComments', () => {
|
||||
it('should fetch comments successfully', async () => {
|
||||
const mockComments = {
|
||||
total: 2,
|
||||
list: [
|
||||
{
|
||||
id: 1,
|
||||
content: '评论内容1',
|
||||
userId: 1,
|
||||
nickname: '用户1',
|
||||
avatar: 'avatar1.jpg',
|
||||
likeCount: 3,
|
||||
isLiked: false,
|
||||
createdAt: '2024-01-01T10:00:00',
|
||||
replies: []
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
content: '评论内容2',
|
||||
userId: 2,
|
||||
nickname: '用户2',
|
||||
avatar: 'avatar2.jpg',
|
||||
likeCount: 1,
|
||||
isLiked: true,
|
||||
createdAt: '2024-01-01T11:00:00',
|
||||
replies: [
|
||||
{
|
||||
id: 3,
|
||||
content: '回复内容',
|
||||
userId: 1,
|
||||
nickname: '用户1',
|
||||
avatar: 'avatar1.jpg',
|
||||
likeCount: 0,
|
||||
isLiked: false,
|
||||
createdAt: '2024-01-01T12:00:00'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const mockResponse = {
|
||||
data: {
|
||||
code: 200,
|
||||
success: true,
|
||||
data: mockComments
|
||||
}
|
||||
}
|
||||
|
||||
mockedAxios.get.mockResolvedValue(mockResponse)
|
||||
|
||||
const result = await forumApi.getComments(1)
|
||||
|
||||
expect(mockedAxios.get).toHaveBeenCalledWith('/comments/post/1')
|
||||
expect(result.data.data.list).toHaveLength(2)
|
||||
expect(result.data.data.list[1].replies).toHaveLength(1)
|
||||
})
|
||||
})
|
||||
|
||||
describe('createComment', () => {
|
||||
it('should create comment successfully', async () => {
|
||||
const commentData = {
|
||||
postId: 1,
|
||||
content: '新评论内容',
|
||||
parentId: null
|
||||
}
|
||||
|
||||
const mockResponse = {
|
||||
data: {
|
||||
code: 200,
|
||||
success: true,
|
||||
data: { commentId: 456 },
|
||||
message: '评论发布成功'
|
||||
}
|
||||
}
|
||||
|
||||
mockedAxios.post.mockResolvedValue(mockResponse)
|
||||
|
||||
const result = await forumApi.createComment(commentData)
|
||||
|
||||
expect(mockedAxios.post).toHaveBeenCalledWith('/comments', commentData)
|
||||
expect(result.data.data.commentId).toBe(456)
|
||||
})
|
||||
|
||||
it('should create reply successfully', async () => {
|
||||
const replyData = {
|
||||
postId: 1,
|
||||
content: '回复内容',
|
||||
parentId: 123
|
||||
}
|
||||
|
||||
const mockResponse = {
|
||||
data: {
|
||||
code: 200,
|
||||
success: true,
|
||||
data: { commentId: 789 },
|
||||
message: '回复发布成功'
|
||||
}
|
||||
}
|
||||
|
||||
mockedAxios.post.mockResolvedValue(mockResponse)
|
||||
|
||||
const result = await forumApi.createComment(replyData)
|
||||
|
||||
expect(mockedAxios.post).toHaveBeenCalledWith('/comments', replyData)
|
||||
expect(result.data.data.commentId).toBe(789)
|
||||
})
|
||||
})
|
||||
|
||||
describe('getCategories', () => {
|
||||
it('should fetch categories successfully', async () => {
|
||||
const mockCategories = {
|
||||
total: 3,
|
||||
list: [
|
||||
{ id: 1, name: '学习讨论', description: '学习相关话题', icon: 'book', sort: 1, status: 1 },
|
||||
{ id: 2, name: '生活分享', description: '生活相关话题', icon: 'heart', sort: 2, status: 1 },
|
||||
{ id: 3, name: '技术交流', description: '技术相关话题', icon: 'code', sort: 3, status: 1 }
|
||||
]
|
||||
}
|
||||
|
||||
const mockResponse = {
|
||||
data: {
|
||||
code: 200,
|
||||
success: true,
|
||||
data: mockCategories
|
||||
}
|
||||
}
|
||||
|
||||
mockedAxios.get.mockResolvedValue(mockResponse)
|
||||
|
||||
const result = await forumApi.getCategories({ status: 1 })
|
||||
|
||||
expect(mockedAxios.get).toHaveBeenCalledWith('/categories', {
|
||||
params: { status: 1 }
|
||||
})
|
||||
expect(result.data.data.list).toHaveLength(3)
|
||||
expect(result.data.data.list[0].name).toBe('学习讨论')
|
||||
})
|
||||
})
|
||||
})
|
@ -0,0 +1,484 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
import axios from 'axios'
|
||||
import * as resourcesApi from '../resources'
|
||||
|
||||
// Mock axios
|
||||
vi.mock('axios')
|
||||
const mockedAxios = vi.mocked(axios)
|
||||
|
||||
describe('Resources API', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('getResources', () => {
|
||||
it('should fetch resources successfully', async () => {
|
||||
const mockResponse = {
|
||||
data: {
|
||||
code: 200,
|
||||
success: true,
|
||||
data: {
|
||||
total: 5,
|
||||
list: [
|
||||
{
|
||||
id: 1,
|
||||
title: '测试资源',
|
||||
description: '测试资源描述',
|
||||
fileName: 'test.pdf',
|
||||
fileUrl: 'http://example.com/test.pdf',
|
||||
fileSize: 1024,
|
||||
fileType: 'pdf',
|
||||
categoryId: 1,
|
||||
userId: 1,
|
||||
downloadCount: 10,
|
||||
likeCount: 5,
|
||||
createdAt: '2024-01-01T10:00:00'
|
||||
}
|
||||
],
|
||||
pages: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mockedAxios.get.mockResolvedValue(mockResponse)
|
||||
|
||||
const result = await resourcesApi.getResources({
|
||||
category: 1,
|
||||
user: 1,
|
||||
keyword: '测试',
|
||||
page: 1,
|
||||
size: 10
|
||||
})
|
||||
|
||||
expect(mockedAxios.get).toHaveBeenCalledWith('/resources', {
|
||||
params: {
|
||||
category: 1,
|
||||
user: 1,
|
||||
keyword: '测试',
|
||||
page: 1,
|
||||
size: 10
|
||||
}
|
||||
})
|
||||
expect(result.data.data.list).toHaveLength(1)
|
||||
expect(result.data.data.list[0].title).toBe('测试资源')
|
||||
})
|
||||
|
||||
it('should handle empty parameters', async () => {
|
||||
const mockResponse = {
|
||||
data: {
|
||||
code: 200,
|
||||
success: true,
|
||||
data: { total: 0, list: [], pages: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
mockedAxios.get.mockResolvedValue(mockResponse)
|
||||
|
||||
await resourcesApi.getResources({})
|
||||
|
||||
expect(mockedAxios.get).toHaveBeenCalledWith('/resources', {
|
||||
params: {}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('getResourceDetail', () => {
|
||||
it('should fetch resource detail successfully', async () => {
|
||||
const mockResource = {
|
||||
id: 1,
|
||||
title: '测试资源详情',
|
||||
description: '详细描述',
|
||||
fileName: 'test.pdf',
|
||||
fileUrl: 'http://example.com/test.pdf',
|
||||
fileSize: 2048,
|
||||
fileType: 'pdf',
|
||||
categoryId: 1,
|
||||
userId: 1,
|
||||
author: {
|
||||
id: 1,
|
||||
nickname: '作者',
|
||||
avatar: 'avatar.jpg'
|
||||
},
|
||||
category: {
|
||||
id: 1,
|
||||
name: '学习资料'
|
||||
},
|
||||
downloadCount: 20,
|
||||
likeCount: 10,
|
||||
isLiked: false,
|
||||
createdAt: '2024-01-01T10:00:00'
|
||||
}
|
||||
|
||||
const mockResponse = {
|
||||
data: {
|
||||
code: 200,
|
||||
success: true,
|
||||
data: mockResource
|
||||
}
|
||||
}
|
||||
|
||||
mockedAxios.get.mockResolvedValue(mockResponse)
|
||||
|
||||
const result = await resourcesApi.getResourceDetail(1)
|
||||
|
||||
expect(mockedAxios.get).toHaveBeenCalledWith('/resources/1')
|
||||
expect(result.data.data.title).toBe('测试资源详情')
|
||||
expect(result.data.data.author.nickname).toBe('作者')
|
||||
})
|
||||
|
||||
it('should handle resource not found', async () => {
|
||||
const mockResponse = {
|
||||
data: {
|
||||
code: 404,
|
||||
success: false,
|
||||
message: '资源不存在'
|
||||
}
|
||||
}
|
||||
|
||||
mockedAxios.get.mockResolvedValue(mockResponse)
|
||||
|
||||
const result = await resourcesApi.getResourceDetail(999)
|
||||
|
||||
expect(result.data.success).toBe(false)
|
||||
expect(result.data.message).toBe('资源不存在')
|
||||
})
|
||||
})
|
||||
|
||||
describe('uploadResource', () => {
|
||||
it('should upload resource successfully', async () => {
|
||||
const file = new File(['test content'], 'test.pdf', { type: 'application/pdf' })
|
||||
const resourceData = {
|
||||
title: '新资源',
|
||||
description: '新资源描述',
|
||||
categoryId: 1
|
||||
}
|
||||
|
||||
const mockResponse = {
|
||||
data: {
|
||||
code: 200,
|
||||
success: true,
|
||||
data: { resourceId: 123 },
|
||||
message: '资源上传成功'
|
||||
}
|
||||
}
|
||||
|
||||
mockedAxios.post.mockResolvedValue(mockResponse)
|
||||
|
||||
const result = await resourcesApi.uploadResource(file, resourceData)
|
||||
|
||||
expect(mockedAxios.post).toHaveBeenCalledWith(
|
||||
'/resources',
|
||||
expect.any(FormData),
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
}
|
||||
)
|
||||
expect(result.data.data.resourceId).toBe(123)
|
||||
expect(result.data.message).toBe('资源上传成功')
|
||||
})
|
||||
|
||||
it('should handle file validation errors', async () => {
|
||||
const file = new File([''], 'empty.pdf', { type: 'application/pdf' })
|
||||
const resourceData = {
|
||||
title: '新资源',
|
||||
description: '新资源描述',
|
||||
categoryId: 1
|
||||
}
|
||||
|
||||
const mockResponse = {
|
||||
data: {
|
||||
code: 400,
|
||||
success: false,
|
||||
message: '文件不能为空'
|
||||
}
|
||||
}
|
||||
|
||||
mockedAxios.post.mockResolvedValue(mockResponse)
|
||||
|
||||
const result = await resourcesApi.uploadResource(file, resourceData)
|
||||
|
||||
expect(result.data.success).toBe(false)
|
||||
expect(result.data.message).toBe('文件不能为空')
|
||||
})
|
||||
|
||||
it('should handle unsupported file type', async () => {
|
||||
const file = new File(['test content'], 'test.exe', { type: 'application/octet-stream' })
|
||||
const resourceData = {
|
||||
title: '新资源',
|
||||
description: '新资源描述',
|
||||
categoryId: 1
|
||||
}
|
||||
|
||||
const mockResponse = {
|
||||
data: {
|
||||
code: 400,
|
||||
success: false,
|
||||
message: '不支持的文件类型'
|
||||
}
|
||||
}
|
||||
|
||||
mockedAxios.post.mockResolvedValue(mockResponse)
|
||||
|
||||
const result = await resourcesApi.uploadResource(file, resourceData)
|
||||
|
||||
expect(result.data.success).toBe(false)
|
||||
expect(result.data.message).toBe('不支持的文件类型')
|
||||
})
|
||||
})
|
||||
|
||||
describe('updateResource', () => {
|
||||
it('should update resource successfully', async () => {
|
||||
const updateData = {
|
||||
title: '更新后的标题',
|
||||
description: '更新后的描述',
|
||||
categoryId: 2
|
||||
}
|
||||
|
||||
const mockResponse = {
|
||||
data: {
|
||||
code: 200,
|
||||
success: true,
|
||||
message: '资源更新成功'
|
||||
}
|
||||
}
|
||||
|
||||
mockedAxios.put.mockResolvedValue(mockResponse)
|
||||
|
||||
const result = await resourcesApi.updateResource(1, updateData)
|
||||
|
||||
expect(mockedAxios.put).toHaveBeenCalledWith('/resources/1', updateData)
|
||||
expect(result.data.message).toBe('资源更新成功')
|
||||
})
|
||||
|
||||
it('should handle unauthorized update', async () => {
|
||||
const updateData = {
|
||||
title: '更新后的标题',
|
||||
description: '更新后的描述',
|
||||
categoryId: 2
|
||||
}
|
||||
|
||||
const mockResponse = {
|
||||
data: {
|
||||
code: 403,
|
||||
success: false,
|
||||
message: '无权限修改此资源'
|
||||
}
|
||||
}
|
||||
|
||||
mockedAxios.put.mockResolvedValue(mockResponse)
|
||||
|
||||
const result = await resourcesApi.updateResource(1, updateData)
|
||||
|
||||
expect(result.data.success).toBe(false)
|
||||
expect(result.data.message).toBe('无权限修改此资源')
|
||||
})
|
||||
})
|
||||
|
||||
describe('deleteResource', () => {
|
||||
it('should delete resource successfully', async () => {
|
||||
const mockResponse = {
|
||||
data: {
|
||||
code: 200,
|
||||
success: true,
|
||||
message: '资源删除成功'
|
||||
}
|
||||
}
|
||||
|
||||
mockedAxios.delete.mockResolvedValue(mockResponse)
|
||||
|
||||
const result = await resourcesApi.deleteResource(1)
|
||||
|
||||
expect(mockedAxios.delete).toHaveBeenCalledWith('/resources/1')
|
||||
expect(result.data.message).toBe('资源删除成功')
|
||||
})
|
||||
})
|
||||
|
||||
describe('downloadResource', () => {
|
||||
it('should download resource successfully', async () => {
|
||||
const mockResponse = {
|
||||
data: {
|
||||
code: 200,
|
||||
success: true,
|
||||
data: {
|
||||
downloadUrl: 'http://example.com/download/test.pdf'
|
||||
},
|
||||
message: '获取下载链接成功'
|
||||
}
|
||||
}
|
||||
|
||||
mockedAxios.get.mockResolvedValue(mockResponse)
|
||||
|
||||
const result = await resourcesApi.downloadResource(1)
|
||||
|
||||
expect(mockedAxios.get).toHaveBeenCalledWith('/resources/1/download')
|
||||
expect(result.data.data.downloadUrl).toBe('http://example.com/download/test.pdf')
|
||||
})
|
||||
|
||||
it('should handle download permission denied', async () => {
|
||||
const mockResponse = {
|
||||
data: {
|
||||
code: 403,
|
||||
success: false,
|
||||
message: '无权限下载此资源'
|
||||
}
|
||||
}
|
||||
|
||||
mockedAxios.get.mockResolvedValue(mockResponse)
|
||||
|
||||
const result = await resourcesApi.downloadResource(1)
|
||||
|
||||
expect(result.data.success).toBe(false)
|
||||
expect(result.data.message).toBe('无权限下载此资源')
|
||||
})
|
||||
})
|
||||
|
||||
describe('likeResource', () => {
|
||||
it('should like resource successfully', async () => {
|
||||
const mockResponse = {
|
||||
data: {
|
||||
code: 200,
|
||||
success: true,
|
||||
message: '点赞成功'
|
||||
}
|
||||
}
|
||||
|
||||
mockedAxios.post.mockResolvedValue(mockResponse)
|
||||
|
||||
const result = await resourcesApi.likeResource(1)
|
||||
|
||||
expect(mockedAxios.post).toHaveBeenCalledWith('/resources/1/like')
|
||||
expect(result.data.message).toBe('点赞成功')
|
||||
})
|
||||
|
||||
it('should unlike resource successfully', async () => {
|
||||
const mockResponse = {
|
||||
data: {
|
||||
code: 200,
|
||||
success: true,
|
||||
message: '取消点赞成功'
|
||||
}
|
||||
}
|
||||
|
||||
mockedAxios.post.mockResolvedValue(mockResponse)
|
||||
|
||||
const result = await resourcesApi.likeResource(1)
|
||||
|
||||
expect(result.data.message).toBe('取消点赞成功')
|
||||
})
|
||||
})
|
||||
|
||||
describe('getUserResources', () => {
|
||||
it('should fetch user resources successfully', async () => {
|
||||
const mockResponse = {
|
||||
data: {
|
||||
code: 200,
|
||||
success: true,
|
||||
data: {
|
||||
total: 3,
|
||||
list: [
|
||||
{
|
||||
id: 1,
|
||||
title: '用户资源1',
|
||||
description: '描述1',
|
||||
fileName: 'file1.pdf',
|
||||
fileType: 'pdf',
|
||||
downloadCount: 5,
|
||||
likeCount: 2,
|
||||
createdAt: '2024-01-01T10:00:00'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: '用户资源2',
|
||||
description: '描述2',
|
||||
fileName: 'file2.docx',
|
||||
fileType: 'docx',
|
||||
downloadCount: 8,
|
||||
likeCount: 3,
|
||||
createdAt: '2024-01-02T10:00:00'
|
||||
}
|
||||
],
|
||||
pages: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mockedAxios.get.mockResolvedValue(mockResponse)
|
||||
|
||||
const result = await resourcesApi.getUserResources(1, {
|
||||
page: 1,
|
||||
size: 10
|
||||
})
|
||||
|
||||
expect(mockedAxios.get).toHaveBeenCalledWith('/resources/user/1', {
|
||||
params: {
|
||||
page: 1,
|
||||
size: 10
|
||||
}
|
||||
})
|
||||
expect(result.data.data.list).toHaveLength(2)
|
||||
expect(result.data.data.list[0].title).toBe('用户资源1')
|
||||
})
|
||||
})
|
||||
|
||||
describe('getMyResources', () => {
|
||||
it('should fetch current user resources successfully', async () => {
|
||||
const mockResponse = {
|
||||
data: {
|
||||
code: 200,
|
||||
success: true,
|
||||
data: {
|
||||
total: 2,
|
||||
list: [
|
||||
{
|
||||
id: 1,
|
||||
title: '我的资源1',
|
||||
description: '我的描述1',
|
||||
fileName: 'myfile1.pdf',
|
||||
fileType: 'pdf',
|
||||
downloadCount: 3,
|
||||
likeCount: 1,
|
||||
createdAt: '2024-01-01T10:00:00'
|
||||
}
|
||||
],
|
||||
pages: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mockedAxios.get.mockResolvedValue(mockResponse)
|
||||
|
||||
const result = await resourcesApi.getMyResources({
|
||||
page: 1,
|
||||
size: 10
|
||||
})
|
||||
|
||||
expect(mockedAxios.get).toHaveBeenCalledWith('/resources/my', {
|
||||
params: {
|
||||
page: 1,
|
||||
size: 10
|
||||
}
|
||||
})
|
||||
expect(result.data.data.list).toHaveLength(1)
|
||||
expect(result.data.data.list[0].title).toBe('我的资源1')
|
||||
})
|
||||
|
||||
it('should handle unauthorized access', async () => {
|
||||
const mockResponse = {
|
||||
data: {
|
||||
code: 401,
|
||||
success: false,
|
||||
message: '未登录'
|
||||
}
|
||||
}
|
||||
|
||||
mockedAxios.get.mockResolvedValue(mockResponse)
|
||||
|
||||
const result = await resourcesApi.getMyResources({})
|
||||
|
||||
expect(result.data.success).toBe(false)
|
||||
expect(result.data.message).toBe('未登录')
|
||||
})
|
||||
})
|
||||
})
|
@ -0,0 +1,617 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
import axios from 'axios'
|
||||
import * as scheduleApi from '../schedule'
|
||||
|
||||
// Mock axios
|
||||
vi.mock('axios')
|
||||
const mockedAxios = vi.mocked(axios)
|
||||
|
||||
describe('Schedule API', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('getSchedules', () => {
|
||||
it('should fetch schedules successfully', async () => {
|
||||
const mockResponse = {
|
||||
data: {
|
||||
code: 200,
|
||||
success: true,
|
||||
data: [
|
||||
{
|
||||
id: 1,
|
||||
title: '高等数学',
|
||||
description: '线性代数课程',
|
||||
startTime: '2024-01-15T09:00:00',
|
||||
endTime: '2024-01-15T10:30:00',
|
||||
location: '教学楼A101',
|
||||
type: 'COURSE',
|
||||
repeatType: 'WEEKLY',
|
||||
repeatEnd: '2024-06-15T10:30:00',
|
||||
userId: 1,
|
||||
createdAt: '2024-01-01T10:00:00'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: '团队会议',
|
||||
description: '项目进度讨论',
|
||||
startTime: '2024-01-16T14:00:00',
|
||||
endTime: '2024-01-16T15:00:00',
|
||||
location: '会议室B201',
|
||||
type: 'MEETING',
|
||||
repeatType: 'NONE',
|
||||
userId: 1,
|
||||
createdAt: '2024-01-01T11:00:00'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
mockedAxios.get.mockResolvedValue(mockResponse)
|
||||
|
||||
const result = await scheduleApi.getSchedules()
|
||||
|
||||
expect(mockedAxios.get).toHaveBeenCalledWith('/schedules')
|
||||
expect(result.data.data).toHaveLength(2)
|
||||
expect(result.data.data[0].title).toBe('高等数学')
|
||||
expect(result.data.data[1].type).toBe('MEETING')
|
||||
})
|
||||
|
||||
it('should handle empty schedule list', async () => {
|
||||
const mockResponse = {
|
||||
data: {
|
||||
code: 200,
|
||||
success: true,
|
||||
data: []
|
||||
}
|
||||
}
|
||||
|
||||
mockedAxios.get.mockResolvedValue(mockResponse)
|
||||
|
||||
const result = await scheduleApi.getSchedules()
|
||||
|
||||
expect(result.data.data).toHaveLength(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe('getSchedulesByRange', () => {
|
||||
it('should fetch schedules by time range successfully', async () => {
|
||||
const startTime = '2024-01-01T00:00:00'
|
||||
const endTime = '2024-01-31T23:59:59'
|
||||
|
||||
const mockResponse = {
|
||||
data: {
|
||||
code: 200,
|
||||
success: true,
|
||||
data: [
|
||||
{
|
||||
id: 1,
|
||||
title: '期末考试',
|
||||
description: '高等数学期末考试',
|
||||
startTime: '2024-01-20T09:00:00',
|
||||
endTime: '2024-01-20T11:00:00',
|
||||
location: '考试教室C301',
|
||||
type: 'EXAM',
|
||||
repeatType: 'NONE',
|
||||
userId: 1
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
mockedAxios.get.mockResolvedValue(mockResponse)
|
||||
|
||||
const result = await scheduleApi.getSchedulesByRange(startTime, endTime)
|
||||
|
||||
expect(mockedAxios.get).toHaveBeenCalledWith('/schedules/range', {
|
||||
params: {
|
||||
startTime,
|
||||
endTime
|
||||
}
|
||||
})
|
||||
expect(result.data.data).toHaveLength(1)
|
||||
expect(result.data.data[0].type).toBe('EXAM')
|
||||
})
|
||||
|
||||
it('should handle invalid time range', async () => {
|
||||
const startTime = '2024-01-31T23:59:59'
|
||||
const endTime = '2024-01-01T00:00:00'
|
||||
|
||||
const mockResponse = {
|
||||
data: {
|
||||
code: 400,
|
||||
success: false,
|
||||
message: '结束时间不能早于开始时间'
|
||||
}
|
||||
}
|
||||
|
||||
mockedAxios.get.mockResolvedValue(mockResponse)
|
||||
|
||||
const result = await scheduleApi.getSchedulesByRange(startTime, endTime)
|
||||
|
||||
expect(result.data.success).toBe(false)
|
||||
expect(result.data.message).toBe('结束时间不能早于开始时间')
|
||||
})
|
||||
})
|
||||
|
||||
describe('getScheduleDetail', () => {
|
||||
it('should fetch schedule detail successfully', async () => {
|
||||
const mockSchedule = {
|
||||
id: 1,
|
||||
title: '数据结构与算法',
|
||||
description: '数据结构课程,包含树、图等内容',
|
||||
startTime: '2024-01-15T14:00:00',
|
||||
endTime: '2024-01-15T15:30:00',
|
||||
location: '计算机楼201',
|
||||
type: 'COURSE',
|
||||
repeatType: 'WEEKLY',
|
||||
repeatEnd: '2024-06-15T15:30:00',
|
||||
userId: 1,
|
||||
createdAt: '2024-01-01T10:00:00',
|
||||
updatedAt: '2024-01-01T10:00:00'
|
||||
}
|
||||
|
||||
const mockResponse = {
|
||||
data: {
|
||||
code: 200,
|
||||
success: true,
|
||||
data: mockSchedule
|
||||
}
|
||||
}
|
||||
|
||||
mockedAxios.get.mockResolvedValue(mockResponse)
|
||||
|
||||
const result = await scheduleApi.getScheduleDetail(1)
|
||||
|
||||
expect(mockedAxios.get).toHaveBeenCalledWith('/schedules/1')
|
||||
expect(result.data.data.title).toBe('数据结构与算法')
|
||||
expect(result.data.data.location).toBe('计算机楼201')
|
||||
})
|
||||
|
||||
it('should handle schedule not found', async () => {
|
||||
const mockResponse = {
|
||||
data: {
|
||||
code: 404,
|
||||
success: false,
|
||||
message: '日程不存在'
|
||||
}
|
||||
}
|
||||
|
||||
mockedAxios.get.mockResolvedValue(mockResponse)
|
||||
|
||||
const result = await scheduleApi.getScheduleDetail(999)
|
||||
|
||||
expect(result.data.success).toBe(false)
|
||||
expect(result.data.message).toBe('日程不存在')
|
||||
})
|
||||
|
||||
it('should handle unauthorized access', async () => {
|
||||
const mockResponse = {
|
||||
data: {
|
||||
code: 403,
|
||||
success: false,
|
||||
message: '无权限查看此日程'
|
||||
}
|
||||
}
|
||||
|
||||
mockedAxios.get.mockResolvedValue(mockResponse)
|
||||
|
||||
const result = await scheduleApi.getScheduleDetail(1)
|
||||
|
||||
expect(result.data.success).toBe(false)
|
||||
expect(result.data.message).toBe('无权限查看此日程')
|
||||
})
|
||||
})
|
||||
|
||||
describe('createSchedule', () => {
|
||||
it('should create schedule successfully', async () => {
|
||||
const scheduleData = {
|
||||
title: '新课程',
|
||||
description: '新课程描述',
|
||||
startTime: '2024-01-16T09:00:00',
|
||||
endTime: '2024-01-16T10:30:00',
|
||||
location: '教学楼D101',
|
||||
type: 'COURSE',
|
||||
repeatType: 'WEEKLY',
|
||||
repeatEnd: '2024-06-16T10:30:00'
|
||||
}
|
||||
|
||||
const mockResponse = {
|
||||
data: {
|
||||
code: 200,
|
||||
success: true,
|
||||
data: { scheduleId: 123 },
|
||||
message: '日程创建成功'
|
||||
}
|
||||
}
|
||||
|
||||
mockedAxios.post.mockResolvedValue(mockResponse)
|
||||
|
||||
const result = await scheduleApi.createSchedule(scheduleData)
|
||||
|
||||
expect(mockedAxios.post).toHaveBeenCalledWith('/schedules', scheduleData)
|
||||
expect(result.data.data.scheduleId).toBe(123)
|
||||
expect(result.data.message).toBe('日程创建成功')
|
||||
})
|
||||
|
||||
it('should handle validation errors', async () => {
|
||||
const invalidScheduleData = {
|
||||
title: '',
|
||||
description: '描述',
|
||||
startTime: '2024-01-16T09:00:00',
|
||||
endTime: '2024-01-16T08:00:00', // 结束时间早于开始时间
|
||||
location: '教学楼D101',
|
||||
type: 'COURSE',
|
||||
repeatType: 'WEEKLY'
|
||||
}
|
||||
|
||||
const mockResponse = {
|
||||
data: {
|
||||
code: 400,
|
||||
success: false,
|
||||
message: '结束时间不能早于开始时间'
|
||||
}
|
||||
}
|
||||
|
||||
mockedAxios.post.mockResolvedValue(mockResponse)
|
||||
|
||||
const result = await scheduleApi.createSchedule(invalidScheduleData)
|
||||
|
||||
expect(result.data.success).toBe(false)
|
||||
expect(result.data.message).toBe('结束时间不能早于开始时间')
|
||||
})
|
||||
|
||||
it('should handle time conflict', async () => {
|
||||
const conflictingScheduleData = {
|
||||
title: '冲突课程',
|
||||
description: '与现有课程时间冲突',
|
||||
startTime: '2024-01-16T09:30:00',
|
||||
endTime: '2024-01-16T11:00:00',
|
||||
location: '教学楼D102',
|
||||
type: 'COURSE',
|
||||
repeatType: 'WEEKLY'
|
||||
}
|
||||
|
||||
const mockResponse = {
|
||||
data: {
|
||||
code: 400,
|
||||
success: false,
|
||||
message: '时间冲突,与现有日程重叠'
|
||||
}
|
||||
}
|
||||
|
||||
mockedAxios.post.mockResolvedValue(mockResponse)
|
||||
|
||||
const result = await scheduleApi.createSchedule(conflictingScheduleData)
|
||||
|
||||
expect(result.data.success).toBe(false)
|
||||
expect(result.data.message).toBe('时间冲突,与现有日程重叠')
|
||||
})
|
||||
})
|
||||
|
||||
describe('updateSchedule', () => {
|
||||
it('should update schedule successfully', async () => {
|
||||
const updateData = {
|
||||
title: '更新后的课程',
|
||||
description: '更新后的描述',
|
||||
startTime: '2024-01-16T10:00:00',
|
||||
endTime: '2024-01-16T11:30:00',
|
||||
location: '教学楼E101',
|
||||
type: 'COURSE',
|
||||
repeatType: 'WEEKLY',
|
||||
repeatEnd: '2024-06-16T11:30:00'
|
||||
}
|
||||
|
||||
const mockResponse = {
|
||||
data: {
|
||||
code: 200,
|
||||
success: true,
|
||||
message: '日程更新成功'
|
||||
}
|
||||
}
|
||||
|
||||
mockedAxios.put.mockResolvedValue(mockResponse)
|
||||
|
||||
const result = await scheduleApi.updateSchedule(1, updateData)
|
||||
|
||||
expect(mockedAxios.put).toHaveBeenCalledWith('/schedules/1', updateData)
|
||||
expect(result.data.message).toBe('日程更新成功')
|
||||
})
|
||||
|
||||
it('should handle unauthorized update', async () => {
|
||||
const updateData = {
|
||||
title: '尝试更新别人的课程',
|
||||
description: '无权限更新',
|
||||
startTime: '2024-01-16T10:00:00',
|
||||
endTime: '2024-01-16T11:30:00',
|
||||
location: '教学楼E101',
|
||||
type: 'COURSE'
|
||||
}
|
||||
|
||||
const mockResponse = {
|
||||
data: {
|
||||
code: 403,
|
||||
success: false,
|
||||
message: '无权限修改此日程'
|
||||
}
|
||||
}
|
||||
|
||||
mockedAxios.put.mockResolvedValue(mockResponse)
|
||||
|
||||
const result = await scheduleApi.updateSchedule(1, updateData)
|
||||
|
||||
expect(result.data.success).toBe(false)
|
||||
expect(result.data.message).toBe('无权限修改此日程')
|
||||
})
|
||||
})
|
||||
|
||||
describe('deleteSchedule', () => {
|
||||
it('should delete schedule successfully', async () => {
|
||||
const mockResponse = {
|
||||
data: {
|
||||
code: 200,
|
||||
success: true,
|
||||
message: '日程删除成功'
|
||||
}
|
||||
}
|
||||
|
||||
mockedAxios.delete.mockResolvedValue(mockResponse)
|
||||
|
||||
const result = await scheduleApi.deleteSchedule(1)
|
||||
|
||||
expect(mockedAxios.delete).toHaveBeenCalledWith('/schedules/1')
|
||||
expect(result.data.message).toBe('日程删除成功')
|
||||
})
|
||||
|
||||
it('should handle unauthorized delete', async () => {
|
||||
const mockResponse = {
|
||||
data: {
|
||||
code: 403,
|
||||
success: false,
|
||||
message: '无权限删除此日程'
|
||||
}
|
||||
}
|
||||
|
||||
mockedAxios.delete.mockResolvedValue(mockResponse)
|
||||
|
||||
const result = await scheduleApi.deleteSchedule(1)
|
||||
|
||||
expect(result.data.success).toBe(false)
|
||||
expect(result.data.message).toBe('无权限删除此日程')
|
||||
})
|
||||
})
|
||||
|
||||
describe('checkConflict', () => {
|
||||
it('should check for no conflicts', async () => {
|
||||
const startTime = '2024-01-16T13:00:00'
|
||||
const endTime = '2024-01-16T14:30:00'
|
||||
|
||||
const mockResponse = {
|
||||
data: {
|
||||
code: 200,
|
||||
success: true,
|
||||
message: '无时间冲突'
|
||||
}
|
||||
}
|
||||
|
||||
mockedAxios.get.mockResolvedValue(mockResponse)
|
||||
|
||||
const result = await scheduleApi.checkConflict(startTime, endTime)
|
||||
|
||||
expect(mockedAxios.get).toHaveBeenCalledWith('/schedules/check-conflict', {
|
||||
params: {
|
||||
startTime,
|
||||
endTime
|
||||
}
|
||||
})
|
||||
expect(result.data.message).toBe('无时间冲突')
|
||||
})
|
||||
|
||||
it('should check for conflicts with exclusion', async () => {
|
||||
const startTime = '2024-01-16T13:00:00'
|
||||
const endTime = '2024-01-16T14:30:00'
|
||||
const excludeScheduleId = 5
|
||||
|
||||
const mockResponse = {
|
||||
data: {
|
||||
code: 200,
|
||||
success: true,
|
||||
message: '无时间冲突'
|
||||
}
|
||||
}
|
||||
|
||||
mockedAxios.get.mockResolvedValue(mockResponse)
|
||||
|
||||
const result = await scheduleApi.checkConflict(startTime, endTime, excludeScheduleId)
|
||||
|
||||
expect(mockedAxios.get).toHaveBeenCalledWith('/schedules/check-conflict', {
|
||||
params: {
|
||||
startTime,
|
||||
endTime,
|
||||
excludeScheduleId
|
||||
}
|
||||
})
|
||||
expect(result.data.message).toBe('无时间冲突')
|
||||
})
|
||||
|
||||
it('should detect time conflicts', async () => {
|
||||
const startTime = '2024-01-16T09:30:00'
|
||||
const endTime = '2024-01-16T11:00:00'
|
||||
|
||||
const mockResponse = {
|
||||
data: {
|
||||
code: 400,
|
||||
success: false,
|
||||
message: '时间冲突,与现有日程重叠'
|
||||
}
|
||||
}
|
||||
|
||||
mockedAxios.get.mockResolvedValue(mockResponse)
|
||||
|
||||
const result = await scheduleApi.checkConflict(startTime, endTime)
|
||||
|
||||
expect(result.data.success).toBe(false)
|
||||
expect(result.data.message).toBe('时间冲突,与现有日程重叠')
|
||||
})
|
||||
})
|
||||
|
||||
describe('getCourses', () => {
|
||||
it('should fetch courses successfully', async () => {
|
||||
const mockResponse = {
|
||||
data: {
|
||||
code: 200,
|
||||
success: true,
|
||||
data: [
|
||||
{
|
||||
id: 1,
|
||||
name: '高等数学',
|
||||
code: 'MATH101',
|
||||
teacher: '张教授',
|
||||
credits: 4,
|
||||
semester: '2024春季',
|
||||
description: '高等数学基础课程'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '数据结构',
|
||||
code: 'CS201',
|
||||
teacher: '李教授',
|
||||
credits: 3,
|
||||
semester: '2024春季',
|
||||
description: '计算机科学基础课程'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
mockedAxios.get.mockResolvedValue(mockResponse)
|
||||
|
||||
const result = await scheduleApi.getCourses()
|
||||
|
||||
expect(mockedAxios.get).toHaveBeenCalledWith('/courses')
|
||||
expect(result.data.data).toHaveLength(2)
|
||||
expect(result.data.data[0].name).toBe('高等数学')
|
||||
expect(result.data.data[1].credits).toBe(3)
|
||||
})
|
||||
})
|
||||
|
||||
describe('createCourse', () => {
|
||||
it('should create course successfully', async () => {
|
||||
const courseData = {
|
||||
name: '操作系统',
|
||||
code: 'CS301',
|
||||
teacher: '王教授',
|
||||
credits: 3,
|
||||
semester: '2024春季',
|
||||
description: '操作系统原理与实践'
|
||||
}
|
||||
|
||||
const mockResponse = {
|
||||
data: {
|
||||
code: 200,
|
||||
success: true,
|
||||
data: { courseId: 456 },
|
||||
message: '课程创建成功'
|
||||
}
|
||||
}
|
||||
|
||||
mockedAxios.post.mockResolvedValue(mockResponse)
|
||||
|
||||
const result = await scheduleApi.createCourse(courseData)
|
||||
|
||||
expect(mockedAxios.post).toHaveBeenCalledWith('/courses', courseData)
|
||||
expect(result.data.data.courseId).toBe(456)
|
||||
expect(result.data.message).toBe('课程创建成功')
|
||||
})
|
||||
|
||||
it('should handle duplicate course code', async () => {
|
||||
const duplicateCourseData = {
|
||||
name: '重复课程',
|
||||
code: 'MATH101', // 已存在的课程代码
|
||||
teacher: '赵教授',
|
||||
credits: 2,
|
||||
semester: '2024春季',
|
||||
description: '重复的课程代码'
|
||||
}
|
||||
|
||||
const mockResponse = {
|
||||
data: {
|
||||
code: 400,
|
||||
success: false,
|
||||
message: '课程代码已存在'
|
||||
}
|
||||
}
|
||||
|
||||
mockedAxios.post.mockResolvedValue(mockResponse)
|
||||
|
||||
const result = await scheduleApi.createCourse(duplicateCourseData)
|
||||
|
||||
expect(result.data.success).toBe(false)
|
||||
expect(result.data.message).toBe('课程代码已存在')
|
||||
})
|
||||
})
|
||||
|
||||
describe('updateCourse', () => {
|
||||
it('should update course successfully', async () => {
|
||||
const updateData = {
|
||||
name: '高等数学(更新)',
|
||||
code: 'MATH101',
|
||||
teacher: '张教授',
|
||||
credits: 4,
|
||||
semester: '2024春季',
|
||||
description: '更新后的高等数学课程'
|
||||
}
|
||||
|
||||
const mockResponse = {
|
||||
data: {
|
||||
code: 200,
|
||||
success: true,
|
||||
message: '课程更新成功'
|
||||
}
|
||||
}
|
||||
|
||||
mockedAxios.put.mockResolvedValue(mockResponse)
|
||||
|
||||
const result = await scheduleApi.updateCourse(1, updateData)
|
||||
|
||||
expect(mockedAxios.put).toHaveBeenCalledWith('/courses/1', updateData)
|
||||
expect(result.data.message).toBe('课程更新成功')
|
||||
})
|
||||
})
|
||||
|
||||
describe('deleteCourse', () => {
|
||||
it('should delete course successfully', async () => {
|
||||
const mockResponse = {
|
||||
data: {
|
||||
code: 200,
|
||||
success: true,
|
||||
message: '课程删除成功'
|
||||
}
|
||||
}
|
||||
|
||||
mockedAxios.delete.mockResolvedValue(mockResponse)
|
||||
|
||||
const result = await scheduleApi.deleteCourse(1)
|
||||
|
||||
expect(mockedAxios.delete).toHaveBeenCalledWith('/courses/1')
|
||||
expect(result.data.message).toBe('课程删除成功')
|
||||
})
|
||||
|
||||
it('should handle course in use error', async () => {
|
||||
const mockResponse = {
|
||||
data: {
|
||||
code: 400,
|
||||
success: false,
|
||||
message: '课程正在使用中,无法删除'
|
||||
}
|
||||
}
|
||||
|
||||
mockedAxios.delete.mockResolvedValue(mockResponse)
|
||||
|
||||
const result = await scheduleApi.deleteCourse(1)
|
||||
|
||||
expect(result.data.success).toBe(false)
|
||||
expect(result.data.message).toBe('课程正在使用中,无法删除')
|
||||
})
|
||||
})
|
||||
})
|
@ -0,0 +1,27 @@
|
||||
import { defineConfig } from 'vitest/config'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import { resolve } from 'path'
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [vue()],
|
||||
test: {
|
||||
environment: 'jsdom',
|
||||
globals: true,
|
||||
coverage: {
|
||||
provider: 'v8',
|
||||
reporter: ['text', 'json', 'html'],
|
||||
exclude: [
|
||||
'node_modules/',
|
||||
'dist/',
|
||||
'**/*.d.ts',
|
||||
'**/*.config.*',
|
||||
'**/coverage/**'
|
||||
]
|
||||
}
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': resolve(__dirname, 'src')
|
||||
}
|
||||
}
|
||||
})
|
@ -0,0 +1,80 @@
|
||||
package com.unilife.config;
|
||||
|
||||
import org.springframework.boot.test.context.TestConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
import org.springframework.mail.javamail.JavaMailSender;
|
||||
import org.springframework.mail.javamail.JavaMailSenderImpl;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* 测试配置类
|
||||
* 为测试环境提供特定的Bean配置
|
||||
*/
|
||||
@TestConfiguration
|
||||
public class TestConfig {
|
||||
|
||||
/**
|
||||
* 测试用的Redis连接工厂
|
||||
*/
|
||||
@Bean
|
||||
@Primary
|
||||
public RedisConnectionFactory testRedisConnectionFactory() {
|
||||
LettuceConnectionFactory factory = new LettuceConnectionFactory("localhost", 6379);
|
||||
factory.setDatabase(1); // 使用数据库1进行测试
|
||||
return factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试用的RedisTemplate
|
||||
*/
|
||||
@Bean
|
||||
@Primary
|
||||
public RedisTemplate<String, Object> testRedisTemplate(RedisConnectionFactory connectionFactory) {
|
||||
RedisTemplate<String, Object> template = new RedisTemplate<>();
|
||||
template.setConnectionFactory(connectionFactory);
|
||||
template.setKeySerializer(new StringRedisSerializer());
|
||||
template.setValueSerializer(new StringRedisSerializer());
|
||||
template.setHashKeySerializer(new StringRedisSerializer());
|
||||
template.setHashValueSerializer(new StringRedisSerializer());
|
||||
return template;
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试用的StringRedisTemplate
|
||||
*/
|
||||
@Bean
|
||||
@Primary
|
||||
public StringRedisTemplate testStringRedisTemplate(RedisConnectionFactory connectionFactory) {
|
||||
StringRedisTemplate template = new StringRedisTemplate();
|
||||
template.setConnectionFactory(connectionFactory);
|
||||
return template;
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试用的邮件发送器
|
||||
*/
|
||||
@Bean
|
||||
@Primary
|
||||
public JavaMailSender testJavaMailSender() {
|
||||
JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
|
||||
mailSender.setHost("smtp.example.com");
|
||||
mailSender.setPort(587);
|
||||
mailSender.setUsername("test@example.com");
|
||||
mailSender.setPassword("testpassword");
|
||||
|
||||
Properties props = mailSender.getJavaMailProperties();
|
||||
props.put("mail.transport.protocol", "smtp");
|
||||
props.put("mail.smtp.auth", "true");
|
||||
props.put("mail.smtp.starttls.enable", "true");
|
||||
props.put("mail.debug", "false"); // 测试时关闭debug
|
||||
|
||||
return mailSender;
|
||||
}
|
||||
}
|
@ -0,0 +1,169 @@
|
||||
package com.unilife.controller;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.unilife.common.result.Result;
|
||||
import com.unilife.model.dto.CreatePostDTO;
|
||||
import com.unilife.model.dto.UpdatePostDTO;
|
||||
import com.unilife.service.PostService;
|
||||
import com.unilife.utils.BaseContext;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
||||
|
||||
@WebMvcTest(PostController.class)
|
||||
class PostControllerTest {
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@MockBean
|
||||
private PostService postService;
|
||||
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
private CreatePostDTO createPostDTO;
|
||||
private UpdatePostDTO updatePostDTO;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
createPostDTO = new CreatePostDTO();
|
||||
createPostDTO.setTitle("测试帖子");
|
||||
createPostDTO.setContent("测试内容");
|
||||
createPostDTO.setCategoryId(1L);
|
||||
|
||||
updatePostDTO = new UpdatePostDTO();
|
||||
updatePostDTO.setTitle("更新标题");
|
||||
updatePostDTO.setContent("更新内容");
|
||||
updatePostDTO.setCategoryId(1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCreatePost_Success() throws Exception {
|
||||
// Mock用户已登录
|
||||
try (var mockedStatic = mockStatic(BaseContext.class)) {
|
||||
mockedStatic.when(BaseContext::getId).thenReturn(1L);
|
||||
|
||||
when(postService.createPost(eq(1L), any(CreatePostDTO.class)))
|
||||
.thenReturn(Result.success("帖子发布成功"));
|
||||
|
||||
mockMvc.perform(post("/posts")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(objectMapper.writeValueAsString(createPostDTO)))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.success").value(true))
|
||||
.andExpect(jsonPath("$.message").value("帖子发布成功"));
|
||||
|
||||
verify(postService).createPost(eq(1L), any(CreatePostDTO.class));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCreatePost_Unauthorized() throws Exception {
|
||||
// Mock用户未登录
|
||||
try (var mockedStatic = mockStatic(BaseContext.class)) {
|
||||
mockedStatic.when(BaseContext::getId).thenReturn(null);
|
||||
|
||||
mockMvc.perform(post("/posts")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(objectMapper.writeValueAsString(createPostDTO)))
|
||||
.andExpect(status().isOk())
|
||||
.andExpected(jsonPath("$.success").value(false))
|
||||
.andExpected(jsonPath("$.code").value(401))
|
||||
.andExpected(jsonPath("$.message").value("未登录"));
|
||||
|
||||
verify(postService, never()).createPost(anyLong(), any(CreatePostDTO.class));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetPostDetail_Success() throws Exception {
|
||||
when(postService.getPostDetail(eq(1L), any()))
|
||||
.thenReturn(Result.success("帖子详情"));
|
||||
|
||||
mockMvc.perform(get("/posts/1"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpected(jsonPath("$.success").value(true));
|
||||
|
||||
verify(postService).getPostDetail(eq(1L), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetPostList_Success() throws Exception {
|
||||
when(postService.getPostList(any(), any(), anyInt(), anyInt(), any(), any()))
|
||||
.thenReturn(Result.success("帖子列表"));
|
||||
|
||||
mockMvc.perform(get("/posts")
|
||||
.param("categoryId", "1")
|
||||
.param("keyword", "测试")
|
||||
.param("page", "1")
|
||||
.param("size", "10")
|
||||
.param("sort", "latest"))
|
||||
.andExpected(status().isOk())
|
||||
.andExpected(jsonPath("$.success").value(true));
|
||||
|
||||
verify(postService).getPostList(eq(1L), eq("测试"), eq(1), eq(10), eq("latest"), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUpdatePost_Success() throws Exception {
|
||||
try (var mockedStatic = mockStatic(BaseContext.class)) {
|
||||
mockedStatic.when(BaseContext::getId).thenReturn(1L);
|
||||
|
||||
when(postService.updatePost(eq(1L), eq(1L), any(UpdatePostDTO.class)))
|
||||
.thenReturn(Result.success("帖子更新成功"));
|
||||
|
||||
mockMvc.perform(put("/posts/1")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(objectMapper.writeValueAsString(updatePostDTO)))
|
||||
.andExpected(status().isOk())
|
||||
.andExpected(jsonPath("$.success").value(true))
|
||||
.andExpected(jsonPath("$.message").value("帖子更新成功"));
|
||||
|
||||
verify(postService).updatePost(eq(1L), eq(1L), any(UpdatePostDTO.class));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDeletePost_Success() throws Exception {
|
||||
try (var mockedStatic = mockStatic(BaseContext.class)) {
|
||||
mockedStatic.when(BaseContext::getId).thenReturn(1L);
|
||||
|
||||
when(postService.deletePost(eq(1L), eq(1L)))
|
||||
.thenReturn(Result.success("帖子删除成功"));
|
||||
|
||||
mockMvc.perform(delete("/posts/1"))
|
||||
.andExpected(status().isOk())
|
||||
.andExpected(jsonPath("$.success").value(true))
|
||||
.andExpected(jsonPath("$.message").value("帖子删除成功"));
|
||||
|
||||
verify(postService).deletePost(eq(1L), eq(1L));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLikePost_Success() throws Exception {
|
||||
try (var mockedStatic = mockStatic(BaseContext.class)) {
|
||||
mockedStatic.when(BaseContext::getId).thenReturn(1L);
|
||||
|
||||
when(postService.likePost(eq(1L), eq(1L)))
|
||||
.thenReturn(Result.success("点赞成功"));
|
||||
|
||||
mockMvc.perform(post("/posts/1/like"))
|
||||
.andExpected(status().isOk())
|
||||
.andExpected(jsonPath("$.success").value(true))
|
||||
.andExpected(jsonPath("$.message").value("点赞成功"));
|
||||
|
||||
verify(postService).likePost(eq(1L), eq(1L));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,287 @@
|
||||
package com.unilife.service;
|
||||
|
||||
import com.unilife.common.result.Result;
|
||||
import com.unilife.mapper.PostMapper;
|
||||
import com.unilife.mapper.UserMapper;
|
||||
import com.unilife.mapper.CategoryMapper;
|
||||
import com.unilife.model.dto.CreatePostDTO;
|
||||
import com.unilife.model.dto.UpdatePostDTO;
|
||||
import com.unilife.model.entity.Post;
|
||||
import com.unilife.model.entity.User;
|
||||
import com.unilife.model.entity.Category;
|
||||
import com.unilife.service.impl.PostServiceImpl;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@SpringBootTest
|
||||
class PostServiceTest {
|
||||
|
||||
@Mock
|
||||
private PostMapper postMapper;
|
||||
|
||||
@Mock
|
||||
private UserMapper userMapper;
|
||||
|
||||
@Mock
|
||||
private CategoryMapper categoryMapper;
|
||||
|
||||
@InjectMocks
|
||||
private PostServiceImpl postService;
|
||||
|
||||
private User testUser;
|
||||
private Category testCategory;
|
||||
private Post testPost;
|
||||
private CreatePostDTO createPostDTO;
|
||||
private UpdatePostDTO updatePostDTO;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
// 初始化测试数据
|
||||
testUser = new User();
|
||||
testUser.setId(1L);
|
||||
testUser.setNickname("测试用户");
|
||||
testUser.setAvatar("avatar.jpg");
|
||||
|
||||
testCategory = new Category();
|
||||
testCategory.setId(1L);
|
||||
testCategory.setName("学习讨论");
|
||||
testCategory.setStatus(1);
|
||||
|
||||
testPost = new Post();
|
||||
testPost.setId(1L);
|
||||
testPost.setTitle("测试帖子");
|
||||
testPost.setContent("这是一个测试帖子的内容");
|
||||
testPost.setUserId(1L);
|
||||
testPost.setCategoryId(1L);
|
||||
testPost.setLikeCount(0);
|
||||
testPost.setViewCount(0);
|
||||
testPost.setCommentCount(0);
|
||||
testPost.setCreatedAt(LocalDateTime.now());
|
||||
testPost.setUpdatedAt(LocalDateTime.now());
|
||||
|
||||
createPostDTO = new CreatePostDTO();
|
||||
createPostDTO.setTitle("新帖子标题");
|
||||
createPostDTO.setContent("新帖子内容");
|
||||
createPostDTO.setCategoryId(1L);
|
||||
|
||||
updatePostDTO = new UpdatePostDTO();
|
||||
updatePostDTO.setTitle("更新后的标题");
|
||||
updatePostDTO.setContent("更新后的内容");
|
||||
updatePostDTO.setCategoryId(1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCreatePost_Success() {
|
||||
// Mock 依赖方法
|
||||
when(userMapper.findById(1L)).thenReturn(testUser);
|
||||
when(categoryMapper.findById(1L)).thenReturn(testCategory);
|
||||
when(postMapper.insert(any(Post.class))).thenReturn(1);
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = postService.createPost(1L, createPostDTO);
|
||||
|
||||
// 验证结果
|
||||
assertTrue(result.isSuccess());
|
||||
assertEquals("帖子发布成功", result.getMessage());
|
||||
|
||||
// 验证方法调用
|
||||
verify(userMapper).findById(1L);
|
||||
verify(categoryMapper).findById(1L);
|
||||
verify(postMapper).insert(any(Post.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCreatePost_UserNotFound() {
|
||||
// Mock 用户不存在
|
||||
when(userMapper.findById(1L)).thenReturn(null);
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = postService.createPost(1L, createPostDTO);
|
||||
|
||||
// 验证结果
|
||||
assertFalse(result.isSuccess());
|
||||
assertEquals(404, result.getCode());
|
||||
assertEquals("用户不存在", result.getMessage());
|
||||
|
||||
// 验证不会尝试创建帖子
|
||||
verify(postMapper, never()).insert(any(Post.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCreatePost_CategoryNotFound() {
|
||||
// Mock 用户存在但分类不存在
|
||||
when(userMapper.findById(1L)).thenReturn(testUser);
|
||||
when(categoryMapper.findById(1L)).thenReturn(null);
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = postService.createPost(1L, createPostDTO);
|
||||
|
||||
// 验证结果
|
||||
assertFalse(result.isSuccess());
|
||||
assertEquals(404, result.getCode());
|
||||
assertEquals("分类不存在", result.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCreatePost_InvalidTitle() {
|
||||
// 测试空标题
|
||||
createPostDTO.setTitle("");
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = postService.createPost(1L, createPostDTO);
|
||||
|
||||
// 验证结果
|
||||
assertFalse(result.isSuccess());
|
||||
assertEquals(400, result.getCode());
|
||||
assertTrue(result.getMessage().contains("标题不能为空"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetPostDetail_Success() {
|
||||
// Mock 依赖方法
|
||||
when(postMapper.findById(1L)).thenReturn(testPost);
|
||||
when(userMapper.findById(1L)).thenReturn(testUser);
|
||||
when(categoryMapper.findById(1L)).thenReturn(testCategory);
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = postService.getPostDetail(1L, 1L);
|
||||
|
||||
// 验证结果
|
||||
assertTrue(result.isSuccess());
|
||||
assertNotNull(result.getData());
|
||||
|
||||
// 验证浏览量增加
|
||||
verify(postMapper).updateViewCount(1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetPostDetail_PostNotFound() {
|
||||
// Mock 帖子不存在
|
||||
when(postMapper.findById(1L)).thenReturn(null);
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = postService.getPostDetail(1L, 1L);
|
||||
|
||||
// 验证结果
|
||||
assertFalse(result.isSuccess());
|
||||
assertEquals(404, result.getCode());
|
||||
assertEquals("帖子不存在", result.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetPostList_Success() {
|
||||
// Mock 帖子列表
|
||||
List<Post> posts = Arrays.asList(testPost);
|
||||
when(postMapper.findByConditions(any(), any(), anyInt(), anyInt(), any())).thenReturn(posts);
|
||||
when(postMapper.countByConditions(any(), any())).thenReturn(1);
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = postService.getPostList(1L, "测试", 1, 10, "latest", 1L);
|
||||
|
||||
// 验证结果
|
||||
assertTrue(result.isSuccess());
|
||||
assertNotNull(result.getData());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUpdatePost_Success() {
|
||||
// Mock 依赖方法
|
||||
when(postMapper.findById(1L)).thenReturn(testPost);
|
||||
when(categoryMapper.findById(1L)).thenReturn(testCategory);
|
||||
when(postMapper.update(any(Post.class))).thenReturn(1);
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = postService.updatePost(1L, 1L, updatePostDTO);
|
||||
|
||||
// 验证结果
|
||||
assertTrue(result.isSuccess());
|
||||
assertEquals("帖子更新成功", result.getMessage());
|
||||
|
||||
// 验证方法调用
|
||||
verify(postMapper).update(any(Post.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUpdatePost_Unauthorized() {
|
||||
// Mock 其他用户的帖子
|
||||
testPost.setUserId(2L);
|
||||
when(postMapper.findById(1L)).thenReturn(testPost);
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = postService.updatePost(1L, 1L, updatePostDTO);
|
||||
|
||||
// 验证结果
|
||||
assertFalse(result.isSuccess());
|
||||
assertEquals(403, result.getCode());
|
||||
assertEquals("无权限修改此帖子", result.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDeletePost_Success() {
|
||||
// Mock 依赖方法
|
||||
when(postMapper.findById(1L)).thenReturn(testPost);
|
||||
when(postMapper.delete(1L)).thenReturn(1);
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = postService.deletePost(1L, 1L);
|
||||
|
||||
// 验证结果
|
||||
assertTrue(result.isSuccess());
|
||||
assertEquals("帖子删除成功", result.getMessage());
|
||||
|
||||
// 验证方法调用
|
||||
verify(postMapper).delete(1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLikePost_Success() {
|
||||
// Mock 依赖方法
|
||||
when(postMapper.findById(1L)).thenReturn(testPost);
|
||||
when(postMapper.isLikedByUser(1L, 1L)).thenReturn(false);
|
||||
when(postMapper.insertLike(1L, 1L)).thenReturn(1);
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = postService.likePost(1L, 1L);
|
||||
|
||||
// 验证结果
|
||||
assertTrue(result.isSuccess());
|
||||
assertEquals("点赞成功", result.getMessage());
|
||||
|
||||
// 验证方法调用
|
||||
verify(postMapper).insertLike(1L, 1L);
|
||||
verify(postMapper).updateLikeCount(1L, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUnlikePost_Success() {
|
||||
// Mock 已点赞状态
|
||||
when(postMapper.findById(1L)).thenReturn(testPost);
|
||||
when(postMapper.isLikedByUser(1L, 1L)).thenReturn(true);
|
||||
when(postMapper.deleteLike(1L, 1L)).thenReturn(1);
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = postService.likePost(1L, 1L);
|
||||
|
||||
// 验证结果
|
||||
assertTrue(result.isSuccess());
|
||||
assertEquals("取消点赞成功", result.getMessage());
|
||||
|
||||
// 验证方法调用
|
||||
verify(postMapper).deleteLike(1L, 1L);
|
||||
verify(postMapper).updateLikeCount(1L, -1);
|
||||
}
|
||||
}
|
@ -0,0 +1,348 @@
|
||||
package com.unilife.service;
|
||||
|
||||
import com.unilife.common.result.Result;
|
||||
import com.unilife.mapper.ResourceMapper;
|
||||
import com.unilife.mapper.UserMapper;
|
||||
import com.unilife.mapper.CategoryMapper;
|
||||
import com.unilife.model.dto.CreateResourceDTO;
|
||||
import com.unilife.model.entity.Resource;
|
||||
import com.unilife.model.entity.User;
|
||||
import com.unilife.model.entity.Category;
|
||||
import com.unilife.service.impl.ResourceServiceImpl;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.mock.web.MockMultipartFile;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@SpringBootTest
|
||||
class ResourceServiceTest {
|
||||
|
||||
@Mock
|
||||
private ResourceMapper resourceMapper;
|
||||
|
||||
@Mock
|
||||
private UserMapper userMapper;
|
||||
|
||||
@Mock
|
||||
private CategoryMapper categoryMapper;
|
||||
|
||||
@InjectMocks
|
||||
private ResourceServiceImpl resourceService;
|
||||
|
||||
private User testUser;
|
||||
private Category testCategory;
|
||||
private Resource testResource;
|
||||
private CreateResourceDTO createResourceDTO;
|
||||
private MockMultipartFile mockFile;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
// 初始化测试数据
|
||||
testUser = new User();
|
||||
testUser.setId(1L);
|
||||
testUser.setNickname("测试用户");
|
||||
testUser.setAvatar("avatar.jpg");
|
||||
|
||||
testCategory = new Category();
|
||||
testCategory.setId(1L);
|
||||
testCategory.setName("学习资料");
|
||||
testCategory.setStatus(1);
|
||||
|
||||
testResource = new Resource();
|
||||
testResource.setId(1L);
|
||||
testResource.setTitle("测试资源");
|
||||
testResource.setDescription("测试资源描述");
|
||||
testResource.setFileName("test.pdf");
|
||||
testResource.setFileUrl("http://example.com/test.pdf");
|
||||
testResource.setFileSize(1024L);
|
||||
testResource.setFileType("pdf");
|
||||
testResource.setUserId(1L);
|
||||
testResource.setCategoryId(1L);
|
||||
testResource.setDownloadCount(0);
|
||||
testResource.setLikeCount(0);
|
||||
testResource.setCreatedAt(LocalDateTime.now());
|
||||
testResource.setUpdatedAt(LocalDateTime.now());
|
||||
|
||||
createResourceDTO = new CreateResourceDTO();
|
||||
createResourceDTO.setTitle("新资源标题");
|
||||
createResourceDTO.setDescription("新资源描述");
|
||||
createResourceDTO.setCategoryId(1L);
|
||||
|
||||
mockFile = new MockMultipartFile(
|
||||
"file",
|
||||
"test.pdf",
|
||||
"application/pdf",
|
||||
"test content".getBytes()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUploadResource_Success() {
|
||||
// Mock 依赖方法
|
||||
when(userMapper.findById(1L)).thenReturn(testUser);
|
||||
when(categoryMapper.findById(1L)).thenReturn(testCategory);
|
||||
when(resourceMapper.insert(any(Resource.class))).thenReturn(1);
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = resourceService.uploadResource(1L, createResourceDTO, mockFile);
|
||||
|
||||
// 验证结果
|
||||
assertTrue(result.isSuccess());
|
||||
assertEquals("资源上传成功", result.getMessage());
|
||||
|
||||
// 验证方法调用
|
||||
verify(userMapper).findById(1L);
|
||||
verify(categoryMapper).findById(1L);
|
||||
verify(resourceMapper).insert(any(Resource.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUploadResource_UserNotFound() {
|
||||
// Mock 用户不存在
|
||||
when(userMapper.findById(1L)).thenReturn(null);
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = resourceService.uploadResource(1L, createResourceDTO, mockFile);
|
||||
|
||||
// 验证结果
|
||||
assertFalse(result.isSuccess());
|
||||
assertEquals(404, result.getCode());
|
||||
assertEquals("用户不存在", result.getMessage());
|
||||
|
||||
// 验证不会尝试上传资源
|
||||
verify(resourceMapper, never()).insert(any(Resource.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUploadResource_CategoryNotFound() {
|
||||
// Mock 用户存在但分类不存在
|
||||
when(userMapper.findById(1L)).thenReturn(testUser);
|
||||
when(categoryMapper.findById(1L)).thenReturn(null);
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = resourceService.uploadResource(1L, createResourceDTO, mockFile);
|
||||
|
||||
// 验证结果
|
||||
assertFalse(result.isSuccess());
|
||||
assertEquals(404, result.getCode());
|
||||
assertEquals("分类不存在", result.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUploadResource_EmptyFile() {
|
||||
// 测试空文件
|
||||
MockMultipartFile emptyFile = new MockMultipartFile(
|
||||
"file",
|
||||
"empty.pdf",
|
||||
"application/pdf",
|
||||
new byte[0]
|
||||
);
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = resourceService.uploadResource(1L, createResourceDTO, emptyFile);
|
||||
|
||||
// 验证结果
|
||||
assertFalse(result.isSuccess());
|
||||
assertEquals(400, result.getCode());
|
||||
assertEquals("文件不能为空", result.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUploadResource_InvalidFileType() {
|
||||
// 测试不支持的文件类型
|
||||
MockMultipartFile invalidFile = new MockMultipartFile(
|
||||
"file",
|
||||
"test.exe",
|
||||
"application/octet-stream",
|
||||
"test content".getBytes()
|
||||
);
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = resourceService.uploadResource(1L, createResourceDTO, invalidFile);
|
||||
|
||||
// 验证结果
|
||||
assertFalse(result.isSuccess());
|
||||
assertEquals(400, result.getCode());
|
||||
assertTrue(result.getMessage().contains("不支持的文件类型"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetResourceDetail_Success() {
|
||||
// Mock 依赖方法
|
||||
when(resourceMapper.findById(1L)).thenReturn(testResource);
|
||||
when(userMapper.findById(1L)).thenReturn(testUser);
|
||||
when(categoryMapper.findById(1L)).thenReturn(testCategory);
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = resourceService.getResourceDetail(1L, 1L);
|
||||
|
||||
// 验证结果
|
||||
assertTrue(result.isSuccess());
|
||||
assertNotNull(result.getData());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetResourceDetail_ResourceNotFound() {
|
||||
// Mock 资源不存在
|
||||
when(resourceMapper.findById(1L)).thenReturn(null);
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = resourceService.getResourceDetail(1L, 1L);
|
||||
|
||||
// 验证结果
|
||||
assertFalse(result.isSuccess());
|
||||
assertEquals(404, result.getCode());
|
||||
assertEquals("资源不存在", result.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetResourceList_Success() {
|
||||
// Mock 资源列表
|
||||
List<Resource> resources = Arrays.asList(testResource);
|
||||
when(resourceMapper.findByConditions(any(), any(), any(), anyInt(), anyInt())).thenReturn(resources);
|
||||
when(resourceMapper.countByConditions(any(), any(), any())).thenReturn(1);
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = resourceService.getResourceList(1L, 1L, "测试", 1, 10, 1L);
|
||||
|
||||
// 验证结果
|
||||
assertTrue(result.isSuccess());
|
||||
assertNotNull(result.getData());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUpdateResource_Success() {
|
||||
// Mock 依赖方法
|
||||
when(resourceMapper.findById(1L)).thenReturn(testResource);
|
||||
when(categoryMapper.findById(1L)).thenReturn(testCategory);
|
||||
when(resourceMapper.update(any(Resource.class))).thenReturn(1);
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = resourceService.updateResource(1L, 1L, createResourceDTO);
|
||||
|
||||
// 验证结果
|
||||
assertTrue(result.isSuccess());
|
||||
assertEquals("资源更新成功", result.getMessage());
|
||||
|
||||
// 验证方法调用
|
||||
verify(resourceMapper).update(any(Resource.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUpdateResource_Unauthorized() {
|
||||
// Mock 其他用户的资源
|
||||
testResource.setUserId(2L);
|
||||
when(resourceMapper.findById(1L)).thenReturn(testResource);
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = resourceService.updateResource(1L, 1L, createResourceDTO);
|
||||
|
||||
// 验证结果
|
||||
assertFalse(result.isSuccess());
|
||||
assertEquals(403, result.getCode());
|
||||
assertEquals("无权限修改此资源", result.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDeleteResource_Success() {
|
||||
// Mock 依赖方法
|
||||
when(resourceMapper.findById(1L)).thenReturn(testResource);
|
||||
when(resourceMapper.delete(1L)).thenReturn(1);
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = resourceService.deleteResource(1L, 1L);
|
||||
|
||||
// 验证结果
|
||||
assertTrue(result.isSuccess());
|
||||
assertEquals("资源删除成功", result.getMessage());
|
||||
|
||||
// 验证方法调用
|
||||
verify(resourceMapper).delete(1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDownloadResource_Success() {
|
||||
// Mock 依赖方法
|
||||
when(resourceMapper.findById(1L)).thenReturn(testResource);
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = resourceService.downloadResource(1L, 1L);
|
||||
|
||||
// 验证结果
|
||||
assertTrue(result.isSuccess());
|
||||
assertNotNull(result.getData());
|
||||
|
||||
// 验证下载量增加
|
||||
verify(resourceMapper).updateDownloadCount(1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLikeResource_Success() {
|
||||
// Mock 依赖方法
|
||||
when(resourceMapper.findById(1L)).thenReturn(testResource);
|
||||
when(resourceMapper.isLikedByUser(1L, 1L)).thenReturn(false);
|
||||
when(resourceMapper.insertLike(1L, 1L)).thenReturn(1);
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = resourceService.likeResource(1L, 1L);
|
||||
|
||||
// 验证结果
|
||||
assertTrue(result.isSuccess());
|
||||
assertEquals("点赞成功", result.getMessage());
|
||||
|
||||
// 验证方法调用
|
||||
verify(resourceMapper).insertLike(1L, 1L);
|
||||
verify(resourceMapper).updateLikeCount(1L, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUnlikeResource_Success() {
|
||||
// Mock 已点赞状态
|
||||
when(resourceMapper.findById(1L)).thenReturn(testResource);
|
||||
when(resourceMapper.isLikedByUser(1L, 1L)).thenReturn(true);
|
||||
when(resourceMapper.deleteLike(1L, 1L)).thenReturn(1);
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = resourceService.likeResource(1L, 1L);
|
||||
|
||||
// 验证结果
|
||||
assertTrue(result.isSuccess());
|
||||
assertEquals("取消点赞成功", result.getMessage());
|
||||
|
||||
// 验证方法调用
|
||||
verify(resourceMapper).deleteLike(1L, 1L);
|
||||
verify(resourceMapper).updateLikeCount(1L, -1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetUserResources_Success() {
|
||||
// Mock 用户资源列表
|
||||
List<Resource> userResources = Arrays.asList(testResource);
|
||||
when(resourceMapper.findByUserId(eq(1L), anyInt(), anyInt())).thenReturn(userResources);
|
||||
when(resourceMapper.countByUserId(1L)).thenReturn(1);
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = resourceService.getUserResources(1L, 1, 10);
|
||||
|
||||
// 验证结果
|
||||
assertTrue(result.isSuccess());
|
||||
assertNotNull(result.getData());
|
||||
|
||||
// 验证方法调用
|
||||
verify(resourceMapper).findByUserId(eq(1L), anyInt(), anyInt());
|
||||
verify(resourceMapper).countByUserId(1L);
|
||||
}
|
||||
}
|
@ -0,0 +1,370 @@
|
||||
package com.unilife.service;
|
||||
|
||||
import com.unilife.common.result.Result;
|
||||
import com.unilife.mapper.ScheduleMapper;
|
||||
import com.unilife.mapper.UserMapper;
|
||||
import com.unilife.model.dto.CreateScheduleDTO;
|
||||
import com.unilife.model.entity.Schedule;
|
||||
import com.unilife.model.entity.User;
|
||||
import com.unilife.service.impl.ScheduleServiceImpl;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@SpringBootTest
|
||||
class ScheduleServiceTest {
|
||||
|
||||
@Mock
|
||||
private ScheduleMapper scheduleMapper;
|
||||
|
||||
@Mock
|
||||
private UserMapper userMapper;
|
||||
|
||||
@InjectMocks
|
||||
private ScheduleServiceImpl scheduleService;
|
||||
|
||||
private User testUser;
|
||||
private Schedule testSchedule;
|
||||
private CreateScheduleDTO createScheduleDTO;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
// 初始化测试数据
|
||||
testUser = new User();
|
||||
testUser.setId(1L);
|
||||
testUser.setNickname("测试用户");
|
||||
testUser.setAvatar("avatar.jpg");
|
||||
|
||||
testSchedule = new Schedule();
|
||||
testSchedule.setId(1L);
|
||||
testSchedule.setTitle("测试课程");
|
||||
testSchedule.setDescription("测试课程描述");
|
||||
testSchedule.setStartTime(LocalDateTime.of(2024, 1, 15, 9, 0));
|
||||
testSchedule.setEndTime(LocalDateTime.of(2024, 1, 15, 10, 30));
|
||||
testSchedule.setLocation("教学楼A101");
|
||||
testSchedule.setType("COURSE");
|
||||
testSchedule.setRepeatType("WEEKLY");
|
||||
testSchedule.setRepeatEnd(LocalDateTime.of(2024, 6, 15, 10, 30));
|
||||
testSchedule.setUserId(1L);
|
||||
testSchedule.setCreatedAt(LocalDateTime.now());
|
||||
testSchedule.setUpdatedAt(LocalDateTime.now());
|
||||
|
||||
createScheduleDTO = new CreateScheduleDTO();
|
||||
createScheduleDTO.setTitle("新课程");
|
||||
createScheduleDTO.setDescription("新课程描述");
|
||||
createScheduleDTO.setStartTime(LocalDateTime.of(2024, 1, 16, 14, 0));
|
||||
createScheduleDTO.setEndTime(LocalDateTime.of(2024, 1, 16, 15, 30));
|
||||
createScheduleDTO.setLocation("教学楼B201");
|
||||
createScheduleDTO.setType("COURSE");
|
||||
createScheduleDTO.setRepeatType("WEEKLY");
|
||||
createScheduleDTO.setRepeatEnd(LocalDateTime.of(2024, 6, 16, 15, 30));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCreateSchedule_Success() {
|
||||
// Mock 依赖方法
|
||||
when(userMapper.findById(1L)).thenReturn(testUser);
|
||||
when(scheduleMapper.findConflictingSchedules(eq(1L), any(), any(), any())).thenReturn(Arrays.asList());
|
||||
when(scheduleMapper.insert(any(Schedule.class))).thenReturn(1);
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = scheduleService.createSchedule(1L, createScheduleDTO);
|
||||
|
||||
// 验证结果
|
||||
assertTrue(result.isSuccess());
|
||||
assertEquals("日程创建成功", result.getMessage());
|
||||
|
||||
// 验证方法调用
|
||||
verify(userMapper).findById(1L);
|
||||
verify(scheduleMapper).findConflictingSchedules(eq(1L), any(), any(), any());
|
||||
verify(scheduleMapper).insert(any(Schedule.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCreateSchedule_UserNotFound() {
|
||||
// Mock 用户不存在
|
||||
when(userMapper.findById(1L)).thenReturn(null);
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = scheduleService.createSchedule(1L, createScheduleDTO);
|
||||
|
||||
// 验证结果
|
||||
assertFalse(result.isSuccess());
|
||||
assertEquals(404, result.getCode());
|
||||
assertEquals("用户不存在", result.getMessage());
|
||||
|
||||
// 验证不会尝试创建日程
|
||||
verify(scheduleMapper, never()).insert(any(Schedule.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCreateSchedule_TimeConflict() {
|
||||
// Mock 时间冲突
|
||||
Schedule conflictingSchedule = new Schedule();
|
||||
conflictingSchedule.setId(2L);
|
||||
conflictingSchedule.setTitle("冲突课程");
|
||||
conflictingSchedule.setStartTime(LocalDateTime.of(2024, 1, 16, 14, 30));
|
||||
conflictingSchedule.setEndTime(LocalDateTime.of(2024, 1, 16, 16, 0));
|
||||
|
||||
when(userMapper.findById(1L)).thenReturn(testUser);
|
||||
when(scheduleMapper.findConflictingSchedules(eq(1L), any(), any(), any()))
|
||||
.thenReturn(Arrays.asList(conflictingSchedule));
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = scheduleService.createSchedule(1L, createScheduleDTO);
|
||||
|
||||
// 验证结果
|
||||
assertFalse(result.isSuccess());
|
||||
assertEquals(400, result.getCode());
|
||||
assertTrue(result.getMessage().contains("时间冲突"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCreateSchedule_InvalidTimeRange() {
|
||||
// 测试结束时间早于开始时间
|
||||
createScheduleDTO.setStartTime(LocalDateTime.of(2024, 1, 16, 16, 0));
|
||||
createScheduleDTO.setEndTime(LocalDateTime.of(2024, 1, 16, 14, 0));
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = scheduleService.createSchedule(1L, createScheduleDTO);
|
||||
|
||||
// 验证结果
|
||||
assertFalse(result.isSuccess());
|
||||
assertEquals(400, result.getCode());
|
||||
assertEquals("结束时间不能早于开始时间", result.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetScheduleDetail_Success() {
|
||||
// Mock 依赖方法
|
||||
when(scheduleMapper.findById(1L)).thenReturn(testSchedule);
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = scheduleService.getScheduleDetail(1L, 1L);
|
||||
|
||||
// 验证结果
|
||||
assertTrue(result.isSuccess());
|
||||
assertNotNull(result.getData());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetScheduleDetail_NotFound() {
|
||||
// Mock 日程不存在
|
||||
when(scheduleMapper.findById(1L)).thenReturn(null);
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = scheduleService.getScheduleDetail(1L, 1L);
|
||||
|
||||
// 验证结果
|
||||
assertFalse(result.isSuccess());
|
||||
assertEquals(404, result.getCode());
|
||||
assertEquals("日程不存在", result.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetScheduleDetail_Unauthorized() {
|
||||
// Mock 其他用户的日程
|
||||
testSchedule.setUserId(2L);
|
||||
when(scheduleMapper.findById(1L)).thenReturn(testSchedule);
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = scheduleService.getScheduleDetail(1L, 1L);
|
||||
|
||||
// 验证结果
|
||||
assertFalse(result.isSuccess());
|
||||
assertEquals(403, result.getCode());
|
||||
assertEquals("无权限查看此日程", result.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetScheduleList_Success() {
|
||||
// Mock 日程列表
|
||||
List<Schedule> schedules = Arrays.asList(testSchedule);
|
||||
when(scheduleMapper.findByUserId(1L)).thenReturn(schedules);
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = scheduleService.getScheduleList(1L);
|
||||
|
||||
// 验证结果
|
||||
assertTrue(result.isSuccess());
|
||||
assertNotNull(result.getData());
|
||||
|
||||
// 验证方法调用
|
||||
verify(scheduleMapper).findByUserId(1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetScheduleListByTimeRange_Success() {
|
||||
LocalDateTime startTime = LocalDateTime.of(2024, 1, 1, 0, 0);
|
||||
LocalDateTime endTime = LocalDateTime.of(2024, 1, 31, 23, 59);
|
||||
|
||||
// Mock 时间范围内的日程列表
|
||||
List<Schedule> schedules = Arrays.asList(testSchedule);
|
||||
when(scheduleMapper.findByUserIdAndTimeRange(1L, startTime, endTime)).thenReturn(schedules);
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = scheduleService.getScheduleListByTimeRange(1L, startTime, endTime);
|
||||
|
||||
// 验证结果
|
||||
assertTrue(result.isSuccess());
|
||||
assertNotNull(result.getData());
|
||||
|
||||
// 验证方法调用
|
||||
verify(scheduleMapper).findByUserIdAndTimeRange(1L, startTime, endTime);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUpdateSchedule_Success() {
|
||||
// Mock 依赖方法
|
||||
when(scheduleMapper.findById(1L)).thenReturn(testSchedule);
|
||||
when(scheduleMapper.findConflictingSchedules(eq(1L), any(), any(), eq(1L))).thenReturn(Arrays.asList());
|
||||
when(scheduleMapper.update(any(Schedule.class))).thenReturn(1);
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = scheduleService.updateSchedule(1L, 1L, createScheduleDTO);
|
||||
|
||||
// 验证结果
|
||||
assertTrue(result.isSuccess());
|
||||
assertEquals("日程更新成功", result.getMessage());
|
||||
|
||||
// 验证方法调用
|
||||
verify(scheduleMapper).update(any(Schedule.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUpdateSchedule_Unauthorized() {
|
||||
// Mock 其他用户的日程
|
||||
testSchedule.setUserId(2L);
|
||||
when(scheduleMapper.findById(1L)).thenReturn(testSchedule);
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = scheduleService.updateSchedule(1L, 1L, createScheduleDTO);
|
||||
|
||||
// 验证结果
|
||||
assertFalse(result.isSuccess());
|
||||
assertEquals(403, result.getCode());
|
||||
assertEquals("无权限修改此日程", result.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDeleteSchedule_Success() {
|
||||
// Mock 依赖方法
|
||||
when(scheduleMapper.findById(1L)).thenReturn(testSchedule);
|
||||
when(scheduleMapper.delete(1L)).thenReturn(1);
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = scheduleService.deleteSchedule(1L, 1L);
|
||||
|
||||
// 验证结果
|
||||
assertTrue(result.isSuccess());
|
||||
assertEquals("日程删除成功", result.getMessage());
|
||||
|
||||
// 验证方法调用
|
||||
verify(scheduleMapper).delete(1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCheckScheduleConflict_NoConflict() {
|
||||
LocalDateTime startTime = LocalDateTime.of(2024, 1, 16, 14, 0);
|
||||
LocalDateTime endTime = LocalDateTime.of(2024, 1, 16, 15, 30);
|
||||
|
||||
// Mock 无冲突
|
||||
when(scheduleMapper.findConflictingSchedules(eq(1L), eq(startTime), eq(endTime), any()))
|
||||
.thenReturn(Arrays.asList());
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = scheduleService.checkScheduleConflict(1L, startTime, endTime, null);
|
||||
|
||||
// 验证结果
|
||||
assertTrue(result.isSuccess());
|
||||
assertEquals("无时间冲突", result.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCheckScheduleConflict_HasConflict() {
|
||||
LocalDateTime startTime = LocalDateTime.of(2024, 1, 16, 14, 0);
|
||||
LocalDateTime endTime = LocalDateTime.of(2024, 1, 16, 15, 30);
|
||||
|
||||
// Mock 有冲突
|
||||
when(scheduleMapper.findConflictingSchedules(eq(1L), eq(startTime), eq(endTime), any()))
|
||||
.thenReturn(Arrays.asList(testSchedule));
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = scheduleService.checkScheduleConflict(1L, startTime, endTime, null);
|
||||
|
||||
// 验证结果
|
||||
assertFalse(result.isSuccess());
|
||||
assertEquals(400, result.getCode());
|
||||
assertTrue(result.getMessage().contains("时间冲突"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testProcessScheduleReminders_Success() {
|
||||
// Mock 需要提醒的日程
|
||||
List<Schedule> upcomingSchedules = Arrays.asList(testSchedule);
|
||||
when(scheduleMapper.findUpcomingSchedules(any())).thenReturn(upcomingSchedules);
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = scheduleService.processScheduleReminders();
|
||||
|
||||
// 验证结果
|
||||
assertTrue(result.isSuccess());
|
||||
assertEquals("提醒处理完成", result.getMessage());
|
||||
|
||||
// 验证方法调用
|
||||
verify(scheduleMapper).findUpcomingSchedules(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCreateSchedule_WeeklyRepeat() {
|
||||
// 测试周重复日程
|
||||
createScheduleDTO.setRepeatType("WEEKLY");
|
||||
createScheduleDTO.setRepeatEnd(LocalDateTime.of(2024, 3, 16, 15, 30));
|
||||
|
||||
when(userMapper.findById(1L)).thenReturn(testUser);
|
||||
when(scheduleMapper.findConflictingSchedules(eq(1L), any(), any(), any())).thenReturn(Arrays.asList());
|
||||
when(scheduleMapper.insert(any(Schedule.class))).thenReturn(1);
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = scheduleService.createSchedule(1L, createScheduleDTO);
|
||||
|
||||
// 验证结果
|
||||
assertTrue(result.isSuccess());
|
||||
|
||||
// 验证会创建多个重复的日程实例
|
||||
verify(scheduleMapper, atLeast(1)).insert(any(Schedule.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCreateSchedule_DailyRepeat() {
|
||||
// 测试日重复日程
|
||||
createScheduleDTO.setRepeatType("DAILY");
|
||||
createScheduleDTO.setRepeatEnd(LocalDateTime.of(2024, 1, 20, 15, 30));
|
||||
|
||||
when(userMapper.findById(1L)).thenReturn(testUser);
|
||||
when(scheduleMapper.findConflictingSchedules(eq(1L), any(), any(), any())).thenReturn(Arrays.asList());
|
||||
when(scheduleMapper.insert(any(Schedule.class))).thenReturn(1);
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = scheduleService.createSchedule(1L, createScheduleDTO);
|
||||
|
||||
// 验证结果
|
||||
assertTrue(result.isSuccess());
|
||||
|
||||
// 验证会创建多个重复的日程实例
|
||||
verify(scheduleMapper, atLeast(1)).insert(any(Schedule.class));
|
||||
}
|
||||
}
|
@ -0,0 +1,438 @@
|
||||
package com.unilife.service;
|
||||
|
||||
import com.unilife.common.result.Result;
|
||||
import com.unilife.mapper.UserMapper;
|
||||
import com.unilife.model.dto.CreateUserDTO;
|
||||
import com.unilife.model.dto.UpdateUserDTO;
|
||||
import com.unilife.model.dto.LoginDTO;
|
||||
import com.unilife.model.entity.User;
|
||||
import com.unilife.service.impl.UserServiceImpl;
|
||||
import com.unilife.utils.JwtUtil;
|
||||
import com.unilife.utils.PasswordUtil;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockedStatic;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.mail.SimpleMailMessage;
|
||||
import org.springframework.mail.javamail.JavaMailSender;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@SpringBootTest
|
||||
class UserServiceTest {
|
||||
|
||||
@Mock
|
||||
private UserMapper userMapper;
|
||||
|
||||
@Mock
|
||||
private StringRedisTemplate redisTemplate;
|
||||
|
||||
@Mock
|
||||
private JavaMailSender mailSender;
|
||||
|
||||
@InjectMocks
|
||||
private UserServiceImpl userService;
|
||||
|
||||
private User testUser;
|
||||
private CreateUserDTO createUserDTO;
|
||||
private UpdateUserDTO updateUserDTO;
|
||||
private LoginDTO loginDTO;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
// 初始化测试数据
|
||||
testUser = new User();
|
||||
testUser.setId(1L);
|
||||
testUser.setUsername("testuser");
|
||||
testUser.setEmail("test@example.com");
|
||||
testUser.setNickname("测试用户");
|
||||
testUser.setPassword("$2a$10$encrypted_password"); // 模拟加密后的密码
|
||||
testUser.setAvatar("avatar.jpg");
|
||||
testUser.setStatus(1);
|
||||
testUser.setCreatedAt(LocalDateTime.now());
|
||||
testUser.setUpdatedAt(LocalDateTime.now());
|
||||
|
||||
createUserDTO = new CreateUserDTO();
|
||||
createUserDTO.setUsername("newuser");
|
||||
createUserDTO.setEmail("newuser@example.com");
|
||||
createUserDTO.setNickname("新用户");
|
||||
createUserDTO.setPassword("password123");
|
||||
|
||||
updateUserDTO = new UpdateUserDTO();
|
||||
updateUserDTO.setNickname("更新后的昵称");
|
||||
updateUserDTO.setAvatar("new_avatar.jpg");
|
||||
|
||||
loginDTO = new LoginDTO();
|
||||
loginDTO.setUsername("testuser");
|
||||
loginDTO.setPassword("password123");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRegister_Success() {
|
||||
// Mock 依赖方法
|
||||
when(userMapper.findByUsername("newuser")).thenReturn(null);
|
||||
when(userMapper.findByEmail("newuser@example.com")).thenReturn(null);
|
||||
when(userMapper.insert(any(User.class))).thenReturn(1);
|
||||
|
||||
try (MockedStatic<PasswordUtil> passwordUtil = mockStatic(PasswordUtil.class)) {
|
||||
passwordUtil.when(() -> PasswordUtil.encode("password123"))
|
||||
.thenReturn("$2a$10$encrypted_password");
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = userService.register(createUserDTO);
|
||||
|
||||
// 验证结果
|
||||
assertTrue(result.isSuccess());
|
||||
assertEquals("注册成功", result.getMessage());
|
||||
|
||||
// 验证方法调用
|
||||
verify(userMapper).findByUsername("newuser");
|
||||
verify(userMapper).findByEmail("newuser@example.com");
|
||||
verify(userMapper).insert(any(User.class));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRegister_UsernameExists() {
|
||||
// Mock 用户名已存在
|
||||
when(userMapper.findByUsername("newuser")).thenReturn(testUser);
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = userService.register(createUserDTO);
|
||||
|
||||
// 验证结果
|
||||
assertFalse(result.isSuccess());
|
||||
assertEquals(400, result.getCode());
|
||||
assertEquals("用户名已存在", result.getMessage());
|
||||
|
||||
// 验证不会尝试插入用户
|
||||
verify(userMapper, never()).insert(any(User.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRegister_EmailExists() {
|
||||
// Mock 邮箱已存在
|
||||
when(userMapper.findByUsername("newuser")).thenReturn(null);
|
||||
when(userMapper.findByEmail("newuser@example.com")).thenReturn(testUser);
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = userService.register(createUserDTO);
|
||||
|
||||
// 验证结果
|
||||
assertFalse(result.isSuccess());
|
||||
assertEquals(400, result.getCode());
|
||||
assertEquals("邮箱已存在", result.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLogin_Success() {
|
||||
// Mock 依赖方法
|
||||
when(userMapper.findByUsername("testuser")).thenReturn(testUser);
|
||||
|
||||
try (MockedStatic<PasswordUtil> passwordUtil = mockStatic(PasswordUtil.class);
|
||||
MockedStatic<JwtUtil> jwtUtil = mockStatic(JwtUtil.class)) {
|
||||
|
||||
passwordUtil.when(() -> PasswordUtil.matches("password123", "$2a$10$encrypted_password"))
|
||||
.thenReturn(true);
|
||||
jwtUtil.when(() -> JwtUtil.generateToken(1L))
|
||||
.thenReturn("mock_jwt_token");
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = userService.login(loginDTO);
|
||||
|
||||
// 验证结果
|
||||
assertTrue(result.isSuccess());
|
||||
assertEquals("登录成功", result.getMessage());
|
||||
assertNotNull(result.getData());
|
||||
|
||||
// 验证方法调用
|
||||
verify(userMapper).findByUsername("testuser");
|
||||
verify(userMapper).updateLastLoginTime(1L);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLogin_UserNotFound() {
|
||||
// Mock 用户不存在
|
||||
when(userMapper.findByUsername("testuser")).thenReturn(null);
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = userService.login(loginDTO);
|
||||
|
||||
// 验证结果
|
||||
assertFalse(result.isSuccess());
|
||||
assertEquals(401, result.getCode());
|
||||
assertEquals("用户名或密码错误", result.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLogin_PasswordIncorrect() {
|
||||
// Mock 密码错误
|
||||
when(userMapper.findByUsername("testuser")).thenReturn(testUser);
|
||||
|
||||
try (MockedStatic<PasswordUtil> passwordUtil = mockStatic(PasswordUtil.class)) {
|
||||
passwordUtil.when(() -> PasswordUtil.matches("password123", "$2a$10$encrypted_password"))
|
||||
.thenReturn(false);
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = userService.login(loginDTO);
|
||||
|
||||
// 验证结果
|
||||
assertFalse(result.isSuccess());
|
||||
assertEquals(401, result.getCode());
|
||||
assertEquals("用户名或密码错误", result.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLogin_UserDisabled() {
|
||||
// Mock 用户被禁用
|
||||
testUser.setStatus(0);
|
||||
when(userMapper.findByUsername("testuser")).thenReturn(testUser);
|
||||
|
||||
try (MockedStatic<PasswordUtil> passwordUtil = mockStatic(PasswordUtil.class)) {
|
||||
passwordUtil.when(() -> PasswordUtil.matches("password123", "$2a$10$encrypted_password"))
|
||||
.thenReturn(true);
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = userService.login(loginDTO);
|
||||
|
||||
// 验证结果
|
||||
assertFalse(result.isSuccess());
|
||||
assertEquals(403, result.getCode());
|
||||
assertEquals("账户已被禁用", result.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetUserInfo_Success() {
|
||||
// Mock 依赖方法
|
||||
when(userMapper.findById(1L)).thenReturn(testUser);
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = userService.getUserInfo(1L);
|
||||
|
||||
// 验证结果
|
||||
assertTrue(result.isSuccess());
|
||||
assertNotNull(result.getData());
|
||||
|
||||
// 验证方法调用
|
||||
verify(userMapper).findById(1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetUserInfo_UserNotFound() {
|
||||
// Mock 用户不存在
|
||||
when(userMapper.findById(1L)).thenReturn(null);
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = userService.getUserInfo(1L);
|
||||
|
||||
// 验证结果
|
||||
assertFalse(result.isSuccess());
|
||||
assertEquals(404, result.getCode());
|
||||
assertEquals("用户不存在", result.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUpdateUserInfo_Success() {
|
||||
// Mock 依赖方法
|
||||
when(userMapper.findById(1L)).thenReturn(testUser);
|
||||
when(userMapper.update(any(User.class))).thenReturn(1);
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = userService.updateUserInfo(1L, updateUserDTO);
|
||||
|
||||
// 验证结果
|
||||
assertTrue(result.isSuccess());
|
||||
assertEquals("用户信息更新成功", result.getMessage());
|
||||
|
||||
// 验证方法调用
|
||||
verify(userMapper).update(any(User.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSendEmailVerificationCode_Success() {
|
||||
String email = "test@example.com";
|
||||
String verificationCode = "123456";
|
||||
|
||||
// Mock Redis操作
|
||||
when(redisTemplate.opsForValue()).thenReturn(mock(org.springframework.data.redis.core.ValueOperations.class));
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = userService.sendEmailVerificationCode(email);
|
||||
|
||||
// 验证结果
|
||||
assertTrue(result.isSuccess());
|
||||
assertEquals("验证码发送成功", result.getMessage());
|
||||
|
||||
// 验证邮件发送
|
||||
verify(mailSender).send(any(SimpleMailMessage.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testVerifyEmailCode_Success() {
|
||||
String email = "test@example.com";
|
||||
String code = "123456";
|
||||
|
||||
// Mock Redis操作
|
||||
when(redisTemplate.opsForValue()).thenReturn(mock(org.springframework.data.redis.core.ValueOperations.class));
|
||||
when(redisTemplate.opsForValue().get("email_code:" + email)).thenReturn(code);
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = userService.verifyEmailCode(email, code);
|
||||
|
||||
// 验证结果
|
||||
assertTrue(result.isSuccess());
|
||||
assertEquals("验证码验证成功", result.getMessage());
|
||||
|
||||
// 验证删除验证码
|
||||
verify(redisTemplate).delete("email_code:" + email);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testVerifyEmailCode_CodeExpired() {
|
||||
String email = "test@example.com";
|
||||
String code = "123456";
|
||||
|
||||
// Mock 验证码不存在(已过期)
|
||||
when(redisTemplate.opsForValue()).thenReturn(mock(org.springframework.data.redis.core.ValueOperations.class));
|
||||
when(redisTemplate.opsForValue().get("email_code:" + email)).thenReturn(null);
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = userService.verifyEmailCode(email, code);
|
||||
|
||||
// 验证结果
|
||||
assertFalse(result.isSuccess());
|
||||
assertEquals(400, result.getCode());
|
||||
assertEquals("验证码已过期", result.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testVerifyEmailCode_CodeIncorrect() {
|
||||
String email = "test@example.com";
|
||||
String code = "123456";
|
||||
String wrongCode = "654321";
|
||||
|
||||
// Mock 验证码错误
|
||||
when(redisTemplate.opsForValue()).thenReturn(mock(org.springframework.data.redis.core.ValueOperations.class));
|
||||
when(redisTemplate.opsForValue().get("email_code:" + email)).thenReturn(wrongCode);
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = userService.verifyEmailCode(email, code);
|
||||
|
||||
// 验证结果
|
||||
assertFalse(result.isSuccess());
|
||||
assertEquals(400, result.getCode());
|
||||
assertEquals("验证码错误", result.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testResetPassword_Success() {
|
||||
String email = "test@example.com";
|
||||
String newPassword = "newpassword123";
|
||||
|
||||
// Mock 依赖方法
|
||||
when(userMapper.findByEmail(email)).thenReturn(testUser);
|
||||
when(userMapper.updatePassword(eq(1L), anyString())).thenReturn(1);
|
||||
|
||||
try (MockedStatic<PasswordUtil> passwordUtil = mockStatic(PasswordUtil.class)) {
|
||||
passwordUtil.when(() -> PasswordUtil.encode(newPassword))
|
||||
.thenReturn("$2a$10$new_encrypted_password");
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = userService.resetPassword(email, newPassword);
|
||||
|
||||
// 验证结果
|
||||
assertTrue(result.isSuccess());
|
||||
assertEquals("密码重置成功", result.getMessage());
|
||||
|
||||
// 验证方法调用
|
||||
verify(userMapper).updatePassword(eq(1L), eq("$2a$10$new_encrypted_password"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetUserList_Success() {
|
||||
// Mock 用户列表
|
||||
List<User> users = Arrays.asList(testUser);
|
||||
when(userMapper.findByConditions(any(), any(), anyInt(), anyInt())).thenReturn(users);
|
||||
when(userMapper.countByConditions(any(), any())).thenReturn(1);
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = userService.getUserList("测试", 1, 1, 10);
|
||||
|
||||
// 验证结果
|
||||
assertTrue(result.isSuccess());
|
||||
assertNotNull(result.getData());
|
||||
|
||||
// 验证方法调用
|
||||
verify(userMapper).findByConditions(any(), any(), anyInt(), anyInt());
|
||||
verify(userMapper).countByConditions(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testChangePassword_Success() {
|
||||
String oldPassword = "oldpassword";
|
||||
String newPassword = "newpassword123";
|
||||
|
||||
// Mock 依赖方法
|
||||
when(userMapper.findById(1L)).thenReturn(testUser);
|
||||
when(userMapper.updatePassword(eq(1L), anyString())).thenReturn(1);
|
||||
|
||||
try (MockedStatic<PasswordUtil> passwordUtil = mockStatic(PasswordUtil.class)) {
|
||||
passwordUtil.when(() -> PasswordUtil.matches(oldPassword, "$2a$10$encrypted_password"))
|
||||
.thenReturn(true);
|
||||
passwordUtil.when(() -> PasswordUtil.encode(newPassword))
|
||||
.thenReturn("$2a$10$new_encrypted_password");
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = userService.changePassword(1L, oldPassword, newPassword);
|
||||
|
||||
// 验证结果
|
||||
assertTrue(result.isSuccess());
|
||||
assertEquals("密码修改成功", result.getMessage());
|
||||
|
||||
// 验证方法调用
|
||||
verify(userMapper).updatePassword(eq(1L), eq("$2a$10$new_encrypted_password"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testChangePassword_OldPasswordIncorrect() {
|
||||
String oldPassword = "wrongpassword";
|
||||
String newPassword = "newpassword123";
|
||||
|
||||
// Mock 依赖方法
|
||||
when(userMapper.findById(1L)).thenReturn(testUser);
|
||||
|
||||
try (MockedStatic<PasswordUtil> passwordUtil = mockStatic(PasswordUtil.class)) {
|
||||
passwordUtil.when(() -> PasswordUtil.matches(oldPassword, "$2a$10$encrypted_password"))
|
||||
.thenReturn(false);
|
||||
|
||||
// 执行测试
|
||||
Result<?> result = userService.changePassword(1L, oldPassword, newPassword);
|
||||
|
||||
// 验证结果
|
||||
assertFalse(result.isSuccess());
|
||||
assertEquals(400, result.getCode());
|
||||
assertEquals("原密码错误", result.getMessage());
|
||||
|
||||
// 验证不会更新密码
|
||||
verify(userMapper, never()).updatePassword(anyLong(), anyString());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,117 @@
|
||||
package com.unilife.utils;
|
||||
|
||||
import com.unilife.model.dto.*;
|
||||
import com.unilife.model.entity.*;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 测试数据构建工具类
|
||||
* 提供各种实体和DTO的测试数据构建方法
|
||||
*/
|
||||
public class TestDataBuilder {
|
||||
|
||||
/**
|
||||
* 构建测试用户
|
||||
*/
|
||||
public static User buildTestUser() {
|
||||
User user = new User();
|
||||
user.setId(1L);
|
||||
user.setUsername("testuser");
|
||||
user.setEmail("test@example.com");
|
||||
user.setNickname("测试用户");
|
||||
user.setPassword("$2a$10$encrypted_password");
|
||||
user.setAvatar("avatar.jpg");
|
||||
user.setStatus(1);
|
||||
user.setCreatedAt(LocalDateTime.now());
|
||||
user.setUpdatedAt(LocalDateTime.now());
|
||||
return user;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建测试分类
|
||||
*/
|
||||
public static Category buildTestCategory() {
|
||||
Category category = new Category();
|
||||
category.setId(1L);
|
||||
category.setName("测试分类");
|
||||
category.setDescription("测试分类描述");
|
||||
category.setIcon("test-icon");
|
||||
category.setSort(1);
|
||||
category.setStatus(1);
|
||||
category.setCreatedAt(LocalDateTime.now());
|
||||
category.setUpdatedAt(LocalDateTime.now());
|
||||
return category;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建测试帖子
|
||||
*/
|
||||
public static Post buildTestPost() {
|
||||
Post post = new Post();
|
||||
post.setId(1L);
|
||||
post.setTitle("测试帖子");
|
||||
post.setContent("测试帖子内容");
|
||||
post.setUserId(1L);
|
||||
post.setCategoryId(1L);
|
||||
post.setLikeCount(0);
|
||||
post.setViewCount(0);
|
||||
post.setCommentCount(0);
|
||||
post.setCreatedAt(LocalDateTime.now());
|
||||
post.setUpdatedAt(LocalDateTime.now());
|
||||
return post;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建测试资源
|
||||
*/
|
||||
public static Resource buildTestResource() {
|
||||
Resource resource = new Resource();
|
||||
resource.setId(1L);
|
||||
resource.setTitle("测试资源");
|
||||
resource.setDescription("测试资源描述");
|
||||
resource.setFileName("test.pdf");
|
||||
resource.setFileUrl("http://example.com/test.pdf");
|
||||
resource.setFileSize(1024L);
|
||||
resource.setFileType("pdf");
|
||||
resource.setUserId(1L);
|
||||
resource.setCategoryId(1L);
|
||||
resource.setDownloadCount(0);
|
||||
resource.setLikeCount(0);
|
||||
resource.setCreatedAt(LocalDateTime.now());
|
||||
resource.setUpdatedAt(LocalDateTime.now());
|
||||
return resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建创建帖子DTO
|
||||
*/
|
||||
public static CreatePostDTO buildCreatePostDTO() {
|
||||
CreatePostDTO dto = new CreatePostDTO();
|
||||
dto.setTitle("新帖子标题");
|
||||
dto.setContent("新帖子内容");
|
||||
dto.setCategoryId(1L);
|
||||
return dto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建创建用户DTO
|
||||
*/
|
||||
public static CreateUserDTO buildCreateUserDTO() {
|
||||
CreateUserDTO dto = new CreateUserDTO();
|
||||
dto.setUsername("newuser");
|
||||
dto.setEmail("newuser@example.com");
|
||||
dto.setNickname("新用户");
|
||||
dto.setPassword("password123");
|
||||
return dto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建带有指定ID的用户
|
||||
*/
|
||||
public static User buildTestUser(Long id) {
|
||||
User user = buildTestUser();
|
||||
user.setId(id);
|
||||
return user;
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
spring:
|
||||
datasource:
|
||||
driver-class-name: org.h2.Driver
|
||||
url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
|
||||
username: sa
|
||||
password:
|
||||
|
||||
h2:
|
||||
console:
|
||||
enabled: true
|
||||
|
||||
jpa:
|
||||
database-platform: org.hibernate.dialect.H2Dialect
|
||||
hibernate:
|
||||
ddl-auto: create-drop
|
||||
show-sql: true
|
||||
|
||||
redis:
|
||||
host: localhost
|
||||
port: 6379
|
||||
database: 1
|
||||
timeout: 2000ms
|
||||
|
||||
mail:
|
||||
host: smtp.example.com
|
||||
port: 587
|
||||
username: test@example.com
|
||||
password: testpassword
|
||||
properties:
|
||||
mail:
|
||||
smtp:
|
||||
auth: true
|
||||
starttls:
|
||||
enable: true
|
||||
|
||||
mybatis:
|
||||
mapper-locations: classpath:mapper/*.xml
|
||||
type-aliases-package: com.unilife.model.entity
|
||||
configuration:
|
||||
map-underscore-to-camel-case: true
|
||||
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||
|
||||
logging:
|
||||
level:
|
||||
com.unilife: DEBUG
|
||||
org.springframework.web: DEBUG
|
||||
pattern:
|
||||
console: "%d{yyyy-MM-dd HH:mm:ss} - %msg%n"
|
||||
|
||||
# JWT配置
|
||||
jwt:
|
||||
secret: test-secret-key-for-unit-testing-purposes-only
|
||||
expiration: 3600000
|
||||
|
||||
# 文件上传配置
|
||||
file:
|
||||
upload:
|
||||
path: /tmp/unilife-test/uploads/
|
||||
max-size: 10MB
|
||||
|
||||
# 测试特定配置
|
||||
test:
|
||||
mock:
|
||||
enabled: true
|
||||
database:
|
||||
cleanup: true
|
Loading…
Reference in new issue