1+ import { createClient } from '@supabase/supabase-js' ;
2+ import postgres from 'postgres' ;
3+ import { createHash } from 'crypto' ;
4+
5+ export async function setup ( ) {
6+ // Create a hash-based channel name with only alphanumeric characters
7+ const timestamp = Date . now ( ) . toString ( ) ;
8+ const random = Math . random ( ) . toString ( ) ;
9+ const hash = createHash ( 'sha1' ) . update ( timestamp + random ) . digest ( 'hex' ) ;
10+ const channelName = `setup${ hash . substring ( 0 , 16 ) } ` ; // Use first 16 chars of hash
11+ console . log ( `[GLOBAL SETUP] Using random channel: ${ channelName } ` ) ;
12+
13+ const supabaseUrl = 'http://localhost:50521' ;
14+ const pgUrl = 'postgresql://postgres:postgres@localhost:50522/postgres' ;
15+
16+ console . log ( '[GLOBAL SETUP] Checking Supabase availability...' ) ;
17+
18+ // Check if Supabase is reachable
19+ try {
20+ const response = await fetch ( `${ supabaseUrl } /rest/v1/` , { method : 'HEAD' } ) ;
21+ console . log ( `[GLOBAL SETUP] Supabase REST API response: ${ response . status } ` ) ;
22+ } catch ( fetchError ) {
23+ console . error ( '[GLOBAL SETUP] ❌ Failed to reach Supabase REST API:' , fetchError instanceof Error ? fetchError . message : fetchError ) ;
24+ console . error ( '[GLOBAL SETUP] Is Supabase running on port 50521?' ) ;
25+ process . exit ( 1 ) ;
26+ }
27+
28+ console . log ( '[GLOBAL SETUP] Creating Supabase client...' ) ;
29+ const supabase = createClient ( supabaseUrl , 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImV4cCI6MTk4MzgxMjk5Nn0.EGIM96RAZx35lJzdJsyH-qQwv8Hdp7fsn3W0YpN81IU' ) ;
30+
31+ console . log ( '[GLOBAL SETUP] Creating PostgreSQL connection...' ) ;
32+ // eslint-disable-next-line @typescript-eslint/no-empty-function
33+ const sql = postgres ( pgUrl , { prepare : false , onnotice : ( ) => { } } ) ;
34+
35+ const channel = supabase . channel ( channelName ) ;
36+ const events : unknown [ ] = [ ] ;
37+
38+ try {
39+ console . log ( '[GLOBAL SETUP] Testing PostgreSQL connection...' ) ;
40+ try {
41+ await sql `SELECT 1` ;
42+ console . log ( '[GLOBAL SETUP] ✓ PostgreSQL connection successful' ) ;
43+ } catch ( pgError ) {
44+ console . error ( '[GLOBAL SETUP] ❌ Failed to connect to PostgreSQL:' , pgError instanceof Error ? pgError . message : pgError ) ;
45+ console . error ( '[GLOBAL SETUP] Is PostgreSQL running on port 50522?' ) ;
46+ throw pgError ;
47+ }
48+
49+ console . log ( '[GLOBAL SETUP] Creating realtime partition...' ) ;
50+ await sql `SELECT pgflow_tests.create_realtime_partition()` ;
51+ console . log ( '[GLOBAL SETUP] ✓ Realtime partition created' ) ;
52+
53+ console . log ( '[GLOBAL SETUP] Setting up broadcast listener...' ) ;
54+ channel . on ( 'broadcast' , { event : '*' } , ( p ) => {
55+ console . log ( '[GLOBAL SETUP] Received broadcast event:' , p ) ;
56+ events . push ( p ) ;
57+ } ) ;
58+
59+ console . log ( '[GLOBAL SETUP] Subscribing to channel...' ) ;
60+ await new Promise < void > ( ( ok , fail ) => {
61+ const t = setTimeout ( ( ) => {
62+ console . error ( '[GLOBAL SETUP] ❌ Channel subscription timed out after 10s' ) ;
63+ fail ( new Error ( 'Channel subscription timeout after 10s' ) ) ;
64+ } , 10000 ) ;
65+
66+ channel . subscribe ( ( s ) => {
67+ console . log ( `[GLOBAL SETUP] Channel status: ${ s } ` ) ;
68+ if ( s === 'SUBSCRIBED' ) {
69+ console . log ( '[GLOBAL SETUP] ✓ Channel subscribed successfully' ) ;
70+ clearTimeout ( t ) ;
71+ ok ( ) ;
72+ }
73+ if ( s === 'TIMED_OUT' ) {
74+ console . error ( '[GLOBAL SETUP] ❌ Channel subscription timed out (status: TIMED_OUT)' ) ;
75+ clearTimeout ( t ) ;
76+ fail ( new Error ( 'Channel status: TIMED_OUT' ) ) ;
77+ }
78+ if ( s === 'CHANNEL_ERROR' ) {
79+ console . error ( '[GLOBAL SETUP] ❌ Channel error occurred (status: CHANNEL_ERROR)' ) ;
80+ console . error ( '[GLOBAL SETUP] This usually means the realtime server is not accessible' ) ;
81+ console . error ( '[GLOBAL SETUP] Check if Supabase realtime is running on ws://localhost:50521' ) ;
82+ clearTimeout ( t ) ;
83+ fail ( new Error ( 'Channel status: CHANNEL_ERROR' ) ) ;
84+ }
85+ } ) ;
86+ } ) ;
87+
88+ // Add stabilization delay for cold channels to fully establish routing
89+ console . log ( '[GLOBAL SETUP] Channel subscribed, waiting 75ms for stabilization...' ) ;
90+ await new Promise ( resolve => setTimeout ( resolve , 75 ) ) ;
91+ console . log ( '[GLOBAL SETUP] Stabilization complete' ) ;
92+
93+ console . log ( '[GLOBAL SETUP] Sending test message via realtime.send()...' ) ;
94+ await sql `SELECT realtime.send('{}', 'e', ${ channelName } , false)` ;
95+ console . log ( '[GLOBAL SETUP] ✓ Message sent' ) ;
96+
97+ console . log ( '[GLOBAL SETUP] Waiting for broadcast event (timeout: 10s)...' ) ;
98+ const start = Date . now ( ) ;
99+ while ( events . length === 0 && Date . now ( ) - start < 10000 ) {
100+ await new Promise ( ( ok ) => setTimeout ( ok , 100 ) ) ;
101+ }
102+
103+ if ( events . length === 0 ) {
104+ console . error ( '[GLOBAL SETUP] ❌ No events received after 10s' ) ;
105+ console . error ( '[GLOBAL SETUP] Message was sent but not received - realtime routing may be broken' ) ;
106+ throw new Error ( 'realtime.send() failed - no events received' ) ;
107+ }
108+
109+ console . log ( `[GLOBAL SETUP] ✓ Received ${ events . length } event(s)` ) ;
110+ console . log ( '[GLOBAL SETUP] ✅ All connectivity checks passed' ) ;
111+ } catch ( e ) {
112+ console . error ( '\n❌ Supabase connectivity check failed' ) ;
113+ console . error ( 'Error:' , e instanceof Error ? e . message : e ) ;
114+ console . error ( '\nTroubleshooting:' ) ;
115+ console . error ( ' 1. Check if Supabase is running: docker ps | grep supabase' ) ;
116+ console . error ( ' 2. Check Supabase logs: supabase status' ) ;
117+ console . error ( ' 3. Try restarting: supabase stop && supabase start' ) ;
118+ console . error ( ' 4. Verify ports 50521 (API) and 50522 (PostgreSQL) are accessible\n' ) ;
119+ process . exit ( 1 ) ;
120+ } finally {
121+ console . log ( '[GLOBAL SETUP] Cleaning up...' ) ;
122+ await supabase . removeChannel ( channel ) ;
123+ await sql . end ( ) ;
124+ console . log ( '[GLOBAL SETUP] Cleanup complete' ) ;
125+ }
126+ }
127+
128+ export async function teardown ( ) {
129+ // Nothing to clean up globally
130+ }
0 commit comments