package repository import ( "context" "encoding/json" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "gorm.io/gorm" "github.com/gochat/gochat/internal/model" ) // --- helpers --------------------------------------------------------------- func createTestUserForPref(t *testing.T, db *gorm.DB) *model.User { t.Helper() u := &model.User{Email: "prefuser@example.com", Name: "PrefUser", Provider: "email"} require.NoError(t, db.Create(u).Error) return u } func createTestAccountForPref(t *testing.T, db *gorm.DB) *model.Account { t.Helper() a := &model.Account{Name: "PrefOrg", Locale: "en", Active: true} require.NoError(t, db.Create(a).Error) return a } func makePref(userID uint, accountID *uint, channel, eventType string, enabled bool) model.NotificationPreference { return model.NotificationPreference{ UserID: userID, AccountID: accountID, Channel: channel, EventType: eventType, Enabled: enabled, Preferences: json.RawMessage(`{"sound": true}`), } } // --- tests ----------------------------------------------------------------- // Test 1: FindByID retrieves a previously upserted preference. func TestNotificationPreferenceRepo_FindByID(t *testing.T) { db := setupTestDB(t) repo := NewNotificationPreferenceRepo(db) user := createTestUserForPref(t, db) account := createTestAccountForPref(t, db) pref := makePref(user.ID, &account.ID, "email", "conversation_created", true) require.NoError(t, repo.Upsert(context.Background(), &pref)) found, err := repo.FindByID(context.Background(), pref.ID) assert.NoError(t, err) assert.Equal(t, pref.ID, found.ID) assert.Equal(t, user.ID, found.UserID) assert.Equal(t, "email", found.Channel) assert.Equal(t, "conversation_created", found.EventType) assert.True(t, found.Enabled) } // Test 2: FindByID returns error for non-existent ID. func TestNotificationPreferenceRepo_FindByID_NotFound(t *testing.T) { db := setupTestDB(t) repo := NewNotificationPreferenceRepo(db) found, err := repo.FindByID(context.Background(), 99999) assert.Error(t, err) assert.Nil(t, found) } // Test 3: ListByUserAndAccount returns only matching preferences. func TestNotificationPreferenceRepo_ListByUserAndAccount(t *testing.T) { db := setupTestDB(t) repo := NewNotificationPreferenceRepo(db) user := createTestUserForPref(t, db) account := createTestAccountForPref(t, db) // Insert two preferences for this user+account p1 := makePref(user.ID, &account.ID, "email", "conversation_created", true) p2 := makePref(user.ID, &account.ID, "push", "assigned_conversation_new_message", false) require.NoError(t, repo.Upsert(context.Background(), &p1)) require.NoError(t, repo.Upsert(context.Background(), &p2)) // Insert a preference for a different account to verify filtering account2 := &model.Account{Name: "OtherOrg", Locale: "en", Active: true} require.NoError(t, db.Create(account2).Error) p3 := makePref(user.ID, &account2.ID, "browser", "conversation_created", true) require.NoError(t, repo.Upsert(context.Background(), &p3)) prefs, err := repo.ListByUserAndAccount(context.Background(), user.ID, account.ID) assert.NoError(t, err) assert.Len(t, prefs, 2) // Verify all returned prefs belong to the correct user+account for _, p := range prefs { assert.Equal(t, user.ID, p.UserID) assert.Equal(t, account.ID, *p.AccountID) } } // Test 4: Upsert creates a new preference and then updates it. func TestNotificationPreferenceRepo_Upsert_CreateAndUpdate(t *testing.T) { db := setupTestDB(t) repo := NewNotificationPreferenceRepo(db) user := createTestUserForPref(t, db) account := createTestAccountForPref(t, db) // First upsert: create pref := makePref(user.ID, &account.ID, "email", "conversation_created", true) require.NoError(t, repo.Upsert(context.Background(), &pref)) assert.NotZero(t, pref.ID) // Verify created state found, err := repo.FindByID(context.Background(), pref.ID) require.NoError(t, err) assert.True(t, found.Enabled) // Second upsert: update — same user+account+channel+event, flip Enabled updatedPref := makePref(user.ID, &account.ID, "email", "conversation_created", false) updatedPref.ID = pref.ID updatedPref.Preferences = json.RawMessage(`{"sound": false}`) require.NoError(t, repo.Upsert(context.Background(), &updatedPref)) // Re-fetch to verify the update found2, err := repo.FindByID(context.Background(), pref.ID) require.NoError(t, err) assert.False(t, found2.Enabled) } // Test 5: BatchUpsert inserts multiple preferences at once. func TestNotificationPreferenceRepo_BatchUpsert(t *testing.T) { db := setupTestDB(t) repo := NewNotificationPreferenceRepo(db) user := createTestUserForPref(t, db) account := createTestAccountForPref(t, db) prefs := []model.NotificationPreference{ makePref(user.ID, &account.ID, "email", "conversation_created", true), makePref(user.ID, &account.ID, "push", "assigned_conversation_new_message", false), makePref(user.ID, &account.ID, "browser", "mention", true), } require.NoError(t, repo.BatchUpsert(context.Background(), prefs)) // Verify all three exist via ListByUserAndAccount result, err := repo.ListByUserAndAccount(context.Background(), user.ID, account.ID) assert.NoError(t, err) assert.Len(t, result, 3) channels := map[string]bool{} for _, p := range result { channels[p.Channel] = true } assert.True(t, channels["email"]) assert.True(t, channels["push"]) assert.True(t, channels["browser"]) } // Test 6: ListByUserAndAccount returns empty list for non-existent user+account. func TestNotificationPreferenceRepo_ListByUserAndAccount_Empty(t *testing.T) { db := setupTestDB(t) repo := NewNotificationPreferenceRepo(db) user := createTestUserForPref(t, db) account := createTestAccountForPref(t, db) // Insert a preference pref := makePref(user.ID, &account.ID, "email", "conversation_created", true) require.NoError(t, repo.Upsert(context.Background(), &pref)) // Query for a different (non-existent) user+account combo prefs, err := repo.ListByUserAndAccount(context.Background(), 99999, 88888) assert.NoError(t, err) assert.Len(t, prefs, 0) } // Test 7: DeleteByUserAndAccount removes preferences and leaves others intact. func TestNotificationPreferenceRepo_DeleteByUserAndAccount(t *testing.T) { db := setupTestDB(t) repo := NewNotificationPreferenceRepo(db) user := createTestUserForPref(t, db) account := createTestAccountForPref(t, db) // Insert preferences for user+account p1 := makePref(user.ID, &account.ID, "email", "conversation_created", true) p2 := makePref(user.ID, &account.ID, "push", "mention", false) require.NoError(t, repo.Upsert(context.Background(), &p1)) require.NoError(t, repo.Upsert(context.Background(), &p2)) // Insert preference for same user but different account account2 := &model.Account{Name: "SurvivorOrg", Locale: "en", Active: true} require.NoError(t, db.Create(account2).Error) p3 := makePref(user.ID, &account2.ID, "browser", "conversation_created", true) require.NoError(t, repo.Upsert(context.Background(), &p3)) // Delete for user+account1 require.NoError(t, repo.DeleteByUserAndAccount(context.Background(), user.ID, account.ID)) // Verify account1 prefs are gone prefs, err := repo.ListByUserAndAccount(context.Background(), user.ID, account.ID) assert.NoError(t, err) assert.Len(t, prefs, 0) // Verify account2 prefs still exist prefs2, err := repo.ListByUserAndAccount(context.Background(), user.ID, account2.ID) assert.NoError(t, err) assert.Len(t, prefs2, 1) assert.Equal(t, "browser", prefs2[0].Channel) }