Files
gochat/backend/internal/repository/notification_repo_test.go
T

445 lines
12 KiB
Go

package repository
import (
"context"
"encoding/json"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gorm.io/gorm"
"github.com/gochat/gochat/internal/model"
)
// ---------------------------------------------------------------------------
// NotificationRepo
// ---------------------------------------------------------------------------
func createTestNotification(t *testing.T, db *gorm.DB, userID, accountID uint, read bool) *model.Notification {
t.Helper()
n := &model.Notification{
UserID: userID,
AccountID: &accountID,
NotificationType: "conversation_created",
PrimaryActorType: "Conversation",
PrimaryActorID: 1,
}
if read {
now := time.Now()
n.ReadAt = &now
}
require.NoError(t, db.Create(n).Error)
return n
}
func TestNotificationRepo_FindByID(t *testing.T) {
db := setupTestDB(t)
ctx := context.Background()
repo := NewNotificationRepo(db)
n := createTestNotification(t, db, 1, 1, false)
found, err := repo.FindByID(ctx, n.ID)
require.NoError(t, err)
assert.Equal(t, n.ID, found.ID)
}
func TestNotificationRepo_FindByID_NotFound(t *testing.T) {
db := setupTestDB(t)
ctx := context.Background()
repo := NewNotificationRepo(db)
_, err := repo.FindByID(ctx, 99999)
assert.ErrorIs(t, err, gorm.ErrRecordNotFound)
}
func TestNotificationRepo_FindByUserAndAccount(t *testing.T) {
db := setupTestDB(t)
ctx := context.Background()
repo := NewNotificationRepo(db)
n := createTestNotification(t, db, 10, 20, false)
found, err := repo.FindByUserAndAccount(ctx, n.ID, 10, 20)
require.NoError(t, err)
assert.Equal(t, n.ID, found.ID)
}
func TestNotificationRepo_FindByUserAndAccount_NotFound(t *testing.T) {
db := setupTestDB(t)
ctx := context.Background()
repo := NewNotificationRepo(db)
_, err := repo.FindByUserAndAccount(ctx, 9999, 9999, 9999)
assert.ErrorIs(t, err, gorm.ErrRecordNotFound)
}
func TestNotificationRepo_ListByUser(t *testing.T) {
db := setupTestDB(t)
ctx := context.Background()
repo := NewNotificationRepo(db)
for i := 0; i < 5; i++ {
createTestNotification(t, db, 10, 1, false)
}
// Different user
createTestNotification(t, db, 20, 1, false)
notifications, total, err := repo.ListByUser(ctx, 10, 0, 10)
require.NoError(t, err)
assert.Equal(t, int64(5), total)
assert.Len(t, notifications, 5)
// Test pagination
notifications, total, err = repo.ListByUser(ctx, 10, 2, 2)
require.NoError(t, err)
assert.Equal(t, int64(5), total)
assert.Len(t, notifications, 2)
}
func TestNotificationRepo_ListByUserAndAccount(t *testing.T) {
db := setupTestDB(t)
ctx := context.Background()
repo := NewNotificationRepo(db)
for i := 0; i < 3; i++ {
createTestNotification(t, db, 10, 1, false)
}
createTestNotification(t, db, 10, 2, false) // different account
notifications, total, err := repo.ListByUserAndAccount(ctx, 10, 1, 0, 10)
require.NoError(t, err)
assert.Equal(t, int64(3), total)
assert.Len(t, notifications, 3)
}
func TestNotificationRepo_ListByUserAndAccountFiltered(t *testing.T) {
db := setupTestDB(t)
ctx := context.Background()
repo := NewNotificationRepo(db)
// Unread, not snoozed
createTestNotification(t, db, 10, 1, false)
// Read
createTestNotification(t, db, 10, 1, true)
notifications, total, unread, err := repo.ListByUserAndAccountFiltered(ctx, 10, 1, 0, 10, NotificationListFilter{})
require.NoError(t, err)
assert.Equal(t, int64(1), total) // Only unread, not snoozed
assert.Equal(t, int64(1), unread)
assert.Len(t, notifications, 1)
// Include read
notifications, total, unread, err = repo.ListByUserAndAccountFiltered(ctx, 10, 1, 0, 10, NotificationListFilter{IncludeRead: true})
require.NoError(t, err)
assert.Equal(t, int64(2), total)
assert.Equal(t, int64(1), unread)
assert.Len(t, notifications, 2)
// Sort ascending
notifications, _, _, err = repo.ListByUserAndAccountFiltered(ctx, 10, 1, 0, 10, NotificationListFilter{IncludeRead: true, SortOrder: "asc"})
require.NoError(t, err)
assert.Len(t, notifications, 2)
}
func TestNotificationRepo_Create(t *testing.T) {
db := setupTestDB(t)
ctx := context.Background()
repo := NewNotificationRepo(db)
n := &model.Notification{
UserID: 1,
AccountID: uintPtr(1),
NotificationType: "message_created",
PrimaryActorType: "Message",
PrimaryActorID: 1,
}
err := repo.Create(ctx, n)
require.NoError(t, err)
assert.NotZero(t, n.ID)
}
func TestNotificationRepo_Update(t *testing.T) {
db := setupTestDB(t)
ctx := context.Background()
repo := NewNotificationRepo(db)
n := createTestNotification(t, db, 1, 1, false)
n.NotificationType = "conversation_assigned"
require.NoError(t, repo.Update(ctx, n))
found, err := repo.FindByID(ctx, n.ID)
require.NoError(t, err)
assert.Equal(t, "conversation_assigned", found.NotificationType)
}
func TestNotificationRepo_MarkRead(t *testing.T) {
db := setupTestDB(t)
ctx := context.Background()
repo := NewNotificationRepo(db)
n := createTestNotification(t, db, 1, 1, false)
require.NoError(t, repo.MarkRead(ctx, n.ID))
found, _ := repo.FindByID(ctx, n.ID)
assert.NotNil(t, found.ReadAt)
}
func TestNotificationRepo_MarkRead_NotFound(t *testing.T) {
db := setupTestDB(t)
ctx := context.Background()
repo := NewNotificationRepo(db)
err := repo.MarkRead(ctx, 99999)
assert.ErrorIs(t, err, gorm.ErrRecordNotFound)
}
func TestNotificationRepo_MarkReadByUserAndAccount(t *testing.T) {
db := setupTestDB(t)
ctx := context.Background()
repo := NewNotificationRepo(db)
n := createTestNotification(t, db, 10, 20, false)
result, err := repo.MarkReadByUserAndAccount(ctx, n.ID, 10, 20)
require.NoError(t, err)
assert.NotNil(t, result)
assert.NotNil(t, result.ReadAt)
}
func TestNotificationRepo_MarkReadByUserAndAccount_NotFound(t *testing.T) {
db := setupTestDB(t)
ctx := context.Background()
repo := NewNotificationRepo(db)
_, err := repo.MarkReadByUserAndAccount(ctx, 9999, 9999, 9999)
assert.ErrorIs(t, err, gorm.ErrRecordNotFound)
}
func TestNotificationRepo_MarkAllReadByUser(t *testing.T) {
db := setupTestDB(t)
ctx := context.Background()
repo := NewNotificationRepo(db)
createTestNotification(t, db, 10, 1, false)
createTestNotification(t, db, 10, 2, false)
createTestNotification(t, db, 10, 3, false)
require.NoError(t, repo.MarkAllReadByUser(ctx, 10))
count, err := repo.CountUnreadByUser(ctx, 10)
require.NoError(t, err)
assert.Equal(t, int64(0), count)
}
func TestNotificationRepo_MarkAllReadByUserAndAccount(t *testing.T) {
db := setupTestDB(t)
ctx := context.Background()
repo := NewNotificationRepo(db)
createTestNotification(t, db, 10, 1, false)
createTestNotification(t, db, 10, 1, false)
createTestNotification(t, db, 10, 2, false) // different account
require.NoError(t, repo.MarkAllReadByUserAndAccount(ctx, 10, 1))
count, err := repo.CountUnreadByUserAndAccount(ctx, 10, 1)
require.NoError(t, err)
assert.Equal(t, int64(0), count)
// Other account should still have unread
count, err = repo.CountUnreadByUserAndAccount(ctx, 10, 2)
require.NoError(t, err)
assert.Equal(t, int64(1), count)
}
func TestNotificationRepo_MarkPrimaryActorReadByUserAndAccount(t *testing.T) {
db := setupTestDB(t)
ctx := context.Background()
repo := NewNotificationRepo(db)
n := &model.Notification{
UserID: 10, AccountID: uintPtr(20),
NotificationType: "msg", PrimaryActorType: "Conversation", PrimaryActorID: 5,
}
require.NoError(t, db.Create(n).Error)
// Different actor
n2 := &model.Notification{
UserID: 10, AccountID: uintPtr(20),
NotificationType: "msg", PrimaryActorType: "Conversation", PrimaryActorID: 6,
}
require.NoError(t, db.Create(n2).Error)
require.NoError(t, repo.MarkPrimaryActorReadByUserAndAccount(ctx, 10, 20, "Conversation", 5))
found, _ := repo.FindByID(ctx, n.ID)
assert.NotNil(t, found.ReadAt)
found2, _ := repo.FindByID(ctx, n2.ID)
assert.Nil(t, found2.ReadAt)
}
func TestNotificationRepo_Delete(t *testing.T) {
db := setupTestDB(t)
ctx := context.Background()
repo := NewNotificationRepo(db)
n := createTestNotification(t, db, 1, 1, false)
require.NoError(t, repo.Delete(ctx, n.ID))
_, err := repo.FindByID(ctx, n.ID)
assert.ErrorIs(t, err, gorm.ErrRecordNotFound)
}
func TestNotificationRepo_DeleteByUserAndAccount(t *testing.T) {
db := setupTestDB(t)
ctx := context.Background()
repo := NewNotificationRepo(db)
n := createTestNotification(t, db, 10, 20, false)
require.NoError(t, repo.DeleteByUserAndAccount(ctx, n.ID, 10, 20))
_, err := repo.FindByID(ctx, n.ID)
assert.ErrorIs(t, err, gorm.ErrRecordNotFound)
}
func TestNotificationRepo_DeleteByUserAndAccount_NotFound(t *testing.T) {
db := setupTestDB(t)
ctx := context.Background()
repo := NewNotificationRepo(db)
err := repo.DeleteByUserAndAccount(ctx, 9999, 9999, 9999)
assert.ErrorIs(t, err, gorm.ErrRecordNotFound)
}
func TestNotificationRepo_CountUnreadByUser(t *testing.T) {
db := setupTestDB(t)
ctx := context.Background()
repo := NewNotificationRepo(db)
createTestNotification(t, db, 10, 1, false)
createTestNotification(t, db, 10, 2, false)
createTestNotification(t, db, 10, 3, true) // read
count, err := repo.CountUnreadByUser(ctx, 10)
require.NoError(t, err)
assert.Equal(t, int64(2), count)
}
func TestNotificationRepo_CountUnreadByUserAndAccount(t *testing.T) {
db := setupTestDB(t)
ctx := context.Background()
repo := NewNotificationRepo(db)
createTestNotification(t, db, 10, 1, false)
createTestNotification(t, db, 10, 1, true) // read
createTestNotification(t, db, 10, 2, false) // different account
count, err := repo.CountUnreadByUserAndAccount(ctx, 10, 1)
require.NoError(t, err)
assert.Equal(t, int64(1), count)
}
func TestNotificationRepo_CountByUserAndAccount(t *testing.T) {
db := setupTestDB(t)
ctx := context.Background()
repo := NewNotificationRepo(db)
createTestNotification(t, db, 10, 1, false)
createTestNotification(t, db, 10, 1, true)
createTestNotification(t, db, 20, 1, false) // different user
count, err := repo.CountByUserAndAccount(ctx, 10, 1)
require.NoError(t, err)
assert.Equal(t, int64(2), count)
}
func TestNotificationRepo_Snooze(t *testing.T) {
db := setupTestDB(t)
ctx := context.Background()
repo := NewNotificationRepo(db)
n := createTestNotification(t, db, 10, 20, true) // read first
snoozedUntil := time.Now().Add(1 * time.Hour)
result, err := repo.Snooze(ctx, n.ID, 10, 20, snoozedUntil)
require.NoError(t, err)
assert.NotNil(t, result)
assert.NotNil(t, result.SnoozedUntil)
assert.Nil(t, result.ReadAt) // read_at should be cleared
// Verify additional_attributes has last_snoozed_at set to nil
var meta map[string]interface{}
require.NoError(t, json.Unmarshal(result.AdditionalAttributes, &meta))
_, hasKey := meta["last_snoozed_at"]
assert.True(t, hasKey)
}
func TestNotificationRepo_Snooze_NotFound(t *testing.T) {
db := setupTestDB(t)
ctx := context.Background()
repo := NewNotificationRepo(db)
_, err := repo.Snooze(ctx, 9999, 9999, 9999, time.Now())
assert.ErrorIs(t, err, gorm.ErrRecordNotFound)
}
func TestNotificationRepo_MarkUnread(t *testing.T) {
db := setupTestDB(t)
ctx := context.Background()
repo := NewNotificationRepo(db)
n := createTestNotification(t, db, 10, 20, true) // read
result, err := repo.MarkUnread(ctx, n.ID, 10, 20)
require.NoError(t, err)
assert.NotNil(t, result)
assert.Nil(t, result.ReadAt)
}
func TestNotificationRepo_MarkUnread_NotFound(t *testing.T) {
db := setupTestDB(t)
ctx := context.Background()
repo := NewNotificationRepo(db)
_, err := repo.MarkUnread(ctx, 9999, 9999, 9999)
assert.ErrorIs(t, err, gorm.ErrRecordNotFound)
}
func TestNotificationRepo_DeleteAllByUser(t *testing.T) {
db := setupTestDB(t)
ctx := context.Background()
repo := NewNotificationRepo(db)
createTestNotification(t, db, 10, 1, false)
createTestNotification(t, db, 10, 2, false)
createTestNotification(t, db, 20, 1, false) // different user
require.NoError(t, repo.DeleteAllByUser(ctx, 10, 1))
count, err := repo.CountByUserAndAccount(ctx, 10, 1)
require.NoError(t, err)
assert.Equal(t, int64(0), count)
// Other account should still have
count, err = repo.CountByUserAndAccount(ctx, 10, 2)
require.NoError(t, err)
assert.Equal(t, int64(1), count)
}
func TestNotificationRepo_DeleteReadByUser(t *testing.T) {
db := setupTestDB(t)
ctx := context.Background()
repo := NewNotificationRepo(db)
createTestNotification(t, db, 10, 1, true) // read
createTestNotification(t, db, 10, 1, false) // unread
require.NoError(t, repo.DeleteReadByUser(ctx, 10, 1))
count, err := repo.CountByUserAndAccount(ctx, 10, 1)
require.NoError(t, err)
assert.Equal(t, int64(1), count) // only unread remains
}