@@ -8,13 +8,12 @@ import (
88 "net/http"
99 "net/http/httptest"
1010 "testing"
11- "time"
1211
1312 "github.com/stretchr/testify/require"
1413
1514 "github.com/testcontainers/testcontainers-go"
16- tcexec "github.com/testcontainers/testcontainers-go/exec"
1715 "github.com/testcontainers/testcontainers-go/network"
16+ "github.com/testcontainers/testcontainers-go/wait"
1817)
1918
2019const (
@@ -23,42 +22,59 @@ const (
2322
2423func TestExposeHostPorts (t * testing.T ) {
2524 tests := []struct {
26- name string
27- numberOfPorts int
28- hasNetwork bool
29- hasHostAccess bool
25+ name string
26+ numberOfPorts int
27+ hasNetwork bool
28+ bindOnPostStarts bool
3029 }{
3130 {
3231 name : "single port" ,
3332 numberOfPorts : 1 ,
34- hasHostAccess : true ,
3533 },
3634 {
3735 name : "single port using a network" ,
3836 numberOfPorts : 1 ,
3937 hasNetwork : true ,
40- hasHostAccess : true ,
4138 },
4239 {
4340 name : "multiple ports" ,
4441 numberOfPorts : 3 ,
45- hasHostAccess : true ,
4642 },
4743 {
48- name : "single port with cancellation " ,
49- numberOfPorts : 1 ,
50- hasHostAccess : false ,
44+ name : "multiple ports bound on PostStarts " ,
45+ numberOfPorts : 3 ,
46+ bindOnPostStarts : true ,
5147 },
5248 }
5349
5450 for _ , tc := range tests {
5551 t .Run (tc .name , func (tt * testing.T ) {
52+ servers := make ([]* httptest.Server , tc .numberOfPorts )
5653 freePorts := make ([]int , tc .numberOfPorts )
54+ waitStrategies := make ([]wait.Strategy , tc .numberOfPorts )
5755 for i := range freePorts {
58- server := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
56+ server := httptest .NewUnstartedServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
5957 fmt .Fprint (w , expectedResponse )
6058 }))
61- freePorts [i ] = server .Listener .Addr ().(* net.TCPAddr ).Port
59+
60+ if ! tc .bindOnPostStarts {
61+ server .Start ()
62+ }
63+
64+ servers [i ] = server
65+ freePort := server .Listener .Addr ().(* net.TCPAddr ).Port
66+ freePorts [i ] = freePort
67+ waitStrategies [i ] = wait .
68+ ForExec ([]string {"wget" , "-q" , "-O" , "-" , fmt .Sprintf ("http://%s:%d" , testcontainers .HostInternal , freePort )}).
69+ WithExitCodeMatcher (func (code int ) bool {
70+ return code == 0
71+ }).
72+ WithResponseMatcher (func (body io.Reader ) bool {
73+ bs , err := io .ReadAll (body )
74+ require .NoError (tt , err )
75+ return string (bs ) == expectedResponse
76+ })
77+
6278 tt .Cleanup (func () {
6379 server .Close ()
6480 })
@@ -69,7 +85,26 @@ func TestExposeHostPorts(t *testing.T) {
6985 ContainerRequest : testcontainers.ContainerRequest {
7086 Image : "alpine:3.17" ,
7187 HostAccessPorts : freePorts ,
72- Cmd : []string {"top" },
88+ WaitingFor : wait .ForAll (waitStrategies ... ),
89+ LifecycleHooks : []testcontainers.ContainerLifecycleHooks {
90+ {
91+ PostStarts : []testcontainers.ContainerHook {
92+ func (ctx context.Context , c testcontainers.Container ) error {
93+ if tc .bindOnPostStarts {
94+ for _ , server := range servers {
95+ server .Start ()
96+ }
97+ }
98+
99+ return nil
100+ },
101+ func (ctx context.Context , c testcontainers.Container ) error {
102+ return waitStrategies [0 ].WaitUntilReady (ctx , c )
103+ },
104+ },
105+ },
106+ },
107+ Cmd : []string {"top" },
73108 },
74109 // }
75110 Started : true ,
@@ -87,66 +122,9 @@ func TestExposeHostPorts(t *testing.T) {
87122 }
88123
89124 ctx := context .Background ()
90- if ! tc .hasHostAccess {
91- var cancel context.CancelFunc
92- ctx , cancel = context .WithTimeout (ctx , 10 * time .Second )
93- defer cancel ()
94- }
95-
96125 c , err := testcontainers .GenericContainer (ctx , req )
97- testcontainers .CleanupContainer (t , c )
98126 require .NoError (tt , err )
99-
100- if tc .hasHostAccess {
101- // create a container that has host access, which will
102- // automatically forward the port to the container
103- assertContainerHasHostAccess (tt , c , freePorts ... )
104- } else {
105- // force cancellation because of timeout
106- time .Sleep (11 * time .Second )
107-
108- assertContainerHasNoHostAccess (tt , c , freePorts ... )
109- }
127+ _ = c .Terminate (ctx )
110128 })
111129 }
112130}
113-
114- func httpRequest (t * testing.T , c testcontainers.Container , port int ) (int , string ) {
115- // wgetHostInternal {
116- code , reader , err := c .Exec (
117- context .Background (),
118- []string {"wget" , "-q" , "-O" , "-" , fmt .Sprintf ("http://%s:%d" , testcontainers .HostInternal , port )},
119- tcexec .Multiplexed (),
120- )
121- // }
122- require .NoError (t , err )
123-
124- // read the response
125- bs , err := io .ReadAll (reader )
126- require .NoError (t , err )
127-
128- return code , string (bs )
129- }
130-
131- func assertContainerHasHostAccess (t * testing.T , c testcontainers.Container , ports ... int ) {
132- for _ , port := range ports {
133- code , response := httpRequest (t , c , port )
134- if code != 0 {
135- t .Fatalf ("expected status code [%d] but got [%d]" , 0 , code )
136- }
137-
138- if response != expectedResponse {
139- t .Fatalf ("expected [%s] but got [%s]" , expectedResponse , response )
140- }
141- }
142- }
143-
144- func assertContainerHasNoHostAccess (t * testing.T , c testcontainers.Container , ports ... int ) {
145- for _ , port := range ports {
146- _ , response := httpRequest (t , c , port )
147-
148- if response == expectedResponse {
149- t .Fatalf ("expected not to get [%s] but got [%s]" , expectedResponse , response )
150- }
151- }
152- }
0 commit comments