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 }