Skip to content

Commit 335fd98

Browse files
committed
test: add shadow db coverage for db test command
Add three tests exercising the useShadowDb=true code path in Run(): happy path with full shadow DB lifecycle (create, health check, migrate, pg_prove), error on shadow creation failure, and error on shadow migration failure. Raises test.go coverage from 15% to 85%.
1 parent eb3349d commit 335fd98

1 file changed

Lines changed: 124 additions & 0 deletions

File tree

internal/db/test/test_test.go

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,20 @@ package test
33
import (
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

Comments
 (0)