github.com/supabase/cli@v1.168.1/internal/migration/apply/apply_test.go (about)

     1  package apply
     2  
     3  import (
     4  	"context"
     5  	"os"
     6  	"path/filepath"
     7  	"testing"
     8  
     9  	"github.com/jackc/pgconn"
    10  	"github.com/jackc/pgerrcode"
    11  	"github.com/spf13/afero"
    12  	"github.com/stretchr/testify/assert"
    13  	"github.com/stretchr/testify/require"
    14  	"github.com/supabase/cli/internal/migration/history"
    15  	"github.com/supabase/cli/internal/testing/fstest"
    16  	"github.com/supabase/cli/internal/testing/pgtest"
    17  	"github.com/supabase/cli/internal/utils"
    18  )
    19  
    20  func TestMigrateDatabase(t *testing.T) {
    21  	t.Run("applies local migration", func(t *testing.T) {
    22  		// Setup in-memory fs
    23  		fsys := afero.NewMemMapFs()
    24  		path := filepath.Join(utils.MigrationsDir, "0_test.sql")
    25  		sql := "create schema public"
    26  		require.NoError(t, afero.WriteFile(fsys, path, []byte(sql), 0644))
    27  		// Setup mock postgres
    28  		conn := pgtest.NewConn()
    29  		defer conn.Close(t)
    30  		pgtest.MockMigrationHistory(conn)
    31  		conn.Query(sql).
    32  			Reply("CREATE SCHEMA").
    33  			Query(history.INSERT_MIGRATION_VERSION, "0", "test", []string{sql}).
    34  			Reply("INSERT 0 1")
    35  		// Connect to mock
    36  		ctx := context.Background()
    37  		mock, err := utils.ConnectLocalPostgres(ctx, pgconn.Config{Port: 5432}, conn.Intercept)
    38  		require.NoError(t, err)
    39  		defer mock.Close(ctx)
    40  		// Run test
    41  		err = MigrateAndSeed(ctx, "", mock, fsys)
    42  		// Check error
    43  		assert.NoError(t, err)
    44  	})
    45  
    46  	t.Run("ignores empty local directory", func(t *testing.T) {
    47  		assert.NoError(t, MigrateAndSeed(context.Background(), "", nil, afero.NewMemMapFs()))
    48  	})
    49  
    50  	t.Run("throws error on open failure", func(t *testing.T) {
    51  		// Setup in-memory fs
    52  		fsys := &fstest.OpenErrorFs{DenyPath: utils.MigrationsDir}
    53  		// Run test
    54  		err := MigrateAndSeed(context.Background(), "", nil, fsys)
    55  		// Check error
    56  		assert.ErrorIs(t, err, os.ErrPermission)
    57  	})
    58  }
    59  
    60  func TestSeedDatabase(t *testing.T) {
    61  	t.Run("seeds from file", func(t *testing.T) {
    62  		// Setup in-memory fs
    63  		fsys := afero.NewMemMapFs()
    64  		// Setup seed file
    65  		sql := "INSERT INTO employees(name) VALUES ('Alice')"
    66  		require.NoError(t, afero.WriteFile(fsys, utils.SeedDataPath, []byte(sql), 0644))
    67  		// Setup mock postgres
    68  		conn := pgtest.NewConn()
    69  		defer conn.Close(t)
    70  		conn.Query(sql).
    71  			Reply("INSERT 0 1")
    72  		// Connect to mock
    73  		ctx := context.Background()
    74  		mock, err := utils.ConnectLocalPostgres(ctx, pgconn.Config{Port: 5432}, conn.Intercept)
    75  		require.NoError(t, err)
    76  		defer mock.Close(ctx)
    77  		// Run test
    78  		assert.NoError(t, SeedDatabase(ctx, mock, fsys))
    79  	})
    80  
    81  	t.Run("ignores missing seed", func(t *testing.T) {
    82  		assert.NoError(t, SeedDatabase(context.Background(), nil, afero.NewMemMapFs()))
    83  	})
    84  
    85  	t.Run("throws error on read failure", func(t *testing.T) {
    86  		// Setup in-memory fs
    87  		fsys := &fstest.OpenErrorFs{DenyPath: utils.SeedDataPath}
    88  		// Run test
    89  		err := SeedDatabase(context.Background(), nil, fsys)
    90  		// Check error
    91  		assert.ErrorIs(t, err, os.ErrPermission)
    92  	})
    93  
    94  	t.Run("throws error on insert failure", func(t *testing.T) {
    95  		// Setup in-memory fs
    96  		fsys := afero.NewMemMapFs()
    97  		// Setup seed file
    98  		sql := "INSERT INTO employees(name) VALUES ('Alice')"
    99  		require.NoError(t, afero.WriteFile(fsys, utils.SeedDataPath, []byte(sql), 0644))
   100  		// Setup mock postgres
   101  		conn := pgtest.NewConn()
   102  		defer conn.Close(t)
   103  		conn.Query(sql).
   104  			ReplyError(pgerrcode.NotNullViolation, `null value in column "age" of relation "employees"`)
   105  		// Connect to mock
   106  		ctx := context.Background()
   107  		mock, err := utils.ConnectLocalPostgres(ctx, pgconn.Config{Port: 5432}, conn.Intercept)
   108  		require.NoError(t, err)
   109  		defer mock.Close(ctx)
   110  		// Run test
   111  		err = SeedDatabase(ctx, mock, fsys)
   112  		// Check error
   113  		assert.ErrorContains(t, err, `ERROR: null value in column "age" of relation "employees" (SQLSTATE 23502)`)
   114  	})
   115  }
   116  
   117  func TestMigrateUp(t *testing.T) {
   118  	t.Run("throws error on missing file", func(t *testing.T) {
   119  		// Setup in-memory fs
   120  		fsys := afero.NewMemMapFs()
   121  		// Setup mock postgres
   122  		conn := pgtest.NewConn()
   123  		defer conn.Close(t)
   124  		pgtest.MockMigrationHistory(conn)
   125  		// Connect to mock
   126  		ctx := context.Background()
   127  		mock, err := utils.ConnectLocalPostgres(ctx, pgconn.Config{Port: 5432}, conn.Intercept)
   128  		require.NoError(t, err)
   129  		defer mock.Close(ctx)
   130  		// Run test
   131  		err = MigrateUp(context.Background(), mock, []string{"20220727064247_missing.sql"}, fsys)
   132  		// Check error
   133  		assert.ErrorIs(t, err, os.ErrNotExist)
   134  	})
   135  }