@@ -0,0 +1,102 @@
1+package http
2+
3+import (
4+ "context"
5+ "errors"
6+
7+ . "github.com/onsi/ginkgo"
8+ . "github.com/onsi/gomega"
9+)
10+
11+var _ = Describe("Server Group", func() {
12+ var m1, m2, m3 *mockServer
13+ var ctx context.Context
14+ var cancel context.CancelFunc
15+ var group Server
16+
17+ BeforeEach(func() {
18+ ctx, cancel = context.WithCancel(context.Background())
19+
20+ m1 = newMockServer()
21+ m2 = newMockServer()
22+ m3 = newMockServer()
23+ group = NewServerGroup(m1, m2, m3)
24+ })
25+
26+ AfterEach(func() {
27+ cancel()
28+ })
29+
30+ It("starts each server in the group", func() {
31+ go func() {
32+ defer GinkgoRecover()
33+ Expect(group.Start(ctx)).To(Succeed())
34+ }()
35+
36+ Eventually(m1.started).Should(BeClosed(), "mock server 1 not started")
37+ Eventually(m2.started).Should(BeClosed(), "mock server 2 not started")
38+ Eventually(m3.started).Should(BeClosed(), "mock server 3 not started")
39+ })
40+
41+ It("stop each server in the group when the context is cancelled", func() {
42+ go func() {
43+ defer GinkgoRecover()
44+ Expect(group.Start(ctx)).To(Succeed())
45+ }()
46+
47+ cancel()
48+ Eventually(m1.stopped).Should(BeClosed(), "mock server 1 not stopped")
49+ Eventually(m2.stopped).Should(BeClosed(), "mock server 2 not stopped")
50+ Eventually(m3.stopped).Should(BeClosed(), "mock server 3 not stopped")
51+ })
52+
53+ It("stop each server in the group when the an error occurs", func() {
54+ err := errors.New("server error")
55+ go func() {
56+ defer GinkgoRecover()
57+ Expect(group.Start(ctx)).To(MatchError(err))
58+ }()
59+
60+ m2.errors <- err
61+ Eventually(m1.stopped).Should(BeClosed(), "mock server 1 not stopped")
62+ Eventually(m2.stopped).Should(BeClosed(), "mock server 2 not stopped")
63+ Eventually(m3.stopped).Should(BeClosed(), "mock server 3 not stopped")
64+ })
65+})
66+
67+// mockServer is used to test the server group can start
68+// and stop multiple servers simultaneously.
69+type mockServer struct {
70+ started chan struct{}
71+ startClosed bool
72+ stopped chan struct{}
73+ stopClosed bool
74+ errors chan error
75+}
76+
77+func newMockServer() *mockServer {
78+ return &mockServer{
79+ started: make(chan struct{}),
80+ stopped: make(chan struct{}),
81+ errors: make(chan error),
82+ }
83+}
84+
85+func (m *mockServer) Start(ctx context.Context) error {
86+ if !m.startClosed {
87+ close(m.started)
88+ m.startClosed = true
89+ }
90+ defer func() {
91+ if !m.stopClosed {
92+ close(m.stopped)
93+ m.stopClosed = true
94+ }
95+ }()
96+ select {
97+ case <-ctx.Done():
98+ return nil
99+ case err := <-m.errors:
100+ return err
101+ }
102+}