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:
@@ -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
|
||||
|
||||
@@ -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
@@ -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() {
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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{}{}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user