github.com/supabase/cli@v1.168.1/internal/migration/up/up_test.go (about)

     1  package up
     2  
     3  import (
     4  	"context"
     5  	"os"
     6  	"path/filepath"
     7  	"testing"
     8  
     9  	"github.com/jackc/pgconn"
    10  	"github.com/spf13/afero"
    11  	"github.com/stretchr/testify/assert"
    12  	"github.com/stretchr/testify/require"
    13  	"github.com/supabase/cli/internal/migration/list"
    14  	"github.com/supabase/cli/internal/testing/fstest"
    15  	"github.com/supabase/cli/internal/testing/pgtest"
    16  	"github.com/supabase/cli/internal/utils"
    17  )
    18  
    19  func TestPendingMigrations(t *testing.T) {
    20  	t.Run("finds pending migrations", func(t *testing.T) {
    21  		// Setup in-memory fs
    22  		fsys := afero.NewMemMapFs()
    23  		files := []string{
    24  			"20221201000000_test.sql",
    25  			"20221201000001_test.sql",
    26  			"20221201000002_test.sql",
    27  			"20221201000003_test.sql",
    28  		}
    29  		for _, name := range files {
    30  			path := filepath.Join(utils.MigrationsDir, name)
    31  			require.NoError(t, afero.WriteFile(fsys, path, []byte(""), 0644))
    32  		}
    33  		// Setup mock postgres
    34  		conn := pgtest.NewConn()
    35  		defer conn.Close(t)
    36  		conn.Query(list.LIST_MIGRATION_VERSION).
    37  			Reply("SELECT 2", []interface{}{"20221201000000"}, []interface{}{"20221201000001"})
    38  		// Connect to mock
    39  		ctx := context.Background()
    40  		mock, err := utils.ConnectLocalPostgres(ctx, pgconn.Config{Port: 5432}, conn.Intercept)
    41  		require.NoError(t, err)
    42  		defer mock.Close(ctx)
    43  		// Run test
    44  		pending, err := GetPendingMigrations(ctx, false, mock, fsys)
    45  		// Check error
    46  		assert.NoError(t, err)
    47  		assert.ElementsMatch(t, files[2:], pending)
    48  	})
    49  
    50  	t.Run("throws error on local load failure", func(t *testing.T) {
    51  		// Setup in-memory fs
    52  		fsys := &fstest.OpenErrorFs{DenyPath: utils.MigrationsDir}
    53  		// Setup mock postgres
    54  		conn := pgtest.NewConn()
    55  		defer conn.Close(t)
    56  		conn.Query(list.LIST_MIGRATION_VERSION).
    57  			Reply("SELECT 0")
    58  		// Connect to mock
    59  		ctx := context.Background()
    60  		mock, err := utils.ConnectLocalPostgres(ctx, pgconn.Config{Port: 5432}, conn.Intercept)
    61  		require.NoError(t, err)
    62  		defer mock.Close(ctx)
    63  		// Run test
    64  		_, err = GetPendingMigrations(ctx, false, mock, fsys)
    65  		// Check error
    66  		assert.ErrorIs(t, err, os.ErrPermission)
    67  	})
    68  
    69  	t.Run("throws error on missing local migration", func(t *testing.T) {
    70  		// Setup in-memory fs
    71  		fsys := afero.NewMemMapFs()
    72  		// Setup mock postgres
    73  		conn := pgtest.NewConn()
    74  		defer conn.Close(t)
    75  		conn.Query(list.LIST_MIGRATION_VERSION).
    76  			Reply("SELECT 1", []interface{}{"0"})
    77  		// Connect to mock
    78  		ctx := context.Background()
    79  		mock, err := utils.ConnectLocalPostgres(ctx, pgconn.Config{Port: 5432}, conn.Intercept)
    80  		require.NoError(t, err)
    81  		defer mock.Close(ctx)
    82  		// Run test
    83  		_, err = GetPendingMigrations(ctx, false, mock, fsys)
    84  		// Check error
    85  		assert.ErrorIs(t, err, errMissingLocal)
    86  		assert.Contains(t, utils.CmdSuggestion, "supabase migration repair --status reverted 0")
    87  	})
    88  
    89  	t.Run("throws error on missing remote version", func(t *testing.T) {
    90  		// Setup in-memory fs
    91  		fsys := afero.NewMemMapFs()
    92  		files := []string{"0_test.sql", "1_test.sql"}
    93  		for _, name := range files {
    94  			path := filepath.Join(utils.MigrationsDir, name)
    95  			require.NoError(t, afero.WriteFile(fsys, path, []byte(""), 0644))
    96  		}
    97  		// Setup mock postgres
    98  		conn := pgtest.NewConn()
    99  		defer conn.Close(t)
   100  		conn.Query(list.LIST_MIGRATION_VERSION).
   101  			Reply("SELECT 1", []interface{}{"1"})
   102  		// Connect to mock
   103  		ctx := context.Background()
   104  		mock, err := utils.ConnectLocalPostgres(ctx, pgconn.Config{Port: 5432}, conn.Intercept)
   105  		require.NoError(t, err)
   106  		defer mock.Close(ctx)
   107  		// Run test
   108  		_, err = GetPendingMigrations(ctx, false, mock, fsys)
   109  		// Check error
   110  		assert.ErrorIs(t, err, errMissingRemote)
   111  	})
   112  }
   113  
   114  func TestIgnoreVersionMismatch(t *testing.T) {
   115  	t.Run("applies out-of-order local migrations", func(t *testing.T) {
   116  		// Setup in-memory fs
   117  		fsys := afero.NewMemMapFs()
   118  		files := []string{
   119  			"20221201000000_test.sql",
   120  			"20221201000001_test.sql",
   121  			"20221201000002_test.sql",
   122  			"20221201000003_test.sql",
   123  		}
   124  		for _, name := range files {
   125  			path := filepath.Join(utils.MigrationsDir, name)
   126  			require.NoError(t, afero.WriteFile(fsys, path, []byte(""), 0644))
   127  		}
   128  		// Setup mock postgres
   129  		conn := pgtest.NewConn()
   130  		defer conn.Close(t)
   131  		conn.Query(list.LIST_MIGRATION_VERSION).
   132  			Reply("SELECT 2", []interface{}{"20221201000000"}, []interface{}{"20221201000002"})
   133  		// Connect to mock
   134  		ctx := context.Background()
   135  		mock, err := utils.ConnectLocalPostgres(ctx, pgconn.Config{Port: 5432}, conn.Intercept)
   136  		require.NoError(t, err)
   137  		defer mock.Close(ctx)
   138  		// Run test
   139  		pending, err := GetPendingMigrations(ctx, true, mock, fsys)
   140  		// Check error
   141  		assert.NoError(t, err)
   142  		assert.ElementsMatch(t, []string{files[1], files[3]}, pending)
   143  	})
   144  
   145  	t.Run("throws error on missing local and remote migration", func(t *testing.T) {
   146  		// Setup in-memory fs
   147  		fsys := afero.NewMemMapFs()
   148  		files := []string{
   149  			"20221201000000_test.sql",
   150  			"20221201000001_test.sql",
   151  			"20221201000002_test.sql",
   152  			"20221201000003_test.sql",
   153  		}
   154  		for _, name := range files {
   155  			path := filepath.Join(utils.MigrationsDir, name)
   156  			require.NoError(t, afero.WriteFile(fsys, path, []byte(""), 0644))
   157  		}
   158  		// Setup mock postgres
   159  		conn := pgtest.NewConn()
   160  		defer conn.Close(t)
   161  		conn.Query(list.LIST_MIGRATION_VERSION).
   162  			Reply("SELECT 2", []interface{}{"20221201000002"}, []interface{}{"20221201000004"})
   163  		// Connect to mock
   164  		ctx := context.Background()
   165  		mock, err := utils.ConnectLocalPostgres(ctx, pgconn.Config{Port: 5432}, conn.Intercept)
   166  		require.NoError(t, err)
   167  		defer mock.Close(ctx)
   168  		// Run test
   169  		_, err = GetPendingMigrations(ctx, true, mock, fsys)
   170  		// Check error
   171  		assert.ErrorIs(t, err, errMissingLocal)
   172  		assert.Contains(t, utils.CmdSuggestion, "supabase migration repair --status reverted 20221201000004")
   173  	})
   174  
   175  	t.Run("throws error on missing local migration", func(t *testing.T) {
   176  		// Setup in-memory fs
   177  		fsys := afero.NewMemMapFs()
   178  		files := []string{
   179  			"20221201000000_test.sql",
   180  			"20221201000002_test.sql",
   181  		}
   182  		for _, name := range files {
   183  			path := filepath.Join(utils.MigrationsDir, name)
   184  			require.NoError(t, afero.WriteFile(fsys, path, []byte(""), 0644))
   185  		}
   186  		// Setup mock postgres
   187  		conn := pgtest.NewConn()
   188  		defer conn.Close(t)
   189  		conn.Query(list.LIST_MIGRATION_VERSION).
   190  			Reply("SELECT 5",
   191  				[]interface{}{"20221201000000"},
   192  				[]interface{}{"20221201000001"},
   193  				[]interface{}{"20221201000002"},
   194  				[]interface{}{"20221201000003"},
   195  				[]interface{}{"20221201000004"},
   196  			)
   197  		// Connect to mock
   198  		ctx := context.Background()
   199  		mock, err := utils.ConnectLocalPostgres(ctx, pgconn.Config{Port: 5432}, conn.Intercept)
   200  		require.NoError(t, err)
   201  		defer mock.Close(ctx)
   202  		// Run test
   203  		_, err = GetPendingMigrations(ctx, true, mock, fsys)
   204  		// Check error
   205  		assert.ErrorIs(t, err, errMissingLocal)
   206  		assert.Contains(t, utils.CmdSuggestion, "supabase migration repair --status reverted 20221201000001 20221201000003 20221201000004")
   207  	})
   208  }