@@ -17,6 +17,7 @@ import (
1717 "testing"
1818 "time"
1919
20+ "github.com/docker/docker/client"
2021 "github.com/stretchr/testify/require"
2122 "google.golang.org/grpc"
2223 "google.golang.org/grpc/credentials/insecure"
@@ -154,3 +155,38 @@ func TestZeroHealth(t *testing.T) {
154155 require .NoError (t , err )
155156 require .Equal (t , string (body ), "OK" )
156157}
158+
159+ func TestZeroGracefulShutdown (t * testing.T ) {
160+ // This test verifies that Zero shuts down cleanly without hanging.
161+ // It catches issues like closer miscount bugs where SignalAndWait() would block indefinitely.
162+
163+ instance := testutil .GetContainerInstance (testutil .DockerPrefix , "zero1" )
164+ c := instance .GetContainer ()
165+ require .NotNil (t , c , "zero1 container not found" )
166+
167+ containerID := c .ID
168+
169+ startTime := time .Now ()
170+ err := testutil .DockerRun ("zero1" , testutil .Stop )
171+ shutdownDuration := time .Since (startTime )
172+
173+ require .NoError (t , err , "Failed to stop zero1 container" )
174+ cli , err := client .NewClientWithOpts (client .FromEnv , client .WithAPIVersionNegotiation ())
175+ require .NoError (t , err )
176+
177+ inspect , err := cli .ContainerInspect (context .Background (), containerID )
178+ require .NoError (t , err )
179+ require .False (t , inspect .State .Running , "Container should not be running after stop" )
180+
181+ if inspect .State .ExitCode == 137 {
182+ t .Errorf ("Zero was killed (exit code 137) instead of shutting down gracefully. " +
183+ "This may indicate a hanging goroutine or closer miscount. Shutdown took %v" , shutdownDuration )
184+ }
185+
186+ // Restart the container so other tests can continue
187+ err = testutil .DockerRun ("zero1" , testutil .Start )
188+ require .NoError (t , err , "Failed to restart zero1 container" )
189+
190+ err = instance .BestEffortWaitForHealthy (6080 )
191+ require .NoError (t , err , "Zero did not become healthy after restart" )
192+ }
0 commit comments