package repository import ( "context" "errors" "testing" "time" "evobgp/internal/store" "github.com/google/uuid" ) func TestJobEnqueueTenantLocksDiffer(t *testing.T) { const tenantA = "11111111-1111-1111-1111-111111111111" const tenantB = "22222222-2222-2222-2222-222222222222" a := jobEnqueueTenantLock(tenantA) b := jobEnqueueTenantLock(tenantB) if a == b || a == jobEnqueueGlobalLock || b == jobEnqueueGlobalLock { t.Fatalf("locks a=%d b=%d global=%d", a, b, jobEnqueueGlobalLock) } if jobEnqueueTenantLock(tenantA) != a { t.Fatal("tenant lock is not stable") } } func TestEnqueueJobTenantLocksDoNotOverlapIntegration(t *testing.T) { p, tenantA := queueIntegration(t) ctx := context.Background() tenantB := uuid.NewString() if _, err := p.pool.Exec(ctx, `INSERT INTO tenant(id,name,slug) VALUES($1,'queue b',$2)`, tenantB, "queue-b-"+tenantB); err != nil { t.Fatal(err) } t.Cleanup(func() { _, _ = p.pool.Exec(context.Background(), `DELETE FROM tenant WHERE id=$1`, tenantB) }) conn, err := p.pool.Acquire(ctx) if err != nil { t.Fatal(err) } defer conn.Release() tx, err := conn.Begin(ctx) if err != nil { t.Fatal(err) } defer func() { _ = tx.Rollback(context.Background()) }() if _, err = tx.Exec(ctx, `SELECT pg_advisory_xact_lock($1::bigint)`, jobEnqueueTenantLock(tenantA)); err != nil { t.Fatal(err) } otherDone := make(chan error, 1) go func() { _, _, err := p.EnqueueJob(ctx, store.JobEnqueue{TenantID: tenantB, Kind: "test"}) otherDone <- err }() select { case err := <-otherDone: if err != nil { t.Fatal(err) } case <-time.After(5 * time.Second): t.Fatal("tenant B enqueue shared tenant A lock") } short, cancel := context.WithTimeout(ctx, 400*time.Millisecond) defer cancel() if _, _, err = p.EnqueueJob(short, store.JobEnqueue{TenantID: tenantA, Kind: "test"}); !errors.Is(err, context.DeadlineExceeded) { t.Fatalf("same tenant did not wait: %v", err) } key := "idem-key" in := store.JobEnqueue{TenantID: tenantB, Kind: "test", IdempotencyKey: &key, Meta: map[string]any{"n": 1}} first, created, err := p.EnqueueJob(ctx, in) if err != nil || !created || first.ID == "" { t.Fatalf("first=%+v created=%v err=%v", first, created, err) } in.Meta = map[string]any{"n": 2} if _, _, err = p.EnqueueJob(ctx, in); !errors.Is(err, store.ErrJobConflict) { t.Fatalf("idempotency conflict: %v", err) } }