Skip to content

Commit

Permalink
chore: add tests for withNetwork option (#1894)
Browse files Browse the repository at this point in the history
* chore: add tests for withNetwork option

* chore: move functional options to its own file

* chore: enrich assertions

* chore: move test to options_test.go
  • Loading branch information
mdelapenya authored Nov 3, 2023
1 parent 153044e commit aacbb4b
Show file tree
Hide file tree
Showing 5 changed files with 317 additions and 243 deletions.
54 changes: 0 additions & 54 deletions container_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -467,60 +467,6 @@ func TestShouldStartContainersInParallel(t *testing.T) {
}
}

func TestOverrideContainerRequest(t *testing.T) {
req := GenericContainerRequest{
ContainerRequest: ContainerRequest{
Env: map[string]string{
"BAR": "BAR",
},
Image: "foo",
ExposedPorts: []string{"12345/tcp"},
WaitingFor: wait.ForNop(
func(ctx context.Context, target wait.StrategyTarget) error {
return nil
},
),
Networks: []string{"foo", "bar", "baaz"},
NetworkAliases: map[string][]string{
"foo": {"foo0", "foo1", "foo2", "foo3"},
},
},
}

toBeMergedRequest := GenericContainerRequest{
ContainerRequest: ContainerRequest{
Env: map[string]string{
"FOO": "FOO",
},
Image: "bar",
ExposedPorts: []string{"67890/tcp"},
Networks: []string{"foo1", "bar1"},
NetworkAliases: map[string][]string{
"foo1": {"bar"},
},
WaitingFor: wait.ForLog("foo"),
},
}

// the toBeMergedRequest should be merged into the req
CustomizeRequest(toBeMergedRequest)(&req)

// toBeMergedRequest should not be changed
assert.Equal(t, "", toBeMergedRequest.Env["BAR"])
assert.Equal(t, 1, len(toBeMergedRequest.ExposedPorts))
assert.Equal(t, "67890/tcp", toBeMergedRequest.ExposedPorts[0])

// req should be merged with toBeMergedRequest
assert.Equal(t, "FOO", req.Env["FOO"])
assert.Equal(t, "BAR", req.Env["BAR"])
assert.Equal(t, "bar", req.Image)
assert.Equal(t, []string{"12345/tcp", "67890/tcp"}, req.ExposedPorts)
assert.Equal(t, []string{"foo", "bar", "baaz", "foo1", "bar1"}, req.Networks)
assert.Equal(t, []string{"foo0", "foo1", "foo2", "foo3"}, req.NetworkAliases["foo"])
assert.Equal(t, []string{"bar"}, req.NetworkAliases["foo1"])
assert.Equal(t, wait.ForLog("foo"), req.WaitingFor)
}

func TestParseDockerIgnore(t *testing.T) {
testCases := []struct {
filePath string
Expand Down
148 changes: 0 additions & 148 deletions generic.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,10 @@ import (
"context"
"errors"
"fmt"
"strings"
"sync"
"time"

"dario.cat/mergo"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/network"

"github.com/testcontainers/testcontainers-go/internal/testcontainersdocker"
"github.com/testcontainers/testcontainers-go/internal/testcontainerssession"
"github.com/testcontainers/testcontainers-go/wait"
)

var (
Expand All @@ -31,147 +24,6 @@ type GenericContainerRequest struct {
Reuse bool // reuse an existing container if it exists or create a new one. a container name mustn't be empty
}

// ContainerCustomizer is an interface that can be used to configure the Testcontainers container
// request. The passed request will be merged with the default one.
type ContainerCustomizer interface {
Customize(req *GenericContainerRequest)
}

// CustomizeRequestOption is a type that can be used to configure the Testcontainers container request.
// The passed request will be merged with the default one.
type CustomizeRequestOption func(req *GenericContainerRequest)

func (opt CustomizeRequestOption) Customize(req *GenericContainerRequest) {
opt(req)
}

// CustomizeRequest returns a function that can be used to merge the passed container request with the one that is used by the container.
// Slices and Maps will be appended.
func CustomizeRequest(src GenericContainerRequest) CustomizeRequestOption {
return func(req *GenericContainerRequest) {
if err := mergo.Merge(req, &src, mergo.WithOverride, mergo.WithAppendSlice); err != nil {
fmt.Printf("error merging container request, keeping the original one. Error: %v", err)
return
}
}
}

// WithImage sets the image for a container
func WithImage(image string) CustomizeRequestOption {
return func(req *GenericContainerRequest) {
req.Image = image
}
}

// imageSubstitutor {
// ImageSubstitutor represents a way to substitute container image names
type ImageSubstitutor interface {
// Description returns the name of the type and a short description of how it modifies the image.
// Useful to be printed in logs
Description() string
Substitute(image string) (string, error)
}

// }

// WithImageSubstitutors sets the image substitutors for a container
func WithImageSubstitutors(fn ...ImageSubstitutor) CustomizeRequestOption {
return func(req *GenericContainerRequest) {
req.ImageSubstitutors = fn
}
}

// WithConfigModifier allows to override the default container config
func WithConfigModifier(modifier func(config *container.Config)) CustomizeRequestOption {
return func(req *GenericContainerRequest) {
req.ConfigModifier = modifier
}
}

// WithEndpointSettingsModifier allows to override the default endpoint settings
func WithEndpointSettingsModifier(modifier func(settings map[string]*network.EndpointSettings)) CustomizeRequestOption {
return func(req *GenericContainerRequest) {
req.EnpointSettingsModifier = modifier
}
}

// WithHostConfigModifier allows to override the default host config
func WithHostConfigModifier(modifier func(hostConfig *container.HostConfig)) CustomizeRequestOption {
return func(req *GenericContainerRequest) {
req.HostConfigModifier = modifier
}
}

// WithNetwork creates a network with the given name and attaches the container to it, setting the network alias
// on that network to the given alias.
// If the network already exists, checking if the network name already exists, it will be reused.
func WithNetwork(networkName string, alias string) CustomizeRequestOption {
return func(req *GenericContainerRequest) {
_, err := GenericNetwork(context.Background(), GenericNetworkRequest{
NetworkRequest: NetworkRequest{
Name: networkName,
CheckDuplicate: true, // force the Docker provider to reuse an existing network
},
})
if err != nil && !strings.Contains(err.Error(), "already exists") {
logger := req.Logger
if logger == nil {
logger = Logger
}
logger.Printf("Failed to create network '%s'. Container won't be attached to this network: %v", networkName, err)
return
}

// attaching to the network because it was created with success or it already existed.
req.Networks = append(req.Networks, networkName)

if req.NetworkAliases == nil {
req.NetworkAliases = make(map[string][]string)
}
req.NetworkAliases[networkName] = []string{alias}
}
}

// Executable represents an executable command to be sent to a container
// as part of the PostStart lifecycle hook.
type Executable interface {
AsCommand() []string
}

// WithStartupCommand will execute the command representation of each Executable into the container.
// It will leverage the container lifecycle hooks to call the command right after the container
// is started.
func WithStartupCommand(execs ...Executable) CustomizeRequestOption {
return func(req *GenericContainerRequest) {
startupCommandsHook := ContainerLifecycleHooks{
PostStarts: []ContainerHook{},
}

for _, exec := range execs {
execFn := func(ctx context.Context, c Container) error {
_, _, err := c.Exec(ctx, exec.AsCommand())
return err
}

startupCommandsHook.PostStarts = append(startupCommandsHook.PostStarts, execFn)
}

req.LifecycleHooks = append(req.LifecycleHooks, startupCommandsHook)
}
}

// WithWaitStrategy sets the wait strategy for a container, using 60 seconds as deadline
func WithWaitStrategy(strategies ...wait.Strategy) CustomizeRequestOption {
return WithWaitStrategyAndDeadline(60*time.Second, strategies...)
}

// WithWaitStrategyAndDeadline sets the wait strategy for a container, including deadline
func WithWaitStrategyAndDeadline(deadline time.Duration, strategies ...wait.Strategy) CustomizeRequestOption {
return func(req *GenericContainerRequest) {
req.WaitingFor = wait.ForAll(strategies...).WithDeadline(deadline)
}
}

// GenericNetworkRequest represents parameters to a generic network
type GenericNetworkRequest struct {
NetworkRequest // embedded request for provider
Expand Down
41 changes: 0 additions & 41 deletions generic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,10 @@ package testcontainers
import (
"context"
"errors"
"io"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/testcontainers/testcontainers-go/exec"
"github.com/testcontainers/testcontainers-go/wait"
)

Expand Down Expand Up @@ -99,41 +96,3 @@ func TestGenericReusableContainer(t *testing.T) {
})
}
}

type testExecutable struct {
cmds []string
}

func (t testExecutable) AsCommand() []string {
return t.cmds
}

func TestWithStartupCommand(t *testing.T) {
req := GenericContainerRequest{
ContainerRequest: ContainerRequest{
Image: "alpine",
Entrypoint: []string{"tail", "-f", "/dev/null"},
},
Started: true,
}

testExec := testExecutable{
cmds: []string{"touch", "/tmp/.testcontainers"},
}

WithStartupCommand(testExec)(&req)

c, err := GenericContainer(context.Background(), req)
require.NoError(t, err)
defer func() {
err = c.Terminate(context.Background())
require.NoError(t, err)
}()

_, reader, err := c.Exec(context.Background(), []string{"ls", "/tmp/.testcontainers"}, exec.Multiplexed())
require.NoError(t, err)

content, err := io.ReadAll(reader)
require.NoError(t, err)
assert.Equal(t, "/tmp/.testcontainers\n", string(content))
}
Loading

0 comments on commit aacbb4b

Please sign in to comment.