Files
gochat/internal/service/notification_service_test.go_BAK
T
2026-06-04 15:44:48 +08:00

498 lines
16 KiB
Plaintext
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 service
import (
"context"
"encoding/json"
"testing"
"github.com/gochat/gochat/internal/model"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// ========== GetNotification ==========
func TestNotificationService_GetNotification_成功(t *testing.T) {
db, _, _, svc := setupNotificationService(t)
account := createTestAccount(t, db)
user := createTestUser(t, db, account.ID)
notification := createTestNotification(t, db, user.ID, &account.ID, "conversation_created")
result, err := svc.GetNotification(context.Background(), notification.ID)
require.NoError(t, err)
assert.Equal(t, notification.ID, result.ID)
assert.Equal(t, user.ID, result.UserID)
assert.Equal(t, "conversation_created", result.NotificationType)
}
func TestNotificationService_GetNotification_不存在(t *testing.T) {
_, _, _, svc := setupNotificationService(t)
result, err := svc.GetNotification(context.Background(), 9999)
assert.Error(t, err)
assert.Nil(t, result)
}
// ========== ListNotifications ==========
func TestNotificationService_ListNotifications_成功(t *testing.T) {
db, _, _, svc := setupNotificationService(t)
account := createTestAccount(t, db)
user := createTestUser(t, db, account.ID)
// 创建多条通知
createTestNotification(t, db, user.ID, &account.ID, "conversation_created")
createTestNotification(t, db, user.ID, &account.ID, "conversation_assigned")
notifications, total, err := svc.ListNotifications(context.Background(), user.ID, 1, 10)
require.NoError(t, err)
assert.Equal(t, int64(2), total)
assert.Len(t, notifications, 2)
}
func TestNotificationService_ListNotifications_分页(t *testing.T) {
db, _, _, svc := setupNotificationService(t)
account := createTestAccount(t, db)
user := createTestUser(t, db, account.ID)
// 创建3条通知
for i := 0; i < 3; i++ {
createTestNotification(t, db, user.ID, &account.ID, "conversation_created")
}
// 第一页,每页2条
notifications, total, err := svc.ListNotifications(context.Background(), user.ID, 1, 2)
require.NoError(t, err)
assert.Equal(t, int64(3), total)
assert.Len(t, notifications, 2)
// 第二页
notifications2, total2, err2 := svc.ListNotifications(context.Background(), user.ID, 2, 2)
require.NoError(t, err2)
assert.Equal(t, int64(3), total2)
assert.Len(t, notifications2, 1)
}
func TestNotificationService_ListNotifications_无通知(t *testing.T) {
_, _, _, svc := setupNotificationService(t)
notifications, total, err := svc.ListNotifications(context.Background(), 9999, 1, 10)
require.NoError(t, err)
assert.Equal(t, int64(0), total)
assert.Empty(t, notifications)
}
// ========== ListNotificationsByAccount ==========
func TestNotificationService_ListNotificationsByAccount_成功(t *testing.T) {
db, _, _, svc := setupNotificationService(t)
account := createTestAccount(t, db)
user := createTestUser(t, db, account.ID)
createTestNotification(t, db, user.ID, &account.ID, "conversation_created")
createTestNotification(t, db, user.ID, &account.ID, "message_created")
notifications, total, err := svc.ListNotificationsByAccount(context.Background(), user.ID, account.ID, 1, 10)
require.NoError(t, err)
assert.Equal(t, int64(2), total)
assert.Len(t, notifications, 2)
}
func TestNotificationService_ListNotificationsByAccount_不同账户隔离(t *testing.T) {
db, _, _, svc := setupNotificationService(t)
account1 := createTestAccount(t, db)
account2 := createTestAccount(t, db)
user := createTestUser(t, db, account1.ID)
// 只在 account1 下创建通知
createTestNotification(t, db, user.ID, &account1.ID, "conversation_created")
// 查询 account2,应该无通知
notifications, total, err := svc.ListNotificationsByAccount(context.Background(), user.ID, account2.ID, 1, 10)
require.NoError(t, err)
assert.Equal(t, int64(0), total)
assert.Empty(t, notifications)
}
func TestNotificationService_ListNotificationsByAccount_分页(t *testing.T) {
db, _, _, svc := setupNotificationService(t)
account := createTestAccount(t, db)
user := createTestUser(t, db, account.ID)
for i := 0; i < 5; i++ {
createTestNotification(t, db, user.ID, &account.ID, "conversation_created")
}
notifications, total, err := svc.ListNotificationsByAccount(context.Background(), user.ID, account.ID, 1, 3)
require.NoError(t, err)
assert.Equal(t, int64(5), total)
assert.Len(t, notifications, 3)
}
// ========== CreateNotification ==========
func TestNotificationService_CreateNotification_成功(t *testing.T) {
db, _, _, svc := setupNotificationService(t)
account := createTestAccount(t, db)
user := createTestUser(t, db, account.ID)
notification := &model.Notification{
UserID: user.ID,
AccountID: &account.ID,
NotificationType: "conversation_assigned",
PrimaryActorType: "User",
PrimaryActorID: user.ID,
PushEnabled: true,
EmailEnabled: false,
}
err := svc.CreateNotification(context.Background(), notification)
require.NoError(t, err)
assert.NotZero(t, notification.ID)
// 验证数据库中确实存在
var count int64
db.Model(&model.Notification{}).Where("id = ?", notification.ID).Count(&count)
assert.Equal(t, int64(1), count)
}
func TestNotificationService_CreateNotification_缺少必填字段(t *testing.T) {
_, _, _, svc := setupNotificationService(t)
notification := &model.Notification{
NotificationType: "conversation_created",
UserID: 0, // SQLite doesn't enforce NOT NULL on uint(0), so this succeeds
}
err := svc.CreateNotification(context.Background(), notification)
// SQLite does not enforce NOT NULL constraints on zero values for integer fields,
// so the create succeeds even with UserID=0. In production (Postgres), this would fail.
assert.NoError(t, err)
}
// ========== MarkRead ==========
func TestNotificationService_MarkRead_成功(t *testing.T) {
db, _, _, svc := setupNotificationService(t)
account := createTestAccount(t, db)
user := createTestUser(t, db, account.ID)
notification := createTestNotification(t, db, user.ID, &account.ID, "conversation_created")
// 初始状态:未读
assert.Nil(t, notification.ReadAt)
err := svc.MarkRead(context.Background(), notification.ID)
require.NoError(t, err)
// 验证已标记为已读
result, err := svc.GetNotification(context.Background(), notification.ID)
require.NoError(t, err)
assert.NotNil(t, result.ReadAt)
}
func TestNotificationService_MarkRead_不存在(t *testing.T) {
_, _, _, svc := setupNotificationService(t)
err := svc.MarkRead(context.Background(), 9999)
// 对不存在的记录标记已读,repo 返回 gorm.ErrRecordNotFound
assert.Error(t, err)
}
// ========== MarkAllRead ==========
func TestNotificationService_MarkAllRead_成功(t *testing.T) {
db, _, _, svc := setupNotificationService(t)
account := createTestAccount(t, db)
user := createTestUser(t, db, account.ID)
createTestNotification(t, db, user.ID, &account.ID, "conversation_created")
createTestNotification(t, db, user.ID, &account.ID, "message_created")
err := svc.MarkAllRead(context.Background(), user.ID)
require.NoError(t, err)
// 验证所有通知都已标记为已读
notifications, _, err := svc.ListNotifications(context.Background(), user.ID, 1, 10)
require.NoError(t, err)
for _, n := range notifications {
assert.NotNil(t, n.ReadAt)
}
}
func TestNotificationService_MarkAllRead_无通知用户(t *testing.T) {
_, _, _, svc := setupNotificationService(t)
// 对没有任何通知的用户标记全部已读,不应报错
err := svc.MarkAllRead(context.Background(), 9999)
require.NoError(t, err)
}
// ========== MarkAllReadByAccount ==========
func TestNotificationService_MarkAllReadByAccount_成功(t *testing.T) {
db, _, _, svc := setupNotificationService(t)
account := createTestAccount(t, db)
user := createTestUser(t, db, account.ID)
createTestNotification(t, db, user.ID, &account.ID, "conversation_created")
createTestNotification(t, db, user.ID, &account.ID, "message_created")
err := svc.MarkAllReadByAccount(context.Background(), user.ID, account.ID)
require.NoError(t, err)
// 验证该账户下的所有通知都已标记为已读
notifications, _, err := svc.ListNotificationsByAccount(context.Background(), user.ID, account.ID, 1, 10)
require.NoError(t, err)
for _, n := range notifications {
assert.NotNil(t, n.ReadAt)
}
}
func TestNotificationService_MarkAllReadByAccount_不同账户隔离(t *testing.T) {
db, _, _, svc := setupNotificationService(t)
account1 := createTestAccount(t, db)
account2 := createTestAccount(t, db)
user := createTestUser(t, db, account1.ID)
// 在 account1 下创建通知
notif1 := createTestNotification(t, db, user.ID, &account1.ID, "conversation_created")
// 在 account2 下创建通知
notif2 := createTestNotification(t, db, user.ID, &account2.ID, "message_created")
// 只标记 account1 下的通知为已读
err := svc.MarkAllReadByAccount(context.Background(), user.ID, account1.ID)
require.NoError(t, err)
// account1 的通知应为已读
result1, err := svc.GetNotification(context.Background(), notif1.ID)
require.NoError(t, err)
assert.NotNil(t, result1.ReadAt)
// account2 的通知应仍为未读
result2, err := svc.GetNotification(context.Background(), notif2.ID)
require.NoError(t, err)
assert.Nil(t, result2.ReadAt)
}
// ========== DeleteNotification ==========
func TestNotificationService_DeleteNotification_成功(t *testing.T) {
db, _, _, svc := setupNotificationService(t)
account := createTestAccount(t, db)
user := createTestUser(t, db, account.ID)
notification := createTestNotification(t, db, user.ID, &account.ID, "conversation_created")
err := svc.DeleteNotification(context.Background(), notification.ID)
require.NoError(t, err)
// 验证软删除后无法通过 GetNotification 获取
result, err := svc.GetNotification(context.Background(), notification.ID)
assert.Error(t, err)
assert.Nil(t, result)
}
func TestNotificationService_DeleteNotification_不存在(t *testing.T) {
_, _, _, svc := setupNotificationService(t)
// GORM soft-delete on a non-existent record returns nil (affects 0 rows but no error)
err := svc.DeleteNotification(context.Background(), 9999)
assert.NoError(t, err)
}
// ========== GetUnreadCount ==========
func TestNotificationService_GetUnreadCount_成功(t *testing.T) {
db, _, _, svc := setupNotificationService(t)
account := createTestAccount(t, db)
user := createTestUser(t, db, account.ID)
createTestNotification(t, db, user.ID, &account.ID, "conversation_created")
createTestNotification(t, db, user.ID, &account.ID, "message_created")
count, err := svc.GetUnreadCount(context.Background(), user.ID)
require.NoError(t, err)
assert.Equal(t, int64(2), count)
}
func TestNotificationService_GetUnreadCount_标记已读后减少(t *testing.T) {
db, _, _, svc := setupNotificationService(t)
account := createTestAccount(t, db)
user := createTestUser(t, db, account.ID)
notif1 := createTestNotification(t, db, user.ID, &account.ID, "conversation_created")
createTestNotification(t, db, user.ID, &account.ID, "message_created")
// 标记一条为已读
err := svc.MarkRead(context.Background(), notif1.ID)
require.NoError(t, err)
count, err := svc.GetUnreadCount(context.Background(), user.ID)
require.NoError(t, err)
assert.Equal(t, int64(1), count)
}
func TestNotificationService_GetUnreadCount_无通知(t *testing.T) {
_, _, _, svc := setupNotificationService(t)
count, err := svc.GetUnreadCount(context.Background(), 9999)
require.NoError(t, err)
assert.Equal(t, int64(0), count)
}
// ========== GetPreferences ==========
func TestNotificationService_GetPreferences_成功(t *testing.T) {
db, _, _, svc := setupNotificationService(t)
account := createTestAccount(t, db)
user := createTestUser(t, db, account.ID)
// 直接通过 repo 创建偏好
pref1 := &model.NotificationPreference{
UserID: user.ID,
AccountID: &account.ID,
Channel: "email",
EventType: "conversation_created",
Enabled: true,
}
require.NoError(t, db.Create(pref1).Error)
pref2 := &model.NotificationPreference{
UserID: user.ID,
AccountID: &account.ID,
Channel: "push",
EventType: "message_created",
Enabled: false,
}
require.NoError(t, db.Create(pref2).Error)
prefs, err := svc.GetPreferences(context.Background(), user.ID, account.ID)
require.NoError(t, err)
assert.Len(t, prefs, 2)
}
func TestNotificationService_GetPreferences_无偏好(t *testing.T) {
db, _, _, svc := setupNotificationService(t)
account := createTestAccount(t, db)
prefs, err := svc.GetPreferences(context.Background(), 9999, account.ID)
require.NoError(t, err)
assert.Empty(t, prefs)
}
// ========== UpdatePreferences ==========
func TestNotificationService_UpdatePreferences_创建新偏好(t *testing.T) {
db, _, _, svc := setupNotificationService(t)
account := createTestAccount(t, db)
user := createTestUser(t, db, account.ID)
prefs := []model.NotificationPreference{
{
UserID: user.ID,
AccountID: &account.ID,
Channel: "email",
EventType: "conversation_created",
Enabled: true,
},
{
UserID: user.ID,
AccountID: &account.ID,
Channel: "push",
EventType: "conversation_assigned",
Enabled: false,
},
}
err := svc.UpdatePreferences(context.Background(), user.ID, account.ID, prefs)
require.NoError(t, err)
// 验证偏好已创建
result, err := svc.GetPreferences(context.Background(), user.ID, account.ID)
require.NoError(t, err)
assert.Len(t, result, 2)
}
func TestNotificationService_UpdatePreferences_更新已有偏好(t *testing.T) {
db, _, _, svc := setupNotificationService(t)
account := createTestAccount(t, db)
user := createTestUser(t, db, account.ID)
// 先创建偏好
prefs := []model.NotificationPreference{
{
UserID: user.ID,
AccountID: &account.ID,
Channel: "email",
EventType: "conversation_created",
Enabled: true,
},
}
err := svc.UpdatePreferences(context.Background(), user.ID, account.ID, prefs)
require.NoError(t, err)
// 更新偏好(将 Enabled 改为 false
updatedPrefs := []model.NotificationPreference{
{
UserID: user.ID,
AccountID: &account.ID,
Channel: "email",
EventType: "conversation_created",
Enabled: false,
},
}
err = svc.UpdatePreferences(context.Background(), user.ID, account.ID, updatedPrefs)
require.NoError(t, err)
// 验证偏好已更新
result, err := svc.GetPreferences(context.Background(), user.ID, account.ID)
require.NoError(t, err)
assert.Len(t, result, 1)
assert.False(t, result[0].Enabled)
}
func TestNotificationService_UpdatePreferences_强制设置UserID和AccountID(t *testing.T) {
db, _, _, svc := setupNotificationService(t)
account := createTestAccount(t, db)
user := createTestUser(t, db, account.ID)
// 即使传入的偏好中 UserID 为 0 或 AccountID 为 nil
// UpdatePreferences 应强制设置正确的 UserID 和 AccountID
prefs := []model.NotificationPreference{
{
Channel: "email",
EventType: "conversation_created",
Enabled: true,
},
}
err := svc.UpdatePreferences(context.Background(), user.ID, account.ID, prefs)
require.NoError(t, err)
// 验证 UserID 和 AccountID 被正确设置
result, err := svc.GetPreferences(context.Background(), user.ID, account.ID)
require.NoError(t, err)
assert.Len(t, result, 1)
assert.Equal(t, user.ID, result[0].UserID)
assert.NotNil(t, result[0].AccountID)
assert.Equal(t, account.ID, *result[0].AccountID)
}
func TestNotificationService_UpdatePreferences_批量创建多种偏好(t *testing.T) {
db, _, _, svc := setupNotificationService(t)
account := createTestAccount(t, db)
user := createTestUser(t, db, account.ID)
prefs := []model.NotificationPreference{
{Channel: "email", EventType: "conversation_created", Enabled: true, Preferences: json.RawMessage(`{"sound": true}`)},
{Channel: "push", EventType: "conversation_created", Enabled: true, Preferences: json.RawMessage(`{"vibration": true}`)},
{Channel: "browser", EventType: "conversation_assigned", Enabled: false, Preferences: json.RawMessage(`{}`)},
{Channel: "email", EventType: "message_created", Enabled: true, Preferences: json.RawMessage(`{}`)},
}
err := svc.UpdatePreferences(context.Background(), user.ID, account.ID, prefs)
require.NoError(t, err)
result, err := svc.GetPreferences(context.Background(), user.ID, account.ID)
require.NoError(t, err)
assert.Len(t, result, 4)
}