Ctrl K
ComunidadGuías…

internal/store/postgres/account_notify_integration_test.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package postgres

import (
	"context"
	"testing"

	"telesrv/internal/domain"
)

// TestNotifySettingsRoundTripPostgres regression for migration 0005: per-scope notify settings are really persisted
// (peer / category default / batch / reset), including nullable fields = unset semantics.
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: only mute_until + silent are set (the rest nil = unset).
	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)
	}

	// The users category default is independent.
	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 is unaffected by the category default.
	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 overwrite + batch (mute_until is a TL int32; use a valid value near the upper bound).
	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: returns all whole-peer settings (peerA+peerB), excluding category defaults (the load source for the per-user notify cache).
	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, category defaults excluded)", len(all))
	}
	if a, ok := all[peerA]; !ok || a.MuteUntil == nil || *a.MuteUntil != mute {
		t.Fatalf("AllPeerNotifySettings peerA = %+v ok %v", a, ok)
	}

	// ListNotifyExceptions: returns only non-default per-peer entries (peerA+peerB), excluding category defaults.
	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, category defaults excluded)", 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 clears all scopes.
	for _, id := range []int{42, 43} {
		if err := store.SaveNotifySettings(ctx, u.ID, domain.NotifyScope{Kind: domain.NotifyScopePeer, Peer: peerB, TopicID: id}, domain.PeerNotifySettings{MuteUntil: &mute}); err != nil {
			t.Fatal(err)
		}
	}
	topics, err := store.GetForumTopicNotifySettings(ctx, u.ID, peerB, []int{1, 42})
	if err != nil || len(topics) != 1 || topics[42].MuteUntil == nil || *topics[42].MuteUntil != mute {
		t.Fatalf("forum batch = %+v, err %v", topics, err)
	}
	for _, isolated := range []struct {
		owner int64
		peer  domain.Peer
	}{{u.ID + 1, peerB}, {u.ID, peerA}} {
		settings, err := store.GetForumTopicNotifySettings(ctx, isolated.owner, isolated.peer, []int{42})
		if err != nil || len(settings) != 0 {
			t.Fatalf("isolated forum settings = %+v, err %v", settings, err)
		}
	}
	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")
	}
}