diff --git a/wait/exit_test.go b/wait/exit_test.go index 7ee0a61c17..6463de081d 100644 --- a/wait/exit_test.go +++ b/wait/exit_test.go @@ -1,64 +1,22 @@ -package wait +package wait_test import ( "context" - "errors" - "io" "testing" "time" "github.com/moby/moby/api/types/container" - "github.com/moby/moby/api/types/network" "github.com/stretchr/testify/require" - tcexec "github.com/testcontainers/testcontainers-go/exec" + "github.com/testcontainers/testcontainers-go/wait" ) -type exitStrategyTarget struct { - isRunning bool -} - -func (st exitStrategyTarget) Host(_ context.Context) (string, error) { - return "", nil -} - -func (st exitStrategyTarget) Inspect(_ context.Context) (*container.InspectResponse, error) { - return nil, nil -} - -// Deprecated: use Inspect instead -func (st exitStrategyTarget) Ports(_ context.Context) (network.PortMap, error) { - return nil, nil -} - -func (st exitStrategyTarget) MappedPort(_ context.Context, n string) (network.Port, error) { - if n == "" { - return network.Port{}, nil - } - return network.ParsePort(n) -} - -func (st exitStrategyTarget) Logs(_ context.Context) (io.ReadCloser, error) { - return nil, nil -} - -func (st exitStrategyTarget) Exec(_ context.Context, _ []string, _ ...tcexec.ProcessOption) (int, io.Reader, error) { - return 0, nil, nil -} - -func (st exitStrategyTarget) State(_ context.Context) (*container.State, error) { - return &container.State{Running: st.isRunning}, nil -} - -func (st exitStrategyTarget) CopyFileFromContainer(context.Context, string) (io.ReadCloser, error) { - return nil, errors.New("not implemented") -} - func TestWaitForExit(t *testing.T) { - target := exitStrategyTarget{ - isRunning: false, - } - wg := NewExitStrategy().WithExitTimeout(100 * time.Millisecond) + target := newMockStrategyTarget(t) + target.EXPECT().State(anyContext). + Return(&container.State{Running: false}, nil) + + wg := wait.NewExitStrategy().WithExitTimeout(100 * time.Millisecond) err := wg.WaitUntilReady(context.Background(), target) require.NoError(t, err) } diff --git a/wait/health_test.go b/wait/health_test.go index 98f6264ef2..f70b968db8 100644 --- a/wait/health_test.go +++ b/wait/health_test.go @@ -1,83 +1,32 @@ -package wait +package wait_test import ( "context" - "errors" - "io" - "sync" "testing" "time" "github.com/moby/moby/api/types/container" - "github.com/moby/moby/api/types/network" "github.com/stretchr/testify/require" - tcexec "github.com/testcontainers/testcontainers-go/exec" + "github.com/testcontainers/testcontainers-go/wait" ) -type healthStrategyTarget struct { - state *container.State - mtx sync.Mutex -} - -func (st *healthStrategyTarget) Host(_ context.Context) (string, error) { - return "", nil -} - -func (st *healthStrategyTarget) Inspect(_ context.Context) (*container.InspectResponse, error) { - return nil, nil -} - -// Deprecated: use Inspect instead -func (st *healthStrategyTarget) Ports(_ context.Context) (network.PortMap, error) { - return nil, nil -} - -func (st *healthStrategyTarget) MappedPort(_ context.Context, n string) (network.Port, error) { - if n == "" { - return network.Port{}, nil - } - return network.ParsePort(n) -} - -func (st *healthStrategyTarget) Logs(_ context.Context) (io.ReadCloser, error) { - return nil, nil -} - -func (st *healthStrategyTarget) Exec(_ context.Context, _ []string, _ ...tcexec.ProcessOption) (int, io.Reader, error) { - return 0, nil, nil -} - -func (st *healthStrategyTarget) State(_ context.Context) (*container.State, error) { - st.mtx.Lock() - defer st.mtx.Unlock() - - // Return a copy to prevent data race. - state := *st.state - - return &state, nil -} - -func (st *healthStrategyTarget) setState(health *container.Health) { - st.mtx.Lock() - defer st.mtx.Unlock() - st.state.Health = health -} - -func (st *healthStrategyTarget) CopyFileFromContainer(_ context.Context, _ string) (io.ReadCloser, error) { - return nil, errors.New("not implemented") +// newStateTarget creates a new mockStrategyTarget whose State method always returns the given state. +func newStateTarget(t *testing.T, state *container.State) *mockStrategyTarget { + target := newMockStrategyTarget(t) + target.EXPECT().State(anyContext).Return(state, nil) + return target } // TestWaitForHealthTimesOutForUnhealthy confirms that an unhealthy container will eventually // time out. func TestWaitForHealthTimesOutForUnhealthy(t *testing.T) { - target := &healthStrategyTarget{ - state: &container.State{ - Running: true, - Health: &container.Health{Status: container.Unhealthy}, - }, - } - wg := NewHealthStrategy().WithStartupTimeout(100 * time.Millisecond) + target := newStateTarget(t, &container.State{ + Running: true, + Health: &container.Health{Status: container.Unhealthy}, + }) + + wg := wait.NewHealthStrategy().WithStartupTimeout(100 * time.Millisecond) err := wg.WaitUntilReady(context.Background(), target) require.Error(t, err) @@ -86,13 +35,12 @@ func TestWaitForHealthTimesOutForUnhealthy(t *testing.T) { // TestWaitForHealthSucceeds ensures that a healthy container always succeeds. func TestWaitForHealthSucceeds(t *testing.T) { - target := &healthStrategyTarget{ - state: &container.State{ - Running: true, - Health: &container.Health{Status: container.Healthy}, - }, - } - wg := NewHealthStrategy().WithStartupTimeout(100 * time.Millisecond) + target := newStateTarget(t, &container.State{ + Running: true, + Health: &container.Health{Status: container.Healthy}, + }) + + wg := wait.NewHealthStrategy().WithStartupTimeout(100 * time.Millisecond) err := wg.WaitUntilReady(context.Background(), target) require.NoError(t, err) @@ -101,36 +49,32 @@ func TestWaitForHealthSucceeds(t *testing.T) { // TestWaitForHealthWithNil checks that an initial `nil` Health will not cause a panic, // and if the container eventually becomes healthy, the HealthStrategy will succeed. func TestWaitForHealthWithNil(t *testing.T) { - target := &healthStrategyTarget{ - state: &container.State{ - Running: true, - Health: nil, - }, - } - wg := NewHealthStrategy(). + target := newMockStrategyTarget(t) + + startTime := time.Now() + target.EXPECT().State(anyContext).RunAndReturn(func(_ context.Context) (*container.State, error) { + if time.Since(startTime) >= 200*time.Millisecond { + return &container.State{Running: true, Health: &container.Health{Status: container.Healthy}}, nil + } + return &container.State{Running: true, Health: nil}, nil + }) + + wg := wait.NewHealthStrategy(). WithStartupTimeout(500 * time.Millisecond). WithPollInterval(100 * time.Millisecond) - go func(target *healthStrategyTarget) { - // wait a bit to simulate startup time and give check time to at least - // try a few times with a nil Health - time.Sleep(200 * time.Millisecond) - target.setState(&container.Health{Status: container.Healthy}) - }(target) - err := wg.WaitUntilReady(context.Background(), target) require.NoError(t, err) } // TestWaitFailsForNilHealth checks that Health always nil fails (but will NOT cause a panic) func TestWaitFailsForNilHealth(t *testing.T) { - target := &healthStrategyTarget{ - state: &container.State{ - Running: true, - Health: nil, - }, - } - wg := NewHealthStrategy(). + target := newStateTarget(t, &container.State{ + Running: true, + Health: nil, + }) + + wg := wait.NewHealthStrategy(). WithStartupTimeout(500 * time.Millisecond). WithPollInterval(100 * time.Millisecond) @@ -140,12 +84,11 @@ func TestWaitFailsForNilHealth(t *testing.T) { } func TestWaitForHealthFailsDueToOOMKilledContainer(t *testing.T) { - target := &healthStrategyTarget{ - state: &container.State{ - OOMKilled: true, - }, - } - wg := NewHealthStrategy(). + target := newStateTarget(t, &container.State{ + OOMKilled: true, + }) + + wg := wait.NewHealthStrategy(). WithStartupTimeout(500 * time.Millisecond). WithPollInterval(100 * time.Millisecond) @@ -155,13 +98,12 @@ func TestWaitForHealthFailsDueToOOMKilledContainer(t *testing.T) { } func TestWaitForHealthFailsDueToExitedContainer(t *testing.T) { - target := &healthStrategyTarget{ - state: &container.State{ - Status: container.StateExited, - ExitCode: 1, - }, - } - wg := NewHealthStrategy(). + target := newStateTarget(t, &container.State{ + Status: container.StateExited, + ExitCode: 1, + }) + + wg := wait.NewHealthStrategy(). WithStartupTimeout(500 * time.Millisecond). WithPollInterval(100 * time.Millisecond) @@ -171,12 +113,11 @@ func TestWaitForHealthFailsDueToExitedContainer(t *testing.T) { } func TestWaitForHealthFailsDueToUnexpectedContainerStatus(t *testing.T) { - target := &healthStrategyTarget{ - state: &container.State{ - Status: container.StateDead, - }, - } - wg := NewHealthStrategy(). + target := newStateTarget(t, &container.State{ + Status: container.StateDead, + }) + + wg := wait.NewHealthStrategy(). WithStartupTimeout(500 * time.Millisecond). WithPollInterval(100 * time.Millisecond)