dft

package module
v0.0.3 Latest Latest
Warning

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

Go to latest
Published: Nov 2, 2024 License: Unlicense Imports: 9 Imported by: 0

README ¶

Go Report Card
Go Reference

🥼 DFT

Docker For Testing is a zero dependency wrapper around the docker command. It is solely based on the std lib.

Only requirement: A running docker daemon.

The package is intended to be used in various testing setups from local testing to CI/CD pipelines. It's main goals are to reduce the need for mocks (especially database ones), and to lower the amount of packages required for testing.

Containers can be spun up with options for ports, environment variables or [CMD] overwrites.

👓 Example

Testing a user service backed by mongoDB

package myawesomepkg_test

import (
	"context"
	"testing"
	"time"

	"my/awesome/pkg/repository"
	"my/awesome/pkg/user"

	"github.com/abecodes/dft"
)

func TestUserService(tt *testing.T) {
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	// start a mongoDB container
	ctr, err := dft.StartContainer(
		ctx,
		"mongo:7-jammy",
		dft.WithRandomPort(27017),
	)
	if err != nil {
		tt.Errorf("[dft.StartContainer] unexpected error: %v", err)
		tt.FailNow()

		return
	}

	// wait for the database
	err = ctr.WaitCmd(
		ctx,
		[]string{
			"mongosh",
			"--norc",
			"--quiet",
			"--host=localhost:27017",
			"--eval",
			"'db.getMongo()'",
		},
		func(stdOut string, stdErr string, code int) bool {
			tt.Logf("got:\n\tcode:%d\n\tout:%s\n\terr:%s\n", code, stdOut, stdErr)

			return code == 0
		},
		// since we use a random port in the example we want to execute the
		// command inside of the container
		dft.WithExecuteInsideContainer(true),
	)
	if err != nil {
		tt.Errorf("[dft.WaitCmd] wait error: %v", err)
		tt.FailNow()

		return
	}

	// let's make sure we clean up after us
	defer func() {
		if ctr != nil {
			ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
			ctr.Stop(ctx)
			cancel()
		}
	}()

	// since we use a random port in the example we want to know its
	// address on the host. Because we can expose an internal port on multiple host ports,
	// this method will return a list of addresses
	addrs, ok := ctr.ExposedPortAddr(27017)
	if !ok {
		tt.Error("[ctr.ExposedPortAddr] did not return any addresses")
		tt.FailNow()

		return
	}

	// get a connection
	conn := createNewMongoClient(addrs[0])

	// create a repo
	userRepo := repository.User(conn)

	// start the service
	userSvc := user.New(userRepo)
	defer userSvc.Close()

	tt.Run(
		"it can store a new user in the database",
		func(t *testing.T) {
			// create a new user
			userSvc.New("awesome", "user")

			// validate the write via the repository or DB client
			users := userRepo.GetAll()
			if len(users) != 1 &&
				users[0].FirstName != "awesome" &&
				users[0].LastName != "user" (
					t.Error("[userSvc.New] unable to create user")
					tt.FailNow()

					return
				)
		},
	)
}

🤖 API

Documentation

StartContainer options
Option Info Example
WithCmd Overwrite [CMD]. WithCmd([]string{"--tlsCAFile", "/run/tls/ca.crt"})
WithEnvVar Set an envvar inside the container.
Can be called multiple times.
If two options use the same key the latest one will overwrite existing ones.
WithEnvVar("intent", "prod")
WithMount Mount a local dir or file
Can be called multiple times.
WithMount("./host/folder", "/target")
WithPort Expose an internal port on a specific host port. WithPort(27017,8080)
WithRandomPort Expose an internal port on a random host port.
Use ExposedPorts or ExposedPortAddresses to get the correct host port.
WithRandomPort(27017)
Wait options
Option Info Example
WithExecuteInsideContainer If the given command should be executed inside of the container (default: false).
This is useful if we want to use a command only present in the container.
WithExecuteInsideContainer(true)

Documentation ¶

Overview ¶

Package dft (Docker For Testing) is a lightweight wrapper around docker based on the std lib.

Only requirement: A running docker daemon.

The package is intended to be used in various testing setups from local testing to CI/CD pipelines. It's main goals are to reduce the need for mocks (especially database ones), and to lower the amount of packages required for testing.

Containers can be spun up with options for ports, environment variables or [CMD] overwrites.

Index ¶

Constants ¶

This section is empty.

Variables ¶

This section is empty.

Functions ¶

This section is empty.

Types ¶

type Container ¶

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

func StartContainer ¶

func StartContainer(
	ctx context.Context,
	imageName string,
	opts ...ContainerOption,
) (*Container, error)

StartContainer tries to spin up a container for the given image. This may take a while if the given image is not present on the host since it will be pulled from the registry.

func (*Container) ExposedPortAddresses ¶

func (c *Container) ExposedPortAddresses(port uint) ([]string, bool)

ExposedPortAddresses will return a list of host ports exposing the internal port in the format of "<IP>:<PORT>"

func (*Container) ExposedPorts ¶

func (c *Container) ExposedPorts(port uint) ([]uint, bool)

ExposedPorts will return a list of host ports exposing the internal port

func (*Container) Logs ¶

func (c *Container) Logs(ctx context.Context) (string, error)

Logs will retrieve the latest logs from the container This call errors once `Stop` was called.

func (Container) Stop ¶

func (c Container) Stop(ctx context.Context) error

Stop will stop the container and remove it (as well as related volumes) from the host system

func (*Container) WaitCmd ¶

func (c *Container) WaitCmd(
	ctx context.Context,
	cmd []string,
	metCondition func(stdOut string, stdErr string, code int) bool,
	opts ...WaitOption,
) error

WaitCmd takes a command in the form of a ["<cmd>", "(<arg> | <-flag> | <flagvalue>)"...] which will be executed periodically until either it returns true or the context expires

type ContainerOption ¶

type ContainerOption func(cfg *containerCfg)

func WithCmd ¶

func WithCmd(args []string) ContainerOption

WithCmd overwrites the [CMD] part of the dockerfile

func WithEnvVar ¶

func WithEnvVar(key string, value string) ContainerOption

WithEnvVar will add "<KEY>=<VALUE>" to the env of the container

func WithMount ¶ added in v0.0.3

func WithMount(dest string, trgt string) ContainerOption

WithExecuteInsideContainer defines if the wait cmd is executed inside the container or on the host machine

func WithPort ¶

func WithPort(port uint, target uint) ContainerOption

WithPort will expose the passed internal port via a given target port on the host.

func WithRandomPort ¶

func WithRandomPort(port uint) ContainerOption

WithRandomPort will expose the passed internal port via a random port on the host. Use

ExposedPort
ExposedPortAddr

to retrieve the actual host port used

(shorthand for `WithPort(x,0)`)

type WaitOption ¶

type WaitOption func(cfg *waitCfg)

func WithExecuteInsideContainer ¶

func WithExecuteInsideContainer(b bool) WaitOption

WithExecuteInsideContainer defines if the wait cmd is executed inside the container or on the host machine

Jump to

Keyboard shortcuts

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