@@ -44,6 +44,8 @@ pub struct GuardOptions {
4444 pub capture_logs : bool ,
4545 /// Reuse existing container if already running (default: false)
4646 pub reuse_if_running : bool ,
47+ /// Automatically wait for container to be ready after start (default: false)
48+ pub wait_for_ready : bool ,
4749}
4850
4951impl Default for GuardOptions {
@@ -54,6 +56,7 @@ impl Default for GuardOptions {
5456 keep_on_panic : false ,
5557 capture_logs : false ,
5658 reuse_if_running : false ,
59+ wait_for_ready : false ,
5760 }
5861 }
5962}
@@ -118,38 +121,83 @@ impl<T: Template> ContainerGuardBuilder<T> {
118121 self
119122 }
120123
124+ /// Set whether to automatically wait for the container to be ready after starting (default: false).
125+ ///
126+ /// When enabled, `start()` will not return until the container passes its
127+ /// readiness check. This is useful for tests that need to immediately connect
128+ /// to the service.
129+ ///
130+ /// # Example
131+ ///
132+ /// ```rust,no_run
133+ /// # use docker_wrapper::testing::ContainerGuard;
134+ /// # use docker_wrapper::RedisTemplate;
135+ /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
136+ /// let guard = ContainerGuard::new(RedisTemplate::new("test"))
137+ /// .wait_for_ready(true)
138+ /// .start()
139+ /// .await?;
140+ /// // Container is guaranteed ready at this point
141+ /// # Ok(())
142+ /// # }
143+ /// ```
144+ #[ must_use]
145+ pub fn wait_for_ready ( mut self , wait : bool ) -> Self {
146+ self . options . wait_for_ready = wait;
147+ self
148+ }
149+
121150 /// Start the container and return a guard that manages its lifecycle.
122151 ///
123152 /// If `reuse_if_running` is enabled and a container is already running,
124153 /// it will be reused instead of starting a new one.
125154 ///
155+ /// If `wait_for_ready` is enabled, this method will block until the
156+ /// container passes its readiness check.
157+ ///
126158 /// # Errors
127159 ///
128160 /// Returns an error if the container fails to start or the readiness check times out.
129161 pub async fn start ( self ) -> Result < ContainerGuard < T > , TemplateError > {
162+ let wait_for_ready = self . options . wait_for_ready ;
163+
130164 // Check if we should reuse an existing container
131165 if self . options . reuse_if_running {
132166 if let Ok ( true ) = self . template . is_running ( ) . await {
133- return Ok ( ContainerGuard {
167+ let guard = ContainerGuard {
134168 template : self . template ,
135169 container_id : None , // We don't have the ID for reused containers
136170 options : self . options ,
137171 was_reused : true ,
138172 cleaned_up : Arc :: new ( AtomicBool :: new ( false ) ) ,
139- } ) ;
173+ } ;
174+
175+ // Wait for ready if configured (even for reused containers)
176+ if wait_for_ready {
177+ guard. wait_for_ready ( ) . await ?;
178+ }
179+
180+ return Ok ( guard) ;
140181 }
141182 }
142183
143184 // Start the container
144185 let container_id = self . template . start_and_wait ( ) . await ?;
145186
146- Ok ( ContainerGuard {
187+ let guard = ContainerGuard {
147188 template : self . template ,
148189 container_id : Some ( container_id) ,
149190 options : self . options ,
150191 was_reused : false ,
151192 cleaned_up : Arc :: new ( AtomicBool :: new ( false ) ) ,
152- } )
193+ } ;
194+
195+ // Wait for ready if configured
196+ if wait_for_ready {
197+ guard. wait_for_ready ( ) . await ?;
198+ }
199+
200+ Ok ( guard)
153201 }
154202}
155203
@@ -223,6 +271,35 @@ impl<T: Template> ContainerGuard<T> {
223271 self . template . is_running ( ) . await
224272 }
225273
274+ /// Wait for the container to be ready.
275+ ///
276+ /// This calls the underlying template's readiness check. The exact behavior
277+ /// depends on the template implementation - for example, Redis templates
278+ /// wait for a successful PING response.
279+ ///
280+ /// # Errors
281+ ///
282+ /// Returns an error if the readiness check times out or the Docker command fails.
283+ ///
284+ /// # Example
285+ ///
286+ /// ```rust,no_run
287+ /// # use docker_wrapper::testing::ContainerGuard;
288+ /// # use docker_wrapper::RedisTemplate;
289+ /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
290+ /// let guard = ContainerGuard::new(RedisTemplate::new("test"))
291+ /// .start()
292+ /// .await?;
293+ ///
294+ /// // Wait for Redis to be ready to accept connections
295+ /// guard.wait_for_ready().await?;
296+ /// # Ok(())
297+ /// # }
298+ /// ```
299+ pub async fn wait_for_ready ( & self ) -> Result < ( ) , TemplateError > {
300+ self . template . wait_for_ready ( ) . await
301+ }
302+
226303 /// Get the host port mapped to a container port.
227304 ///
228305 /// This is useful when using dynamic port allocation - Docker assigns
@@ -417,6 +494,7 @@ mod tests {
417494 assert ! ( !opts. keep_on_panic) ;
418495 assert ! ( !opts. capture_logs) ;
419496 assert ! ( !opts. reuse_if_running) ;
497+ assert ! ( !opts. wait_for_ready) ;
420498 }
421499
422500 #[ test]
0 commit comments