445 lines
12 KiB
Go
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
|
|
}
|