Restructure the monorepo into clear top-level directories: - backend/: Go module root (cmd, internal, pkg, configs, migrations, docs/swagger, scripts, tests, go.mod, Makefile, .air.toml) - deploy/: Docker (Dockerfile, docker-compose*), quickstart, fluentd - docs/: project documentation + reports/ (moved from repo root) - AGENTS.md: new AI coding-agent guide at repo root Update all references to the new layout: - Dockerfile: COPY backend/go.mod, COPY backend/ (context = repo root) - docker-compose files: context ../.., dockerfile deploy/docker/Dockerfile, env_file ../../.env, volume mounts ../../backend:/app - deploy/quickstart/compose.yaml: dockerfile deploy/docker/Dockerfile - CI: working-directory: backend for go commands, file deploy/docker/Dockerfile, coverage path backend/coverage.out, health_check backend/scripts/ - backend/Makefile: docker target uses -f ../deploy/docker/Dockerfile ../ - README: architecture tree, quickstart, config paths updated Move root stray scripts (rename_models.*, run_m11_tests.sh, verify_build.sh, gorm_bool_main.go) to backend/scripts/legacy/. All moves via git mv to preserve history. Build, vet, SQLite tests, and docker compose config verified.
619 lines
20 KiB
Go
619 lines
20 KiB
Go
package service
|
||
|
||
import (
|
||
"context"
|
||
"encoding/json"
|
||
"fmt"
|
||
"testing"
|
||
"time"
|
||
|
||
"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)
|
||
}
|
||
|
||
// --- G8 extension service tests: Snooze, Unread, DeleteAll ---
|
||
// Reference: Chatwoot notifications_controller.rb#snooze, #unread, #destroy_all
|
||
|
||
func TestNotificationService_SnoozeNotification(t *testing.T) {
|
||
db, _, _, svc := setupNotificationService(t)
|
||
|
||
accountID := uint(1)
|
||
user := createTestUser(t, db, accountID)
|
||
notif := createTestNotification(t, db, user.ID, &accountID, "conversation_assignment")
|
||
|
||
// Mark notification as read first
|
||
readTime := time.Now()
|
||
require.NoError(t, db.Model(notif).Update("read_at", &readTime).Error)
|
||
|
||
// Snooze it — should clear read_at and set snoozed_until
|
||
snoozeUntil := time.Now().Add(2 * time.Hour)
|
||
result, err := svc.SnoozeNotification(context.Background(), notif.ID, user.ID, accountID, snoozeUntil)
|
||
require.NoError(t, err)
|
||
assert.NotNil(t, result)
|
||
assert.NotNil(t, result.SnoozedUntil)
|
||
assert.Nil(t, result.ReadAt) // snooze clears read status
|
||
}
|
||
|
||
func TestNotificationService_SnoozeNotification_WrongUser(t *testing.T) {
|
||
db, _, _, svc := setupNotificationService(t)
|
||
|
||
accountID := uint(1)
|
||
user := createTestUser(t, db, accountID)
|
||
otherUser := createTestUser(t, db, accountID)
|
||
notif := createTestNotification(t, db, user.ID, &accountID, "conversation_assignment")
|
||
|
||
snoozeUntil := time.Now().Add(2 * time.Hour)
|
||
_, err := svc.SnoozeNotification(context.Background(), notif.ID, otherUser.ID, accountID, snoozeUntil)
|
||
assert.Error(t, err) // should fail — wrong user
|
||
}
|
||
|
||
func TestNotificationService_MarkNotificationUnread(t *testing.T) {
|
||
db, _, _, svc := setupNotificationService(t)
|
||
|
||
accountID := uint(1)
|
||
user := createTestUser(t, db, accountID)
|
||
notif := createTestNotification(t, db, user.ID, &accountID, "conversation_assignment")
|
||
|
||
// Mark notification as read first
|
||
readTime := time.Now()
|
||
require.NoError(t, db.Model(notif).Update("read_at", &readTime).Error)
|
||
|
||
// Mark it unread — should clear read_at
|
||
result, err := svc.MarkNotificationUnread(context.Background(), notif.ID, user.ID, accountID)
|
||
require.NoError(t, err)
|
||
assert.NotNil(t, result)
|
||
assert.Nil(t, result.ReadAt)
|
||
}
|
||
|
||
func TestNotificationService_MarkNotificationUnread_WrongUser(t *testing.T) {
|
||
db, _, _, svc := setupNotificationService(t)
|
||
|
||
accountID := uint(1)
|
||
user := createTestUser(t, db, accountID)
|
||
otherUser := createTestUser(t, db, accountID)
|
||
notif := createTestNotification(t, db, user.ID, &accountID, "conversation_assignment")
|
||
|
||
_, err := svc.MarkNotificationUnread(context.Background(), notif.ID, otherUser.ID, accountID)
|
||
assert.Error(t, err)
|
||
}
|
||
|
||
func TestNotificationService_DeleteAllNotifications(t *testing.T) {
|
||
db, _, _, svc := setupNotificationService(t)
|
||
|
||
accountID := uint(1)
|
||
user := createTestUser(t, db, accountID)
|
||
|
||
// Create multiple notifications for this user
|
||
for i := 0; i < 5; i++ {
|
||
createTestNotification(t, db, user.ID, &accountID, fmt.Sprintf("type_%d", i))
|
||
}
|
||
|
||
// Verify they exist
|
||
var count int64
|
||
require.NoError(t, db.Model(&model.Notification{}).Where("user_id = ? AND account_id = ?", user.ID, accountID).Count(&count).Error)
|
||
assert.Equal(t, int64(5), count)
|
||
|
||
// Delete all
|
||
err := svc.DeleteAllNotifications(context.Background(), user.ID, accountID)
|
||
require.NoError(t, err)
|
||
|
||
// Verify all deleted
|
||
require.NoError(t, db.Model(&model.Notification{}).Where("user_id = ? AND account_id = ?", user.ID, accountID).Count(&count).Error)
|
||
assert.Equal(t, int64(0), count)
|
||
}
|
||
|
||
func TestNotificationService_DeleteAllNotifications_OnlyDeletesForUser(t *testing.T) {
|
||
db, _, _, svc := setupNotificationService(t)
|
||
|
||
accountID := uint(1)
|
||
user1 := createTestUser(t, db, accountID)
|
||
user2 := createTestUser(t, db, accountID)
|
||
|
||
// Create notifications for both users
|
||
for i := 0; i < 3; i++ {
|
||
createTestNotification(t, db, user1.ID, &accountID, fmt.Sprintf("user1_type_%d", i))
|
||
createTestNotification(t, db, user2.ID, &accountID, fmt.Sprintf("user2_type_%d", i))
|
||
}
|
||
|
||
// Delete all for user1 only
|
||
err := svc.DeleteAllNotifications(context.Background(), user1.ID, accountID)
|
||
require.NoError(t, err)
|
||
|
||
// user1's notifications should be gone
|
||
var count1 int64
|
||
require.NoError(t, db.Model(&model.Notification{}).Where("user_id = ?", user1.ID).Count(&count1).Error)
|
||
assert.Equal(t, int64(0), count1)
|
||
|
||
// user2's notifications should remain
|
||
var count2 int64
|
||
require.NoError(t, db.Model(&model.Notification{}).Where("user_id = ?", user2.ID).Count(&count2).Error)
|
||
assert.Equal(t, int64(3), count2)
|
||
} |