Skip to content

Commit 59d72a9

Browse files
authored
fix: allow specifying empty migrations (#21)
1 parent 0952e2c commit 59d72a9

File tree

3 files changed

+150
-7
lines changed

3 files changed

+150
-7
lines changed

fixture_test.go

+79
Original file line numberDiff line numberDiff line change
@@ -294,3 +294,82 @@ func createTestContextWithRSATokenAuth(t testing.TB) *TestContext {
294294
authTokenString,
295295
)
296296
}
297+
298+
type MigrationTestContext struct {
299+
migrator *dbMigrator
300+
db *sqlx.DB
301+
cleanUpDB func(t testing.TB)
302+
}
303+
304+
func (mtc *MigrationTestContext) Migrator() *dbMigrator {
305+
return mtc.migrator
306+
}
307+
308+
func (mtc *MigrationTestContext) CleanUp(t testing.TB) {
309+
if mtc.cleanUpDB != nil {
310+
mtc.cleanUpDB(t)
311+
}
312+
}
313+
314+
func NewMigrationTestContext(
315+
t testing.TB,
316+
migrations map[string]string,
317+
) *MigrationTestContext {
318+
t.Log("creating test dir")
319+
dir, err := os.MkdirTemp("", "sqlite-rest-test")
320+
if err != nil {
321+
t.Fatal(err)
322+
return nil
323+
}
324+
325+
migrationsDir := filepath.Join(dir, "migrations")
326+
if err := os.MkdirAll(migrationsDir, 0755); err != nil {
327+
t.Fatal(err)
328+
return nil
329+
}
330+
331+
t.Log("writing migrations")
332+
for filename, content := range migrations {
333+
p := filepath.Join(migrationsDir, filename)
334+
if err := os.WriteFile(p, []byte(content), 0644); err != nil {
335+
t.Fatal(err)
336+
return nil
337+
}
338+
}
339+
340+
t.Log("craeting in-memory db")
341+
db, err := sqlx.Open("sqlite3", "")
342+
if err != nil {
343+
t.Fatal(err)
344+
return nil
345+
}
346+
347+
t.Log("creating migrator")
348+
migratorOpts := &MigrateOptions{
349+
Logger: createTestLogger(t).WithName("test"),
350+
DB: db.DB,
351+
SourceDIR: migrationsDir,
352+
}
353+
354+
migrator, err := NewMigrator(migratorOpts)
355+
if err != nil {
356+
t.Fatal(err)
357+
return nil
358+
}
359+
360+
return &MigrationTestContext{
361+
migrator: migrator,
362+
db: db,
363+
cleanUpDB: func(t testing.TB) {
364+
if err := db.Close(); err != nil {
365+
t.Errorf("closing in-memory db: %s", err)
366+
return
367+
}
368+
369+
if err := os.RemoveAll(dir); err != nil {
370+
t.Fatalf("removing test dir %q: %s", dir, err)
371+
return
372+
}
373+
},
374+
}
375+
}

integration_migrate_test.go

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"testing"
6+
"time"
7+
8+
"github.com/stretchr/testify/assert"
9+
)
10+
11+
func TestMigration(t *testing.T) {
12+
t.Run("empty migrations", func(t *testing.T) {
13+
tc := NewMigrationTestContext(t, nil)
14+
defer tc.CleanUp(t)
15+
16+
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
17+
defer cancel()
18+
19+
err := tc.Migrator().Up(ctx)
20+
assert.NoError(t, err)
21+
})
22+
23+
t.Run("apply migrations", func(t *testing.T) {
24+
tc := NewMigrationTestContext(t, map[string]string{
25+
"1_test.up.sql": `create table test (id int);`,
26+
"1_test.down.sql": `drop table test;`,
27+
})
28+
defer tc.CleanUp(t)
29+
30+
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
31+
defer cancel()
32+
33+
err := tc.Migrator().Up(ctx)
34+
assert.NoError(t, err)
35+
36+
t.Log("rerunning migrations")
37+
err = tc.Migrator().Up(ctx)
38+
assert.NoError(t, err)
39+
})
40+
41+
t.Run("failed migrations", func(t *testing.T) {
42+
tc := NewMigrationTestContext(t, map[string]string{
43+
"1_test.up.sql": `create table test invalid sql;`,
44+
"1_test.down.sql": `drop table test;`,
45+
})
46+
defer tc.CleanUp(t)
47+
48+
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
49+
defer cancel()
50+
51+
err := tc.Migrator().Up(ctx)
52+
assert.Error(t, err)
53+
})
54+
}

migrate.go

+17-7
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"database/sql"
66
"errors"
77
"fmt"
8+
"io/fs"
89
"os"
910
"path/filepath"
1011

@@ -112,7 +113,6 @@ func NewMigrator(opts *MigrateOptions) (*dbMigrator, error) {
112113
if err != nil {
113114
return nil, err
114115
}
115-
fmt.Println("file://" + opts.SourceDIR)
116116
migrator, err := migrate.NewWithDatabaseInstance(
117117
"file://"+opts.SourceDIR,
118118
"sqlite3", driver,
@@ -134,17 +134,27 @@ func (m *dbMigrator) Up(ctx context.Context) error {
134134
logger.Info("applying operation")
135135

136136
err := m.migrator.Up()
137+
if err == nil {
138+
logger.Info("applied operation")
139+
140+
return nil
141+
}
142+
137143
if errors.Is(err, migrate.ErrNoChange) {
138144
// no update
139-
err = nil
140145
logger.V(8).Info("no pending migrations")
146+
return nil
141147
}
142-
if err != nil {
143148

144-
logger.Error(err, "failed to apply operation")
145-
return fmt.Errorf("up: %w", err)
149+
var pathErr *fs.PathError
150+
if errors.As(err, &pathErr) {
151+
// no migrations set
152+
if pathErr.Op == "first" && errors.Is(pathErr.Err, fs.ErrNotExist) {
153+
logger.Info("no migrations to apply")
154+
return nil
155+
}
146156
}
147-
logger.Info("applied operation")
148157

149-
return nil
158+
logger.Error(err, "failed to apply operation")
159+
return fmt.Errorf("up: %w", err)
150160
}

0 commit comments

Comments
 (0)