-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(status): Add status command (#29)
- Loading branch information
1 parent
71d2432
commit 6fa28e1
Showing
5 changed files
with
195 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
package migrations | ||
|
||
import ( | ||
"bytes" | ||
"fmt" | ||
"io" | ||
"sort" | ||
"strings" | ||
"unicode/utf8" | ||
|
||
"github.com/go-pg/pg/v10" | ||
) | ||
|
||
type migrationWithStatus struct { | ||
migration | ||
} | ||
|
||
func status(db *pg.DB, w io.Writer) error { | ||
// sort the registered migrations by name (which will sort by the | ||
// timestamp in their names) | ||
sort.Slice(migrations, func(i, j int) bool { | ||
return migrations[i].Name < migrations[j].Name | ||
}) | ||
|
||
// look at the migrations table to see the already run migrations | ||
completed, err := getCompletedMigrations(db) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// diff the completed migrations from the registered migrations to find | ||
// the migrations we still need to run | ||
uncompleted := filterMigrations(migrations, completed, false) | ||
|
||
return writeStatusTable(w, completed, uncompleted) | ||
Check failure on line 35 in status.go GitHub Actions / build
Check failure on line 35 in status.go GitHub Actions / build
Check failure on line 35 in status.go GitHub Actions / build
|
||
} | ||
|
||
func writeStatusTable(w io.Writer, completed []migration, uncompleted []migration) error { | ||
if len(completed)+len(uncompleted) == 0 { | ||
_, err := fmt.Fprintln(w, "No migrations found") | ||
return err | ||
} | ||
|
||
maxNameLength := 20 | ||
for _, m := range completed { | ||
maxNameLength = maxInt(maxNameLength, utf8.RuneCountInString(m.Name)) | ||
} | ||
for _, m := range uncompleted { | ||
maxNameLength = maxInt(maxNameLength, utf8.RuneCountInString(m.Name)) | ||
} | ||
|
||
bf := bytes.NewBuffer(nil) | ||
|
||
// write header | ||
bf.WriteString("+---------+" + strings.Repeat("-", maxNameLength+2) + "+-------+\n") | ||
bf.WriteString("| Applied | Migration" + strings.Repeat(" ", maxNameLength-8) + "| Batch |\n") | ||
bf.WriteString("+---------+" + strings.Repeat("-", maxNameLength+2) + "+-------+\n") | ||
|
||
// write completed migrations | ||
for _, m := range completed { | ||
bf.WriteString("| √ | " + m.Name + strings.Repeat(" ", maxNameLength-len(m.Name)) + " | " + fmt.Sprintf("%5d", m.Batch) + " |\n") | ||
} | ||
|
||
// write uncompleted migrations | ||
for _, m := range uncompleted { | ||
bf.WriteString("| | " + m.Name + strings.Repeat(" ", maxNameLength-len(m.Name)) + " | |\n") | ||
} | ||
|
||
// write footer | ||
bf.WriteString("+---------+" + strings.Repeat("-", maxNameLength+2) + "+-------+\n") | ||
|
||
_, err := bf.WriteTo(w) | ||
return err | ||
} | ||
|
||
func maxInt(a, b int) int { | ||
if a > b { | ||
return a | ||
} else { | ||
return b | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
package migrations | ||
|
||
import ( | ||
"bytes" | ||
"os" | ||
"strings" | ||
"testing" | ||
|
||
"github.com/go-pg/pg/v10" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestStatus(t *testing.T) { | ||
db := pg.Connect(&pg.Options{ | ||
Addr: "localhost:5432", | ||
User: os.Getenv("TEST_DATABASE_USER"), | ||
Database: os.Getenv("TEST_DATABASE_NAME"), | ||
}) | ||
|
||
db.AddQueryHook(logQueryHook{}) | ||
|
||
err := ensureMigrationTables(db) | ||
require.Nil(t, err) | ||
|
||
defer clearMigrations(t, db) | ||
defer resetMigrations(t) | ||
|
||
completed := []migration{ | ||
{Name: "2021_02_26_151503_dump", Up: noopMigration, Down: noopMigration, Batch: 1}, | ||
{Name: "2021_02_26_151504_create_a_dump_table_for_test", Up: noopMigration, Down: noopMigration, Batch: 2}, | ||
} | ||
uncompleted := []migration{ | ||
{Name: "2021_02_26_151502_create_2nd_dump_table", Up: noopMigration, Down: noopMigration}, | ||
{Name: "2021_02_26_151505_create_3rd_dump_table", Up: noopMigration, Down: noopMigration}, | ||
} | ||
expected := strings.TrimSpace(` | ||
+---------+------------------------------------------------+-------+ | ||
| Applied | Migration | Batch | | ||
+---------+------------------------------------------------+-------+ | ||
| √ | 2021_02_26_151503_dump | 1 | | ||
| √ | 2021_02_26_151504_create_a_dump_table_for_test | 2 | | ||
| | 2021_02_26_151502_create_2nd_dump_table | | | ||
| | 2021_02_26_151505_create_3rd_dump_table | | | ||
+---------+------------------------------------------------+-------+ | ||
`) | ||
|
||
t.Run("status_command", func(tt *testing.T) { | ||
clearMigrations(tt, db) | ||
resetMigrations(tt) | ||
|
||
migrations = completed[:1] | ||
err := migrate(db) | ||
require.Nil(tt, err, "migrate: %v", err) | ||
migrations = completed[:2] | ||
err = migrate(db) | ||
require.Nil(tt, err, "migrate: %v", err) | ||
|
||
migrations = append(migrations, uncompleted...) | ||
bf := bytes.NewBuffer(nil) | ||
err = status(db, bf) | ||
require.Nil(tt, err, "status: %v", err) | ||
|
||
got := strings.TrimSpace(bf.String()) | ||
if got != expected { | ||
tt.Errorf("status table not match:\nEXPECTED:\n%s\nACTUAL:\n%s", expected, got) | ||
} | ||
}) | ||
|
||
t.Run("write_status_table", func(tt *testing.T) { | ||
bf := bytes.NewBuffer(nil) | ||
err := writeStatusTable(bf, completed, uncompleted) | ||
require.Nil(tt, err, "write_status_table: %v", err) | ||
|
||
got := strings.TrimSpace(bf.String()) | ||
if got != expected { | ||
tt.Errorf("status table not match:\nEXPECTED:\n%s\nACTUAL:\n%s", expected, got) | ||
} | ||
}) | ||
|
||
t.Run("no_migrations_found", func(tt *testing.T) { | ||
expected := strings.TrimSpace(`No migrations found`) | ||
|
||
bf := bytes.NewBuffer(nil) | ||
err := writeStatusTable(bf, nil, nil) | ||
require.Nil(tt, err, "write_status_table: %v", err) | ||
|
||
got := strings.TrimSpace(bf.String()) | ||
if got != expected { | ||
tt.Errorf("status table not match:\nEXPECTED:\n%s\nACTUAL:\n%s", expected, got) | ||
} | ||
}) | ||
} |