testcontainer

package module
v0.0.0-...-229c18a Latest Latest
Warning

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

Go to latest
Published: Mar 11, 2023 License: Apache-2.0 Imports: 26 Imported by: 0

README

Test containers

Introduction

Package that makes it simple to create and clean up container-based dependencies for automated integration/smoke tests. For implementation used testcontainers-go library.
Implemented containers for postgres, apache zookeeper, apache kafka

Features

Postgresql
  • Starting and stopping a container
  • Database preparation (database creation, users creation, schema creation)
  • applying all up migrations
Kafka
  • Starting and stopping a containers (zookeeper + kafka). ZooKeeper and broker started in the common docker network. A new network is created for each instance of the KafkaContainer. This avoids concurrent tests.
  • Custom strategy for kafka broker readiness check (through metadata acquisition) implemented

Installation

 go get github.com/Hemiun/testcontainer

Getting Started

  1. Copy "scripts" folder to your project Or create your own struct. In this case you must set path variables
testcontainer.CreateDatabasePath = "..."
testcontainer.CreateSchemaPath = "..."
testcontainer.MigrationPath = "..."
  1. By default, folder script/migrations is empty and migrations are not applied (param ApplyMigrations == false). You can put your migrations to this folder and change param ApplyMigrations
testcontainer.ApplyMigrations = true
  1. By default, we use testcontainers-go. If your prefer another tool your can implement your own MigrationApplyFn function For example, for goose should be something like this:
testcontainer.MigrationApplyFn  = func(dsn string) error {
        db, err = sql.Open("postgres", dsn)
        if err != nil {
           panic(err)
        }
        defer db.Close()
        goose.SetBaseFS(embedMigrations)
        if err := goose.SetDialect("postgres"); err != nil {
            panic(err)
        }
        if err := goose.Up(db, path.Clean(buildPath(migrationPath))); err != nil {
            return err
        }
		return nil
   }
  1. Using
    db, err := testcontainer.NewDatabaseContainer(ctx, postgresConfig, nil)
	if err != nil {
		fmt.Printf("can't init db container: %v", err)
	}
	err = db.PrepareDB(ctx)
	if err != nil {
		panic("can't prepare db")
	}
	defer db.Close(ctx)
	dsn := db.ConnectionString(ctx)

	//  your database is ready/ Use dsn for connect
	conn, err := pgx.Connect(ctx, dsn)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Unable to connect to database: %v\n", err)
		os.Exit(1)
	}
	defer conn.Close(context.Background())

	var num int64
	err = conn.QueryRow(context.Background(), "select 10").Scan(&num)
	if err != nil {
		fmt.Fprintf(os.Stderr, "QueryRow failed: %v\n", err)
		os.Exit(1)
	}

	fmt.Println(num)

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// KafkaImage - docker image name for apache kafka broker
	KafkaImage = "confluentinc/cp-server:7.2.2"
	// ZooImage - docker image name for zookeeper
	ZooImage = "confluentinc/cp-zookeeper:7.2.2"

	// ExposeZooPort - zookeeper port for expose from container
	ExposeZooPort = "2181/tcp"
	// ZooPort - zookeeper port
	ZooPort = "2181"

	// BrokerExternalPort - broker port for external communications
	BrokerExternalPort = "9092"
	// BrokerInternalPort - broker port for internal communications
	BrokerInternalPort = "29092"
)
View Source
var (
	ErrNoBrokerAvailable     = errors.New("no broker available")
	ErrConfigValidationError = errors.New("config validation error")
	ErrNoDockerClient        = errors.New("no docker client available")
)
View Source
var (
	// CreateDatabasePath - path to database creation script template (see ./script/init/01.database.sql for example)
	CreateDatabasePath = "script/init/01.database.sql"

	// CreateSchemaPath  - path to database schema creation script template (see ./script/init/02.schema.sql for example)
	CreateSchemaPath = "script/init/02.schema.sql"
	// MigrationPath - path to migration scripts
	MigrationPath = "script/migrations"
	// ApplyMigrations - if false migrations doesn't apply
	ApplyMigrations = false

	// DefaultDB - default database name (used for first connection)
	DefaultDB = "postgres"
	// DefaultDBUser - database user
	DefaultDBUser = "postgres"
	// DefaultDBPass - pass for DefaultDBUser
	DefaultDBPass = "postgres"

	// ExposePostgresPort - database port for expose from container
	ExposePostgresPort = "5432/tcp"

	// PostgresImage - docker image name for postgres
	PostgresImage = "postgres:14.2"
)

Functions

This section is empty.

Types

type DatabaseContainer

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

DatabaseContainer - struct for db container

func NewDatabaseContainer

func NewDatabaseContainer(ctx context.Context, cfg DatabaseContainerConfig, log Logger) (*DatabaseContainer, error)

NewDatabaseContainer returns new DatabaseContainer ctx and cfg are mandatory params Optionally you can pass logger

func (*DatabaseContainer) Close

func (db *DatabaseContainer) Close(ctx context.Context)

Close - close created container

func (*DatabaseContainer) ConnectionString

func (db *DatabaseContainer) ConnectionString(ctx context.Context) string

ConnectionString - returns connection string in dsn format

func (*DatabaseContainer) Port

func (db *DatabaseContainer) Port(ctx context.Context) int

Port - return port for db connection (looking for port mapped into default postgresql port - 5432 )

func (*DatabaseContainer) PrepareDB

func (db *DatabaseContainer) PrepareDB(ctx context.Context) error

PrepareDB - prepare database structure (new db, new schema, applying migration)

type DatabaseContainerConfig

type DatabaseContainerConfig struct {
	DatabaseName    string        `validate:"required"`
	SchemaOwner     string        `validate:"required"`
	SchemaOwnerPass string        `validate:"required"`
	ServiceUser     string        `validate:"required"`
	ServiceUserPass string        `validate:"required"`
	Timeout         time.Duration `validate:"required"`
}

DatabaseContainerConfig - configuration for database container (postgresql) Usually we use the couple of users. First one is schema owner. For example, with this user we apply database migrations. Second is service user. It is restricted user and have only privileges needed for service work. Service connect to database with this credentials

func (*DatabaseContainerConfig) Validate

func (c *DatabaseContainerConfig) Validate() error

Validate - validation for DatabaseContainerConfig

type KafkaContainer

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

KafkaContainer - test container for kafka broker

func NewKafkaContainer

func NewKafkaContainer(ctx context.Context, cfg KafkaContainerConfig, logger Logger) (*KafkaContainer, error)

NewKafkaContainer - returns new KafkaContainer

func (*KafkaContainer) Close

func (target *KafkaContainer) Close(ctx context.Context)

Close - destruct all run containers

func (*KafkaContainer) GetBrokerList

func (target *KafkaContainer) GetBrokerList(ctx context.Context) ([]string, error)

GetBrokerList - return endpoint list for kafka connecting

type KafkaContainerConfig

type KafkaContainerConfig struct {
	// Timeout. After expiration context will be canceled
	Timeout time.Duration `validate:"required"`
	// Network. Prefix for network name
	Network string `validate:"required"`
	// Kafka. Prefix for kafka container name
	Kafka string
	// ZooKeeper  Prefix for zookeeper container name
	ZooKeeper string

	// Waiting. Time period for kafka launch waiting for
	Waiting time.Duration
}

KafkaContainerConfig - config struct for container with kafka broker

func (*KafkaContainerConfig) Validate

func (c *KafkaContainerConfig) Validate() error

Validate - validate config struct

type Logger

type Logger interface {
	LogPanic(ctx context.Context, msg string, err error)
	LogDebug(ctx context.Context, msg string)
	LogError(ctx context.Context, msg string, err error)
}

Logger - interface for logging inside module testcontainers

type MetadataWaitStrategy

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

MetadataWaitStrategy - strategy for waiting kafka readiness

func NewMetadataWaitStrategy

func NewMetadataWaitStrategy(startupTimeout time.Duration, logger Logger) *MetadataWaitStrategy

NewMetadataWaitStrategy returns new MetadataWaitStrategy

func (*MetadataWaitStrategy) WaitUntilReady

func (mw *MetadataWaitStrategy) WaitUntilReady(ctx context.Context, target wait.StrategyTarget) error

WaitUntilReady - strategy for waiting kafka readiness

type MigrationApplyFunc

type MigrationApplyFunc func(dsn string) error
var MigrationApplyFn MigrationApplyFunc = func(dsn string) error {
	m, err := migrate.New("file://"+path.Clean(MigrationPath), dsn)
	if err != nil {

		return err
	}
	err = m.Up()
	if err != nil {

		return err
	}
	return nil
}

Jump to

Keyboard shortcuts

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