supabase-cli/internal/db/pull/pull_test.go

195 lines
6.5 KiB
Go

package pull
import (
"context"
"errors"
"os"
"path/filepath"
"testing"
"github.com/h2non/gock"
"github.com/jackc/pgconn"
"github.com/jackc/pgerrcode"
"github.com/spf13/afero"
"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/utils"
"github.com/supabase/cli/pkg/migration"
"github.com/supabase/cli/pkg/pgtest"
)
var dbConfig = pgconn.Config{
Host: "db.supabase.co",
Port: 5432,
User: "admin",
Password: "password",
Database: "postgres",
}
func TestPullCommand(t *testing.T) {
t.Run("throws error on connect failure", func(t *testing.T) {
// Setup in-memory fs
fsys := afero.NewMemMapFs()
// Run test
err := Run(context.Background(), nil, pgconn.Config{}, "", fsys)
// Check error
assert.ErrorContains(t, err, "invalid port (outside range)")
assert.Empty(t, apitest.ListUnmatchedRequests())
})
t.Run("throws error on sync failure", func(t *testing.T) {
// Setup in-memory fs
fsys := afero.NewMemMapFs()
// Setup mock postgres
conn := pgtest.NewConn()
defer conn.Close(t)
conn.Query(migration.LIST_MIGRATION_VERSION).
ReplyError(pgerrcode.InvalidCatalogName, `database "postgres" does not exist`)
// Run test
err := Run(context.Background(), nil, dbConfig, "", fsys, conn.Intercept)
// Check error
assert.ErrorContains(t, err, `ERROR: database "postgres" does not exist (SQLSTATE 3D000)`)
assert.Empty(t, apitest.ListUnmatchedRequests())
})
}
func TestPullSchema(t *testing.T) {
t.Run("dumps remote schema", func(t *testing.T) {
// Setup in-memory fs
fsys := afero.NewMemMapFs()
// Setup mock docker
require.NoError(t, apitest.MockDocker(utils.Docker))
defer gock.OffAll()
apitest.MockDockerStart(utils.Docker, utils.GetRegistryImageUrl(utils.Config.Db.Image), "test-db")
require.NoError(t, apitest.MockDockerLogs(utils.Docker, "test-db", "test"))
// Setup mock postgres
conn := pgtest.NewConn()
defer conn.Close(t)
conn.Query(migration.LIST_MIGRATION_VERSION).
Reply("SELECT 0")
// Run test
err := utils.RunProgram(context.Background(), func(p utils.Program, ctx context.Context) error {
return run(p, ctx, nil, "0_test.sql", conn.MockClient(t), fsys)
})
// Check error
assert.NoError(t, err)
assert.Empty(t, apitest.ListUnmatchedRequests())
contents, err := afero.ReadFile(fsys, "0_test.sql")
assert.NoError(t, err)
assert.Equal(t, []byte("test"), contents)
})
t.Run("throws error on load user schema failure", func(t *testing.T) {
// Setup in-memory fs
fsys := afero.NewMemMapFs()
path := filepath.Join(utils.MigrationsDir, "0_test.sql")
require.NoError(t, afero.WriteFile(fsys, path, []byte(""), 0644))
// Setup mock postgres
conn := pgtest.NewConn()
defer conn.Close(t)
conn.Query(migration.LIST_MIGRATION_VERSION).
Reply("SELECT 1", []interface{}{"0"}).
Query(migration.ListSchemas, migration.ManagedSchemas).
ReplyError(pgerrcode.DuplicateTable, `relation "test" already exists`)
// Run test
err := utils.RunProgram(context.Background(), func(p utils.Program, ctx context.Context) error {
return run(p, ctx, nil, "", conn.MockClient(t), fsys)
})
// Check error
assert.ErrorContains(t, err, `ERROR: relation "test" already exists (SQLSTATE 42P07)`)
})
t.Run("throws error on diff failure", func(t *testing.T) {
// Setup in-memory fs
fsys := afero.NewMemMapFs()
path := filepath.Join(utils.MigrationsDir, "0_test.sql")
require.NoError(t, afero.WriteFile(fsys, path, []byte(""), 0644))
// Setup mock docker
require.NoError(t, apitest.MockDocker(utils.Docker))
defer gock.OffAll()
gock.New(utils.Docker.DaemonHost()).
Get("/v" + utils.Docker.ClientVersion() + "/images/" + utils.GetRegistryImageUrl(utils.Config.Db.Image) + "/json").
ReplyError(errors.New("network error"))
// Setup mock postgres
conn := pgtest.NewConn()
defer conn.Close(t)
conn.Query(migration.LIST_MIGRATION_VERSION).
Reply("SELECT 1", []interface{}{"0"})
// Run test
err := utils.RunProgram(context.Background(), func(p utils.Program, ctx context.Context) error {
return run(p, ctx, []string{"public"}, "", conn.MockClient(t), fsys)
})
// Check error
assert.ErrorContains(t, err, "network error")
assert.Empty(t, apitest.ListUnmatchedRequests())
})
}
func TestSyncRemote(t *testing.T) {
t.Run("throws error on permission denied", func(t *testing.T) {
// Setup in-memory fs
fsys := &fstest.OpenErrorFs{DenyPath: utils.MigrationsDir}
// Setup mock postgres
conn := pgtest.NewConn()
defer conn.Close(t)
conn.Query(migration.LIST_MIGRATION_VERSION).
Reply("SELECT 0")
// Run test
err := assertRemoteInSync(context.Background(), conn.MockClient(t), fsys)
// Check error
assert.ErrorIs(t, err, os.ErrPermission)
assert.Empty(t, apitest.ListUnmatchedRequests())
})
t.Run("throws error on mismatched length", func(t *testing.T) {
// Setup in-memory fs
fsys := afero.NewMemMapFs()
path := filepath.Join(utils.MigrationsDir, "0_test.sql")
require.NoError(t, afero.WriteFile(fsys, path, []byte(""), 0644))
// Setup mock postgres
conn := pgtest.NewConn()
defer conn.Close(t)
conn.Query(migration.LIST_MIGRATION_VERSION).
Reply("SELECT 0")
// Run test
err := assertRemoteInSync(context.Background(), conn.MockClient(t), fsys)
// Check error
assert.ErrorIs(t, err, errConflict)
assert.Empty(t, apitest.ListUnmatchedRequests())
})
t.Run("throws error on mismatched migration", func(t *testing.T) {
// Setup in-memory fs
fsys := afero.NewMemMapFs()
path := filepath.Join(utils.MigrationsDir, "0_test.sql")
require.NoError(t, afero.WriteFile(fsys, path, []byte(""), 0644))
// Setup mock postgres
conn := pgtest.NewConn()
defer conn.Close(t)
conn.Query(migration.LIST_MIGRATION_VERSION).
Reply("SELECT 1", []interface{}{"20220727064247"})
// Run test
err := assertRemoteInSync(context.Background(), conn.MockClient(t), fsys)
// Check error
assert.ErrorIs(t, err, errConflict)
assert.Empty(t, apitest.ListUnmatchedRequests())
})
t.Run("throws error on missing migration", func(t *testing.T) {
// Setup in-memory fs
fsys := afero.NewMemMapFs()
// Setup mock postgres
conn := pgtest.NewConn()
defer conn.Close(t)
conn.Query(migration.LIST_MIGRATION_VERSION).
Reply("SELECT 0")
// Run test
err := assertRemoteInSync(context.Background(), conn.MockClient(t), fsys)
// Check error
assert.ErrorIs(t, err, errMissing)
assert.Empty(t, apitest.ListUnmatchedRequests())
})
}