@@ -3,14 +3,20 @@ package test
33import (
44 "context"
55 "errors"
6+ "net/http"
67 "testing"
8+ "time"
79
10+ "github.com/docker/docker/api/types"
11+ "github.com/docker/docker/api/types/container"
812 "github.com/h2non/gock"
913 "github.com/jackc/pgconn"
1014 "github.com/jackc/pgerrcode"
15+ "github.com/jackc/pgx/v4"
1116 "github.com/spf13/afero"
1217 "github.com/stretchr/testify/assert"
1318 "github.com/stretchr/testify/require"
19+ "github.com/supabase/cli/internal/db/diff"
1420 "github.com/supabase/cli/internal/testing/apitest"
1521 "github.com/supabase/cli/internal/utils"
1622 "github.com/supabase/cli/pkg/config"
@@ -49,6 +55,124 @@ func TestRunCommand(t *testing.T) {
4955 assert .NoError (t , err )
5056 })
5157
58+ t .Run ("runs tests with pg_prove using shadow db" , func (t * testing.T ) {
59+ utils .Config .Db .MajorVersion = 14
60+ utils .Config .Db .ShadowPort = 54320
61+ utils .Config .Db .HealthTimeout = 10 * time .Second
62+ utils .GlobalsSql = "create schema public"
63+ utils .InitialSchemaPg14Sql = "create schema private"
64+ // Setup in-memory fs
65+ fsys := afero .NewMemMapFs ()
66+ require .NoError (t , utils .WriteConfig (fsys , false ))
67+ // Setup mock postgres - two connections needed:
68+ // conn1: MigrateShadowDatabase (GlobalsSql, InitialSchemaPg14Sql, CREATE_TEMPLATE)
69+ // conn2: Run main path (ENABLE_PGTAP, DISABLE_PGTAP)
70+ conn1 := pgtest .NewConn ()
71+ defer conn1 .Close (t )
72+ conn1 .Query (utils .GlobalsSql ).
73+ Reply ("CREATE SCHEMA" ).
74+ Query (utils .InitialSchemaPg14Sql ).
75+ Reply ("CREATE SCHEMA" ).
76+ Query (diff .CREATE_TEMPLATE ).
77+ Reply ("CREATE DATABASE" )
78+ conn2 := pgtest .NewConn ()
79+ defer conn2 .Close (t )
80+ conn2 .Query (ENABLE_PGTAP ).
81+ Reply ("CREATE EXTENSION" ).
82+ Query (DISABLE_PGTAP ).
83+ Reply ("DROP EXTENSION" )
84+ // Interceptor routes by call order
85+ called := false
86+ interceptor := func (cc * pgx.ConnConfig ) {
87+ if ! called {
88+ called = true
89+ conn1 .Intercept (cc )
90+ } else {
91+ conn2 .Intercept (cc )
92+ }
93+ }
94+ // Setup mock docker
95+ require .NoError (t , apitest .MockDocker (utils .Docker ))
96+ defer gock .OffAll ()
97+ shadowId := "test-shadow-db"
98+ apitest .MockDockerStart (utils .Docker , utils .GetRegistryImageUrl (utils .Config .Db .Image ), shadowId )
99+ gock .New (utils .Docker .DaemonHost ()).
100+ Get ("/v" + utils .Docker .ClientVersion () + "/containers/" + shadowId + "/json" ).
101+ Reply (http .StatusOK ).
102+ JSON (container.InspectResponse {ContainerJSONBase : & container.ContainerJSONBase {
103+ State : & container.State {
104+ Running : true ,
105+ Health : & container.Health {Status : types .Healthy },
106+ },
107+ }})
108+ gock .New (utils .Docker .DaemonHost ()).
109+ Delete ("/v" + utils .Docker .ClientVersion () + "/containers/" + shadowId ).
110+ Reply (http .StatusOK )
111+ pgProveId := "test-pg-prove"
112+ apitest .MockDockerStart (utils .Docker , utils .GetRegistryImageUrl (config .Images .PgProve ), pgProveId )
113+ require .NoError (t , apitest .MockDockerLogs (utils .Docker , pgProveId , "Result: SUCCESS" ))
114+ // Run test
115+ err := Run (context .Background (), []string {"nested" }, dbConfig , true , fsys , interceptor )
116+ // Check error
117+ assert .NoError (t , err )
118+ assert .Empty (t , apitest .ListUnmatchedRequests ())
119+ })
120+
121+ t .Run ("throws error on shadow db creation failure" , func (t * testing.T ) {
122+ errNetwork := errors .New ("network error" )
123+ // Setup in-memory fs
124+ fsys := afero .NewMemMapFs ()
125+ require .NoError (t , utils .WriteConfig (fsys , false ))
126+ // Setup mock docker
127+ require .NoError (t , apitest .MockDocker (utils .Docker ))
128+ defer gock .OffAll ()
129+ gock .New (utils .Docker .DaemonHost ()).
130+ Get ("/v" + utils .Docker .ClientVersion () + "/images/" + utils .GetRegistryImageUrl (utils .Config .Db .Image ) + "/json" ).
131+ ReplyError (errNetwork )
132+ // Run test
133+ err := Run (context .Background (), nil , dbConfig , true , fsys )
134+ // Check error
135+ assert .ErrorIs (t , err , errNetwork )
136+ assert .Empty (t , apitest .ListUnmatchedRequests ())
137+ })
138+
139+ t .Run ("throws error on shadow db migration failure" , func (t * testing.T ) {
140+ utils .Config .Db .MajorVersion = 14
141+ utils .Config .Db .ShadowPort = 54320
142+ utils .Config .Db .HealthTimeout = 10 * time .Second
143+ utils .GlobalsSql = "create schema public"
144+ // Setup in-memory fs
145+ fsys := afero .NewMemMapFs ()
146+ require .NoError (t , utils .WriteConfig (fsys , false ))
147+ // Setup mock postgres
148+ conn := pgtest .NewConn ()
149+ defer conn .Close (t )
150+ conn .Query (utils .GlobalsSql ).
151+ ReplyError (pgerrcode .DuplicateSchema , `schema "public" already exists` )
152+ // Setup mock docker
153+ require .NoError (t , apitest .MockDocker (utils .Docker ))
154+ defer gock .OffAll ()
155+ shadowId := "test-shadow-db"
156+ apitest .MockDockerStart (utils .Docker , utils .GetRegistryImageUrl (utils .Config .Db .Image ), shadowId )
157+ gock .New (utils .Docker .DaemonHost ()).
158+ Get ("/v" + utils .Docker .ClientVersion () + "/containers/" + shadowId + "/json" ).
159+ Reply (http .StatusOK ).
160+ JSON (container.InspectResponse {ContainerJSONBase : & container.ContainerJSONBase {
161+ State : & container.State {
162+ Running : true ,
163+ Health : & container.Health {Status : types .Healthy },
164+ },
165+ }})
166+ gock .New (utils .Docker .DaemonHost ()).
167+ Delete ("/v" + utils .Docker .ClientVersion () + "/containers/" + shadowId ).
168+ Reply (http .StatusOK )
169+ // Run test
170+ err := Run (context .Background (), nil , dbConfig , true , fsys , conn .Intercept )
171+ // Check error
172+ assert .ErrorContains (t , err , `ERROR: schema "public" already exists (SQLSTATE 42P06)` )
173+ assert .Empty (t , apitest .ListUnmatchedRequests ())
174+ })
175+
52176 t .Run ("throws error on connect failure" , func (t * testing.T ) {
53177 // Setup in-memory fs
54178 fsys := afero .NewMemMapFs ()
0 commit comments