Skip to content

Commit

Permalink
test: Clean up tests, improve coverage
Browse files Browse the repository at this point in the history
I have been looking at code coverage reports and was surprised at what I
saw. For instance, the source code at godfish.go has most of the core
functionality for this project and its coverage was 3%! The test
coverage is actually a little better than that because those tests live
in package ./internal/test. The quickest way to make that look better
with the native golang code coverage tooling is to run the stub driver
tests twice.

Actually improve code coverage for some areas, like the exported funcs
in godfish.go and some helper functionality in package internal. This is
mostly to test error handling behavior and API boundaries.

Refactor ParseMigration tests
Change package from internal to internal_test. This makes the filename,
migration tests more consistent with all the other tests in this
project. Before, it was package internal because it was constructing a
literal mutation value. Though, this is unnecessary.
Use subtests, rather than table-driven tests. These are easier to
isolate if a test fails.

Make stub driver tests easier to set up. Now that the go.mod file
specifies v1.17, don't need to pass in a DB_DSN env var each time.
That was getting annoying.

refactor(stub): Remove error fields from stub Driver implementation

These error fields aren't of use right now. In the future, might want to
expand this type to return a different error for specific methods. This
could help test error handling cases that are otherwise hard to set up.

refactor(stub): Remove Version implementation

A stubbed version is overkill. When looking closer at what's using the
stub package, most tests just use the actual version implementation.
Adjust remaining callers to use the real Version implementation.

refactor: Replace unused unexported error type, runMigrationError
  • Loading branch information
rafaelespinoza committed Feb 28, 2022
1 parent 2982a55 commit 13adafc
Show file tree
Hide file tree
Showing 11 changed files with 393 additions and 198 deletions.
19 changes: 1 addition & 18 deletions godfish.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,19 +162,6 @@ func figureOutBasename(directoryPath string, direction internal.Direction, versi
return
}

type runMigrationError struct {
driverName string
originalError error
path string
}

func (e *runMigrationError) Error() string {
return fmt.Sprintf(
"driver: %q, path: %q, error: %v",
e.driverName, e.path, e.originalError,
)
}

// runMigration executes a migration against the database. The input, pathToFile
// should be relative to the current working directory.
func runMigration(driver Driver, pathToFile string, mig internal.Migration) (err error) {
Expand All @@ -189,11 +176,7 @@ func runMigration(driver Driver, pathToFile string, mig internal.Migration) (err
fmt.Fprintf(os.Stderr, "%s version %q ... ", gerund, mig.Version().String())

if err = driver.Execute(string(data)); err != nil {
err = &runMigrationError{
driverName: driver.Name(),
originalError: err,
path: pathToFile,
}
err = fmt.Errorf("%w, path_to_file: %q", err, pathToFile)
return
}
if err = driver.CreateSchemaMigrationsTable(); err != nil {
Expand Down
134 changes: 124 additions & 10 deletions godfish_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,134 @@ import (
"database/sql"
"encoding/json"
"os"
"strings"
"testing"

"github.com/rafaelespinoza/godfish"
"github.com/rafaelespinoza/godfish/internal"
"github.com/rafaelespinoza/godfish/internal/stub"
"github.com/rafaelespinoza/godfish/internal/test"
)

const baseTestOutputDir = "/tmp/godfish_test"
const (
baseTestOutputDir = "/tmp/godfish_test"
dsnKey = "DB_DSN"
)

func TestInit(t *testing.T) {
var err error
testOutputDir := baseTestOutputDir + "/" + t.Name()
if err = os.MkdirAll(testOutputDir, 0755); err != nil {
func makeTestDir(t *testing.T, basedir string) (outpath string) {
if basedir == "" {
basedir = os.TempDir()
}
err := os.MkdirAll(basedir, 0750)
if err != nil {
t.Fatal(err)
}

var pathToFile string
if outdirPath, err := os.MkdirTemp(testOutputDir, ""); err != nil {
outpath, err = os.MkdirTemp(basedir, strings.Replace(t.Name(), "/", "_", -1))
if err != nil {
t.Fatal(err)
} else {
pathToFile = outdirPath + "/config.json"
}
return
}

func TestCreateMigrationFiles(t *testing.T) {
t.Run("err", func(t *testing.T) {
testdir := makeTestDir(t, baseTestOutputDir)
err := godfish.CreateMigrationFiles("err_test", true, testdir, "bad", "bad2")
if err == nil {
t.Fatal(err)
}
})

t.Run("ok", func(t *testing.T) {
testdir := makeTestDir(t, "")
err := godfish.CreateMigrationFiles("err_test", true, testdir, "", "")
if err != nil {
t.Fatal(err)
}

entries, err := os.ReadDir(testdir)
if err != nil {
t.Fatal(err)
}
if len(entries) != 2 {
t.Fatalf("wrong number of entries, got %d, expected %d", len(entries), 2)
}

for i, direction := range []string{"forward", "reverse"} {
got := entries[i].Name()
if !strings.HasPrefix(got, direction) {
t.Errorf("expected filename, %q, to have prefix %q", got, direction)
}
if !strings.HasSuffix(got, "err_test.sql") {
t.Errorf("expected filename, %q, to have suffix %q", got, "err_test.sql")
}
}
})
}

func TestMigrate(t *testing.T) {
t.Run("missing DB_DSN", func(t *testing.T) {
t.Setenv(dsnKey, "")

testdir := makeTestDir(t, baseTestOutputDir)
err := godfish.Migrate(stub.NewDriver(), testdir, false, "")
if err == nil {
t.Fatalf("expected an error, got %v", err)
}
got := err.Error()
if !strings.Contains(got, dsnKey) {
t.Errorf("expected error message %q to mention %q", got, dsnKey)
}
})
}

func TestApplyMigration(t *testing.T) {
t.Run("missing DB_DSN", func(t *testing.T) {
t.Setenv(dsnKey, "")

testdir := makeTestDir(t, baseTestOutputDir)
err := godfish.ApplyMigration(stub.NewDriver(), testdir, false, "")
if err == nil {
t.Fatalf("expected an error, got %v", err)
}
got := err.Error()
if !strings.Contains(got, dsnKey) {
t.Errorf("expected error message %q to mention %q", got, dsnKey)
}
})
}

func TestInfo(t *testing.T) {
t.Run("missing DB_DSN", func(t *testing.T) {
t.Setenv(dsnKey, "")

testdir := makeTestDir(t, baseTestOutputDir)
err := godfish.Info(stub.NewDriver(), testdir, false, "", os.Stderr, "")
if err == nil {
t.Fatalf("expected an error, got %v", err)
}
got := err.Error()
if !strings.Contains(got, dsnKey) {
t.Errorf("expected error message %q to mention %q", got, dsnKey)
}
})

t.Run("unknown format does not error out", func(t *testing.T) {
t.Setenv(dsnKey, "test")

testdir := makeTestDir(t, baseTestOutputDir)
err := godfish.Info(stub.NewDriver(), testdir, false, "", os.Stderr, "tea_ess_vee")
if err != nil {
t.Fatalf("unexpected error, %v", err)
}
})
}

func TestInit(t *testing.T) {
var err error
testOutputDir := makeTestDir(t, "")

pathToFile := testOutputDir + "/config.json"

// setup: file should not exist at first
if _, err = os.Stat(pathToFile); !os.IsNotExist(err) {
Expand Down Expand Up @@ -78,3 +185,10 @@ func TestAppliedVersions(t *testing.T) {
t.Fatalf("expected %T to implement godfish.AppliedVersions", thing)
}
}

func TestDriver(t *testing.T) {
// These tests also run in the stub package. They are duplicated here to
// make test coverage tool consider the tests in godfish.go as covered.
t.Setenv("DB_DSN", "stub_dsn")
test.RunDriverTests(t, stub.NewDriver(), test.DefaultQueries)
}
Loading

0 comments on commit 13adafc

Please sign in to comment.