mysqlbox

package module
v0.4.8 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 11, 2023 License: MIT Imports: 24 Imported by: 0

README

mysqlbox

MySQLBox logo

mysqlbox creates a ready to use MySQL server in a Docker container that can be used in Go tests. The Start() function returns a MySQLBox that has a container running MySQL server. It has a Stop() function that stops the container. The DB() function returns a connected sql.DB that can be used to send queries to MySQL.

Install mysqlbox
go get github.com/virgild/mysqlbox
Basic usage
package mytests

import (
	"testing"
	"time"

	"github.com/virgild/mysqlbox"
)

func TestMyCode(t *testing.T) {
	// Start MySQL container
	box, err := mysqlbox.Start(&mysqlbox.Config{})
	if err != nil {
	    t.Fatal(err)
	}

	// Register the stop func to stop the container after the test.
	t.Cleanup(func() {
	    err := box.Stop()
	    if err != nil {
	        t.Fatal(err)
	    }
	})
	
	// Use the box's sql.DB object to query the database.
	row := box.MustDB().QueryRow("SELECT NOW()")
	
	var now time.Time
	err = row.Scan(&row)
	if err != nil {
	    t.Error(err)
	}
	
	if now.IsZero() {
	    t.Error("now is zero")
	}
}
Other Features
Initial script

MySQL server can be started with an initial SQL script that is run after the service starts. It can be provided as an io.Reader or a []byte buffer.

Specifying the initial script from a file/reader
schemaFile, err := os.Open("testdata/schema.sql")
if err != nil {
    t.Fatal(err)
}
defer schemaFile.Close()

box, err = mysqlbox.Start(&Config{
    InitialSQL: mysqlbox.DataFromReader(schemaFile),
}
if err != nil {
    t.Fatal(err)
}

t.Cleanup(func() {
    err := b.Stop()
    if err != nil {
        t.Fatal(err)
    }
})
Specifying the initial script from a byte buffer
sql := []byte(`
	CREATE TABLE users
	(
		id         varchar(128) NOT NULL,
		email      varchar(128) NOT NULL,
		created_at datetime     NOT NULL,
		updated_at datetime     NOT NULL,
		PRIMARY KEY (id),
		UNIQUE KEY users_email_uindex (email),
		UNIQUE KEY users_id_uindex (id)
	) ENGINE = InnoDB
	DEFAULT CHARSET = utf8mb4;
`)

b, err := mysqlbox.Start(&Config{
	InitialSQL: mysqlbox.DataFromBuffer(sql),
})
if err != nil {
	t.Fatal(err)
}
t.Cleanup(func() {
	err := b.Stop()
	if err != nil {
		t.Fatal(err)
	}
})

query := "INSERT INTO users (id, email, created_at, updated_at) VALUES (?, ?, ?, ?)"
now := time.Now()
_, err = b.DB().Exec(query, "U-TEST1", "user1@example.com", now, now)
if err != nil {
	t.Error(err)
}
Cleaning tables

All tables can be truncated by calling CleanAllTables(). This runs TRUNCATE on all tables in the database, except for those specified in the Config.DoNotCleanTables array. Another function called CleanTables() can be used to truncate just specific tables you want to clean. Any table passed to CleanTables() will always truncate it even if it is included in the DoNotCleanTables list.

Using MySQLBox outside tests

It is not recommended to use MySQLBox as a normal MySQL database. This component is designed to be ephemeral, and no precautions are implemented to protect the database data.

Troubleshooting and other notes
  • I forgot to call Stop() and now I have a several containers that are still running.

    The following command can be run to stop the containers started by MySQLBox.

    docker ps -a -f "label=com.github.virgild.mysqlbox" --format '{{.ID}}' | xargs docker stop
    

Documentation

Overview

Package mysqlbox provides MySQLBox, a utility for starting a MySQL server in a Docker container.

It creates a ready to use MySQL server in a Docker container that can be used in tests. The Start() function returns a MySQLBox that has a running MySQL server. It has a Stop() function that stops the container. The DB() function returns a connected sql.DB that can be used to send queries to MySQL.

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// ErrTimeout represents a timeout in an operation.
	ErrTimeout = errors.New("operation timed out")
)

Functions

This section is empty.

Types

type Config

type Config struct {
	// ContainerName specifies the MySQL container name. If blank, it will be generated as "mysqlbox-<random name>".
	ContainerName string

	// Image specifies what Docker image to use. If blank, it defaults to "mysql:8".
	Image string

	// Database specifies the name of the database to create. If blank, it defaults to "testing".
	Database string

	// RootPassword specifies the password of the MySQL root user.
	RootPassword string

	// MySQLPort specifies which port the MySQL server port (3306) will be bound to in the container.
	MySQLPort int

	// InitialSQL specifies an SQL script stored in a file or a buffer that will be run against the Database
	// when the MySQL server container is started.
	InitialSQL *Data

	// DoNotCleanTables specifies a list of MySQL tables in Database that will not be cleaned when CleanAllTables()
	// is called.
	DoNotCleanTables []string

	// Stdout is an optional writer where the container log stdout will be sent to.
	Stdout io.Writer
	// Stderr is an optional writer where the container log stderr will be sent to.
	Stderr io.Writer

	// LoggedErrors is an optional list of strings that will contain error messages from the container stderr logs.
	LoggedErrors *[]string

	// StartTimeout is the maximum time to wait for the container to start and MySQL ready to accept connections.
	// The default is 30 seconds.
	StartTimeout time.Duration

	// StopTimeout is the amount of time to wait for the container to gracefully stop when Stop() is called.
	// When the timeout is reached, the container is forcefully stopped.
	StopTimeout time.Duration
}

Config contains MySQLBox settings.

func (*Config) LoadDefaults

func (c *Config) LoadDefaults()

LoadDefaults initializes some blank attributes of Config to default values.

type Data

type Data struct {
	// contains filtered or unexported fields
}

Data contains data.

func DataFromBuffer

func DataFromBuffer(buf []byte) *Data

DataFromBuffer can be used to load data from a byte array.

func DataFromFile added in v0.4.0

func DataFromFile(filename string) *Data

DataFromFile can be used to load data from a file.

func DataFromReader

func DataFromReader(reader io.Reader) *Data

DataFromReader can be used to load data from a reader object.

type MySQLBox

type MySQLBox struct {
	// contains filtered or unexported fields
}

MySQLBox is an interface to a MySQL server running in a Docker container.

func Start

func Start(c *Config) (*MySQLBox, error)

Start creates a Docker container that runs an instance of MySQL server. The passed Config object contains settings for the container, the MySQL service, and initial data. To stop the created container, call the Stop() method. Start() returns an ErrTimeout if the container MySQL service cannot accept connections within the timeout period (see Config.StartTimeout). When ErrTimeout is returned, the container is still running and an instance of MySQLBox is returned along with the error.

Example
package main

import (
	"log"

	"github.com/virgild/mysqlbox"
)

func main() {
	// Start the MySQL server container
	b, err := mysqlbox.Start(&mysqlbox.Config{})
	if err != nil {
		log.Printf("MySQLBox failed to start: %s\n", err.Error())
		return
	}

	// Query the database
	db, err := b.DB()
	if err != nil {
		log.Printf("db error: %s\n", err.Error())
		return
	}
	_, err = db.Query("SELECT * FROM users LIMIT 5")
	if err != nil {
		log.Printf("select failed: %s\n", err.Error())
		return
	}

	// Stop the container
	err = b.Stop()
	if err != nil {
		log.Printf("stop container failed: %s\n", err.Error())
	}
}
Output:

func (*MySQLBox) CleanAllTables

func (b *MySQLBox) CleanAllTables() error

CleanAllTables truncates all tables in the Database, except those provided in Config.DoNotCleanTables.

func (*MySQLBox) CleanTables

func (b *MySQLBox) CleanTables(tables ...string) error

CleanTables truncates the specified tables in the Database.

func (*MySQLBox) ConnectDB added in v0.4.2

func (b *MySQLBox) ConnectDB(dbname string) (*sql.DB, string, error)

ConnectDB returns a DB connection and the DSN for the specified database.

func (*MySQLBox) ContainerName

func (b *MySQLBox) ContainerName() (string, error)

ContainerName returns the name of the created container.

func (*MySQLBox) DB

func (b *MySQLBox) DB() (*sql.DB, error)

DB returns an sql.DB connected to the running MySQL server.

func (*MySQLBox) DBAddr added in v0.4.2

func (b *MySQLBox) DBAddr() string

DBAddr returns the container's MySQL address.

func (*MySQLBox) DSN added in v0.3.1

func (b *MySQLBox) DSN() (string, error)

DSN returns the MySQL database DSN that can be used to connect to the MySQL service.

func (*MySQLBox) MustCleanAllTables

func (b *MySQLBox) MustCleanAllTables()

MustCleanAllTables truncates all tables in the Database, except those provided in Config.DoNotCleanTables.

func (*MySQLBox) MustCleanTables

func (b *MySQLBox) MustCleanTables(tables ...string)

MustCleanTables truncates the specified tables in the Database.

func (*MySQLBox) MustContainerName

func (b *MySQLBox) MustContainerName() string

MustContainerName returns the name of the created container.

func (*MySQLBox) MustDB

func (b *MySQLBox) MustDB() *sql.DB

MustDB returns an sql.DB conencted to the running MySQL server.

func (*MySQLBox) MustDSN added in v0.3.1

func (b *MySQLBox) MustDSN() string

MustDSN returns the MySQL database DSN that can be used to connect to the MySQL service.

func (*MySQLBox) MustStart added in v0.4.2

func (b *MySQLBox) MustStart(c *Config) *MySQLBox

MustStart is the same as Start() but panics instead of returning an error.

func (*MySQLBox) MustStop

func (b *MySQLBox) MustStop()

MustStop stops the MySQL container.

func (*MySQLBox) RootPassword added in v0.4.2

func (b *MySQLBox) RootPassword() string

RootPassword returns the MySQL root user password.

func (*MySQLBox) Stop

func (b *MySQLBox) Stop() error

Stop stops the MySQL container.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL