498 lines
16 KiB
Plaintext
498 lines
16 KiB
Plaintext
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)
|
||
} |