Ctrl K
ComunidadGuías…

internal/store/postgres/groupcall_integration_test.go

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

Archivo original ↗Preguntar a KiBot
package postgres

import (
	"context"
	"sync"
	"testing"
	"time"

	"telesrv/internal/domain"
	"telesrv/internal/store"
	"telesrv/internal/store/storetest"
)

// TestGroupCallStoreContractPostgres runs the same contract as the memory implementation (gated on TELESRV_TEST_POSTGRES_DSN).
func TestGroupCallStoreContractPostgres(t *testing.T) {
	pool := testPool(t)
	ctx := context.Background()
	// A run-unique namespace keeps filtered subtest runs independent from stale
	// rows left by an interrupted older run. Per-subtest cleanup below still makes
	// successful runs leave no state behind.
	var nextChannel = int64(1_000_000_000 + time.Now().UnixNano()%100_000_000)
	storetest.RunGroupCallStoreContract(t, func(t *testing.T) (store.GroupCallStore, int64) {
		nextChannel++
		channelID := nextChannel
		// Conference contract rows have channel_id=0 and derive their call IDs from
		// this synthetic channel namespace. Clean both shapes before and after each
		// subtest so a previous failed run cannot feed discarded calls into the next
		// run (and so conference invite/chain rows cascade away as well).
		cleanup := func() {
			_, _ = pool.Exec(ctx, `
DELETE FROM group_calls
WHERE channel_id = $1
   OR (call_id >= $1 * 100 AND call_id < $1 * 100 + 100)`, channelID)
		}
		cleanup()
		t.Cleanup(cleanup)
		return NewGroupCallStore(pool), channelID
	})
}

// TestGroupCallStoreM2ContractPostgres runs the overrides/raise-hand contract (DSN-gated).
func TestGroupCallStoreM2ContractPostgres(t *testing.T) {
	pool := testPool(t)
	ctx := context.Background()
	var nextChannel int64 = 920_000_000
	storetest.RunGroupCallStoreM2Contract(t, func(t *testing.T) (store.GroupCallStore, int64) {
		nextChannel++
		channelID := nextChannel
		t.Cleanup(func() {
			_, _ = pool.Exec(ctx, "DELETE FROM group_calls WHERE channel_id = $1", channelID)
		})
		return NewGroupCallStore(pool), channelID
	})
}

// TestGroupCallStoreConcurrentJoinVersions verifies that version has no skips under row-lock serialization.
func TestGroupCallStoreConcurrentJoinVersions(t *testing.T) {
	pool := testPool(t)
	ctx := context.Background()
	const channelID = 910_900_001
	t.Cleanup(func() {
		_, _ = pool.Exec(ctx, "DELETE FROM group_calls WHERE channel_id = $1", channelID)
	})
	st := NewGroupCallStore(pool)
	// Use the run time as the time basis: the test database may be shared with a running dev
	// server, where an ancient last_check_date would be swept away as a ghost by the online
	// sweeper and pollute the version/count assertions.
	now := int(time.Now().Unix())
	call, err := st.CreateGroupCall(ctx, domain.GroupCall{ID: 910_900_100, AccessHash: 5, ChannelID: channelID, CreatorUserID: 1, CreatedAt: now})
	if err != nil {
		t.Fatalf("create: %v", err)
	}
	const joiners = 8
	var wg sync.WaitGroup
	versions := make(chan int, joiners)
	for i := int64(0); i < joiners; i++ {
		wg.Add(1)
		go func(userID int64) {
			defer wg.Done()
			mut, err := st.JoinGroupCall(ctx, domain.JoinGroupCallRequest{CallID: call.ID, UserID: userID, SSRC: 7000 + userID, Now: now})
			if err != nil {
				t.Errorf("join %d: %v", userID, err)
				return
			}
			versions <- mut.Call.Version
		}(100 + i)
	}
	wg.Wait()
	close(versions)
	seen := map[int]bool{}
	for v := range versions {
		if seen[v] {
			t.Fatalf("duplicate version %d under concurrency", v)
		}
		seen[v] = true
	}
	final, _, err := st.GetGroupCall(ctx, call.ID)
	if err != nil || final.Version != 1+joiners || final.ParticipantsCount != joiners {
		t.Fatalf("final call = %+v err=%v", final, err)
	}
}