Files
collective-lists/supabase/tests/004_realtime_publication.sql
Oier Bravo Urtasun 4c913cf495 feat(realtime): Fase 2b.0 infra + first R-series tests green
Enable postgres_changes broadcasts on shopping_items / shopping_lists and prove
the path end-to-end with Vitest Realtime tests and a pgTAP configuration check.

Infra changes that were silently blocking events
- Migration 007: shopping_items + shopping_lists added to supabase_realtime
  publication with REPLICA IDENTITY FULL (UPDATE/DELETE payloads need the full
  row so list_id-based filters work and RLS can evaluate DELETE against OLD)
- Realtime service: DB_USER=supabase_admin (superuser; supabase_replication_admin
  lacks CREATE on the `realtime` schema that the service auto-creates per tenant)
- Realtime service: SELF_HOST_TENANT_NAME=realtime so the seed tenant name
  matches the default supabase-js resolves for localhost URLs (was realtime-dev
  → TenantNotFound)

Tests
- pgTAP 004_realtime_publication.sql — P-01..P-04: publication membership and
  REPLICA IDENTITY FULL for both shopping tables
- Vitest realtime-postgres-changes.test.ts — R-01 INSERT broadcast,
  R-02 UPDATE carries full row, R-03 list_id filter isolates events
- test-utils realtime-helpers.ts — subscribePostgresChanges() returns a
  waitFor(predicate, timeout) helper that captures the SUBSCRIBED handshake
  before returning so mutations issued right after are not lost
- createClientAs now calls realtime.setAuth(token) so the server evaluates
  RLS against the test user's JWT
- Justfile test-db now globs supabase/tests/*.sql so new pgTAP files are picked
  up automatically

Totals: 54→57 Vitest, 12→16 pgTAP, 15 Playwright = 88 tests green.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 02:33:40 +02:00

56 lines
1.9 KiB
PL/PgSQL

-- pgTAP: Realtime publication + replica identity — Fase 2b prep
-- Run with: psql -U postgres -d postgres -f supabase/tests/004_realtime_publication.sql
--
-- These invariants must hold for Supabase Realtime to forward Postgres Changes
-- events for shopping_items / shopping_lists to subscribed clients with the
-- correct row payloads. Breaking any of them silently drops events.
CREATE EXTENSION IF NOT EXISTS pgtap;
BEGIN;
SELECT plan(4);
-- P-01: shopping_items is in the supabase_realtime publication
SELECT ok(
EXISTS (
SELECT 1 FROM pg_publication_tables
WHERE pubname = 'supabase_realtime'
AND schemaname = 'public'
AND tablename = 'shopping_items'
),
'P-01: shopping_items is part of the supabase_realtime publication'
);
-- P-02: shopping_lists is in the supabase_realtime publication
SELECT ok(
EXISTS (
SELECT 1 FROM pg_publication_tables
WHERE pubname = 'supabase_realtime'
AND schemaname = 'public'
AND tablename = 'shopping_lists'
),
'P-02: shopping_lists is part of the supabase_realtime publication'
);
-- P-03: shopping_items has REPLICA IDENTITY FULL so UPDATE/DELETE events
-- carry the full row (needed for list_id-based client filtering and RLS on DELETE)
SELECT results_eq(
$$ SELECT relreplident::text FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE n.nspname = 'public' AND c.relname = 'shopping_items' $$,
$$ VALUES ('f') $$,
'P-03: shopping_items has REPLICA IDENTITY FULL'
);
-- P-04: same for shopping_lists
SELECT results_eq(
$$ SELECT relreplident::text FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE n.nspname = 'public' AND c.relname = 'shopping_lists' $$,
$$ VALUES ('f') $$,
'P-04: shopping_lists has REPLICA IDENTITY FULL'
);
SELECT * FROM finish();
ROLLBACK;