Files
server/tests/services/notification_test.go
T
2026-01-16 15:49:34 +08:00

378 lines
11 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package services
import (
"testing"
"time"
"github.com/goravel/framework/facades"
"github.com/stretchr/testify/suite"
apperrors "goravel/app/errors"
"goravel/app/models"
"goravel/app/services"
"goravel/tests"
)
type NotificationServiceTestSuite struct {
suite.Suite
tests.TestCase
service services.NotificationService
}
func TestNotificationServiceTestSuite(t *testing.T) {
suite.Run(t, &NotificationServiceTestSuite{})
}
func (s *NotificationServiceTestSuite) SetupSuite() {
s.service = services.NewNotificationServiceImpl()
}
func (s *NotificationServiceTestSuite) SetupTest() {
s.RefreshDatabase()
s.Seed()
}
func (s *NotificationServiceTestSuite) TearDownTest() {
// 清理测试数据
_, _ = facades.Orm().Query().Where("type", "test").Delete(&models.Notification{})
}
// ==================== Create 方法测试 ====================
// TestCreate_SingleNotification 测试创建单个通知
func (s *NotificationServiceTestSuite) TestCreate_SingleNotification() {
var admin models.Admin
err := facades.Orm().Query().First(&admin)
s.Require().NoError(err)
receiverID := admin.ID
notification, err := s.service.Create("测试标题", "测试内容", "test", nil, &receiverID)
s.NoError(err)
s.NotNil(notification)
s.Equal("测试标题", notification.Title)
s.Equal("测试内容", notification.Content)
s.Equal("test", notification.Type)
s.Equal(receiverID, *notification.ReceiverID)
s.NotZero(notification.ID)
}
// TestCreate_BroadcastToAllAdmins 测试批量创建通知给所有管理员
func (s *NotificationServiceTestSuite) TestCreate_BroadcastToAllAdmins() {
// 获取所有管理员
var admins []models.Admin
err := facades.Orm().Query().Find(&admins)
s.Require().NoError(err)
s.Require().NotEmpty(admins, "应该有至少一个管理员")
// 创建通知给所有管理员(receiverID 为 nil
notification, err := s.service.Create("系统通知", "这是一条系统通知", "system", nil, nil)
s.NoError(err)
s.NotNil(notification)
s.Equal("系统通知", notification.Title)
// 验证所有管理员都收到了通知
count, err := facades.Orm().Query().
Model(&models.Notification{}).
Where("title", "系统通知").
Where("type", "system").
Count()
s.NoError(err)
s.Equal(int64(len(admins)), count, "应该为每个管理员创建一条通知")
}
// TestCreate_NoAdminsFound 测试没有管理员时的错误处理
func (s *NotificationServiceTestSuite) TestCreate_NoAdminsFound() {
// 删除所有管理员
_, err := facades.Orm().Query().Delete(&models.Admin{})
s.Require().NoError(err)
// 尝试创建通知给所有管理员
notification, err := s.service.Create("测试", "内容", "test", nil, nil)
s.Error(err)
s.Nil(notification)
// 验证返回的是业务错误
businessErr, ok := apperrors.GetBusinessError(err)
s.True(ok, "应该返回业务错误")
s.Equal("record_not_found", businessErr.Code)
}
// ==================== List 方法测试 ====================
// TestList_BasicQuery 测试基本查询
func (s *NotificationServiceTestSuite) TestList_BasicQuery() {
var admin models.Admin
err := facades.Orm().Query().First(&admin)
s.Require().NoError(err)
// 创建几条测试通知
for i := 0; i < 5; i++ {
_, err := s.service.Create("测试通知", "内容", "test", nil, &admin.ID)
s.Require().NoError(err)
}
// 查询通知列表
notifications, total, err := s.service.List(admin.ID, 1, 10, "", "")
s.NoError(err)
s.GreaterOrEqual(int(total), 5)
s.NotEmpty(notifications)
}
// TestList_WithTypeFilter 测试按类型筛选
func (s *NotificationServiceTestSuite) TestList_WithTypeFilter() {
var admin models.Admin
err := facades.Orm().Query().First(&admin)
s.Require().NoError(err)
// 创建不同类型的通知
_, err = s.service.Create("系统通知", "内容", "system", nil, &admin.ID)
s.Require().NoError(err)
_, err = s.service.Create("测试通知", "内容", "test", nil, &admin.ID)
s.Require().NoError(err)
// 只查询 system 类型的通知
notifications, total, err := s.service.List(admin.ID, 1, 10, "system", "")
s.NoError(err)
s.GreaterOrEqual(int(total), 1)
for _, notif := range notifications {
s.Equal("system", notif.Type)
}
}
// TestList_WithReadStatusFilter 测试按已读状态筛选
func (s *NotificationServiceTestSuite) TestList_WithReadStatusFilter() {
var admin models.Admin
err := facades.Orm().Query().First(&admin)
s.Require().NoError(err)
// 创建通知
notification, err := s.service.Create("测试通知", "内容", "test", nil, &admin.ID)
s.Require().NoError(err)
// 标记为已读
err = s.service.MarkRead(admin.ID, notification.ID)
s.Require().NoError(err)
// 查询未读通知
unread, total, err := s.service.List(admin.ID, 1, 10, "", "false")
s.NoError(err)
// 验证未读通知不包含已读的通知
found := false
for _, notif := range unread {
if notif.ID == notification.ID {
found = true
break
}
}
s.False(found, "已读通知不应出现在未读列表中")
// 查询已读通知
read, total, err := s.service.List(admin.ID, 1, 10, "", "true")
s.NoError(err)
s.GreaterOrEqual(int(total), 1)
// 验证已读通知包含该通知
found = false
for _, notif := range read {
if notif.ID == notification.ID {
found = true
s.True(notif.IsRead)
break
}
}
s.True(found, "已读通知应出现在已读列表中")
}
// TestList_Pagination 测试分页
func (s *NotificationServiceTestSuite) TestList_Pagination() {
var admin models.Admin
err := facades.Orm().Query().First(&admin)
s.Require().NoError(err)
// 创建 15 条通知
for i := 0; i < 15; i++ {
_, err := s.service.Create("测试通知", "内容", "test", nil, &admin.ID)
s.Require().NoError(err)
}
// 第一页,每页 10 条
page1, total, err := s.service.List(admin.ID, 1, 10, "", "")
s.NoError(err)
s.GreaterOrEqual(int(total), 15)
s.LessOrEqual(len(page1), 10)
// 第二页
page2, _, err := s.service.List(admin.ID, 2, 10, "", "")
s.NoError(err)
s.LessOrEqual(len(page2), 10)
// 验证两页的数据不重复
if len(page1) > 0 && len(page2) > 0 {
s.NotEqual(page1[0].ID, page2[0].ID, "两页的数据应该不同")
}
}
// ==================== MarkRead 方法测试 ====================
// TestMarkRead_Success 测试标记为已读成功
func (s *NotificationServiceTestSuite) TestMarkRead_Success() {
var admin models.Admin
err := facades.Orm().Query().First(&admin)
s.Require().NoError(err)
// 创建通知
notification, err := s.service.Create("测试通知", "内容", "test", nil, &admin.ID)
s.Require().NoError(err)
s.False(notification.IsRead, "新通知应该是未读状态")
// 标记为已读
err = s.service.MarkRead(admin.ID, notification.ID)
s.NoError(err)
// 验证已读状态
var updated models.Notification
err = facades.Orm().Query().Where("id", notification.ID).First(&updated)
s.NoError(err)
s.True(updated.IsRead)
s.NotNil(updated.ReadAt)
}
// TestMarkRead_NotFound 测试通知不存在
func (s *NotificationServiceTestSuite) TestMarkRead_NotFound() {
var admin models.Admin
err := facades.Orm().Query().First(&admin)
s.Require().NoError(err)
// 尝试标记不存在的通知为已读
err = s.service.MarkRead(admin.ID, 99999)
s.Error(err)
// 验证返回的是业务错误
businessErr, ok := apperrors.GetBusinessError(err)
s.True(ok, "应该返回业务错误")
s.Equal("record_not_found", businessErr.Code)
}
// TestMarkRead_AlreadyRead 测试已读通知再次标记
func (s *NotificationServiceTestSuite) TestMarkRead_AlreadyRead() {
var admin models.Admin
err := facades.Orm().Query().First(&admin)
s.Require().NoError(err)
// 创建并标记为已读
notification, err := s.service.Create("测试通知", "内容", "test", nil, &admin.ID)
s.Require().NoError(err)
err = s.service.MarkRead(admin.ID, notification.ID)
s.Require().NoError(err)
// 再次标记为已读(应该不报错)
err = s.service.MarkRead(admin.ID, notification.ID)
s.NoError(err, "已读通知再次标记应该不报错")
}
// ==================== MarkAllRead 方法测试 ====================
// TestMarkAllRead_Success 测试标记所有通知为已读
func (s *NotificationServiceTestSuite) TestMarkAllRead_Success() {
var admin models.Admin
err := facades.Orm().Query().First(&admin)
s.Require().NoError(err)
// 创建多条未读通知
for i := 0; i < 5; i++ {
_, err := s.service.Create("测试通知", "内容", "test", nil, &admin.ID)
s.Require().NoError(err)
}
// 标记所有为已读
err = s.service.MarkAllRead(admin.ID)
s.NoError(err)
// 验证未读数量为 0
count, err := s.service.UnreadCount(admin.ID)
s.NoError(err)
s.Equal(int64(0), count)
}
// ==================== UnreadCount 方法测试 ====================
// TestUnreadCount_Basic 测试未读数量统计
func (s *NotificationServiceTestSuite) TestUnreadCount_Basic() {
var admin models.Admin
err := facades.Orm().Query().First(&admin)
s.Require().NoError(err)
// 创建 3 条未读通知
for i := 0; i < 3; i++ {
_, err := s.service.Create("测试通知", "内容", "test", nil, &admin.ID)
s.Require().NoError(err)
}
// 验证未读数量
count, err := s.service.UnreadCount(admin.ID)
s.NoError(err)
s.GreaterOrEqual(count, int64(3))
// 标记一条为已读
notifications, _, err := s.service.List(admin.ID, 1, 1, "", "false")
s.Require().NoError(err)
s.Require().NotEmpty(notifications)
err = s.service.MarkRead(admin.ID, notifications[0].ID)
s.Require().NoError(err)
// 验证未读数量减少
newCount, err := s.service.UnreadCount(admin.ID)
s.NoError(err)
s.Equal(count-1, newCount)
}
// ==================== ListRecent 方法测试 ====================
// TestListRecent_Basic 测试获取最近通知
func (s *NotificationServiceTestSuite) TestListRecent_Basic() {
var admin models.Admin
err := facades.Orm().Query().First(&admin)
s.Require().NoError(err)
// 创建多条通知
for i := 0; i < 10; i++ {
_, err := s.service.Create("测试通知", "内容", "test", nil, &admin.ID)
s.Require().NoError(err)
time.Sleep(10 * time.Millisecond) // 确保时间戳不同
}
// 获取最近 5 条
recent, err := s.service.ListRecent(admin.ID, 5)
s.NoError(err)
s.LessOrEqual(len(recent), 5)
s.GreaterOrEqual(len(recent), 1)
// 验证返回了数据(排序验证在数据库层面完成,这里只验证数据存在)
s.NotEmpty(recent, "应该返回最近的通知")
}
// TestListRecent_LimitValidation 测试限制验证
func (s *NotificationServiceTestSuite) TestListRecent_LimitValidation() {
var admin models.Admin
err := facades.Orm().Query().First(&admin)
s.Require().NoError(err)
// 测试负数限制(应该使用默认值 5)
recent, err := s.service.ListRecent(admin.ID, -1)
s.NoError(err)
s.LessOrEqual(len(recent), 5)
// 测试超过最大值的限制(应该使用默认值 5)
recent, err = s.service.ListRecent(admin.ID, 100)
s.NoError(err)
s.LessOrEqual(len(recent), 5)
}