Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion internal/db/reset/reset_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ func TestResetDatabase(t *testing.T) {
// Run test
err := resetDatabase(context.Background(), fsys)
// Check error
assert.ErrorContains(t, err, "invalid port")
assert.ErrorContains(t, err, "invalid port (outside range)")
})

t.Run("throws error on duplicate schema", func(t *testing.T) {
Expand Down
8 changes: 7 additions & 1 deletion internal/db/start/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ EOF
return config
}

var noBackupVolume bool = true

func StartDatabase(ctx context.Context, fsys afero.Fs, w io.Writer, options ...func(*pgx.ConnConfig)) error {
config := NewContainerConfig()
hostPort := strconv.FormatUint(uint64(utils.Config.Db.Port), 10)
Expand All @@ -88,14 +90,15 @@ func StartDatabase(ctx context.Context, fsys afero.Fs, w io.Writer, options ...f
fmt.Fprintln(w, "Starting database...")
// Creating volume will not override existing volume, so we must inspect explicitly
_, err := utils.Docker.VolumeInspect(ctx, utils.DbId)
noBackupVolume = client.IsErrNotFound(err)
if _, err := utils.DockerStart(ctx, config, hostConfig, utils.DbId); err != nil {
return err
}
if !reset.WaitForHealthyService(ctx, utils.DbId, 20*time.Second) {
fmt.Fprintln(os.Stderr, "Database is not healthy.")
}
// Initialise if we are on PG14 and there's no existing db volume
if client.IsErrNotFound(err) && utils.Config.Db.MajorVersion <= 14 {
if noBackupVolume && utils.Config.Db.MajorVersion <= 14 {
if err := initDatabase(ctx, w, options...); err != nil {
return err
}
Expand Down Expand Up @@ -141,6 +144,9 @@ func initDatabase(ctx context.Context, w io.Writer, options ...func(*pgx.ConnCon
}

func SetupDatabase(ctx context.Context, fsys afero.Fs, w io.Writer, options ...func(*pgx.ConnConfig)) error {
if !noBackupVolume {
return nil
}
conn, err := utils.ConnectLocalPostgres(ctx, "localhost", utils.Config.Db.Port, "postgres", options...)
if err != nil {
return err
Expand Down
90 changes: 73 additions & 17 deletions internal/db/start/start_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/supabase/cli/internal/testing/apitest"
"github.com/supabase/cli/internal/testing/fstest"
"github.com/supabase/cli/internal/testing/pgtest"
"github.com/supabase/cli/internal/utils"
"gopkg.in/h2non/gock.v1"
Expand All @@ -34,18 +35,6 @@ func (m *StatErrorFs) Stat(name string) (fs.FileInfo, error) {
return m.MemMapFs.Stat(name)
}

type OpenErrorFs struct {
afero.MemMapFs
DenyPath string
}

func (m *OpenErrorFs) OpenFile(name string, flag int, perm os.FileMode) (afero.File, error) {
if strings.HasPrefix(name, m.DenyPath) {
return nil, fs.ErrPermission
}
return m.MemMapFs.OpenFile(name, flag, perm)
}

func TestInitBranch(t *testing.T) {
t.Run("throws error on permission denied", func(t *testing.T) {
// Setup in-memory fs
Expand All @@ -59,7 +48,6 @@ func TestInitBranch(t *testing.T) {
t.Run("throws error on stat failure", func(t *testing.T) {
// Setup in-memory fs
fsys := &StatErrorFs{DenyPath: utils.CurrBranchPath}
// Setup mock docker
// Run test
err := initCurrentBranch(fsys)
// Check error
Expand All @@ -68,7 +56,7 @@ func TestInitBranch(t *testing.T) {

t.Run("throws error on write failure", func(t *testing.T) {
// Setup in-memory fs
fsys := &OpenErrorFs{DenyPath: utils.CurrBranchPath}
fsys := &fstest.OpenErrorFs{DenyPath: utils.CurrBranchPath}
// Run test
err := initCurrentBranch(fsys)
// Check error
Expand All @@ -82,7 +70,7 @@ func TestInitDatabase(t *testing.T) {
// Run test
err := initDatabase(context.Background(), io.Discard)
// Check error
assert.ErrorContains(t, err, "invalid port")
assert.ErrorContains(t, err, "invalid port (outside range)")
})

t.Run("throws error on exec failure", func(t *testing.T) {
Expand All @@ -101,12 +89,13 @@ func TestInitDatabase(t *testing.T) {
}

func TestStartDatabase(t *testing.T) {
t.Cleanup(func() {
teardown := func() {
utils.Containers = []string{}
utils.Volumes = []string{}
})
}

t.Run("initialise main branch", func(t *testing.T) {
defer teardown()
utils.DbImage = utils.Pg15Image
utils.Config.Db.MajorVersion = 15
utils.DbId = "supabase_db_test"
Expand Down Expand Up @@ -142,6 +131,7 @@ func TestStartDatabase(t *testing.T) {
})

t.Run("recover from backup volume", func(t *testing.T) {
defer teardown()
utils.DbImage = utils.Pg15Image
utils.Config.Db.MajorVersion = 15
utils.DbId = "supabase_db_test"
Expand Down Expand Up @@ -176,6 +166,7 @@ func TestStartDatabase(t *testing.T) {
})

t.Run("throws error on start failure", func(t *testing.T) {
defer teardown()
utils.DbImage = utils.Pg15Image
utils.Config.Db.MajorVersion = 15
utils.DbId = "supabase_db_test"
Expand Down Expand Up @@ -284,3 +275,68 @@ func TestStartCommand(t *testing.T) {
assert.Empty(t, apitest.ListUnmatchedRequests())
})
}

func TestSetupDatabase(t *testing.T) {
t.Run("skips when backup exists", func(t *testing.T) {
noBackupVolume = false
// Run test
err := SetupDatabase(context.Background(), nil, io.Discard)
// Check error
assert.NoError(t, err)
// Reset variable
noBackupVolume = true
})

t.Run("initialises database", func(t *testing.T) {
utils.Config.Db.Port = 5432
// Setup in-memory fs
fsys := afero.NewMemMapFs()
// Setup mock postgres
conn := pgtest.NewConn()
defer conn.Close(t)
// Run test
err := SetupDatabase(context.Background(), fsys, io.Discard, conn.Intercept)
// Check error
assert.NoError(t, err)
})

t.Run("throws error on connect failure", func(t *testing.T) {
utils.Config.Db.Port = 0
// Setup in-memory fs
fsys := &fstest.OpenErrorFs{DenyPath: utils.CustomRolesPath}
// Run test
err := SetupDatabase(context.Background(), fsys, io.Discard)
// Check error
assert.ErrorContains(t, err, "invalid port (outside range)")
})

t.Run("throws error on read failure", func(t *testing.T) {
utils.Config.Db.Port = 5432
// Setup in-memory fs
fsys := &fstest.OpenErrorFs{DenyPath: utils.CustomRolesPath}
// Setup mock postgres
conn := pgtest.NewConn()
defer conn.Close(t)
// Run test
err := SetupDatabase(context.Background(), fsys, io.Discard, conn.Intercept)
// Check error
assert.ErrorIs(t, err, os.ErrPermission)
})

t.Run("throws error on exec failure", func(t *testing.T) {
utils.Config.Db.Port = 5432
// Setup in-memory fs
fsys := afero.NewMemMapFs()
sql := "create role postgres"
require.NoError(t, afero.WriteFile(fsys, utils.CustomRolesPath, []byte(sql), 0644))
// Setup mock postgres
conn := pgtest.NewConn()
defer conn.Close(t)
conn.Query(sql).
ReplyError(pgerrcode.DuplicateObject, `role "postgres" already exists`)
// Run test
err := SetupDatabase(context.Background(), fsys, io.Discard, conn.Intercept)
// Check error
assert.ErrorContains(t, err, `ERROR: role "postgres" already exists (SQLSTATE 42710)`)
})
}
48 changes: 25 additions & 23 deletions internal/link/link_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,12 @@ var dbConfig = pgconn.Config{
func TestPreRun(t *testing.T) {
// Reset global variable
copy := utils.Config
t.Cleanup(func() {
teardown := func() {
utils.Config = copy
})
}

t.Run("passes sanity check", func(t *testing.T) {
defer teardown()
project := apitest.RandomProjectRef()
// Setup in-memory fs
fsys := afero.NewMemMapFs()
Expand All @@ -49,6 +50,7 @@ func TestPreRun(t *testing.T) {
})

t.Run("throws error on invalid project ref", func(t *testing.T) {
defer teardown()
// Setup in-memory fs
fsys := afero.NewMemMapFs()
// Run test
Expand All @@ -58,6 +60,7 @@ func TestPreRun(t *testing.T) {
})

t.Run("throws error on missing config", func(t *testing.T) {
defer teardown()
project := apitest.RandomProjectRef()
// Setup in-memory fs
fsys := afero.NewMemMapFs()
Expand All @@ -68,8 +71,16 @@ func TestPreRun(t *testing.T) {
})
}

// Reset global variable
func teardown() {
for k := range updatedConfig {
delete(updatedConfig, k)
}
}

func TestPostRun(t *testing.T) {
t.Run("prints completion message", func(t *testing.T) {
defer teardown()
project := "test-project"
// Setup in-memory fs
fsys := afero.NewMemMapFs()
Expand All @@ -82,6 +93,7 @@ func TestPostRun(t *testing.T) {
})

t.Run("prints changed config", func(t *testing.T) {
defer teardown()
project := "test-project"
updatedConfig["api"] = "test"
// Setup in-memory fs
Expand All @@ -103,14 +115,8 @@ func TestLinkCommand(t *testing.T) {
// Mock credentials store
keyring.MockInit()

// Reset global variable
t.Cleanup(func() {
for k := range updatedConfig {
delete(updatedConfig, k)
}
})

t.Run("link valid project", func(t *testing.T) {
defer teardown()
// Setup in-memory fs
fsys := afero.NewMemMapFs()
// Setup mock postgres
Expand Down Expand Up @@ -152,6 +158,7 @@ func TestLinkCommand(t *testing.T) {
})

t.Run("throws error on connect failure", func(t *testing.T) {
defer teardown()
// Setup in-memory fs
fsys := afero.NewMemMapFs()
// Flush pending mocks after test execution
Expand All @@ -171,6 +178,7 @@ func TestLinkCommand(t *testing.T) {
})

t.Run("throws error on write failure", func(t *testing.T) {
defer teardown()
// Setup in-memory fs
fsys := afero.NewReadOnlyFs(afero.NewMemMapFs())
// Flush pending mocks after test execution
Expand All @@ -197,14 +205,8 @@ func TestLinkPostgrest(t *testing.T) {
token := apitest.RandomAccessToken(t)
t.Setenv("SUPABASE_ACCESS_TOKEN", string(token))

// Reset global variable
t.Cleanup(func() {
for k := range updatedConfig {
delete(updatedConfig, k)
}
})

t.Run("ignores matching config", func(t *testing.T) {
defer teardown()
// Flush pending mocks after test execution
defer gock.OffAll()
gock.New(utils.DefaultApiHost).
Expand All @@ -220,6 +222,7 @@ func TestLinkPostgrest(t *testing.T) {
})

t.Run("updates api on newer config", func(t *testing.T) {
defer teardown()
// Flush pending mocks after test execution
defer gock.OffAll()
gock.New(utils.DefaultApiHost).
Expand All @@ -244,6 +247,7 @@ func TestLinkPostgrest(t *testing.T) {
})

t.Run("throws error on network failure", func(t *testing.T) {
defer teardown()
// Flush pending mocks after test execution
defer gock.OffAll()
gock.New(utils.DefaultApiHost).
Expand All @@ -257,6 +261,7 @@ func TestLinkPostgrest(t *testing.T) {
})

t.Run("throws error on server unavailable", func(t *testing.T) {
defer teardown()
// Flush pending mocks after test execution
defer gock.OffAll()
gock.New(utils.DefaultApiHost).
Expand All @@ -276,14 +281,8 @@ func TestSliceEqual(t *testing.T) {
}

func TestLinkDatabase(t *testing.T) {
// Reset global variable
t.Cleanup(func() {
for k := range updatedConfig {
delete(updatedConfig, k)
}
})

t.Run("throws error on connect failure", func(t *testing.T) {
defer teardown()
// Run test
err := linkDatabase(context.Background(), pgconn.Config{})
// Check error
Expand All @@ -292,6 +291,7 @@ func TestLinkDatabase(t *testing.T) {
})

t.Run("ignores missing server version", func(t *testing.T) {
defer teardown()
// Setup mock postgres
conn := pgtest.NewWithStatus(map[string]string{
"standard_conforming_strings": "on",
Expand All @@ -309,6 +309,7 @@ func TestLinkDatabase(t *testing.T) {
})

t.Run("updates config to newer db version", func(t *testing.T) {
defer teardown()
utils.Config.Db.MajorVersion = 14
// Setup mock postgres
conn := pgtest.NewWithStatus(map[string]string{
Expand All @@ -331,6 +332,7 @@ func TestLinkDatabase(t *testing.T) {
})

t.Run("throws error on query failure", func(t *testing.T) {
defer teardown()
utils.Config.Db.MajorVersion = 14
// Setup mock postgres
conn := pgtest.NewConn()
Expand Down
Loading