init
This commit is contained in:
@@ -0,0 +1,377 @@
|
||||
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)
|
||||
}
|
||||
Reference in New Issue
Block a user