Migrate logging to go-mana-component; delete logging/ package

Replaces the flat key-value logging scheme with component-based structured
logging via go-mana-component. Each layer (pool, worker, connection) builds
its own component identity and derives a *slog.Logger from a caller-supplied
slog.Handler.

- Delete logging/ package (logging.go, logging_test.go)
- Strip LoggingEnabled and LogLevel from ConnectionConfig, PoolConfig,
 WorkerConfig; remove associated option funcs
- Change NewConnection and NewConnectionFromSocket to accept ctx and
 slog.Handler instead of *slog.Logger; constructors build component
 identity via MustNew/MustExtend internally
- Change WorkerFactory, NewWorker, connect, and RunDialer to carry
 slog.Handler; remove PoolPlugin.Handler
- Change NewPool to establish pool component identity via MustNew;
 remove pool_id field, PoolPlugin.ID, and ErrInvalidPoolID
- Fix data race in MockSlogHandler: WithAttrs now shares parent mutex
 pointer rather than allocating a new one per child
- Run go fix
This commit is contained in:
Jay
2026-05-20 11:44:54 -04:00
parent 5b31db304a
commit b44a46ed2f
28 changed files with 179 additions and 464 deletions
-20
View File
@@ -1,7 +1,6 @@
package transport
import (
"log/slog"
"net/http"
"time"
)
@@ -15,8 +14,6 @@ type ConnectionConfig struct {
PingInterval time.Duration
IncomingBufferSize int
ErrorsBufferSize int
LoggingEnabled bool
LogLevel *slog.Level
Retry *RetryConfig
}
@@ -50,8 +47,6 @@ func GetDefaultConnectionConfig() *ConnectionConfig {
PingInterval: 20 * time.Second,
IncomingBufferSize: 100,
ErrorsBufferSize: 10,
LoggingEnabled: true,
LogLevel: nil,
Retry: GetDefaultRetryConfig(),
}
}
@@ -216,21 +211,6 @@ func WithErrorsBufferSize(value int) ConnectionOption {
}
}
func WithLoggingEnabled(value bool) ConnectionOption {
return func(c *ConnectionConfig) error {
c.LoggingEnabled = value
return nil
}
}
func WithLogLevel(level slog.Level) ConnectionOption {
return func(c *ConnectionConfig) error {
l := level
c.LogLevel = &l
return nil
}
}
func WithoutRetry() ConnectionOption {
return func(c *ConnectionConfig) error {
c.Retry = nil
-7
View File
@@ -2,7 +2,6 @@ package transport
import (
"github.com/stretchr/testify/assert"
"log/slog"
"net/http"
"testing"
"time"
@@ -36,8 +35,6 @@ func TestDefaultConnectionConfig(t *testing.T) {
PingInterval: 20 * time.Second,
IncomingBufferSize: 100,
ErrorsBufferSize: 10,
LoggingEnabled: true,
LogLevel: nil,
Retry: GetDefaultRetryConfig(),
})
}
@@ -61,8 +58,6 @@ func TestApplyConnectionOptions(t *testing.T) {
conf,
WithIncomingBufferSize(256),
WithErrorsBufferSize(100),
WithLoggingEnabled(false),
WithLogLevel(slog.LevelError),
WithRetryMaxRetries(0),
WithRetryInitialDelay(3*time.Second),
WithRetryJitterFactor(0.5),
@@ -71,8 +66,6 @@ func TestApplyConnectionOptions(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, 256, conf.IncomingBufferSize)
assert.Equal(t, 100, conf.ErrorsBufferSize)
assert.False(t, conf.LoggingEnabled)
assert.Equal(t, slog.LevelError, *conf.LogLevel)
assert.Equal(t, 0, conf.Retry.MaxRetries)
assert.Equal(t, 3*time.Second, conf.Retry.InitialDelay)
assert.Equal(t, 0.5, conf.Retry.JitterFactor)
+31 -10
View File
@@ -12,6 +12,7 @@ import (
"time"
"git.wisehodl.dev/jay/go-honeybee/types"
component "git.wisehodl.dev/jay/go-mana-component"
"github.com/gorilla/websocket"
)
@@ -74,7 +75,7 @@ type Connection struct {
cleanupOnce sync.Once
}
func NewConnection(urlStr string, config *ConnectionConfig, logger *slog.Logger) (*Connection, error) {
func NewConnection(ctx context.Context, urlStr string, config *ConnectionConfig, handler slog.Handler) (*Connection, error) {
if config == nil {
config = GetDefaultConnectionConfig()
}
@@ -88,6 +89,18 @@ func NewConnection(urlStr string, config *ConnectionConfig, logger *slog.Logger)
return nil, err
}
if component.FromContext(ctx) == nil {
ctx = component.MustNew(ctx, "honeybee", "connection")
} else {
ctx = component.MustExtend(ctx, "connection")
}
var logger *slog.Logger
if handler != nil {
c := component.FromContext(ctx)
logger = slog.New(handler).With(slog.Any("component", c))
}
conn := &Connection{
url: url,
dialer: NewDialer(),
@@ -108,7 +121,7 @@ func NewConnection(urlStr string, config *ConnectionConfig, logger *slog.Logger)
}
func NewConnectionFromSocket(
socket types.Socket, config *ConnectionConfig, logger *slog.Logger,
ctx context.Context, socket types.Socket, config *ConnectionConfig, handler slog.Handler,
) (*Connection, error) {
if socket == nil {
return nil, NewConnectionError(ErrNilSocket)
@@ -122,6 +135,18 @@ func NewConnectionFromSocket(
return nil, err
}
if component.FromContext(ctx) == nil {
ctx = component.MustNew(ctx, "honeybee", "connection")
} else {
ctx = component.MustExtend(ctx, "connection")
}
var logger *slog.Logger
if handler != nil {
c := component.FromContext(ctx)
logger = slog.New(handler).With(slog.Any("component", c))
}
conn := &Connection{
url: nil,
dialer: nil,
@@ -293,9 +318,7 @@ func (c *Connection) shutdownLogComplete() {
}
func (c *Connection) startReader() {
c.wg.Add(1)
go func() {
defer c.wg.Done()
c.wg.Go(func() {
defer c.shutdownInternal()
for {
@@ -362,7 +385,7 @@ func (c *Connection) startReader() {
}
}
}()
})
}
func (c *Connection) setupPongHandler() {
@@ -381,9 +404,7 @@ func (c *Connection) startPinger() {
return
}
c.wg.Add(1)
go func() {
defer c.wg.Done()
c.wg.Go(func() {
defer c.shutdownInternal()
// Calculate 10% jitter window
@@ -404,7 +425,7 @@ func (c *Connection) startPinger() {
}
}
}
}()
})
}
+7 -6
View File
@@ -2,6 +2,7 @@ package transport
import (
"bytes"
"context"
"fmt"
"git.wisehodl.dev/jay/go-honeybee/honeybeetest"
"github.com/gorilla/websocket"
@@ -11,7 +12,7 @@ import (
func TestDisconnectedConnectionClose(t *testing.T) {
t.Run("close succeeds on disconnected connection", func(t *testing.T) {
conn, err := NewConnection("ws://test", nil, nil)
conn, err := NewConnection(context.Background(), "ws://test", nil, nil)
assert.NoError(t, err)
assert.Equal(t, StateDisconnected, conn.State())
@@ -20,7 +21,7 @@ func TestDisconnectedConnectionClose(t *testing.T) {
})
t.Run("close is idempotent", func(t *testing.T) {
conn, err := NewConnection("ws://test", nil, nil)
conn, err := NewConnection(context.Background(), "ws://test", nil, nil)
assert.NoError(t, err)
conn.Close()
@@ -29,7 +30,7 @@ func TestDisconnectedConnectionClose(t *testing.T) {
})
t.Run("close with nil socket", func(t *testing.T) {
conn, err := NewConnection("ws://test", nil, nil)
conn, err := NewConnection(context.Background(), "ws://test", nil, nil)
assert.NoError(t, err)
assert.Nil(t, conn.socket)
@@ -44,7 +45,7 @@ func TestDisconnectedConnectionClose(t *testing.T) {
return expectedErr
}
conn, err := NewConnection("ws://test", nil, nil)
conn, err := NewConnection(context.Background(), "ws://test", nil, nil)
assert.NoError(t, err)
conn.socket = mockSocket
@@ -53,7 +54,7 @@ func TestDisconnectedConnectionClose(t *testing.T) {
})
t.Run("channels close after close", func(t *testing.T) {
conn, err := NewConnection("ws://test", nil, nil)
conn, err := NewConnection(context.Background(), "ws://test", nil, nil)
assert.NoError(t, err)
conn.Close()
@@ -66,7 +67,7 @@ func TestDisconnectedConnectionClose(t *testing.T) {
})
t.Run("send fails after close", func(t *testing.T) {
conn, err := NewConnection("ws://test", nil, nil)
conn, err := NewConnection(context.Background(), "ws://test", nil, nil)
assert.NoError(t, err)
conn.Close()
+2 -1
View File
@@ -1,6 +1,7 @@
package transport
import (
"context"
"git.wisehodl.dev/jay/go-honeybee/honeybeetest"
"github.com/gorilla/websocket"
"github.com/stretchr/testify/assert"
@@ -66,7 +67,7 @@ func TestStartReader(t *testing.T) {
return 0, nil, io.EOF
}
conn, err := NewConnectionFromSocket(mockSocket, nil, nil)
conn, err := NewConnectionFromSocket(context.Background(), mockSocket, nil, nil)
assert.NoError(t, err)
honeybeetest.Eventually(t, func() bool {
+8 -7
View File
@@ -1,6 +1,7 @@
package transport
import (
"context"
"fmt"
"git.wisehodl.dev/jay/go-honeybee/honeybeetest"
"github.com/gorilla/websocket"
@@ -62,12 +63,12 @@ func TestConnectionSend(t *testing.T) {
defer close(done)
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
for i := range 5 {
wg.Add(1)
go func(id int) {
defer wg.Done()
for j := 0; j < 10; j++ {
data := []byte(fmt.Sprintf("msg-%d-%d", id, j))
for j := range 10 {
data := fmt.Appendf(nil, "msg-%d-%d", id, j)
for {
// send and retry until success
err := conn.Send(data)
@@ -129,7 +130,7 @@ func TestConnectionSend(t *testing.T) {
return nil
}
conn, err := NewConnectionFromSocket(mockSocket, config, nil)
conn, err := NewConnectionFromSocket(context.Background(), mockSocket, config, nil)
assert.NoError(t, err)
defer conn.Close()
@@ -175,7 +176,7 @@ func TestConnectionSend(t *testing.T) {
return nil
}
conn, err := NewConnectionFromSocket(mockSocket, config, nil)
conn, err := NewConnectionFromSocket(context.Background(), mockSocket, config, nil)
assert.NoError(t, err)
defer conn.Close()
@@ -208,7 +209,7 @@ func TestConnectionSend(t *testing.T) {
return fmt.Errorf("test error")
}
conn, err := NewConnectionFromSocket(mockSocket, config, nil)
conn, err := NewConnectionFromSocket(context.Background(), mockSocket, config, nil)
assert.NoError(t, err)
defer conn.Close()
@@ -228,7 +229,7 @@ func TestConnectionSend(t *testing.T) {
return writeErr
}
conn, err := NewConnectionFromSocket(mockSocket, nil, nil)
conn, err := NewConnectionFromSocket(context.Background(), mockSocket, nil, nil)
assert.NoError(t, err)
defer conn.Close()
+20 -20
View File
@@ -39,11 +39,11 @@ func TestConnectionStateString(t *testing.T) {
func TestConnectionState(t *testing.T) {
// Test initial state
conn, _ := NewConnection("ws://test", nil, nil)
conn, _ := NewConnection(context.Background(), "ws://test", nil, nil)
assert.Equal(t, StateDisconnected, conn.State())
// Test state after FromSocket (should be Connected)
conn2, _ := NewConnectionFromSocket(honeybeetest.NewMockSocket(), nil, nil)
conn2, _ := NewConnectionFromSocket(context.Background(), honeybeetest.NewMockSocket(), nil, nil)
assert.Equal(t, StateConnected, conn2.State())
// Test state after close
@@ -94,7 +94,7 @@ func TestNewConnection(t *testing.T) {
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
conn, err := NewConnection(tc.url, tc.config, nil)
conn, err := NewConnection(context.Background(), tc.url, tc.config, nil)
if tc.wantErr {
assert.Error(t, err)
@@ -194,7 +194,7 @@ func TestNewConnectionFromSocket(t *testing.T) {
}
}
conn, err := NewConnectionFromSocket(tc.socket, tc.config, nil)
conn, err := NewConnectionFromSocket(context.Background(), tc.socket, tc.config, nil)
if tc.wantErr {
assert.Error(t, err)
@@ -236,7 +236,7 @@ func TestNewConnectionFromSocket(t *testing.T) {
func TestConnect(t *testing.T) {
t.Run("connect fails when socket already present", func(t *testing.T) {
conn, err := NewConnection("ws://test", nil, nil)
conn, err := NewConnection(context.Background(), "ws://test", nil, nil)
assert.NoError(t, err)
conn.socket = honeybeetest.NewMockSocket()
@@ -248,7 +248,7 @@ func TestConnect(t *testing.T) {
})
t.Run("connect fails when connection closed", func(t *testing.T) {
conn, err := NewConnection("ws://test", nil, nil)
conn, err := NewConnection(context.Background(), "ws://test", nil, nil)
assert.NoError(t, err)
conn.Close()
@@ -260,7 +260,7 @@ func TestConnect(t *testing.T) {
})
t.Run("connect succeeds and starts goroutines", func(t *testing.T) {
conn, err := NewConnection("ws://test", nil, nil)
conn, err := NewConnection(context.Background(), "ws://test", nil, nil)
assert.NoError(t, err)
outgoingData := make(chan honeybeetest.MockOutgoingData, 10)
@@ -306,7 +306,7 @@ func TestConnect(t *testing.T) {
JitterFactor: 0.0,
},
}
conn, err := NewConnection("ws://test", config, nil)
conn, err := NewConnection(context.Background(), "ws://test", config, nil)
assert.NoError(t, err)
attemptCount := 0
@@ -338,7 +338,7 @@ func TestConnect(t *testing.T) {
JitterFactor: 0.0,
},
}
conn, err := NewConnection("ws://test", config, nil)
conn, err := NewConnection(context.Background(), "ws://test", config, nil)
assert.NoError(t, err)
mockDialer := &honeybeetest.MockDialer{
@@ -355,7 +355,7 @@ func TestConnect(t *testing.T) {
})
t.Run("state transitions during connect", func(t *testing.T) {
conn, err := NewConnection("ws://test", nil, nil)
conn, err := NewConnection(context.Background(), "ws://test", nil, nil)
assert.NoError(t, err)
assert.Equal(t, StateDisconnected, conn.State())
@@ -383,7 +383,7 @@ func TestConnect(t *testing.T) {
return nil
},
}
conn, err := NewConnection("ws://test", config, nil)
conn, err := NewConnection(context.Background(), "ws://test", config, nil)
assert.NoError(t, err)
mockSocket := honeybeetest.NewMockSocket()
@@ -408,7 +408,7 @@ func TestConnect(t *testing.T) {
t.Run("passes headers when configured", func(t *testing.T) {
header := http.Header{"X-Custom": []string{"val"}}
conf, _ := NewConnectionConfig(WithRequestHeader(header))
conn, _ := NewConnection("ws://test", conf, nil)
conn, _ := NewConnection(context.Background(), "ws://test", conf, nil)
dialCalled := false
conn.dialer = &honeybeetest.MockDialer{
@@ -436,7 +436,7 @@ func TestConnectContextCancellation(t *testing.T) {
JitterFactor: 0.0,
},
}
conn, err := NewConnection("ws://test", config, nil)
conn, err := NewConnection(context.Background(), "ws://test", config, nil)
assert.NoError(t, err)
dialCount := atomic.Int32{}
@@ -475,7 +475,7 @@ func TestConnectContextCancellation(t *testing.T) {
// Connection method tests
func TestConnectionIncoming(t *testing.T) {
conn, err := NewConnection("ws://test", nil, nil)
conn, err := NewConnection(context.Background(), "ws://test", nil, nil)
assert.NoError(t, err)
incoming := conn.Incoming()
@@ -498,7 +498,7 @@ func TestConnectionErrors(t *testing.T) {
}
}
conn, err := NewConnectionFromSocket(mockSocket, nil, nil)
conn, err := NewConnectionFromSocket(context.Background(), mockSocket, nil, nil)
assert.NoError(t, err)
defer conn.Close()
@@ -521,7 +521,7 @@ func TestConnectionErrors(t *testing.T) {
}
}
conn, err := NewConnectionFromSocket(mockSocket, nil, nil)
conn, err := NewConnectionFromSocket(context.Background(), mockSocket, nil, nil)
assert.NoError(t, err)
defer conn.Close()
@@ -541,7 +541,7 @@ func TestConnectionErrors(t *testing.T) {
return 0, nil, io.EOF
}
conn, err := NewConnectionFromSocket(mockSocket, nil, nil)
conn, err := NewConnectionFromSocket(context.Background(), mockSocket, nil, nil)
assert.NoError(t, err)
defer conn.Close()
@@ -573,7 +573,7 @@ func TestConnectionHeartbeat(t *testing.T) {
)
assert.NoError(t, err)
conn, _ := NewConnectionFromSocket(socket, conf, nil)
conn, _ := NewConnectionFromSocket(context.Background(), socket, conf, nil)
defer conn.Close()
honeybeetest.Eventually(t,
@@ -586,7 +586,7 @@ func TestConnectionHeartbeat(t *testing.T) {
socket, _, _ := honeybeetest.SetupTestSocket(t)
socket.SetPongHandlerFunc = func(h func(string) error) { handler = h }
conn, _ := NewConnectionFromSocket(socket, nil, nil)
conn, _ := NewConnectionFromSocket(context.Background(), socket, nil, nil)
defer conn.Close()
honeybeetest.Eventually(t, func() bool {
@@ -620,7 +620,7 @@ func setupTestConnection(t *testing.T) (
socket, incoming, outgoing = honeybeetest.SetupTestSocket(t)
var err error
conn, err = NewConnectionFromSocket(socket, nil, nil)
conn, err = NewConnectionFromSocket(context.Background(), socket, nil, nil)
assert.NoError(t, err)
return
}
+12 -21
View File
@@ -8,6 +8,7 @@ import (
"net/http"
"testing"
"time"
// slog used for ExpectedLog level constants
"git.wisehodl.dev/jay/go-honeybee/honeybeetest"
"git.wisehodl.dev/jay/go-honeybee/types"
@@ -26,9 +27,8 @@ func log(level slog.Level, msg string, attrs map[string]any) honeybeetest.Expect
func TestConnectLogging(t *testing.T) {
t.Run("success", func(t *testing.T) {
mockHandler := honeybeetest.NewMockSlogHandler()
logger := slog.New(mockHandler)
conn, err := NewConnection("ws://test", nil, logger)
conn, err := NewConnection(context.Background(), "ws://test", nil, mockHandler)
assert.NoError(t, err)
mockSocket := honeybeetest.NewMockSocket()
@@ -57,7 +57,6 @@ func TestConnectLogging(t *testing.T) {
t.Run("max retries failure", func(t *testing.T) {
mockHandler := honeybeetest.NewMockSlogHandler()
logger := slog.New(mockHandler)
config := &ConnectionConfig{
Retry: &RetryConfig{
@@ -68,7 +67,7 @@ func TestConnectLogging(t *testing.T) {
},
}
conn, err := NewConnection("ws://test", config, logger)
conn, err := NewConnection(context.Background(), "ws://test", config, mockHandler)
assert.NoError(t, err)
dialErr := fmt.Errorf("dial error")
@@ -100,7 +99,6 @@ func TestConnectLogging(t *testing.T) {
t.Run("success after retry", func(t *testing.T) {
mockHandler := honeybeetest.NewMockSlogHandler()
logger := slog.New(mockHandler)
config := &ConnectionConfig{
Retry: &RetryConfig{
@@ -111,7 +109,7 @@ func TestConnectLogging(t *testing.T) {
},
}
conn, err := NewConnection("ws://test", config, logger)
conn, err := NewConnection(context.Background(), "ws://test", config, mockHandler)
assert.NoError(t, err)
attemptCount := 0
@@ -151,10 +149,9 @@ func TestConnectLogging(t *testing.T) {
func TestCloseLogging(t *testing.T) {
t.Run("normal close", func(t *testing.T) {
mockHandler := honeybeetest.NewMockSlogHandler()
logger := slog.New(mockHandler)
mockSocket := honeybeetest.NewMockSocket()
conn, err := NewConnectionFromSocket(mockSocket, nil, logger)
conn, err := NewConnectionFromSocket(context.Background(), mockSocket, nil, mockHandler)
assert.NoError(t, err)
conn.Close()
@@ -176,7 +173,6 @@ func TestCloseLogging(t *testing.T) {
t.Run("close with socket error", func(t *testing.T) {
mockHandler := honeybeetest.NewMockSlogHandler()
logger := slog.New(mockHandler)
closeErr := fmt.Errorf("close error")
mockSocket := honeybeetest.NewMockSocket()
@@ -184,7 +180,7 @@ func TestCloseLogging(t *testing.T) {
return closeErr
}
conn, err := NewConnectionFromSocket(mockSocket, nil, logger)
conn, err := NewConnectionFromSocket(context.Background(), mockSocket, nil, mockHandler)
assert.NoError(t, err)
conn.Close()
@@ -208,7 +204,6 @@ func TestCloseLogging(t *testing.T) {
func TestReaderLogging(t *testing.T) {
t.Run("clean close by peer", func(t *testing.T) {
mockHandler := honeybeetest.NewMockSlogHandler()
logger := slog.New(mockHandler)
mockSocket := honeybeetest.NewMockSocket()
mockSocket.ReadMessageFunc = func() (int, []byte, error) {
@@ -218,7 +213,7 @@ func TestReaderLogging(t *testing.T) {
}
}
conn, err := NewConnectionFromSocket(mockSocket, nil, logger)
conn, err := NewConnectionFromSocket(context.Background(), mockSocket, nil, mockHandler)
assert.NoError(t, err)
defer conn.Close()
@@ -236,7 +231,6 @@ func TestReaderLogging(t *testing.T) {
t.Run("unexpected close", func(t *testing.T) {
mockHandler := honeybeetest.NewMockSlogHandler()
logger := slog.New(mockHandler)
mockSocket := honeybeetest.NewMockSocket()
mockSocket.ReadMessageFunc = func() (int, []byte, error) {
@@ -246,7 +240,7 @@ func TestReaderLogging(t *testing.T) {
}
}
conn, err := NewConnectionFromSocket(mockSocket, nil, logger)
conn, err := NewConnectionFromSocket(context.Background(), mockSocket, nil, mockHandler)
assert.NoError(t, err)
defer conn.Close()
@@ -264,14 +258,13 @@ func TestReaderLogging(t *testing.T) {
t.Run("read error", func(t *testing.T) {
mockHandler := honeybeetest.NewMockSlogHandler()
logger := slog.New(mockHandler)
mockSocket := honeybeetest.NewMockSocket()
mockSocket.ReadMessageFunc = func() (int, []byte, error) {
return 0, nil, io.EOF
}
conn, err := NewConnectionFromSocket(mockSocket, nil, logger)
conn, err := NewConnectionFromSocket(context.Background(), mockSocket, nil, mockHandler)
assert.NoError(t, err)
defer conn.Close()
@@ -285,7 +278,6 @@ func TestReaderLogging(t *testing.T) {
func TestWriterLogging(t *testing.T) {
t.Run("write deadline error", func(t *testing.T) {
mockHandler := honeybeetest.NewMockSlogHandler()
logger := slog.New(mockHandler)
config := &ConnectionConfig{WriteTimeout: 1 * time.Millisecond}
@@ -295,7 +287,7 @@ func TestWriterLogging(t *testing.T) {
return deadlineErr
}
conn, err := NewConnectionFromSocket(mockSocket, config, logger)
conn, err := NewConnectionFromSocket(context.Background(), mockSocket, config, mockHandler)
assert.NoError(t, err)
err = conn.Send([]byte("test"))
@@ -317,7 +309,6 @@ func TestWriterLogging(t *testing.T) {
t.Run("write message error", func(t *testing.T) {
mockHandler := honeybeetest.NewMockSlogHandler()
logger := slog.New(mockHandler)
writeErr := fmt.Errorf("write error")
mockSocket := honeybeetest.NewMockSocket()
@@ -325,7 +316,7 @@ func TestWriterLogging(t *testing.T) {
return writeErr
}
conn, err := NewConnectionFromSocket(mockSocket, nil, logger)
conn, err := NewConnectionFromSocket(context.Background(), mockSocket, nil, mockHandler)
assert.NoError(t, err)
err = conn.Send([]byte("test"))
@@ -350,7 +341,7 @@ func TestLoggingDisabled(t *testing.T) {
t.Run("nil logger produces no logs", func(t *testing.T) {
mockHandler := honeybeetest.NewMockSlogHandler()
conn, err := NewConnection("ws://test", nil, nil)
conn, err := NewConnection(context.Background(), "ws://test", nil, nil)
assert.NoError(t, err)
mockSocket := honeybeetest.NewMockSocket()
+4 -10
View File
@@ -59,22 +59,16 @@ func (r *RetryManager) CalculateDelay() time.Duration {
}
// Exponential backoff: InitialDelay * 2^(attempts-1)
shift := r.retryCount - 1
if shift > 62 {
shift = 62
} // prevent overflow
shift := min(r.retryCount-1, 62) // prevent overflow
backoffMultiplier := float64(int64(1) << shift)
baseDelay := float64(r.config.InitialDelay) * backoffMultiplier
// Apply jitter: delay * (1 + jitterFactor * (random - 0.5))
random := rand.Float64()
jitterMultiplier := 1 + r.config.JitterFactor*(random-0.5)
delay := time.Duration(baseDelay * jitterMultiplier)
// Cap at MaxDelay
if delay > r.config.MaxDelay {
delay = r.config.MaxDelay
}
delay := min(
// Cap at MaxDelay
time.Duration(baseDelay*jitterMultiplier), r.config.MaxDelay)
return delay
}
+4 -6
View File
@@ -12,15 +12,14 @@ import (
func TestIdleWatchdog(t *testing.T) {
t.Run("heartbeat resets timer, onTimeout not called", func(t *testing.T) {
activity := make(chan struct{})
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
ctx := t.Context()
called := atomic.Bool{}
go IdleWatchdog(
ctx, activity, 200*time.Millisecond, func() { called.Store(true) },
)
for i := 0; i < 5; i++ {
for range 5 {
time.Sleep(20 * time.Millisecond)
activity <- struct{}{}
}
@@ -32,8 +31,7 @@ func TestIdleWatchdog(t *testing.T) {
t.Run("timeout fires onTimeout exactly once", func(t *testing.T) {
activity := make(chan struct{})
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
ctx := t.Context()
count := atomic.Int32{}
done := make(chan struct{})
@@ -123,7 +121,7 @@ func TestIdleWatchdog(t *testing.T) {
}()
// these must not block
for i := 0; i < 5; i++ {
for range 5 {
activity <- struct{}{}
}