github.com/supabase/cli@v1.168.1/internal/migration/list/list_test.go (about)

     1  package list
     2  
     3  import (
     4  	"context"
     5  	"os"
     6  	"path/filepath"
     7  	"strings"
     8  	"testing"
     9  
    10  	"github.com/jackc/pgconn"
    11  	"github.com/jackc/pgerrcode"
    12  	"github.com/spf13/afero"
    13  	"github.com/stretchr/testify/assert"
    14  	"github.com/stretchr/testify/require"
    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  var dbConfig = pgconn.Config{
    21  	Host:     "127.0.0.1",
    22  	Port:     5432,
    23  	User:     "admin",
    24  	Password: "password",
    25  	Database: "postgres",
    26  }
    27  
    28  func TestMigrationList(t *testing.T) {
    29  	t.Run("lists remote migrations", func(t *testing.T) {
    30  		// Setup in-memory fs
    31  		fsys := afero.NewMemMapFs()
    32  		// Setup mock postgres
    33  		conn := pgtest.NewConn()
    34  		defer conn.Close(t)
    35  		conn.Query(LIST_MIGRATION_VERSION).
    36  			Reply("SELECT 0")
    37  		// Run test
    38  		err := Run(context.Background(), dbConfig, fsys, conn.Intercept)
    39  		// Check error
    40  		assert.NoError(t, err)
    41  	})
    42  
    43  	t.Run("throws error on remote failure", func(t *testing.T) {
    44  		// Setup in-memory fs
    45  		fsys := afero.NewMemMapFs()
    46  		// Run test
    47  		err := Run(context.Background(), pgconn.Config{}, fsys)
    48  		// Check error
    49  		assert.ErrorContains(t, err, "invalid port (outside range)")
    50  	})
    51  
    52  	t.Run("throws error on open failure", func(t *testing.T) {
    53  		// Setup in-memory fs
    54  		fsys := &fstest.OpenErrorFs{DenyPath: utils.MigrationsDir}
    55  		// Setup mock postgres
    56  		conn := pgtest.NewConn()
    57  		defer conn.Close(t)
    58  		conn.Query(LIST_MIGRATION_VERSION).
    59  			Reply("SELECT 0")
    60  		// Run test
    61  		err := Run(context.Background(), dbConfig, fsys, conn.Intercept)
    62  		// Check error
    63  		assert.ErrorIs(t, err, os.ErrPermission)
    64  	})
    65  }
    66  
    67  func TestRemoteMigrations(t *testing.T) {
    68  	t.Run("loads migration versions", func(t *testing.T) {
    69  		// Setup mock postgres
    70  		conn := pgtest.NewConn()
    71  		defer conn.Close(t)
    72  		conn.Query(LIST_MIGRATION_VERSION).
    73  			Reply("SELECT 1", []interface{}{"20220727064247"})
    74  		// Run test
    75  		versions, err := loadRemoteVersions(context.Background(), dbConfig, conn.Intercept)
    76  		// Check error
    77  		assert.NoError(t, err)
    78  		assert.ElementsMatch(t, []string{"20220727064247"}, versions)
    79  	})
    80  
    81  	t.Run("throws error on connect failure", func(t *testing.T) {
    82  		// Run test
    83  		_, err := loadRemoteVersions(context.Background(), pgconn.Config{})
    84  		// Check error
    85  		assert.ErrorContains(t, err, "invalid port (outside range)")
    86  	})
    87  
    88  	t.Run("loads empty migrations on missing table", func(t *testing.T) {
    89  		// Setup mock postgres
    90  		conn := pgtest.NewConn()
    91  		defer conn.Close(t)
    92  		conn.Query(LIST_MIGRATION_VERSION).
    93  			ReplyError(pgerrcode.UndefinedTable, "relation \"supabase_migrations.schema_migrations\" does not exist")
    94  		// Run test
    95  		versions, err := loadRemoteVersions(context.Background(), dbConfig, conn.Intercept)
    96  		// Check error
    97  		assert.NoError(t, err)
    98  		assert.Empty(t, versions)
    99  	})
   100  
   101  	t.Run("throws error on invalid row", func(t *testing.T) {
   102  		// Setup mock postgres
   103  		conn := pgtest.NewConn()
   104  		defer conn.Close(t)
   105  		conn.Query(LIST_MIGRATION_VERSION).
   106  			Reply("SELECT 1", []interface{}{})
   107  		// Run test
   108  		_, err := loadRemoteVersions(context.Background(), dbConfig, conn.Intercept)
   109  		// Check error
   110  		assert.ErrorContains(t, err, "number of field descriptions must equal number of destinations, got 0 and 1")
   111  	})
   112  }
   113  
   114  func TestLocalMigrations(t *testing.T) {
   115  	t.Run("loads migration versions", func(t *testing.T) {
   116  		// Setup in-memory fs
   117  		fsys := afero.NewMemMapFs()
   118  		path := filepath.Join(utils.MigrationsDir, "20220727064246_test.sql")
   119  		require.NoError(t, afero.WriteFile(fsys, path, []byte{}, 0644))
   120  		path = filepath.Join(utils.MigrationsDir, "20220727064248_test.sql")
   121  		require.NoError(t, afero.WriteFile(fsys, path, []byte{}, 0644))
   122  		// Run test
   123  		versions, err := LoadLocalVersions(fsys)
   124  		// Check error
   125  		assert.NoError(t, err)
   126  		assert.ElementsMatch(t, []string{"20220727064246", "20220727064248"}, versions)
   127  	})
   128  
   129  	t.Run("ignores outdated and invalid files", func(t *testing.T) {
   130  		// Setup in-memory fs
   131  		fsys := afero.NewMemMapFs()
   132  		path := filepath.Join(utils.MigrationsDir, "20211208000000_init.sql")
   133  		require.NoError(t, afero.WriteFile(fsys, path, []byte{}, 0644))
   134  		path = filepath.Join(utils.MigrationsDir, "20211208000001_invalid.ts")
   135  		require.NoError(t, afero.WriteFile(fsys, path, []byte{}, 0644))
   136  		// Run test
   137  		versions, err := LoadLocalVersions(fsys)
   138  		// Check error
   139  		assert.NoError(t, err)
   140  		assert.Empty(t, versions)
   141  	})
   142  
   143  	t.Run("throws error on open failure", func(t *testing.T) {
   144  		// Setup in-memory fs
   145  		fsys := &fstest.OpenErrorFs{DenyPath: utils.MigrationsDir}
   146  		// Run test
   147  		_, err := LoadLocalVersions(fsys)
   148  		// Check error
   149  		assert.ErrorIs(t, err, os.ErrPermission)
   150  	})
   151  }
   152  
   153  func TestMakeTable(t *testing.T) {
   154  	t.Run("tabulate version", func(t *testing.T) {
   155  		// Run test
   156  		table := makeTable([]string{"0", "2"}, []string{"0", "1"})
   157  		// Check error
   158  		lines := strings.Split(strings.TrimSpace(table), "\n")
   159  		assert.ElementsMatch(t, []string{
   160  			"|Local|Remote|Time (UTC)|",
   161  			"|-|-|-|",
   162  			"|`0`|`0`|`0`|",
   163  			"|`1`|` `|`1`|",
   164  			"|` `|`2`|`2`|",
   165  		}, lines)
   166  	})
   167  
   168  	t.Run("tabulate timestamp", func(t *testing.T) {
   169  		// Run test
   170  		table := makeTable([]string{"20220727064246", "20220727064248"}, []string{"20220727064246", "20220727064247"})
   171  		// Check error
   172  		lines := strings.Split(strings.TrimSpace(table), "\n")
   173  		assert.ElementsMatch(t, []string{
   174  			"|Local|Remote|Time (UTC)|",
   175  			"|-|-|-|",
   176  			"|`20220727064246`|`20220727064246`|`2022-07-27 06:42:46`|",
   177  			"|`20220727064247`|` `|`2022-07-27 06:42:47`|",
   178  			"|` `|`20220727064248`|`2022-07-27 06:42:48`|",
   179  		}, lines)
   180  	})
   181  
   182  	t.Run("ignores string values", func(t *testing.T) {
   183  		// Run test
   184  		table := makeTable([]string{"a", "c"}, []string{"a", "b"})
   185  		// Check error
   186  		lines := strings.Split(strings.TrimSpace(table), "\n")
   187  		assert.ElementsMatch(t, []string{
   188  			"|Local|Remote|Time (UTC)|",
   189  			"|-|-|-|",
   190  		}, lines)
   191  	})
   192  }