Bladeren bron

Add tests for circuit breaker

tags/v2.0.0-rc1
9seconds 5 jaren geleden
bovenliggende
commit
7c43a4b0b7

+ 2
- 1
mtglib/network/circuit_breaker.go Bestand weergeven

@@ -71,7 +71,7 @@ func (c *circuitBreakerDialer) doClosed(ctx context.Context,
71 71
 
72 72
 	c.failuresCount++
73 73
 
74
-	if c.state == circuitBreakerStateClosed && c.failuresCount > c.openThreshold {
74
+	if c.state == circuitBreakerStateClosed && c.failuresCount >= c.openThreshold {
75 75
 		c.switchState(circuitBreakerStateOpened)
76 76
 	}
77 77
 
@@ -184,6 +184,7 @@ func newCircuitBreakerDialer(baseDialer Dialer,
184 184
 	openThreshold uint32, halfOpenTimeout, resetFailuresTimeout time.Duration) Dialer {
185 185
 	cb := &circuitBreakerDialer{
186 186
 		Dialer:               baseDialer,
187
+		stateMutexChan:       make(chan bool, 1),
187 188
 		openThreshold:        openThreshold,
188 189
 		halfOpenTimeout:      halfOpenTimeout,
189 190
 		resetFailuresTimeout: resetFailuresTimeout,

+ 139
- 0
mtglib/network/circuit_breaker_internal_test.go Bestand weergeven

@@ -0,0 +1,139 @@
1
+package network
2
+
3
+import (
4
+	"context"
5
+	"errors"
6
+	"io"
7
+	"net"
8
+	"sync"
9
+	"testing"
10
+	"time"
11
+
12
+	"github.com/stretchr/testify/mock"
13
+	"github.com/stretchr/testify/suite"
14
+)
15
+
16
+type CircuitBreakerTestSuite struct {
17
+	suite.Suite
18
+
19
+	d              Dialer
20
+	mutex          sync.Mutex
21
+	ctx            context.Context
22
+	ctxCancel      context.CancelFunc
23
+	connMock       *ConnMock
24
+	baseDialerMock *DialerMock
25
+}
26
+
27
+func (suite *CircuitBreakerTestSuite) SetupTest() {
28
+	suite.mutex = sync.Mutex{}
29
+	suite.ctx, suite.ctxCancel = context.WithCancel(context.Background())
30
+	suite.baseDialerMock = &DialerMock{}
31
+	suite.connMock = &ConnMock{}
32
+	suite.d = newCircuitBreakerDialer(suite.baseDialerMock,
33
+		3, 100*time.Millisecond, 50*time.Millisecond)
34
+}
35
+
36
+func (suite *CircuitBreakerTestSuite) TearDownTest() {
37
+	suite.ctxCancel()
38
+	suite.baseDialerMock.AssertExpectations(suite.T())
39
+	suite.connMock.AssertExpectations(suite.T())
40
+}
41
+
42
+func (suite *CircuitBreakerTestSuite) TestMultipleRunsOk() {
43
+	suite.connMock.On("RemoteAddr").
44
+		Times(5).
45
+		Return(&net.TCPAddr{
46
+			IP:   net.ParseIP("127.0.0.1"),
47
+			Port: 3128,
48
+		})
49
+	suite.baseDialerMock.On("DialContext", mock.Anything, "tcp", "127.0.0.1").
50
+		Times(5).
51
+		Return(suite.connMock, nil)
52
+
53
+	wg := &sync.WaitGroup{}
54
+
55
+	wg.Add(5)
56
+
57
+	go func() {
58
+		wg.Wait()
59
+		suite.ctxCancel()
60
+	}()
61
+
62
+	for i := 0; i < 5; i++ {
63
+		go func() {
64
+			defer wg.Done()
65
+
66
+			conn, err := suite.d.DialContext(suite.ctx, "tcp", "127.0.0.1")
67
+
68
+			suite.mutex.Lock()
69
+			defer suite.mutex.Unlock()
70
+
71
+			suite.NoError(err)
72
+			suite.Equal("127.0.0.1:3128", conn.RemoteAddr().String())
73
+		}()
74
+	}
75
+
76
+	suite.Eventually(func() bool {
77
+		_, ok := <-suite.ctx.Done()
78
+
79
+		return !ok
80
+	}, time.Second, 10*time.Millisecond)
81
+}
82
+
83
+func (suite *CircuitBreakerTestSuite) TestFromClosedToOpen() {
84
+	suite.baseDialerMock.On("DialContext", mock.Anything, "tcp", "127.0.0.1").
85
+		Times(3).
86
+		Return(&net.TCPConn{}, io.EOF)
87
+
88
+	_, err := suite.d.DialContext(suite.ctx, "tcp", "127.0.0.1")
89
+	suite.True(errors.Is(err, io.EOF))
90
+
91
+	_, err = suite.d.DialContext(suite.ctx, "tcp", "127.0.0.1")
92
+	suite.True(errors.Is(err, io.EOF))
93
+
94
+	_, err = suite.d.DialContext(suite.ctx, "tcp", "127.0.0.1")
95
+	suite.True(errors.Is(err, io.EOF))
96
+
97
+	_, err = suite.d.DialContext(suite.ctx, "tcp", "127.0.0.1")
98
+	suite.True(errors.Is(err, ErrCircuitBreakerOpened))
99
+}
100
+
101
+func (suite *CircuitBreakerTestSuite) TestHalfOpen() {
102
+	suite.baseDialerMock.On("DialContext", mock.Anything, "tcp", "127.0.0.1").
103
+		Times(4).
104
+		Return(&net.TCPConn{}, io.EOF)
105
+	suite.baseDialerMock.On("DialContext", mock.Anything, "tcp", "127.0.0.2").
106
+		Twice().
107
+		Return(suite.connMock, nil)
108
+	suite.connMock.On("RemoteAddr").Return(&net.TCPAddr{
109
+		IP:   net.ParseIP("10.0.0.10"),
110
+		Port: 80,
111
+	})
112
+
113
+	suite.d.DialContext(suite.ctx, "tcp", "127.0.0.1")
114
+	suite.d.DialContext(suite.ctx, "tcp", "127.0.0.1")
115
+	suite.d.DialContext(suite.ctx, "tcp", "127.0.0.1")
116
+	suite.d.DialContext(suite.ctx, "tcp", "127.0.0.1")
117
+
118
+	time.Sleep(500 * time.Millisecond)
119
+
120
+	_, err := suite.d.DialContext(suite.ctx, "tcp", "127.0.0.1")
121
+	suite.True(errors.Is(err, io.EOF))
122
+
123
+	_, err = suite.d.DialContext(suite.ctx, "tcp", "127.0.0.1")
124
+	suite.True(errors.Is(err, ErrCircuitBreakerOpened))
125
+
126
+	time.Sleep(500 * time.Millisecond)
127
+
128
+	conn, err := suite.d.DialContext(suite.ctx, "tcp", "127.0.0.2")
129
+
130
+	suite.NoError(err)
131
+	suite.Equal("10.0.0.10:80", conn.RemoteAddr().String())
132
+
133
+	_, err = suite.d.DialContext(suite.ctx, "tcp", "127.0.0.2")
134
+	suite.NoError(err)
135
+}
136
+
137
+func TestCircuitBreaker(t *testing.T) {
138
+	suite.Run(t, &CircuitBreakerTestSuite{})
139
+}

+ 65
- 0
mtglib/network/init_internal_test.go Bestand weergeven

@@ -0,0 +1,65 @@
1
+package network
2
+
3
+import (
4
+	"context"
5
+	"net"
6
+	"time"
7
+
8
+	"github.com/stretchr/testify/mock"
9
+)
10
+
11
+type ConnMock struct {
12
+	mock.Mock
13
+}
14
+
15
+func (c *ConnMock) Read(b []byte) (int, error) {
16
+	args := c.Called(b)
17
+
18
+	return args.Int(0), args.Error(1)
19
+}
20
+
21
+func (c *ConnMock) Write(b []byte) (int, error) {
22
+	args := c.Called(b)
23
+
24
+	return args.Int(0), args.Error(1)
25
+}
26
+
27
+func (c *ConnMock) Close() error {
28
+	return c.Called().Error(0)
29
+}
30
+
31
+func (c *ConnMock) LocalAddr() net.Addr {
32
+	return c.Called().Get(0).(net.Addr)
33
+}
34
+
35
+func (c *ConnMock) RemoteAddr() net.Addr {
36
+	return c.Called().Get(0).(net.Addr)
37
+}
38
+
39
+func (c *ConnMock) SetDeadline(t time.Time) error {
40
+	return c.Called(t).Error(0)
41
+}
42
+
43
+func (c *ConnMock) SetReadDeadline(t time.Time) error {
44
+	return c.Called(t).Error(0)
45
+}
46
+
47
+func (c *ConnMock) SetWriteDeadline(t time.Time) error {
48
+	return c.Called(t).Error(0)
49
+}
50
+
51
+type DialerMock struct {
52
+	mock.Mock
53
+}
54
+
55
+func (d *DialerMock) Dial(network, address string) (net.Conn, error) {
56
+	args := d.Called(network, address)
57
+
58
+	return args.Get(0).(net.Conn), args.Error(1)
59
+}
60
+
61
+func (d *DialerMock) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
62
+	args := d.Called(ctx, network, address)
63
+
64
+	return args.Get(0).(net.Conn), args.Error(1)
65
+}

+ 0
- 60
mtglib/network/init_test.go Bestand weergeven

@@ -1,73 +1,13 @@
1 1
 package network_test
2 2
 
3 3
 import (
4
-	"context"
5
-	"net"
6 4
 	"net/http/httptest"
7 5
 	"strings"
8
-	"time"
9 6
 
10 7
 	"github.com/mccutchen/go-httpbin/httpbin"
11
-	"github.com/stretchr/testify/mock"
12 8
 	"github.com/stretchr/testify/suite"
13 9
 )
14 10
 
15
-type ConnMock struct {
16
-	mock.Mock
17
-}
18
-
19
-func (c *ConnMock) Read(b []byte) (int, error) {
20
-	args := c.Called(b)
21
-
22
-	return args.Int(0), args.Error(1)
23
-}
24
-
25
-func (c *ConnMock) Write(b []byte) (int, error) {
26
-	args := c.Called(b)
27
-
28
-	return args.Int(0), args.Error(1)
29
-}
30
-
31
-func (c *ConnMock) Close() error {
32
-	return c.Called().Error(0)
33
-}
34
-
35
-func (c *ConnMock) LocalAddr() net.Addr {
36
-	return c.Called().Get(0).(net.Addr)
37
-}
38
-
39
-func (c *ConnMock) RemoteAddr() net.Addr {
40
-	return c.Called().Get(0).(net.Addr)
41
-}
42
-
43
-func (c *ConnMock) SetDeadline(t time.Time) error {
44
-	return c.Called(t).Error(0)
45
-}
46
-
47
-func (c *ConnMock) SetReadDeadline(t time.Time) error {
48
-	return c.Called(t).Error(0)
49
-}
50
-
51
-func (c *ConnMock) SetWriteDeadline(t time.Time) error {
52
-	return c.Called(t).Error(0)
53
-}
54
-
55
-type DialerMock struct {
56
-	mock.Mock
57
-}
58
-
59
-func (d *DialerMock) Dial(network, address string) (net.Conn, error) {
60
-	args := d.Called(network, address)
61
-
62
-	return args.Get(0).(net.Conn), args.Error(1)
63
-}
64
-
65
-func (d *DialerMock) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
66
-	args := d.Called(ctx, network, address)
67
-
68
-	return args.Get(0).(net.Conn), args.Error(1)
69
-}
70
-
71 11
 type HTTPServerTestSuite struct {
72 12
 	suite.Suite
73 13
 

Laden…
Annuleren
Opslaan