owpengram-server/internal/store/postgres/account_notify_integration_test.go

128 lines
4.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package postgres
import (
"context"
"testing"
"telesrv/internal/domain"
)
// TestNotifySettingsRoundTripPostgres 回归迁移 0005:per-scope 通知设置真实持久化
// (peer / 类别默认 / 批量 / reset),含可空字段=未设置语义。
func TestNotifySettingsRoundTripPostgres(t *testing.T) {
pool := testPool(t)
ctx := context.Background()
store := NewPasswordStore(pool)
users := NewUserStore(pool)
suffix := randomSuffix(t)
u, err := users.Create(ctx, domain.User{AccessHash: 92, Phone: "+1663" + suffix + "01", FirstName: "NotifyOwner"})
if err != nil {
t.Fatalf("create user: %v", err)
}
t.Cleanup(func() {
_, _ = pool.Exec(ctx, "DELETE FROM notify_settings WHERE owner_user_id = $1", u.ID)
_, _ = pool.Exec(ctx, "DELETE FROM users WHERE id = $1", u.ID)
})
peerA := domain.Peer{Type: domain.PeerTypeUser, ID: 7001}
peerB := domain.Peer{Type: domain.PeerTypeChannel, ID: 7002}
scopeA := domain.NotifyScope{Kind: domain.NotifyScopePeer, Peer: peerA}
// not found.
if _, found, err := store.GetNotifySettings(ctx, u.ID, scopeA); err != nil || found {
t.Fatalf("get before save = found %v err %v", found, err)
}
// peer A:只设 mute_until + silent(其余 nil=未设置)。
mute := 2000000000
silent := true
if err := store.SaveNotifySettings(ctx, u.ID, scopeA, domain.PeerNotifySettings{MuteUntil: &mute, Silent: &silent}); err != nil {
t.Fatalf("save scope A: %v", err)
}
got, found, err := store.GetNotifySettings(ctx, u.ID, scopeA)
if err != nil || !found {
t.Fatalf("get scope A = found %v err %v", found, err)
}
if got.MuteUntil == nil || *got.MuteUntil != mute || got.Silent == nil || !*got.Silent {
t.Fatalf("scope A = %+v, want mute=%d silent=true", got, mute)
}
if got.ShowPreviews != nil || got.StoriesMuted != nil {
t.Fatalf("unset fields must stay nil, got %+v", got)
}
// 类别默认(users)独立。
usersScope := domain.NotifyScope{Kind: domain.NotifyScopeUsers}
dmute := 100
if err := store.SaveNotifySettings(ctx, u.ID, usersScope, domain.PeerNotifySettings{MuteUntil: &dmute}); err != nil {
t.Fatalf("save users default: %v", err)
}
if g, found, _ := store.GetNotifySettings(ctx, u.ID, usersScope); !found || g.MuteUntil == nil || *g.MuteUntil != dmute {
t.Fatalf("users default = %+v found %v, want mute=%d", g, found, dmute)
}
// peer A 不受类别默认影响。
if g, _, _ := store.GetNotifySettings(ctx, u.ID, scopeA); g.MuteUntil == nil || *g.MuteUntil != mute {
t.Fatalf("scope A polluted by users default: %+v", g)
}
// upsert 覆盖 + 批量(mute_until 是 TL int32,取接近上限的合法值)。
mute2 := 2100000000
if err := store.SaveNotifySettings(ctx, u.ID, domain.NotifyScope{Kind: domain.NotifyScopePeer, Peer: peerB}, domain.PeerNotifySettings{MuteUntil: &mute2}); err != nil {
t.Fatalf("save scope B: %v", err)
}
batch, err := store.GetPeerNotifySettings(ctx, u.ID, []domain.Peer{peerA, peerB, {Type: domain.PeerTypeUser, ID: 9999}})
if err != nil {
t.Fatalf("batch: %v", err)
}
if len(batch) != 2 {
t.Fatalf("batch len = %d, want 2 (peerA+peerB, 9999 unset)", len(batch))
}
if a, ok := batch[peerA]; !ok || a.MuteUntil == nil || *a.MuteUntil != mute {
t.Fatalf("batch peerA = %+v ok %v", a, ok)
}
if b, ok := batch[peerB]; !ok || b.MuteUntil == nil || *b.MuteUntil != mute2 {
t.Fatalf("batch peerB = %+v ok %v", b, ok)
}
// AllPeerNotifySettings:返全部整-peer 设置(peerA+peerB),排除类别默认(per-user notify 缓存的加载源)。
all, err := store.AllPeerNotifySettings(ctx, u.ID)
if err != nil {
t.Fatalf("all peer notify settings: %v", err)
}
if len(all) != 2 {
t.Fatalf("AllPeerNotifySettings = %d, want 2 (peerA+peerB, 类别默认不计)", len(all))
}
if a, ok := all[peerA]; !ok || a.MuteUntil == nil || *a.MuteUntil != mute {
t.Fatalf("AllPeerNotifySettings peerA = %+v ok %v", a, ok)
}
// ListNotifyExceptions:只返 per-peer 非默认(peerA+peerB),排除类别默认。
exceptions, err := store.ListNotifyExceptions(ctx, u.ID)
if err != nil {
t.Fatalf("list exceptions: %v", err)
}
if len(exceptions) != 2 {
t.Fatalf("exceptions = %d, want 2 (peerA+peerB, 类别默认不计)", len(exceptions))
}
seen := map[domain.Peer]bool{}
for _, ex := range exceptions {
seen[ex.Peer] = true
}
if !seen[peerA] || !seen[peerB] {
t.Fatalf("exceptions peers = %v, want peerA+peerB", seen)
}
// reset 清空全部作用域。
if err := store.ResetNotifySettings(ctx, u.ID); err != nil {
t.Fatalf("reset: %v", err)
}
if ex, _ := store.ListNotifyExceptions(ctx, u.ID); len(ex) != 0 {
t.Fatalf("exceptions after reset = %d, want 0", len(ex))
}
if _, found, _ := store.GetNotifySettings(ctx, u.ID, scopeA); found {
t.Fatalf("scope A must be gone after reset")
}
if _, found, _ := store.GetNotifySettings(ctx, u.ID, usersScope); found {
t.Fatalf("users default must be gone after reset")
}
}