Ctrl K
ComunidadGuías…

internal/store/postgres/account_deletion_notifications_integration_test.go

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

Archivo original ↗Preguntar a KiBot
package postgres

import (
	"context"
	"fmt"
	"testing"
	"time"

	"telesrv/internal/domain"
)

// A deleted account has to reach every viewer who ever shared a contact or a
// dialog with it, including the ones that are offline at that moment. The
// online-only fan-out cannot do that, so the audience is committed with the
// tombstone and served from the durable queue.
func TestAccountDeletionEnqueuesTombstoneNotificationsPostgres(t *testing.T) {
	pool := testPool(t)
	ctx := context.Background()
	nonce := time.Now().UnixNano()
	users := NewUserStore(pool)
	deleted := createTestUser(t, ctx, users, fmt.Sprintf("15591%d", nonce), "Ghost", "Owner")
	contact := createTestUser(t, ctx, users, fmt.Sprintf("15592%d", nonce), "Contact", "Peer")
	dialogPeer := createTestUser(t, ctx, users, fmt.Sprintf("15593%d", nonce), "Dialog", "Peer")
	stranger := createTestUser(t, ctx, users, fmt.Sprintf("15594%d", nonce), "Nobody", "Saw")
	t.Cleanup(func() {
		_, _ = pool.Exec(ctx, `DELETE FROM account_deletion_notifications WHERE deleted_user_id = $1`, deleted.ID)
		_, _ = pool.Exec(ctx, `DELETE FROM account_deletion_requests WHERE user_id = ANY($1)`, []int64{deleted.ID})
		_, _ = pool.Exec(ctx, `DELETE FROM contacts WHERE user_id = ANY($1) OR contact_user_id = ANY($1)`, []int64{deleted.ID, contact.ID, dialogPeer.ID})
		_, _ = pool.Exec(ctx, `DELETE FROM private_messages WHERE sender_user_id = ANY($1) OR recipient_user_id = ANY($1)`, []int64{deleted.ID, contact.ID, dialogPeer.ID})
		_, _ = pool.Exec(ctx, `DELETE FROM message_boxes WHERE owner_user_id = ANY($1)`, []int64{deleted.ID, contact.ID, dialogPeer.ID})
		_, _ = pool.Exec(ctx, `DELETE FROM users WHERE id = ANY($1)`, []int64{deleted.ID, contact.ID, dialogPeer.ID, stranger.ID})
	})

	if _, err := pool.Exec(ctx, `INSERT INTO contacts
(user_id, contact_user_id, contact_phone, contact_first_name, contact_last_name)
VALUES ($1, $2, '', 'Contact', 'Peer')`, contact.ID, deleted.ID); err != nil {
		t.Fatalf("insert contact: %v", err)
	}
	if _, err := NewMessageStore(pool).SendPrivateText(ctx, domain.SendPrivateTextRequest{
		SenderUserID: deleted.ID, RecipientUserID: dialogPeer.ID, RandomID: nonce, Message: "keep a dialog row",
	}); err != nil {
		t.Fatalf("send dialog seed message: %v", err)
	}

	lifecycle := NewAccountLifecycleStore(pool)
	if _, err := lifecycle.ExecuteAccountDeletion(ctx, deleted.ID, domain.AccountDeletionManual, "manual", time.Now().UTC()); err != nil {
		t.Fatalf("execute account deletion: %v", err)
	}

	var queued int
	if err := pool.QueryRow(ctx, `
SELECT count(*) FROM account_deletion_notifications
WHERE deleted_user_id = $1 AND status = 'pending'`, deleted.ID).Scan(&queued); err != nil {
		t.Fatalf("count queued notifications: %v", err)
	}
	if queued != 2 {
		t.Fatalf("queued tombstone notifications = %d, want the contact and the dialog peer", queued)
	}

	claimed, err := lifecycle.ClaimAccountDeletionNotifications(ctx, time.Now().UTC().Add(time.Second), 50, time.Minute)
	if err != nil {
		t.Fatalf("claim notifications: %v", err)
	}
	// The queue is global: another test may have rows pending. Complete them all
	// (the claim leased them) but only assert on this deletion's audience.
	viewers := map[int64]bool{}
	for _, n := range claimed {
		if err := lifecycle.CompleteAccountDeletionNotification(ctx, n.ID, time.Now().UTC()); err != nil {
			t.Fatalf("complete notification %d: %v", n.ID, err)
		}
		if n.DeletedUserID != deleted.ID {
			continue
		}
		viewers[n.TargetUserID] = true
	}
	if !viewers[contact.ID] || !viewers[dialogPeer.ID] {
		t.Fatalf("claim audience = %v, want contact %d and dialog peer %d", viewers, contact.ID, dialogPeer.ID)
	}
	if viewers[stranger.ID] {
		t.Fatal("a peer without a contact or dialog relation must not be notified")
	}

	// A second claim must not replay this deletion's delivered tombstones.
	again, err := lifecycle.ClaimAccountDeletionNotifications(ctx, time.Now().UTC().Add(time.Second), 50, time.Minute)
	if err != nil {
		t.Fatalf("re-claim notifications: %v", err)
	}
	for _, n := range again {
		if n.DeletedUserID == deleted.ID {
			t.Fatalf("re-claimed a delivered tombstone for this deletion: %+v", n)
		}
	}
}