github.com/supabase/cli@v1.168.1/internal/utils/connect_test.go (about) 1 package utils 2 3 import ( 4 "context" 5 "net" 6 "net/http" 7 "testing" 8 9 "github.com/go-errors/errors" 10 "github.com/jackc/pgconn" 11 "github.com/spf13/viper" 12 "github.com/stretchr/testify/assert" 13 "github.com/stretchr/testify/require" 14 "github.com/supabase/cli/internal/testing/apitest" 15 "github.com/supabase/cli/internal/testing/pgtest" 16 "github.com/supabase/cli/internal/utils/cloudflare" 17 "gopkg.in/h2non/gock.v1" 18 ) 19 20 var dbConfig = pgconn.Config{ 21 Host: GetSupabaseDbHost(apitest.RandomProjectRef()), 22 Port: 6543, 23 User: "admin", 24 Password: "password", 25 Database: "postgres", 26 } 27 28 const ( 29 PG13_POOLER_URL = "postgres://postgres:[YOUR-PASSWORD]@aws-0-ap-southeast-1.pooler.supabase.com:6543/postgres?options=reference%3Dzupyfdrjfhbeevcogohz" 30 PG15_POOLER_URL = "postgres://postgres.zupyfdrjfhbeevcogohz:[YOUR-PASSWORD]@fly-0-sin.pooler.supabase.com:6543/postgres" 31 ) 32 33 func TestConnectByConfig(t *testing.T) { 34 t.Run("connects to remote postgres with DoH", func(t *testing.T) { 35 Config.Db.Pooler.ConnectionString = "" 36 DNSResolver.Value = DNS_OVER_HTTPS 37 // Setup http mock 38 defer gock.OffAll() 39 // pgx makes 2 calls to resolve ip for each connect request 40 gock.New("https://1.1.1.1"). 41 Get("/dns-query"). 42 MatchParam("name", dbConfig.Host). 43 MatchHeader("accept", "application/dns-json"). 44 Reply(http.StatusOK). 45 JSON(&cloudflare.DNSResponse{Answer: []cloudflare.DNSAnswer{ 46 {Type: cloudflare.TypeA, Data: "127.0.0.1"}, 47 }}) 48 gock.New("https://1.1.1.1"). 49 Get("/dns-query"). 50 MatchParam("name", dbConfig.Host). 51 MatchHeader("accept", "application/dns-json"). 52 Reply(http.StatusOK). 53 JSON(&cloudflare.DNSResponse{Answer: []cloudflare.DNSAnswer{ 54 {Type: cloudflare.TypeA, Data: "127.0.0.1"}, 55 }}) 56 // Setup mock postgres 57 conn := pgtest.NewConn() 58 defer conn.Close(t) 59 // Run test 60 c, err := ConnectByConfig(context.Background(), dbConfig, conn.Intercept) 61 require.NoError(t, err) 62 defer c.Close(context.Background()) 63 assert.NoError(t, err) 64 }) 65 66 t.Run("connects with unescaped db password", func(t *testing.T) { 67 DNSResolver.Value = DNS_GO_NATIVE 68 // Setup mock postgres 69 conn := pgtest.NewConn() 70 defer conn.Close(t) 71 // Run test 72 config := *dbConfig.Copy() 73 config.Host = "localhost" 74 config.Password = "pass word" 75 c, err := ConnectByConfig(context.Background(), config, conn.Intercept) 76 require.NoError(t, err) 77 defer c.Close(context.Background()) 78 assert.Equal(t, config.Password, c.Config().Password) 79 }) 80 81 t.Run("no retry on connecting successfully with pooler", func(t *testing.T) { 82 Config.Db.Pooler.ConnectionString = PG15_POOLER_URL 83 DNSResolver.Value = DNS_GO_NATIVE 84 // Setup mock postgres 85 conn := pgtest.NewConn() 86 defer conn.Close(t) 87 // Run test 88 c, err := ConnectByConfig(context.Background(), dbConfig, conn.Intercept) 89 // Check error 90 require.NoError(t, err) 91 defer c.Close(context.Background()) 92 assert.Empty(t, apitest.ListUnmatchedRequests()) 93 }) 94 95 t.Run("fallback to postgres port on dial error", func(t *testing.T) { 96 Config.Db.Pooler.ConnectionString = PG15_POOLER_URL 97 DNSResolver.Value = DNS_OVER_HTTPS 98 netErr := errors.New("network error") 99 // Setup http mock 100 defer gock.OffAll() 101 gock.New("https://1.1.1.1"). 102 Get("/dns-query"). 103 MatchParam("name", dbConfig.Host). 104 MatchHeader("accept", "application/dns-json"). 105 ReplyError(&net.OpError{Op: "dial", Err: netErr}) 106 gock.New("https://1.1.1.1"). 107 Get("/dns-query"). 108 MatchParam("name", "fly-0-sin.pooler.supabase.com"). 109 MatchHeader("accept", "application/dns-json"). 110 ReplyError(&net.OpError{Op: "dial", Err: netErr}) 111 // Run test 112 _, err := ConnectByConfig(context.Background(), dbConfig) 113 // Check error 114 require.ErrorIs(t, err, netErr) 115 assert.Empty(t, apitest.ListUnmatchedRequests()) 116 }) 117 } 118 119 func TestConnectLocal(t *testing.T) { 120 t.Run("connects with debug log", func(t *testing.T) { 121 viper.Set("DEBUG", true) 122 _, err := ConnectLocalPostgres(context.Background(), pgconn.Config{Host: "0", Port: 6543}) 123 assert.ErrorContains(t, err, "failed to connect to postgres") 124 }) 125 126 t.Run("throws error on invalid port", func(t *testing.T) { 127 Config.Db.Port = 0 128 _, err := ConnectLocalPostgres(context.Background(), pgconn.Config{}) 129 assert.ErrorContains(t, err, "invalid port (outside range)") 130 }) 131 } 132 133 func TestPoolerConfig(t *testing.T) { 134 t.Run("parses options ref", func(t *testing.T) { 135 Config.Db.Pooler.ConnectionString = PG13_POOLER_URL 136 assert.NotNil(t, GetPoolerConfig("zupyfdrjfhbeevcogohz")) 137 }) 138 139 t.Run("parses username ref", func(t *testing.T) { 140 Config.Db.Pooler.ConnectionString = PG15_POOLER_URL 141 assert.NotNil(t, GetPoolerConfig("zupyfdrjfhbeevcogohz")) 142 }) 143 144 t.Run("returns nil on missing url", func(t *testing.T) { 145 Config.Db.Pooler.ConnectionString = "" 146 assert.Nil(t, GetPoolerConfig("zupyfdrjfhbeevcogohz")) 147 }) 148 149 t.Run("returns nil on malformed url", func(t *testing.T) { 150 Config.Db.Pooler.ConnectionString = "malformed" 151 assert.Nil(t, GetPoolerConfig("zupyfdrjfhbeevcogohz")) 152 }) 153 154 t.Run("returns nil on mismatched project", func(t *testing.T) { 155 Config.Db.Pooler.ConnectionString = PG13_POOLER_URL 156 assert.Nil(t, GetPoolerConfig("nlhaskwsizylhnffaqkd")) 157 Config.Db.Pooler.ConnectionString = PG15_POOLER_URL 158 assert.Nil(t, GetPoolerConfig("nlhaskwsizylhnffaqkd")) 159 }) 160 161 t.Run("returns nil on invalid host", func(t *testing.T) { 162 Config.Db.Pooler.ConnectionString = "postgres://postgres.zupyfdrjfhbeevcogohz:[YOUR-PASSWORD]@localhost:6543/postgres" 163 assert.Nil(t, GetPoolerConfig("zupyfdrjfhbeevcogohz")) 164 }) 165 } 166 167 func TestPostgresURL(t *testing.T) { 168 url := ToPostgresURL(pgconn.Config{ 169 Host: "2406:da18:4fd:9b0d:80ec:9812:3e65:450b", 170 Port: 5432, 171 User: "postgres", 172 Password: "!@#$%^&*()", 173 RuntimeParams: map[string]string{ 174 "options": "test", 175 }, 176 }) 177 assert.Equal(t, `postgresql://postgres:%21%40%23$%25%5E&%2A%28%29@[2406:da18:4fd:9b0d:80ec:9812:3e65:450b]:5432/?connect_timeout=10&options=test`, url) 178 }