package http import ( "context" "errors" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Server Group", func() { var m1, m2, m3 *mockServer var ctx context.Context var cancel context.CancelFunc var group Server BeforeEach(func() { ctx, cancel = context.WithCancel(context.Background()) m1 = newMockServer() m2 = newMockServer() m3 = newMockServer() group = NewServerGroup(m1, m2, m3) }) AfterEach(func() { cancel() }) It("starts each server in the group", func() { go func() { defer GinkgoRecover() Expect(group.Start(ctx)).To(Succeed()) }() Eventually(m1.started).Should(BeClosed(), "mock server 1 not started") Eventually(m2.started).Should(BeClosed(), "mock server 2 not started") Eventually(m3.started).Should(BeClosed(), "mock server 3 not started") }) It("stop each server in the group when the context is cancelled", func() { go func() { defer GinkgoRecover() Expect(group.Start(ctx)).To(Succeed()) }() cancel() Eventually(m1.stopped).Should(BeClosed(), "mock server 1 not stopped") Eventually(m2.stopped).Should(BeClosed(), "mock server 2 not stopped") Eventually(m3.stopped).Should(BeClosed(), "mock server 3 not stopped") }) It("stop each server in the group when the an error occurs", func() { err := errors.New("server error") go func() { defer GinkgoRecover() Expect(group.Start(ctx)).To(MatchError(err)) }() m2.errors <- err Eventually(m1.stopped).Should(BeClosed(), "mock server 1 not stopped") Eventually(m2.stopped).Should(BeClosed(), "mock server 2 not stopped") Eventually(m3.stopped).Should(BeClosed(), "mock server 3 not stopped") }) }) // mockServer is used to test the server group can start // and stop multiple servers simultaneously. type mockServer struct { started chan struct{} startClosed bool stopped chan struct{} stopClosed bool errors chan error } func newMockServer() *mockServer { return &mockServer{ started: make(chan struct{}), stopped: make(chan struct{}), errors: make(chan error), } } func (m *mockServer) Start(ctx context.Context) error { if !m.startClosed { close(m.started) m.startClosed = true } defer func() { if !m.stopClosed { close(m.stopped) m.stopClosed = true } }() select { case <-ctx.Done(): return nil case err := <-m.errors: return err } }