100 lines
2.9 KiB
Go
100 lines
2.9 KiB
Go
|
|
package store
|
||
|
|
|
||
|
|
import (
|
||
|
|
"strings"
|
||
|
|
"testing"
|
||
|
|
"testing/fstest"
|
||
|
|
)
|
||
|
|
|
||
|
|
func TestParseMigrationVersion(t *testing.T) {
|
||
|
|
cases := []struct {
|
||
|
|
name string
|
||
|
|
want int
|
||
|
|
wantErr string
|
||
|
|
}{
|
||
|
|
{"0001_initial.sql", 1, ""},
|
||
|
|
{"0042_add_oauth_clients.sql", 42, ""},
|
||
|
|
{"0001.sql", 0, "expected NNNN_name.sql"}, // no underscore
|
||
|
|
{"_orphan.sql", 0, "expected NNNN_name.sql"}, // empty version
|
||
|
|
{"abc_notnumeric.sql", 0, "non-numeric version"}, // not a number
|
||
|
|
{"0000_zero.sql", 0, "version must be > 0"}, // zero rejected
|
||
|
|
}
|
||
|
|
for _, tc := range cases {
|
||
|
|
t.Run(tc.name, func(t *testing.T) {
|
||
|
|
got, err := parseMigrationVersion(tc.name)
|
||
|
|
switch {
|
||
|
|
case tc.wantErr == "" && err != nil:
|
||
|
|
t.Errorf("unexpected error: %v", err)
|
||
|
|
case tc.wantErr != "" && err == nil:
|
||
|
|
t.Errorf("expected error containing %q, got nil", tc.wantErr)
|
||
|
|
case tc.wantErr != "" && !strings.Contains(err.Error(), tc.wantErr):
|
||
|
|
t.Errorf("error %q does not contain %q", err, tc.wantErr)
|
||
|
|
case tc.wantErr == "" && got != tc.want:
|
||
|
|
t.Errorf("got %d, want %d", got, tc.want)
|
||
|
|
}
|
||
|
|
})
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestLoadMigrations_SortsByVersion(t *testing.T) {
|
||
|
|
fsys := fstest.MapFS{
|
||
|
|
"m/0003_c.sql": {Data: []byte("-- c")},
|
||
|
|
"m/0001_a.sql": {Data: []byte("-- a")},
|
||
|
|
"m/0002_b.sql": {Data: []byte("-- b")},
|
||
|
|
}
|
||
|
|
got, err := loadMigrations(fsys, "m")
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("unexpected error: %v", err)
|
||
|
|
}
|
||
|
|
if len(got) != 3 {
|
||
|
|
t.Fatalf("got %d migrations, want 3", len(got))
|
||
|
|
}
|
||
|
|
for i, want := range []int{1, 2, 3} {
|
||
|
|
if got[i].version != want {
|
||
|
|
t.Errorf("migrations[%d].version = %d, want %d", i, got[i].version, want)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestLoadMigrations_SkipsNonSQL(t *testing.T) {
|
||
|
|
fsys := fstest.MapFS{
|
||
|
|
"m/0001_a.sql": {Data: []byte("-- a")},
|
||
|
|
"m/README.md": {Data: []byte("ignore me")},
|
||
|
|
"m/notes.txt": {Data: []byte("ignore me too")},
|
||
|
|
}
|
||
|
|
got, err := loadMigrations(fsys, "m")
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("unexpected error: %v", err)
|
||
|
|
}
|
||
|
|
if len(got) != 1 {
|
||
|
|
t.Errorf("got %d migrations, want 1 (non-.sql must be skipped)", len(got))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestLoadMigrations_RejectsDuplicateVersion(t *testing.T) {
|
||
|
|
fsys := fstest.MapFS{
|
||
|
|
"m/0001_a.sql": {Data: []byte("-- a")},
|
||
|
|
"m/0001_b.sql": {Data: []byte("-- b")},
|
||
|
|
}
|
||
|
|
_, err := loadMigrations(fsys, "m")
|
||
|
|
if err == nil || !strings.Contains(err.Error(), "duplicate migration version 1") {
|
||
|
|
t.Errorf("want duplicate-version error, got %v", err)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestLoadMigrations_RejectsBadFilename(t *testing.T) {
|
||
|
|
fsys := fstest.MapFS{
|
||
|
|
"m/nogood.sql": {Data: []byte("-- bad")},
|
||
|
|
}
|
||
|
|
_, err := loadMigrations(fsys, "m")
|
||
|
|
if err == nil || !strings.Contains(err.Error(), "expected NNNN_name.sql") {
|
||
|
|
t.Errorf("want bad-filename error, got %v", err)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestLoadMigrations_MissingDir(t *testing.T) {
|
||
|
|
_, err := loadMigrations(fstest.MapFS{}, "nope")
|
||
|
|
if err == nil || !strings.Contains(err.Error(), "read migrations dir") {
|
||
|
|
t.Errorf("want missing-dir error, got %v", err)
|
||
|
|
}
|
||
|
|
}
|