Highly-opinionated (ex-bullshit-free) MTPROTO proxy for Telegram. If you use v1.0 or upgrade broke you proxy, please read the chapter Version 2
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

circuit_breaker_internal_test.go 3.6KB

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