| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196 |
- package executor
- import (
- "testing"
- "time"
- "github.com/danfragoso/pizzasql-next/pkg/storage"
- )
- func newNotifyExec(t *testing.T, hub *storage.Hub, schema *storage.SchemaManager, table *storage.TableManager, pid int32) *Executor {
- t.Helper()
- e := newExec(schema, table)
- e.SetBackendPID(pid)
- e.SetNotificationHub(hub)
- t.Cleanup(func() { _ = e.Close() })
- return e
- }
- func recvNotification(t *testing.T, e *Executor) storage.Notification {
- t.Helper()
- select {
- case n := <-e.NotificationChannel():
- return n
- case <-time.After(2 * time.Second):
- t.Fatal("timed out waiting for notification")
- return storage.Notification{}
- }
- }
- func expectNoNotification(t *testing.T, e *Executor) {
- t.Helper()
- select {
- case n := <-e.NotificationChannel():
- t.Fatalf("unexpected notification: %+v", n)
- case <-time.After(50 * time.Millisecond):
- }
- }
- func TestNotifyDeliversToListener(t *testing.T) {
- _, schema, table := newTestDB(t)
- hub := storage.NewHub()
- listener := newNotifyExec(t, hub, schema, table, 101)
- sender := newNotifyExec(t, hub, schema, table, 202)
- execMust(t, listener, "LISTEN updates")
- execMust(t, sender, "NOTIFY updates, 'hello'")
- n := recvNotification(t, listener)
- if n.Channel != "updates" || n.Payload != "hello" || n.PID != 202 {
- t.Fatalf("unexpected notification: %+v", n)
- }
- }
- func TestListenTakesEffectAtCommit(t *testing.T) {
- _, schema, table := newTestDB(t)
- hub := storage.NewHub()
- listener := newNotifyExec(t, hub, schema, table, 101)
- sender := newNotifyExec(t, hub, schema, table, 202)
- execMust(t, listener, "BEGIN")
- execMust(t, listener, "LISTEN late")
- execMust(t, sender, "NOTIFY late, 'before'")
- expectNoNotification(t, listener)
- execMust(t, listener, "COMMIT")
- execMust(t, sender, "NOTIFY late, 'after'")
- n := recvNotification(t, listener)
- if n.Payload != "after" {
- t.Fatalf("payload = %q, want after", n.Payload)
- }
- }
- func TestListenRollbackDiscardsSubscription(t *testing.T) {
- _, schema, table := newTestDB(t)
- hub := storage.NewHub()
- listener := newNotifyExec(t, hub, schema, table, 101)
- sender := newNotifyExec(t, hub, schema, table, 202)
- execMust(t, listener, "BEGIN")
- execMust(t, listener, "LISTEN rolledback")
- execMust(t, listener, "ROLLBACK")
- execMust(t, sender, "NOTIFY rolledback, 'nope'")
- expectNoNotification(t, listener)
- }
- func TestNotifyOnlyDeliveredAfterCommit(t *testing.T) {
- _, schema, table := newTestDB(t)
- hub := storage.NewHub()
- listener := newNotifyExec(t, hub, schema, table, 101)
- sender := newNotifyExec(t, hub, schema, table, 202)
- execMust(t, listener, "LISTEN events")
- execMust(t, sender, "BEGIN")
- execMust(t, sender, "NOTIFY events, 'pending'")
- expectNoNotification(t, listener)
- execMust(t, sender, "COMMIT")
- n := recvNotification(t, listener)
- if n.Payload != "pending" || n.LSN == 0 {
- t.Fatalf("unexpected notification: %+v", n)
- }
- }
- func TestNotifyRollbackDiscards(t *testing.T) {
- _, schema, table := newTestDB(t)
- hub := storage.NewHub()
- listener := newNotifyExec(t, hub, schema, table, 101)
- sender := newNotifyExec(t, hub, schema, table, 202)
- execMust(t, listener, "LISTEN events")
- execMust(t, sender, "BEGIN")
- execMust(t, sender, "NOTIFY events, 'gone'")
- execMust(t, sender, "ROLLBACK")
- expectNoNotification(t, listener)
- }
- func TestNotifySavepointRollback(t *testing.T) {
- _, schema, table := newTestDB(t)
- hub := storage.NewHub()
- listener := newNotifyExec(t, hub, schema, table, 101)
- sender := newNotifyExec(t, hub, schema, table, 202)
- execMust(t, listener, "LISTEN events")
- execMust(t, sender, "BEGIN")
- execMust(t, sender, "NOTIFY events, 'kept'")
- execMust(t, sender, "SAVEPOINT sp")
- execMust(t, sender, "NOTIFY events, 'discarded'")
- execMust(t, sender, "ROLLBACK TO SAVEPOINT sp")
- execMust(t, sender, "COMMIT")
- n := recvNotification(t, listener)
- if n.Payload != "kept" {
- t.Fatalf("payload = %q, want kept", n.Payload)
- }
- expectNoNotification(t, listener)
- }
- func TestNotifyDuplicatesCollapseInTransaction(t *testing.T) {
- _, schema, table := newTestDB(t)
- hub := storage.NewHub()
- listener := newNotifyExec(t, hub, schema, table, 101)
- sender := newNotifyExec(t, hub, schema, table, 202)
- execMust(t, listener, "LISTEN events")
- execMust(t, sender, "BEGIN")
- execMust(t, sender, "NOTIFY events, 'same'")
- execMust(t, sender, "NOTIFY events, 'same'")
- execMust(t, sender, "NOTIFY events, 'different'")
- execMust(t, sender, "COMMIT")
- first := recvNotification(t, listener)
- second := recvNotification(t, listener)
- if first.Payload != "same" || second.Payload != "different" {
- t.Fatalf("unexpected notifications: %+v %+v", first, second)
- }
- expectNoNotification(t, listener)
- }
- func TestUnlistenAllAndDisconnectCleanup(t *testing.T) {
- _, schema, table := newTestDB(t)
- hub := storage.NewHub()
- listener := newNotifyExec(t, hub, schema, table, 101)
- sender := newNotifyExec(t, hub, schema, table, 202)
- execMust(t, listener, "LISTEN a")
- execMust(t, listener, "LISTEN b")
- execMust(t, listener, "UNLISTEN *")
- execMust(t, sender, "NOTIFY a, 'x'")
- execMust(t, sender, "NOTIFY b, 'y'")
- expectNoNotification(t, listener)
- execMust(t, listener, "LISTEN c")
- _ = listener.Close()
- execMust(t, sender, "NOTIFY c, 'z'")
- expectNoNotification(t, listener)
- }
- func TestCommitLSNIsMonotonic(t *testing.T) {
- _, schema, table := newTestDB(t)
- e := newExec(schema, table)
- execMust(t, e, "CREATE TABLE lsn_test (id INTEGER PRIMARY KEY, v TEXT)")
- execMust(t, e, "BEGIN")
- execMust(t, e, "INSERT INTO lsn_test VALUES (1, 'a')")
- first := execMust(t, e, "COMMIT")
- if first.LSN == 0 {
- t.Fatal("first commit LSN is zero")
- }
- execMust(t, e, "BEGIN")
- execMust(t, e, "INSERT INTO lsn_test VALUES (2, 'b')")
- second := execMust(t, e, "COMMIT")
- if second.LSN <= first.LSN {
- t.Fatalf("second commit LSN %d did not advance past %d", second.LSN, first.LSN)
- }
- }
|