Skip to content

Instantly share code, notes, and snippets.

@CannibalVox
Last active April 18, 2022 19:25
Show Gist options
  • Save CannibalVox/51a2bfe1371f3e470acf0a25852a0c1b to your computer and use it in GitHub Desktop.
Save CannibalVox/51a2bfe1371f3e470acf0a25852a0c1b to your computer and use it in GitHub Desktop.
Spin up db for automated testing
package queries
import (
"context"
"database/sql"
"flag"
"fmt"
"github.com/Cannibalvox/project/data"
"github.com/google/uuid"
"github.com/ory/dockertest/v3"
"github.com/ory/dockertest/v3/docker"
"github.com/pressly/goose"
"go.uber.org/zap"
"os"
"runtime"
"testing"
)
var dbCluster *data.DatabaseCluster
var queries SQLQueries
// TestMain overrides the main method for testing this package- we do this to spin up
// a postgres database in docker so that we can do automated tests against a clean database.
func TestMain(m *testing.M) {
flag.Parse()
if testing.Short() {
return
}
logger, _ := zap.NewDevelopment()
sugaredLogger := logger.Sugar()
defer func() {
_ = logger.Sync()
}()
sugaredLogger.Info("Setting up data integration test rig!")
sugaredLogger.Info("Loading docker pool...")
poolTarget := ""
if runtime.GOOS == "windows" {
poolTarget = "npipe:////./pipe/docker_engine"
}
pool, err := dockertest.NewPool(poolTarget)
if err != nil {
sugaredLogger.Fatalw("could not connect to docker", "error", err)
}
sugaredLogger.Info("Creating postgres resource...")
resource, err := pool.RunWithOptions(
&dockertest.RunOptions{
Repository: "postgres",
Tag: "14-alpine",
Env: []string{"POSTGRES_HOST_AUTH_METHOD=trust"},
}, func(config *docker.HostConfig) {
config.AutoRemove = true
config.RestartPolicy = docker.RestartPolicy{Name: "no"}
})
if err != nil {
sugaredLogger.Fatalw("could not start resource", "error", err)
}
var exitCode int
defer func() {
purgeResource(sugaredLogger, pool, resource)
if exitCode != 0 {
os.Exit(exitCode)
}
}()
localhost := "localhost"
dockerHost := os.Getenv("TEST_DOCKER_HOST")
if dockerHost != "" {
localhost = dockerHost
}
connStr := fmt.Sprintf("postgres://postgres@%s:%s?sslmode=disable", localhost, resource.GetPort("5432/tcp"))
// exponential backoff/retry, we need to wait for the container to be ready
err = pool.Retry(func() error {
db, err := sql.Open("postgres", connStr)
if err != nil {
return err
}
return db.Ping()
})
if err != nil {
sugaredLogger.Fatalw("could not connect to docker", "error", err)
}
sugaredLogger.Info("Resource online!")
err = resource.Expire(60)
if err != nil {
sugaredLogger.Fatalw("could not set expiration on resource", "error", err)
}
dbCluster, err = data.NewDatabaseCluster(context.Background(), sugaredLogger, "postgres", connStr, "")
if err != nil {
sugaredLogger.Fatalw("error building cluster object", "error", err)
}
sugaredLogger.Info("Running goose migrations...")
err = goose.Up(dbCluster.Writer(), "../../migrations")
if err != nil {
sugaredLogger.Fatalw("error running goose migrations", "error", err)
}
queries = SQLQueries{Logger: sugaredLogger}
// The database migrations add a test admin for functional testing, but it doesn't help us here
testAdminID, err := uuid.Parse("3d46d293-c1f9-4376-8d26-e30031c0f310")
if err != nil {
sugaredLogger.Fatalw("error parsing test admin id", "id", "3d46d293-c1f9-4376-8d26-e30031c0f310", "error", err)
}
err = queries.DeleteUserByID(context.Background(), dbCluster.Writer(), testAdminID)
if err != nil {
sugaredLogger.Fatalw("error deleting test admin", "error", err)
}
sugaredLogger.Info("Database ready for testing!")
exitCode = m.Run()
}
func purgeResource(logger *zap.SugaredLogger, pool *dockertest.Pool, resource *dockertest.Resource) {
err := pool.Purge(resource)
if err != nil {
logger.Fatalw("could not purge resource after test", "error", err)
}
logger.Info("Database successfully decommissioned!")
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment