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
Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

circuit_breaker_internal_test.go 3.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  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.EssentialsConnMock
  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.EssentialsConnMock{}
  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. for range 5 {
  48. wg.Go(func() {
  49. conn, err := suite.d.DialContext(suite.ctx, "tcp", "127.0.0.1")
  50. suite.mutex.Lock()
  51. defer suite.mutex.Unlock()
  52. suite.NoError(err)
  53. suite.Equal("127.0.0.1:3128", conn.RemoteAddr().String())
  54. })
  55. }
  56. go func() {
  57. wg.Wait()
  58. suite.ctxCancel()
  59. }()
  60. suite.Eventually(func() bool {
  61. _, ok := <-suite.ctx.Done()
  62. return !ok
  63. }, time.Second, 10*time.Millisecond)
  64. }
  65. func (suite *CircuitBreakerTestSuite) TestFromClosedToOpen() {
  66. suite.baseDialerMock.On("DialContext", mock.Anything, "tcp", "127.0.0.1").
  67. Times(3).
  68. Return(&net.TCPConn{}, io.EOF)
  69. _, err := suite.d.DialContext(suite.ctx, "tcp", "127.0.0.1")
  70. suite.True(errors.Is(err, 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, ErrCircuitBreakerOpened))
  77. }
  78. func (suite *CircuitBreakerTestSuite) TestHalfOpen() {
  79. suite.baseDialerMock.On("DialContext", mock.Anything, "tcp", "127.0.0.1").
  80. Times(4).
  81. Return(&net.TCPConn{}, io.EOF)
  82. suite.baseDialerMock.On("DialContext", mock.Anything, "tcp", "127.0.0.2").
  83. Twice().
  84. Return(suite.connMock, nil)
  85. suite.connMock.On("RemoteAddr").Return(&net.TCPAddr{
  86. IP: net.ParseIP("10.0.0.10"),
  87. Port: 80,
  88. })
  89. suite.d.DialContext(suite.ctx, "tcp", "127.0.0.1") //nolint: errcheck
  90. suite.d.DialContext(suite.ctx, "tcp", "127.0.0.1") //nolint: errcheck
  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. time.Sleep(500 * time.Millisecond)
  94. _, err := suite.d.DialContext(suite.ctx, "tcp", "127.0.0.1")
  95. suite.True(errors.Is(err, io.EOF))
  96. _, err = suite.d.DialContext(suite.ctx, "tcp", "127.0.0.1")
  97. suite.True(errors.Is(err, ErrCircuitBreakerOpened))
  98. time.Sleep(500 * time.Millisecond)
  99. conn, err := suite.d.DialContext(suite.ctx, "tcp", "127.0.0.2")
  100. suite.NoError(err)
  101. suite.Equal("10.0.0.10:80", conn.RemoteAddr().String())
  102. _, err = suite.d.DialContext(suite.ctx, "tcp", "127.0.0.2")
  103. suite.NoError(err)
  104. }
  105. func TestCircuitBreaker(t *testing.T) {
  106. t.Parallel()
  107. suite.Run(t, &CircuitBreakerTestSuite{})
  108. }