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
+1 -39
View File
@@ -12,7 +12,7 @@ import (
type WorkerFactory func( type WorkerFactory func(
ctx context.Context, ctx context.Context,
id string, id string,
logger *slog.Logger, handler slog.Handler,
) (Worker, error) ) (Worker, error)
// Pool Config // Pool Config
@@ -20,8 +20,6 @@ type WorkerFactory func(
type PoolConfig struct { type PoolConfig struct {
InboxBufferSize int InboxBufferSize int
EventsBufferSize int EventsBufferSize int
LoggingEnabled bool
LogLevel *slog.Level
ConnectionConfig *transport.ConnectionConfig ConnectionConfig *transport.ConnectionConfig
WorkerFactory WorkerFactory WorkerFactory WorkerFactory
WorkerConfig *WorkerConfig WorkerConfig *WorkerConfig
@@ -44,8 +42,6 @@ func GetDefaultPoolConfig() *PoolConfig {
return &PoolConfig{ return &PoolConfig{
InboxBufferSize: 256, InboxBufferSize: 256,
EventsBufferSize: 10, EventsBufferSize: 10,
LoggingEnabled: true,
LogLevel: nil,
ConnectionConfig: nil, ConnectionConfig: nil,
WorkerFactory: nil, WorkerFactory: nil,
WorkerConfig: nil, WorkerConfig: nil,
@@ -108,21 +104,6 @@ func WithEventsBufferSize(value int) PoolOption {
} }
} }
func WithPoolLoggingEnabled(value bool) PoolOption {
return func(c *PoolConfig) error {
c.LoggingEnabled = value
return nil
}
}
func WithPoolLogLevel(level slog.Level) PoolOption {
return func(c *PoolConfig) error {
l := level
c.LogLevel = &l
return nil
}
}
func WithConnectionConfig(cc *transport.ConnectionConfig) PoolOption { func WithConnectionConfig(cc *transport.ConnectionConfig) PoolOption {
return func(c *PoolConfig) error { return func(c *PoolConfig) error {
err := transport.ValidateConnectionConfig(cc) err := transport.ValidateConnectionConfig(cc)
@@ -157,8 +138,6 @@ func WithWorkerFactory(wf WorkerFactory) PoolOption {
type WorkerConfig struct { type WorkerConfig struct {
KeepaliveTimeout time.Duration KeepaliveTimeout time.Duration
ReconnectDelay time.Duration ReconnectDelay time.Duration
LoggingEnabled bool
LogLevel *slog.Level
} }
type WorkerOption func(*WorkerConfig) error type WorkerOption func(*WorkerConfig) error
@@ -178,8 +157,6 @@ func GetDefaultWorkerConfig() *WorkerConfig {
return &WorkerConfig{ return &WorkerConfig{
KeepaliveTimeout: 60 * time.Second, KeepaliveTimeout: 60 * time.Second,
ReconnectDelay: 2 * time.Second, ReconnectDelay: 2 * time.Second,
LoggingEnabled: true,
LogLevel: nil,
} }
} }
@@ -237,18 +214,3 @@ func WithReconnectDelay(value time.Duration) WorkerOption {
return nil return nil
} }
} }
func WithWorkerLoggingEnabled(value bool) WorkerOption {
return func(c *WorkerConfig) error {
c.LoggingEnabled = value
return nil
}
}
func WithWorkerLogLevel(level slog.Level) WorkerOption {
return func(c *WorkerConfig) error {
l := level
c.LogLevel = &l
return nil
}
}
-4
View File
@@ -14,8 +14,6 @@ func TestNewPoolConfig(t *testing.T) {
assert.Equal(t, conf, &PoolConfig{ assert.Equal(t, conf, &PoolConfig{
InboxBufferSize: 256, InboxBufferSize: 256,
EventsBufferSize: 10, EventsBufferSize: 10,
LoggingEnabled: true,
LogLevel: nil,
ConnectionConfig: nil, ConnectionConfig: nil,
WorkerConfig: nil, WorkerConfig: nil,
WorkerFactory: nil, WorkerFactory: nil,
@@ -28,8 +26,6 @@ func TestDefaultPoolConfig(t *testing.T) {
assert.Equal(t, conf, &PoolConfig{ assert.Equal(t, conf, &PoolConfig{
InboxBufferSize: 256, InboxBufferSize: 256,
EventsBufferSize: 10, EventsBufferSize: 10,
LoggingEnabled: true,
LogLevel: nil,
ConnectionConfig: nil, ConnectionConfig: nil,
WorkerConfig: nil, WorkerConfig: nil,
WorkerFactory: nil, WorkerFactory: nil,
-1
View File
@@ -10,7 +10,6 @@ var (
InvalidBufferSize = errors.New("buffer size must be greater than zero") InvalidBufferSize = errors.New("buffer size must be greater than zero")
// Pool errors // Pool errors
ErrInvalidPoolID = errors.New("pool id cannot be empty")
ErrPoolClosed = errors.New("pool is closed") ErrPoolClosed = errors.New("pool is closed")
ErrPeerNotFound = errors.New("peer not found") ErrPeerNotFound = errors.New("peer not found")
ErrPeerExists = errors.New("peer already exists") ErrPeerExists = errors.New("peer already exists")
+2 -1
View File
@@ -1,6 +1,6 @@
module git.wisehodl.dev/jay/go-honeybee module git.wisehodl.dev/jay/go-honeybee
go 1.23.5 go 1.25.0
require ( require (
github.com/gorilla/websocket v1.5.3 github.com/gorilla/websocket v1.5.3
@@ -8,6 +8,7 @@ require (
) )
require ( require (
git.wisehodl.dev/jay/go-mana-component v0.1.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
+2
View File
@@ -1,3 +1,5 @@
git.wisehodl.dev/jay/go-mana-component v0.1.0 h1:wWYN5MzC9Hq3tEt4z7FjrwNuQz3rZY3RWAmgmNE8EZE=
git.wisehodl.dev/jay/go-mana-component v0.1.0/go.mod h1:r2ZaTjKzwV5JJfC5boikxtjAKusPrzlJU/7qul0EUqA=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
+2 -1
View File
@@ -1,6 +1,7 @@
package honeybee package honeybee
import ( import (
"context"
"git.wisehodl.dev/jay/go-honeybee/honeybeetest" "git.wisehodl.dev/jay/go-honeybee/honeybeetest"
"git.wisehodl.dev/jay/go-honeybee/transport" "git.wisehodl.dev/jay/go-honeybee/transport"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@@ -18,7 +19,7 @@ func setupTestConnection(t *testing.T) (
socket, incoming, outgoing = honeybeetest.SetupTestSocket(t) socket, incoming, outgoing = honeybeetest.SetupTestSocket(t)
var err error var err error
conn, err = transport.NewConnectionFromSocket(socket, nil, nil) conn, err = transport.NewConnectionFromSocket(context.Background(), socket, nil, nil)
assert.NoError(t, err) assert.NoError(t, err)
return return
} }
+3 -1
View File
@@ -98,7 +98,7 @@ func (m *MockSocket) SetPongHandler(h func(s string) error) {
type MockSlogHandler struct { type MockSlogHandler struct {
records *[]slog.Record records *[]slog.Record
attrs []slog.Attr attrs []slog.Attr
mu sync.RWMutex mu *sync.RWMutex
} }
func NewMockSlogHandler() *MockSlogHandler { func NewMockSlogHandler() *MockSlogHandler {
@@ -106,6 +106,7 @@ func NewMockSlogHandler() *MockSlogHandler {
return &MockSlogHandler{ return &MockSlogHandler{
records: &records, records: &records,
attrs: make([]slog.Attr, 0), attrs: make([]slog.Attr, 0),
mu: &sync.RWMutex{},
} }
} }
@@ -126,6 +127,7 @@ func (m *MockSlogHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
defer m.mu.RUnlock() defer m.mu.RUnlock()
return &MockSlogHandler{ return &MockSlogHandler{
records: m.records, // shared records slice records: m.records, // shared records slice
mu: m.mu, // shared mutex
attrs: append(m.attrs, attrs...), attrs: append(m.attrs, attrs...),
} }
} }
-111
View File
@@ -1,111 +0,0 @@
package logging
import (
"context"
"log/slog"
)
// Constants
const KEY_MODULE = "module"
const KEY_COMPONENT = "component"
const KEY_POOL_ID = "pool_id"
const KEY_PEER_ID = "peer_id"
const MODULE_NAME = "honeybee"
const COMPONENT_OUTBOUND_POOL = "outbound_pool"
const COMPONENT_OUTBOUND_WORKER = "outbound_worker"
const COMPONENT_INBOUND_POOL = "inbound_pool"
const COMPONENT_INBOUND_WORKER = "inbound_worker"
const COMPONENT_CONNECTION = "connection"
// Constructors
func NewOutboundPoolLogger(handler slog.Handler, poolID string) *slog.Logger {
return newLogger(handler,
KEY_MODULE, MODULE_NAME,
KEY_COMPONENT, COMPONENT_OUTBOUND_POOL,
KEY_POOL_ID, poolID,
)
}
func NewOutboundWorkerLogger(handler slog.Handler, poolID string, peerID string) *slog.Logger {
return newLogger(handler,
KEY_MODULE, MODULE_NAME,
KEY_COMPONENT, COMPONENT_OUTBOUND_WORKER,
KEY_POOL_ID, poolID,
KEY_PEER_ID, peerID,
)
}
func NewInboundPoolLogger(handler slog.Handler, poolID string) *slog.Logger {
return newLogger(handler,
KEY_MODULE, MODULE_NAME,
KEY_COMPONENT, COMPONENT_INBOUND_POOL,
KEY_POOL_ID, poolID,
)
}
func NewInboundWorkerLogger(handler slog.Handler, poolID string, peerID string) *slog.Logger {
return newLogger(handler,
KEY_MODULE, MODULE_NAME,
KEY_COMPONENT, COMPONENT_INBOUND_WORKER,
KEY_POOL_ID, poolID,
KEY_PEER_ID, peerID,
)
}
func NewConnectionLogger(handler slog.Handler, poolID string, peerID string) *slog.Logger {
return newLogger(handler,
KEY_MODULE, MODULE_NAME,
KEY_COMPONENT, COMPONENT_CONNECTION,
KEY_POOL_ID, poolID,
KEY_PEER_ID, peerID,
)
}
// Helpers
func newLogger(handler slog.Handler, attrs ...any) *slog.Logger {
return slog.New(handler).With(attrs...)
}
// Handlers
type ForcedLevelHandler struct {
level slog.Level
next slog.Handler
}
func NewForcedLevelHandler(level slog.Level, next slog.Handler) slog.Handler {
return &ForcedLevelHandler{
level: level,
next: next,
}
}
func (h *ForcedLevelHandler) Enabled(_ context.Context, l slog.Level) bool {
return l >= h.level
}
func (h *ForcedLevelHandler) Handle(ctx context.Context, r slog.Record) error {
return h.next.Handle(ctx, r)
}
func (h *ForcedLevelHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
return &ForcedLevelHandler{level: h.level, next: h.next.WithAttrs(attrs)}
}
func (h *ForcedLevelHandler) WithGroup(name string) slog.Handler {
return &ForcedLevelHandler{level: h.level, next: h.next.WithGroup(name)}
}
func WrapOrDefault(level *slog.Level, handler slog.Handler) slog.Handler {
if level != nil {
return NewForcedLevelHandler(*level, handler)
}
return handler
}
-84
View File
@@ -1,84 +0,0 @@
package logging
import (
"git.wisehodl.dev/jay/go-honeybee/honeybeetest"
// "github.com/stretchr/testify/assert"
"log/slog"
"testing"
)
// Helpers
func log(level slog.Level, msg string, attrs map[string]any) honeybeetest.ExpectedLog {
return honeybeetest.ExpectedLog{Level: level, Msg: msg, Attrs: attrs}
}
// Tests
func TestOutboundLogger(t *testing.T) {
const POOL_ID = "pool-1"
const PEER_ID = "wss://test"
handler := honeybeetest.NewMockSlogHandler()
poolLogger := NewOutboundPoolLogger(handler, POOL_ID)
workerLogger := NewOutboundWorkerLogger(handler, POOL_ID, PEER_ID)
connLogger := NewConnectionLogger(handler, POOL_ID, PEER_ID)
poolLogger.Info("test")
workerLogger.Info("test")
connLogger.Info("test")
honeybeetest.Eventually(t, func() bool {
return len(handler.GetRecords()) == 3
}, "expected a log record")
records := handler.GetRecords()
honeybeetest.AssertAttributePresent(t, records[0], KEY_MODULE, MODULE_NAME)
honeybeetest.AssertAttributePresent(t, records[0], KEY_COMPONENT, COMPONENT_OUTBOUND_POOL)
honeybeetest.AssertAttributePresent(t, records[0], KEY_POOL_ID, POOL_ID)
honeybeetest.AssertAttributePresent(t, records[1], KEY_MODULE, MODULE_NAME)
honeybeetest.AssertAttributePresent(t, records[1], KEY_COMPONENT, COMPONENT_OUTBOUND_WORKER)
honeybeetest.AssertAttributePresent(t, records[1], KEY_POOL_ID, POOL_ID)
honeybeetest.AssertAttributePresent(t, records[1], KEY_PEER_ID, PEER_ID)
honeybeetest.AssertAttributePresent(t, records[2], KEY_MODULE, MODULE_NAME)
honeybeetest.AssertAttributePresent(t, records[2], KEY_COMPONENT, COMPONENT_CONNECTION)
honeybeetest.AssertAttributePresent(t, records[2], KEY_POOL_ID, POOL_ID)
honeybeetest.AssertAttributePresent(t, records[2], KEY_PEER_ID, PEER_ID)
}
func TestInboundLogger(t *testing.T) {
const POOL_ID = "pool-1"
const PEER_ID = "peer-1"
handler := honeybeetest.NewMockSlogHandler()
poolLogger := NewInboundPoolLogger(handler, POOL_ID)
workerLogger := NewInboundWorkerLogger(handler, POOL_ID, PEER_ID)
connLogger := NewConnectionLogger(handler, POOL_ID, PEER_ID)
poolLogger.Info("test")
workerLogger.Info("test")
connLogger.Info("test")
honeybeetest.Eventually(t, func() bool {
return len(handler.GetRecords()) == 3
}, "expected a log record")
records := handler.GetRecords()
honeybeetest.AssertAttributePresent(t, records[0], KEY_MODULE, MODULE_NAME)
honeybeetest.AssertAttributePresent(t, records[0], KEY_COMPONENT, COMPONENT_INBOUND_POOL)
honeybeetest.AssertAttributePresent(t, records[0], KEY_POOL_ID, POOL_ID)
honeybeetest.AssertAttributePresent(t, records[1], KEY_MODULE, MODULE_NAME)
honeybeetest.AssertAttributePresent(t, records[1], KEY_COMPONENT, COMPONENT_INBOUND_WORKER)
honeybeetest.AssertAttributePresent(t, records[1], KEY_POOL_ID, POOL_ID)
honeybeetest.AssertAttributePresent(t, records[1], KEY_PEER_ID, PEER_ID)
honeybeetest.AssertAttributePresent(t, records[2], KEY_MODULE, MODULE_NAME)
honeybeetest.AssertAttributePresent(t, records[2], KEY_COMPONENT, COMPONENT_CONNECTION)
honeybeetest.AssertAttributePresent(t, records[2], KEY_POOL_ID, POOL_ID)
honeybeetest.AssertAttributePresent(t, records[2], KEY_PEER_ID, PEER_ID)
}
+13 -33
View File
@@ -2,10 +2,11 @@ package honeybee
import ( import (
"context" "context"
"git.wisehodl.dev/jay/go-honeybee/logging" "log/slog"
"git.wisehodl.dev/jay/go-honeybee/transport" "git.wisehodl.dev/jay/go-honeybee/transport"
"git.wisehodl.dev/jay/go-honeybee/types" "git.wisehodl.dev/jay/go-honeybee/types"
"log/slog" component "git.wisehodl.dev/jay/go-mana-component"
"sync" "sync"
"sync/atomic" "sync/atomic"
"time" "time"
@@ -50,13 +51,11 @@ type PeerStats struct {
} }
type PoolPlugin struct { type PoolPlugin struct {
ID string
Inbox chan<- types.InboxMessage Inbox chan<- types.InboxMessage
Events chan<- PoolEvent Events chan<- PoolEvent
InboxCounter *atomic.Uint64 InboxCounter *atomic.Uint64
Dialer types.Dialer Dialer types.Dialer
ConnectionConfig *transport.ConnectionConfig ConnectionConfig *transport.ConnectionConfig
Handler slog.Handler
} }
// Pool // Pool
@@ -70,8 +69,6 @@ type Pool struct {
ctx context.Context ctx context.Context
cancel context.CancelFunc cancel context.CancelFunc
id string
peers map[string]*Peer peers map[string]*Peer
inbox chan types.InboxMessage inbox chan types.InboxMessage
events chan PoolEvent events chan PoolEvent
@@ -89,12 +86,8 @@ type Pool struct {
closed bool closed bool
} }
func NewPool(ctx context.Context, id string, config *PoolConfig, handler slog.Handler, func NewPool(ctx context.Context, config *PoolConfig, handler slog.Handler,
) (*Pool, error) { ) (*Pool, error) {
if id == "" {
return nil, ErrInvalidPoolID
}
if config == nil { if config == nil {
config = GetDefaultPoolConfig() config = GetDefaultPoolConfig()
} }
@@ -104,8 +97,8 @@ func NewPool(ctx context.Context, id string, config *PoolConfig, handler slog.Ha
// deadlocks. // deadlocks.
if config.WorkerFactory == nil { if config.WorkerFactory == nil {
config.WorkerFactory = func( config.WorkerFactory = func(
ctx context.Context, id string, logger *slog.Logger) (Worker, error) { ctx context.Context, id string, handler slog.Handler) (Worker, error) {
return NewWorker(ctx, id, config.WorkerConfig, logger) return NewWorker(ctx, id, config.WorkerConfig, handler)
} }
} }
@@ -113,18 +106,17 @@ func NewPool(ctx context.Context, id string, config *PoolConfig, handler slog.Ha
return nil, err return nil, err
} }
pctx, cancel := context.WithCancel(ctx) pctx, cancel := context.WithCancel(component.MustNew(ctx, "honeybee", "pool"))
var logger *slog.Logger var logger *slog.Logger
if handler != nil && config.LoggingEnabled { if handler != nil {
logger = logging.NewOutboundPoolLogger( c := component.FromContext(pctx)
logging.WrapOrDefault(config.LogLevel, handler), id) logger = slog.New(handler).With(slog.Any("component", c))
} }
return &Pool{ return &Pool{
ctx: pctx, ctx: pctx,
cancel: cancel, cancel: cancel,
id: id,
peers: make(map[string]*Peer), peers: make(map[string]*Peer),
inbox: make(chan types.InboxMessage, config.InboxBufferSize), inbox: make(chan types.InboxMessage, config.InboxBufferSize),
events: make(chan PoolEvent, config.EventsBufferSize), events: make(chan PoolEvent, config.EventsBufferSize),
@@ -254,35 +246,23 @@ func (p *Pool) Connect(id string) error {
return NewPoolError(ErrPeerExists) return NewPoolError(ErrPeerExists)
} }
var logger *slog.Logger
if p.handler != nil && p.config.WorkerConfig != nil {
if p.config.WorkerConfig.LoggingEnabled {
logger = logging.NewOutboundWorkerLogger(
logging.WrapOrDefault(p.config.WorkerConfig.LogLevel, p.handler), p.id, id)
}
}
// The worker factory must be non-blocking to avoid deadlocks // The worker factory must be non-blocking to avoid deadlocks
worker, err := p.config.WorkerFactory(p.ctx, id, logger) worker, err := p.config.WorkerFactory(p.ctx, id, p.handler)
if err != nil { if err != nil {
return err return err
} }
pool := PoolPlugin{ pool := PoolPlugin{
ID: p.id,
Inbox: p.inbox, Inbox: p.inbox,
Events: p.events, Events: p.events,
InboxCounter: p.inboxCounter, InboxCounter: p.inboxCounter,
Dialer: p.dialer, Dialer: p.dialer,
ConnectionConfig: p.config.ConnectionConfig, ConnectionConfig: p.config.ConnectionConfig,
Handler: p.handler,
} }
p.wg.Add(1) p.wg.Go(func() {
go func() {
worker.Start(pool) worker.Start(pool)
p.wg.Done() })
}()
p.peers[id] = &Peer{id: id, worker: worker} p.peers[id] = &Peer{id: id, worker: worker}
+4 -9
View File
@@ -15,7 +15,7 @@ import (
func setupPool(t *testing.T) (*Pool, *honeybeetest.MockDialer) { func setupPool(t *testing.T) (*Pool, *honeybeetest.MockDialer) {
t.Helper() t.Helper()
pool, err := NewPool(context.Background(), "pool-1", nil, nil) pool, err := NewPool(context.Background(), nil, nil)
assert.NoError(t, err) assert.NoError(t, err)
dialer := &honeybeetest.MockDialer{ dialer := &honeybeetest.MockDialer{
DialContextFunc: func(context.Context, string, http.Header) (types.Socket, *http.Response, error) { DialContextFunc: func(context.Context, string, http.Header) (types.Socket, *http.Response, error) {
@@ -45,11 +45,6 @@ func expectEvent(
// Tests // Tests
func TestPoolID(t *testing.T) {
_, err := NewPool(context.Background(), "", nil, nil)
assert.ErrorIs(t, err, ErrInvalidPoolID)
}
func TestPoolConnect(t *testing.T) { func TestPoolConnect(t *testing.T) {
t.Run("successfully adds connection", func(t *testing.T) { t.Run("successfully adds connection", func(t *testing.T) {
pool, _ := setupPool(t) pool, _ := setupPool(t)
@@ -90,7 +85,7 @@ func TestPoolConnect(t *testing.T) {
func TestPoolClose(t *testing.T) { func TestPoolClose(t *testing.T) {
t.Run("channels close after pool close", func(t *testing.T) { t.Run("channels close after pool close", func(t *testing.T) {
pool, _ := NewPool(context.Background(), "pool-1", nil, nil) pool, _ := NewPool(context.Background(), nil, nil)
pool.Close() pool.Close()
_, ok := <-pool.Inbox() _, ok := <-pool.Inbox()
assert.False(t, ok) assert.False(t, ok)
@@ -99,7 +94,7 @@ func TestPoolClose(t *testing.T) {
}) })
t.Run("connect after close returns error", func(t *testing.T) { t.Run("connect after close returns error", func(t *testing.T) {
pool, _ := NewPool(context.Background(), "pool-1", nil, nil) pool, _ := NewPool(context.Background(), nil, nil)
pool.Close() pool.Close()
err := pool.Connect("wss://test") err := pool.Connect("wss://test")
assert.ErrorIs(t, err, ErrPoolClosed) assert.ErrorIs(t, err, ErrPoolClosed)
@@ -157,7 +152,7 @@ func TestPoolSend(t *testing.T) {
}, },
} }
pool, err := NewPool(context.Background(), "pool-1", nil, nil) pool, err := NewPool(context.Background(), nil, nil)
assert.NoError(t, err) assert.NoError(t, err)
pool.dialer = mockDialer pool.dialer = mockDialer
-20
View File
@@ -1,7 +1,6 @@
package transport package transport
import ( import (
"log/slog"
"net/http" "net/http"
"time" "time"
) )
@@ -15,8 +14,6 @@ type ConnectionConfig struct {
PingInterval time.Duration PingInterval time.Duration
IncomingBufferSize int IncomingBufferSize int
ErrorsBufferSize int ErrorsBufferSize int
LoggingEnabled bool
LogLevel *slog.Level
Retry *RetryConfig Retry *RetryConfig
} }
@@ -50,8 +47,6 @@ func GetDefaultConnectionConfig() *ConnectionConfig {
PingInterval: 20 * time.Second, PingInterval: 20 * time.Second,
IncomingBufferSize: 100, IncomingBufferSize: 100,
ErrorsBufferSize: 10, ErrorsBufferSize: 10,
LoggingEnabled: true,
LogLevel: nil,
Retry: GetDefaultRetryConfig(), 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 { func WithoutRetry() ConnectionOption {
return func(c *ConnectionConfig) error { return func(c *ConnectionConfig) error {
c.Retry = nil c.Retry = nil
-7
View File
@@ -2,7 +2,6 @@ package transport
import ( import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"log/slog"
"net/http" "net/http"
"testing" "testing"
"time" "time"
@@ -36,8 +35,6 @@ func TestDefaultConnectionConfig(t *testing.T) {
PingInterval: 20 * time.Second, PingInterval: 20 * time.Second,
IncomingBufferSize: 100, IncomingBufferSize: 100,
ErrorsBufferSize: 10, ErrorsBufferSize: 10,
LoggingEnabled: true,
LogLevel: nil,
Retry: GetDefaultRetryConfig(), Retry: GetDefaultRetryConfig(),
}) })
} }
@@ -61,8 +58,6 @@ func TestApplyConnectionOptions(t *testing.T) {
conf, conf,
WithIncomingBufferSize(256), WithIncomingBufferSize(256),
WithErrorsBufferSize(100), WithErrorsBufferSize(100),
WithLoggingEnabled(false),
WithLogLevel(slog.LevelError),
WithRetryMaxRetries(0), WithRetryMaxRetries(0),
WithRetryInitialDelay(3*time.Second), WithRetryInitialDelay(3*time.Second),
WithRetryJitterFactor(0.5), WithRetryJitterFactor(0.5),
@@ -71,8 +66,6 @@ func TestApplyConnectionOptions(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, 256, conf.IncomingBufferSize) assert.Equal(t, 256, conf.IncomingBufferSize)
assert.Equal(t, 100, conf.ErrorsBufferSize) 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, 0, conf.Retry.MaxRetries)
assert.Equal(t, 3*time.Second, conf.Retry.InitialDelay) assert.Equal(t, 3*time.Second, conf.Retry.InitialDelay)
assert.Equal(t, 0.5, conf.Retry.JitterFactor) assert.Equal(t, 0.5, conf.Retry.JitterFactor)
+31 -10
View File
@@ -12,6 +12,7 @@ import (
"time" "time"
"git.wisehodl.dev/jay/go-honeybee/types" "git.wisehodl.dev/jay/go-honeybee/types"
component "git.wisehodl.dev/jay/go-mana-component"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
) )
@@ -74,7 +75,7 @@ type Connection struct {
cleanupOnce sync.Once 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 { if config == nil {
config = GetDefaultConnectionConfig() config = GetDefaultConnectionConfig()
} }
@@ -88,6 +89,18 @@ func NewConnection(urlStr string, config *ConnectionConfig, logger *slog.Logger)
return nil, err 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{ conn := &Connection{
url: url, url: url,
dialer: NewDialer(), dialer: NewDialer(),
@@ -108,7 +121,7 @@ func NewConnection(urlStr string, config *ConnectionConfig, logger *slog.Logger)
} }
func NewConnectionFromSocket( func NewConnectionFromSocket(
socket types.Socket, config *ConnectionConfig, logger *slog.Logger, ctx context.Context, socket types.Socket, config *ConnectionConfig, handler slog.Handler,
) (*Connection, error) { ) (*Connection, error) {
if socket == nil { if socket == nil {
return nil, NewConnectionError(ErrNilSocket) return nil, NewConnectionError(ErrNilSocket)
@@ -122,6 +135,18 @@ func NewConnectionFromSocket(
return nil, err 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{ conn := &Connection{
url: nil, url: nil,
dialer: nil, dialer: nil,
@@ -293,9 +318,7 @@ func (c *Connection) shutdownLogComplete() {
} }
func (c *Connection) startReader() { func (c *Connection) startReader() {
c.wg.Add(1) c.wg.Go(func() {
go func() {
defer c.wg.Done()
defer c.shutdownInternal() defer c.shutdownInternal()
for { for {
@@ -362,7 +385,7 @@ func (c *Connection) startReader() {
} }
} }
}() })
} }
func (c *Connection) setupPongHandler() { func (c *Connection) setupPongHandler() {
@@ -381,9 +404,7 @@ func (c *Connection) startPinger() {
return return
} }
c.wg.Add(1) c.wg.Go(func() {
go func() {
defer c.wg.Done()
defer c.shutdownInternal() defer c.shutdownInternal()
// Calculate 10% jitter window // Calculate 10% jitter window
@@ -404,7 +425,7 @@ func (c *Connection) startPinger() {
} }
} }
} }
}() })
} }
+7 -6
View File
@@ -2,6 +2,7 @@ package transport
import ( import (
"bytes" "bytes"
"context"
"fmt" "fmt"
"git.wisehodl.dev/jay/go-honeybee/honeybeetest" "git.wisehodl.dev/jay/go-honeybee/honeybeetest"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
@@ -11,7 +12,7 @@ import (
func TestDisconnectedConnectionClose(t *testing.T) { func TestDisconnectedConnectionClose(t *testing.T) {
t.Run("close succeeds on disconnected connection", func(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.NoError(t, err)
assert.Equal(t, StateDisconnected, conn.State()) assert.Equal(t, StateDisconnected, conn.State())
@@ -20,7 +21,7 @@ func TestDisconnectedConnectionClose(t *testing.T) {
}) })
t.Run("close is idempotent", func(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) assert.NoError(t, err)
conn.Close() conn.Close()
@@ -29,7 +30,7 @@ func TestDisconnectedConnectionClose(t *testing.T) {
}) })
t.Run("close with nil socket", func(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.NoError(t, err)
assert.Nil(t, conn.socket) assert.Nil(t, conn.socket)
@@ -44,7 +45,7 @@ func TestDisconnectedConnectionClose(t *testing.T) {
return expectedErr return expectedErr
} }
conn, err := NewConnection("ws://test", nil, nil) conn, err := NewConnection(context.Background(), "ws://test", nil, nil)
assert.NoError(t, err) assert.NoError(t, err)
conn.socket = mockSocket conn.socket = mockSocket
@@ -53,7 +54,7 @@ func TestDisconnectedConnectionClose(t *testing.T) {
}) })
t.Run("channels close after close", func(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) assert.NoError(t, err)
conn.Close() conn.Close()
@@ -66,7 +67,7 @@ func TestDisconnectedConnectionClose(t *testing.T) {
}) })
t.Run("send fails after close", func(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) assert.NoError(t, err)
conn.Close() conn.Close()
+2 -1
View File
@@ -1,6 +1,7 @@
package transport package transport
import ( import (
"context"
"git.wisehodl.dev/jay/go-honeybee/honeybeetest" "git.wisehodl.dev/jay/go-honeybee/honeybeetest"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@@ -66,7 +67,7 @@ func TestStartReader(t *testing.T) {
return 0, nil, io.EOF return 0, nil, io.EOF
} }
conn, err := NewConnectionFromSocket(mockSocket, nil, nil) conn, err := NewConnectionFromSocket(context.Background(), mockSocket, nil, nil)
assert.NoError(t, err) assert.NoError(t, err)
honeybeetest.Eventually(t, func() bool { honeybeetest.Eventually(t, func() bool {
+8 -7
View File
@@ -1,6 +1,7 @@
package transport package transport
import ( import (
"context"
"fmt" "fmt"
"git.wisehodl.dev/jay/go-honeybee/honeybeetest" "git.wisehodl.dev/jay/go-honeybee/honeybeetest"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
@@ -62,12 +63,12 @@ func TestConnectionSend(t *testing.T) {
defer close(done) defer close(done)
var wg sync.WaitGroup var wg sync.WaitGroup
for i := 0; i < 5; i++ { for i := range 5 {
wg.Add(1) wg.Add(1)
go func(id int) { go func(id int) {
defer wg.Done() defer wg.Done()
for j := 0; j < 10; j++ { for j := range 10 {
data := []byte(fmt.Sprintf("msg-%d-%d", id, j)) data := fmt.Appendf(nil, "msg-%d-%d", id, j)
for { for {
// send and retry until success // send and retry until success
err := conn.Send(data) err := conn.Send(data)
@@ -129,7 +130,7 @@ func TestConnectionSend(t *testing.T) {
return nil return nil
} }
conn, err := NewConnectionFromSocket(mockSocket, config, nil) conn, err := NewConnectionFromSocket(context.Background(), mockSocket, config, nil)
assert.NoError(t, err) assert.NoError(t, err)
defer conn.Close() defer conn.Close()
@@ -175,7 +176,7 @@ func TestConnectionSend(t *testing.T) {
return nil return nil
} }
conn, err := NewConnectionFromSocket(mockSocket, config, nil) conn, err := NewConnectionFromSocket(context.Background(), mockSocket, config, nil)
assert.NoError(t, err) assert.NoError(t, err)
defer conn.Close() defer conn.Close()
@@ -208,7 +209,7 @@ func TestConnectionSend(t *testing.T) {
return fmt.Errorf("test error") return fmt.Errorf("test error")
} }
conn, err := NewConnectionFromSocket(mockSocket, config, nil) conn, err := NewConnectionFromSocket(context.Background(), mockSocket, config, nil)
assert.NoError(t, err) assert.NoError(t, err)
defer conn.Close() defer conn.Close()
@@ -228,7 +229,7 @@ func TestConnectionSend(t *testing.T) {
return writeErr return writeErr
} }
conn, err := NewConnectionFromSocket(mockSocket, nil, nil) conn, err := NewConnectionFromSocket(context.Background(), mockSocket, nil, nil)
assert.NoError(t, err) assert.NoError(t, err)
defer conn.Close() defer conn.Close()
+20 -20
View File
@@ -39,11 +39,11 @@ func TestConnectionStateString(t *testing.T) {
func TestConnectionState(t *testing.T) { func TestConnectionState(t *testing.T) {
// Test initial state // Test initial state
conn, _ := NewConnection("ws://test", nil, nil) conn, _ := NewConnection(context.Background(), "ws://test", nil, nil)
assert.Equal(t, StateDisconnected, conn.State()) assert.Equal(t, StateDisconnected, conn.State())
// Test state after FromSocket (should be Connected) // 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()) assert.Equal(t, StateConnected, conn2.State())
// Test state after close // Test state after close
@@ -94,7 +94,7 @@ func TestNewConnection(t *testing.T) {
for _, tc := range cases { for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) { 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 { if tc.wantErr {
assert.Error(t, err) 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 { if tc.wantErr {
assert.Error(t, err) assert.Error(t, err)
@@ -236,7 +236,7 @@ func TestNewConnectionFromSocket(t *testing.T) {
func TestConnect(t *testing.T) { func TestConnect(t *testing.T) {
t.Run("connect fails when socket already present", func(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) assert.NoError(t, err)
conn.socket = honeybeetest.NewMockSocket() conn.socket = honeybeetest.NewMockSocket()
@@ -248,7 +248,7 @@ func TestConnect(t *testing.T) {
}) })
t.Run("connect fails when connection closed", func(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) assert.NoError(t, err)
conn.Close() conn.Close()
@@ -260,7 +260,7 @@ func TestConnect(t *testing.T) {
}) })
t.Run("connect succeeds and starts goroutines", func(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) assert.NoError(t, err)
outgoingData := make(chan honeybeetest.MockOutgoingData, 10) outgoingData := make(chan honeybeetest.MockOutgoingData, 10)
@@ -306,7 +306,7 @@ func TestConnect(t *testing.T) {
JitterFactor: 0.0, JitterFactor: 0.0,
}, },
} }
conn, err := NewConnection("ws://test", config, nil) conn, err := NewConnection(context.Background(), "ws://test", config, nil)
assert.NoError(t, err) assert.NoError(t, err)
attemptCount := 0 attemptCount := 0
@@ -338,7 +338,7 @@ func TestConnect(t *testing.T) {
JitterFactor: 0.0, JitterFactor: 0.0,
}, },
} }
conn, err := NewConnection("ws://test", config, nil) conn, err := NewConnection(context.Background(), "ws://test", config, nil)
assert.NoError(t, err) assert.NoError(t, err)
mockDialer := &honeybeetest.MockDialer{ mockDialer := &honeybeetest.MockDialer{
@@ -355,7 +355,7 @@ func TestConnect(t *testing.T) {
}) })
t.Run("state transitions during connect", func(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.NoError(t, err)
assert.Equal(t, StateDisconnected, conn.State()) assert.Equal(t, StateDisconnected, conn.State())
@@ -383,7 +383,7 @@ func TestConnect(t *testing.T) {
return nil return nil
}, },
} }
conn, err := NewConnection("ws://test", config, nil) conn, err := NewConnection(context.Background(), "ws://test", config, nil)
assert.NoError(t, err) assert.NoError(t, err)
mockSocket := honeybeetest.NewMockSocket() mockSocket := honeybeetest.NewMockSocket()
@@ -408,7 +408,7 @@ func TestConnect(t *testing.T) {
t.Run("passes headers when configured", func(t *testing.T) { t.Run("passes headers when configured", func(t *testing.T) {
header := http.Header{"X-Custom": []string{"val"}} header := http.Header{"X-Custom": []string{"val"}}
conf, _ := NewConnectionConfig(WithRequestHeader(header)) conf, _ := NewConnectionConfig(WithRequestHeader(header))
conn, _ := NewConnection("ws://test", conf, nil) conn, _ := NewConnection(context.Background(), "ws://test", conf, nil)
dialCalled := false dialCalled := false
conn.dialer = &honeybeetest.MockDialer{ conn.dialer = &honeybeetest.MockDialer{
@@ -436,7 +436,7 @@ func TestConnectContextCancellation(t *testing.T) {
JitterFactor: 0.0, JitterFactor: 0.0,
}, },
} }
conn, err := NewConnection("ws://test", config, nil) conn, err := NewConnection(context.Background(), "ws://test", config, nil)
assert.NoError(t, err) assert.NoError(t, err)
dialCount := atomic.Int32{} dialCount := atomic.Int32{}
@@ -475,7 +475,7 @@ func TestConnectContextCancellation(t *testing.T) {
// Connection method tests // Connection method tests
func TestConnectionIncoming(t *testing.T) { 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) assert.NoError(t, err)
incoming := conn.Incoming() 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) assert.NoError(t, err)
defer conn.Close() 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) assert.NoError(t, err)
defer conn.Close() defer conn.Close()
@@ -541,7 +541,7 @@ func TestConnectionErrors(t *testing.T) {
return 0, nil, io.EOF return 0, nil, io.EOF
} }
conn, err := NewConnectionFromSocket(mockSocket, nil, nil) conn, err := NewConnectionFromSocket(context.Background(), mockSocket, nil, nil)
assert.NoError(t, err) assert.NoError(t, err)
defer conn.Close() defer conn.Close()
@@ -573,7 +573,7 @@ func TestConnectionHeartbeat(t *testing.T) {
) )
assert.NoError(t, err) assert.NoError(t, err)
conn, _ := NewConnectionFromSocket(socket, conf, nil) conn, _ := NewConnectionFromSocket(context.Background(), socket, conf, nil)
defer conn.Close() defer conn.Close()
honeybeetest.Eventually(t, honeybeetest.Eventually(t,
@@ -586,7 +586,7 @@ func TestConnectionHeartbeat(t *testing.T) {
socket, _, _ := honeybeetest.SetupTestSocket(t) socket, _, _ := honeybeetest.SetupTestSocket(t)
socket.SetPongHandlerFunc = func(h func(string) error) { handler = h } socket.SetPongHandlerFunc = func(h func(string) error) { handler = h }
conn, _ := NewConnectionFromSocket(socket, nil, nil) conn, _ := NewConnectionFromSocket(context.Background(), socket, nil, nil)
defer conn.Close() defer conn.Close()
honeybeetest.Eventually(t, func() bool { honeybeetest.Eventually(t, func() bool {
@@ -620,7 +620,7 @@ func setupTestConnection(t *testing.T) (
socket, incoming, outgoing = honeybeetest.SetupTestSocket(t) socket, incoming, outgoing = honeybeetest.SetupTestSocket(t)
var err error var err error
conn, err = NewConnectionFromSocket(socket, nil, nil) conn, err = NewConnectionFromSocket(context.Background(), socket, nil, nil)
assert.NoError(t, err) assert.NoError(t, err)
return return
} }
+12 -21
View File
@@ -8,6 +8,7 @@ import (
"net/http" "net/http"
"testing" "testing"
"time" "time"
// slog used for ExpectedLog level constants
"git.wisehodl.dev/jay/go-honeybee/honeybeetest" "git.wisehodl.dev/jay/go-honeybee/honeybeetest"
"git.wisehodl.dev/jay/go-honeybee/types" "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) { func TestConnectLogging(t *testing.T) {
t.Run("success", func(t *testing.T) { t.Run("success", func(t *testing.T) {
mockHandler := honeybeetest.NewMockSlogHandler() 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) assert.NoError(t, err)
mockSocket := honeybeetest.NewMockSocket() mockSocket := honeybeetest.NewMockSocket()
@@ -57,7 +57,6 @@ func TestConnectLogging(t *testing.T) {
t.Run("max retries failure", func(t *testing.T) { t.Run("max retries failure", func(t *testing.T) {
mockHandler := honeybeetest.NewMockSlogHandler() mockHandler := honeybeetest.NewMockSlogHandler()
logger := slog.New(mockHandler)
config := &ConnectionConfig{ config := &ConnectionConfig{
Retry: &RetryConfig{ 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) assert.NoError(t, err)
dialErr := fmt.Errorf("dial error") dialErr := fmt.Errorf("dial error")
@@ -100,7 +99,6 @@ func TestConnectLogging(t *testing.T) {
t.Run("success after retry", func(t *testing.T) { t.Run("success after retry", func(t *testing.T) {
mockHandler := honeybeetest.NewMockSlogHandler() mockHandler := honeybeetest.NewMockSlogHandler()
logger := slog.New(mockHandler)
config := &ConnectionConfig{ config := &ConnectionConfig{
Retry: &RetryConfig{ 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) assert.NoError(t, err)
attemptCount := 0 attemptCount := 0
@@ -151,10 +149,9 @@ func TestConnectLogging(t *testing.T) {
func TestCloseLogging(t *testing.T) { func TestCloseLogging(t *testing.T) {
t.Run("normal close", func(t *testing.T) { t.Run("normal close", func(t *testing.T) {
mockHandler := honeybeetest.NewMockSlogHandler() mockHandler := honeybeetest.NewMockSlogHandler()
logger := slog.New(mockHandler)
mockSocket := honeybeetest.NewMockSocket() mockSocket := honeybeetest.NewMockSocket()
conn, err := NewConnectionFromSocket(mockSocket, nil, logger) conn, err := NewConnectionFromSocket(context.Background(), mockSocket, nil, mockHandler)
assert.NoError(t, err) assert.NoError(t, err)
conn.Close() conn.Close()
@@ -176,7 +173,6 @@ func TestCloseLogging(t *testing.T) {
t.Run("close with socket error", func(t *testing.T) { t.Run("close with socket error", func(t *testing.T) {
mockHandler := honeybeetest.NewMockSlogHandler() mockHandler := honeybeetest.NewMockSlogHandler()
logger := slog.New(mockHandler)
closeErr := fmt.Errorf("close error") closeErr := fmt.Errorf("close error")
mockSocket := honeybeetest.NewMockSocket() mockSocket := honeybeetest.NewMockSocket()
@@ -184,7 +180,7 @@ func TestCloseLogging(t *testing.T) {
return closeErr return closeErr
} }
conn, err := NewConnectionFromSocket(mockSocket, nil, logger) conn, err := NewConnectionFromSocket(context.Background(), mockSocket, nil, mockHandler)
assert.NoError(t, err) assert.NoError(t, err)
conn.Close() conn.Close()
@@ -208,7 +204,6 @@ func TestCloseLogging(t *testing.T) {
func TestReaderLogging(t *testing.T) { func TestReaderLogging(t *testing.T) {
t.Run("clean close by peer", func(t *testing.T) { t.Run("clean close by peer", func(t *testing.T) {
mockHandler := honeybeetest.NewMockSlogHandler() mockHandler := honeybeetest.NewMockSlogHandler()
logger := slog.New(mockHandler)
mockSocket := honeybeetest.NewMockSocket() mockSocket := honeybeetest.NewMockSocket()
mockSocket.ReadMessageFunc = func() (int, []byte, error) { 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) assert.NoError(t, err)
defer conn.Close() defer conn.Close()
@@ -236,7 +231,6 @@ func TestReaderLogging(t *testing.T) {
t.Run("unexpected close", func(t *testing.T) { t.Run("unexpected close", func(t *testing.T) {
mockHandler := honeybeetest.NewMockSlogHandler() mockHandler := honeybeetest.NewMockSlogHandler()
logger := slog.New(mockHandler)
mockSocket := honeybeetest.NewMockSocket() mockSocket := honeybeetest.NewMockSocket()
mockSocket.ReadMessageFunc = func() (int, []byte, error) { 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) assert.NoError(t, err)
defer conn.Close() defer conn.Close()
@@ -264,14 +258,13 @@ func TestReaderLogging(t *testing.T) {
t.Run("read error", func(t *testing.T) { t.Run("read error", func(t *testing.T) {
mockHandler := honeybeetest.NewMockSlogHandler() mockHandler := honeybeetest.NewMockSlogHandler()
logger := slog.New(mockHandler)
mockSocket := honeybeetest.NewMockSocket() mockSocket := honeybeetest.NewMockSocket()
mockSocket.ReadMessageFunc = func() (int, []byte, error) { mockSocket.ReadMessageFunc = func() (int, []byte, error) {
return 0, nil, io.EOF return 0, nil, io.EOF
} }
conn, err := NewConnectionFromSocket(mockSocket, nil, logger) conn, err := NewConnectionFromSocket(context.Background(), mockSocket, nil, mockHandler)
assert.NoError(t, err) assert.NoError(t, err)
defer conn.Close() defer conn.Close()
@@ -285,7 +278,6 @@ func TestReaderLogging(t *testing.T) {
func TestWriterLogging(t *testing.T) { func TestWriterLogging(t *testing.T) {
t.Run("write deadline error", func(t *testing.T) { t.Run("write deadline error", func(t *testing.T) {
mockHandler := honeybeetest.NewMockSlogHandler() mockHandler := honeybeetest.NewMockSlogHandler()
logger := slog.New(mockHandler)
config := &ConnectionConfig{WriteTimeout: 1 * time.Millisecond} config := &ConnectionConfig{WriteTimeout: 1 * time.Millisecond}
@@ -295,7 +287,7 @@ func TestWriterLogging(t *testing.T) {
return deadlineErr return deadlineErr
} }
conn, err := NewConnectionFromSocket(mockSocket, config, logger) conn, err := NewConnectionFromSocket(context.Background(), mockSocket, config, mockHandler)
assert.NoError(t, err) assert.NoError(t, err)
err = conn.Send([]byte("test")) err = conn.Send([]byte("test"))
@@ -317,7 +309,6 @@ func TestWriterLogging(t *testing.T) {
t.Run("write message error", func(t *testing.T) { t.Run("write message error", func(t *testing.T) {
mockHandler := honeybeetest.NewMockSlogHandler() mockHandler := honeybeetest.NewMockSlogHandler()
logger := slog.New(mockHandler)
writeErr := fmt.Errorf("write error") writeErr := fmt.Errorf("write error")
mockSocket := honeybeetest.NewMockSocket() mockSocket := honeybeetest.NewMockSocket()
@@ -325,7 +316,7 @@ func TestWriterLogging(t *testing.T) {
return writeErr return writeErr
} }
conn, err := NewConnectionFromSocket(mockSocket, nil, logger) conn, err := NewConnectionFromSocket(context.Background(), mockSocket, nil, mockHandler)
assert.NoError(t, err) assert.NoError(t, err)
err = conn.Send([]byte("test")) 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) { t.Run("nil logger produces no logs", func(t *testing.T) {
mockHandler := honeybeetest.NewMockSlogHandler() mockHandler := honeybeetest.NewMockSlogHandler()
conn, err := NewConnection("ws://test", nil, nil) conn, err := NewConnection(context.Background(), "ws://test", nil, nil)
assert.NoError(t, err) assert.NoError(t, err)
mockSocket := honeybeetest.NewMockSocket() mockSocket := honeybeetest.NewMockSocket()
+3 -9
View File
@@ -59,22 +59,16 @@ func (r *RetryManager) CalculateDelay() time.Duration {
} }
// Exponential backoff: InitialDelay * 2^(attempts-1) // Exponential backoff: InitialDelay * 2^(attempts-1)
shift := r.retryCount - 1 shift := min(r.retryCount-1, 62) // prevent overflow
if shift > 62 {
shift = 62
} // prevent overflow
backoffMultiplier := float64(int64(1) << shift) backoffMultiplier := float64(int64(1) << shift)
baseDelay := float64(r.config.InitialDelay) * backoffMultiplier baseDelay := float64(r.config.InitialDelay) * backoffMultiplier
// Apply jitter: delay * (1 + jitterFactor * (random - 0.5)) // Apply jitter: delay * (1 + jitterFactor * (random - 0.5))
random := rand.Float64() random := rand.Float64()
jitterMultiplier := 1 + r.config.JitterFactor*(random-0.5) jitterMultiplier := 1 + r.config.JitterFactor*(random-0.5)
delay := time.Duration(baseDelay * jitterMultiplier) delay := min(
// Cap at MaxDelay // Cap at MaxDelay
if delay > r.config.MaxDelay { time.Duration(baseDelay*jitterMultiplier), r.config.MaxDelay)
delay = r.config.MaxDelay
}
return delay return delay
} }
+4 -6
View File
@@ -12,15 +12,14 @@ import (
func TestIdleWatchdog(t *testing.T) { func TestIdleWatchdog(t *testing.T) {
t.Run("heartbeat resets timer, onTimeout not called", func(t *testing.T) { t.Run("heartbeat resets timer, onTimeout not called", func(t *testing.T) {
activity := make(chan struct{}) activity := make(chan struct{})
ctx, cancel := context.WithCancel(context.Background()) ctx := t.Context()
defer cancel()
called := atomic.Bool{} called := atomic.Bool{}
go IdleWatchdog( go IdleWatchdog(
ctx, activity, 200*time.Millisecond, func() { called.Store(true) }, ctx, activity, 200*time.Millisecond, func() { called.Store(true) },
) )
for i := 0; i < 5; i++ { for range 5 {
time.Sleep(20 * time.Millisecond) time.Sleep(20 * time.Millisecond)
activity <- struct{}{} activity <- struct{}{}
} }
@@ -32,8 +31,7 @@ func TestIdleWatchdog(t *testing.T) {
t.Run("timeout fires onTimeout exactly once", func(t *testing.T) { t.Run("timeout fires onTimeout exactly once", func(t *testing.T) {
activity := make(chan struct{}) activity := make(chan struct{})
ctx, cancel := context.WithCancel(context.Background()) ctx := t.Context()
defer cancel()
count := atomic.Int32{} count := atomic.Int32{}
done := make(chan struct{}) done := make(chan struct{})
@@ -123,7 +121,7 @@ func TestIdleWatchdog(t *testing.T) {
}() }()
// these must not block // these must not block
for i := 0; i < 5; i++ { for range 5 {
activity <- struct{}{} activity <- struct{}{}
} }
+24 -13
View File
@@ -2,13 +2,14 @@ package honeybee
import ( import (
"context" "context"
"git.wisehodl.dev/jay/go-honeybee/logging"
"git.wisehodl.dev/jay/go-honeybee/transport"
"git.wisehodl.dev/jay/go-honeybee/types"
"log/slog" "log/slog"
"sync" "sync"
"sync/atomic" "sync/atomic"
"time" "time"
"git.wisehodl.dev/jay/go-honeybee/transport"
"git.wisehodl.dev/jay/go-honeybee/types"
component "git.wisehodl.dev/jay/go-mana-component"
) )
// Worker // Worker
@@ -45,6 +46,7 @@ type DefaultWorker struct {
config *WorkerConfig config *WorkerConfig
ctx context.Context ctx context.Context
cancel context.CancelFunc cancel context.CancelFunc
handler slog.Handler
logger *slog.Logger logger *slog.Logger
} }
@@ -52,7 +54,7 @@ func NewWorker(
ctx context.Context, ctx context.Context,
id string, id string,
config *WorkerConfig, config *WorkerConfig,
logger *slog.Logger, handler slog.Handler,
) (*DefaultWorker, error) { ) (*DefaultWorker, error) {
if config == nil { if config == nil {
config = GetDefaultWorkerConfig() config = GetDefaultWorkerConfig()
@@ -61,6 +63,18 @@ func NewWorker(
return nil, err return nil, err
} }
if component.FromContext(ctx) == nil {
ctx = component.MustNew(ctx, "honeybee", "worker")
} else {
ctx = component.MustExtend(ctx, "worker")
}
var logger *slog.Logger
if handler != nil {
c := component.FromContext(ctx)
logger = slog.New(handler).With(slog.Any("component", c), slog.String("peer_id", id))
}
wctx, wcancel := context.WithCancel(ctx) wctx, wcancel := context.WithCancel(ctx)
w := &DefaultWorker{ w := &DefaultWorker{
id: id, id: id,
@@ -71,6 +85,7 @@ func NewWorker(
restartCount: &atomic.Uint64{}, restartCount: &atomic.Uint64{},
ctx: wctx, ctx: wctx,
cancel: wcancel, cancel: wcancel,
handler: handler,
logger: logger, logger: logger,
} }
@@ -91,7 +106,7 @@ func (w *DefaultWorker) Start(pool PoolPlugin) {
go func() { go func() {
defer wg.Done() defer wg.Done()
RunDialer(w.id, w.ctx, pool, dial, newConn, w.logger) RunDialer(w.id, w.ctx, pool, dial, newConn, w.handler, w.logger)
}() }()
go func() { go func() {
@@ -447,14 +462,9 @@ func connect(
id string, id string,
ctx context.Context, ctx context.Context,
pool PoolPlugin, pool PoolPlugin,
handler slog.Handler,
) (*transport.Connection, error) { ) (*transport.Connection, error) {
var logger *slog.Logger conn, err := transport.NewConnection(ctx, id, pool.ConnectionConfig, handler)
if pool.Handler != nil && pool.ConnectionConfig.LoggingEnabled {
logger = logging.NewConnectionLogger(
logging.WrapOrDefault(pool.ConnectionConfig.LogLevel, pool.Handler), pool.ID, id)
}
conn, err := transport.NewConnection(id, pool.ConnectionConfig, logger)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -471,6 +481,7 @@ func RunDialer(
dial <-chan struct{}, dial <-chan struct{},
newConn chan<- *transport.Connection, newConn chan<- *transport.Connection,
handler slog.Handler,
logger *slog.Logger, logger *slog.Logger,
) { ) {
for { for {
@@ -482,7 +493,7 @@ func RunDialer(
logger.Debug("dialer: dialing") logger.Debug("dialer: dialing")
} }
// dial a new connection // dial a new connection
conn, err := connect(id, ctx, pool) conn, err := connect(id, ctx, pool, handler)
// send error if dial failed and continue // send error if dial failed and continue
if err != nil { if err != nil {
+9 -12
View File
@@ -19,8 +19,7 @@ func TestRunDialer(t *testing.T) {
url := "wss://test" url := "wss://test"
dial := make(chan struct{}, 1) dial := make(chan struct{}, 1)
newConn := make(chan *transport.Connection, 1) newConn := make(chan *transport.Connection, 1)
ctx, cancel := context.WithCancel(context.Background()) ctx := t.Context()
defer cancel()
mockSocket := honeybeetest.NewMockSocket() mockSocket := honeybeetest.NewMockSocket()
pool := PoolPlugin{ pool := PoolPlugin{
@@ -31,7 +30,7 @@ func TestRunDialer(t *testing.T) {
}, },
} }
go RunDialer(url, ctx, pool, dial, newConn, nil) go RunDialer(url, ctx, pool, dial, newConn, nil, nil)
dial <- struct{}{} dial <- struct{}{}
honeybeetest.Eventually(t, func() bool { honeybeetest.Eventually(t, func() bool {
@@ -49,8 +48,7 @@ func TestRunDialer(t *testing.T) {
url := "wss://test" url := "wss://test"
dial := make(chan struct{}, 1) dial := make(chan struct{}, 1)
newConn := make(chan *transport.Connection, 1) newConn := make(chan *transport.Connection, 1)
ctx, cancel := context.WithCancel(context.Background()) ctx := t.Context()
defer cancel()
gate := make(chan struct{}) gate := make(chan struct{})
dialCount := atomic.Int32{} dialCount := atomic.Int32{}
@@ -71,14 +69,14 @@ func TestRunDialer(t *testing.T) {
ConnectionConfig: connConfig, ConnectionConfig: connConfig,
} }
go RunDialer(url, ctx, pool, dial, newConn, nil) go RunDialer(url, ctx, pool, dial, newConn, nil, nil)
dial <- struct{}{} dial <- struct{}{}
// wait for dial to start blocking on gate // wait for dial to start blocking on gate
<-started <-started
// flood dial while dialer is blocked // flood dial while dialer is blocked
for i := 0; i < 5; i++ { for range 5 {
select { select {
case dial <- struct{}{}: case dial <- struct{}{}:
default: default:
@@ -114,8 +112,7 @@ func TestRunDialer(t *testing.T) {
url := "wss://test" url := "wss://test"
dial := make(chan struct{}, 1) dial := make(chan struct{}, 1)
newConn := make(chan *transport.Connection, 1) newConn := make(chan *transport.Connection, 1)
ctx, cancel := context.WithCancel(context.Background()) ctx := t.Context()
defer cancel()
// use atomic counter to fail first dial and pass second // use atomic counter to fail first dial and pass second
dialCount := atomic.Int32{} dialCount := atomic.Int32{}
@@ -137,7 +134,7 @@ func TestRunDialer(t *testing.T) {
ConnectionConfig: connConfig, ConnectionConfig: connConfig,
} }
go RunDialer(url, ctx, pool, dial, newConn, nil) go RunDialer(url, ctx, pool, dial, newConn, nil, nil)
dial <- struct{}{} dial <- struct{}{}
dial <- struct{}{} dial <- struct{}{}
@@ -161,7 +158,7 @@ func TestRunDialer(t *testing.T) {
done := make(chan struct{}) done := make(chan struct{})
go func() { go func() {
RunDialer(url, ctx, pool, dial, newConn, nil) RunDialer(url, ctx, pool, dial, newConn, nil, nil)
close(done) close(done)
}() }()
@@ -198,7 +195,7 @@ func TestRunDialer(t *testing.T) {
done := make(chan struct{}) done := make(chan struct{})
go func() { go func() {
RunDialer(url, ctx, pool, dial, newConn, nil) RunDialer(url, ctx, pool, dial, newConn, nil, nil)
close(done) close(done)
}() }()
+5 -8
View File
@@ -12,13 +12,12 @@ func TestRunKeepalive(t *testing.T) {
heartbeat := make(chan struct{}) heartbeat := make(chan struct{})
keepalive := make(chan struct{}, 1) keepalive := make(chan struct{}, 1)
timeout := 200 * time.Millisecond timeout := 200 * time.Millisecond
ctx, cancel := context.WithCancel(context.Background()) ctx := t.Context()
defer cancel()
go RunKeepalive(ctx, heartbeat, keepalive, timeout, nil) go RunKeepalive(ctx, heartbeat, keepalive, timeout, nil)
// send heartbeats faster than the timeout // send heartbeats faster than the timeout
for i := 0; i < 5; i++ { for range 5 {
time.Sleep(20 * time.Millisecond) time.Sleep(20 * time.Millisecond)
heartbeat <- struct{}{} heartbeat <- struct{}{}
} }
@@ -38,8 +37,7 @@ func TestRunKeepalive(t *testing.T) {
heartbeat := make(chan struct{}, 1) heartbeat := make(chan struct{}, 1)
keepalive := make(chan struct{}, 1) keepalive := make(chan struct{}, 1)
timeout := 20 * time.Millisecond timeout := 20 * time.Millisecond
ctx, cancel := context.WithCancel(context.Background()) ctx := t.Context()
defer cancel()
go RunKeepalive(ctx, heartbeat, keepalive, timeout, nil) go RunKeepalive(ctx, heartbeat, keepalive, timeout, nil)
@@ -80,13 +78,12 @@ func TestRunKeepalive(t *testing.T) {
t.Run("disabled keepalive drains heartbeats without blocking", func(t *testing.T) { t.Run("disabled keepalive drains heartbeats without blocking", func(t *testing.T) {
heartbeat := make(chan struct{}) heartbeat := make(chan struct{})
keepalive := make(chan struct{}, 1) keepalive := make(chan struct{}, 1)
ctx, cancel := context.WithCancel(context.Background()) ctx := t.Context()
defer cancel()
go RunKeepalive(ctx, heartbeat, keepalive, 0, nil) go RunKeepalive(ctx, heartbeat, keepalive, 0, nil)
// these must not block // these must not block
for i := 0; i < 5; i++ { for range 5 {
heartbeat <- struct{}{} heartbeat <- struct{}{}
} }
+2 -2
View File
@@ -81,8 +81,8 @@ func TestWorkerSend(t *testing.T) {
}() }()
const count = 3 const count = 3
for i := 0; i < count; i++ { for i := range count {
err := w.Send([]byte(fmt.Sprintf("msg-%d", i))) err := w.Send(fmt.Appendf(nil, "msg-%d", i))
assert.NoError(t, err) assert.NoError(t, err)
} }
+4 -5
View File
@@ -68,10 +68,10 @@ func TestRunReader(t *testing.T) {
go RunReader("wss://test", ctx, cancel, conn, inbox, heartbeat, nil) go RunReader("wss://test", ctx, cancel, conn, inbox, heartbeat, nil)
const count = 3 const count = 3
for i := 0; i < count; i++ { for i := range count {
incomingData <- honeybeetest.MockIncomingData{ incomingData <- honeybeetest.MockIncomingData{
MsgType: websocket.TextMessage, MsgType: websocket.TextMessage,
Data: []byte(fmt.Sprintf("msg-%d", i)), Data: fmt.Appendf(nil, "msg-%d", i),
} }
} }
@@ -150,12 +150,11 @@ func TestHeartbeatForwarder(t *testing.T) {
var pongHandler func(string) error var pongHandler func(string) error
socket.SetPongHandlerFunc = func(h func(string) error) { pongHandler = h } socket.SetPongHandlerFunc = func(h func(string) error) { pongHandler = h }
conn, err := transport.NewConnectionFromSocket(socket, nil, nil) conn, err := transport.NewConnectionFromSocket(context.Background(), socket, nil, nil)
assert.NoError(t, err) assert.NoError(t, err)
heartbeat := make(chan struct{}, 1) heartbeat := make(chan struct{}, 1)
ctx, cancel := context.WithCancel(context.Background()) ctx := t.Context()
defer cancel()
go RunHeartbeatForwarder(ctx, conn, heartbeat, nil) go RunHeartbeatForwarder(ctx, conn, heartbeat, nil)
+1 -1
View File
@@ -140,7 +140,7 @@ func TestRunSessionDial(t *testing.T) {
// drain initial dial // drain initial dial
expectDial(t, v.dial) expectDial(t, v.dial)
for i := 0; i < 3; i++ { for range 3 {
v.keepalive <- struct{}{} v.keepalive <- struct{}{}
expectDial(t, v.dial) expectDial(t, v.dial)
} }
+12 -24
View File
@@ -65,11 +65,9 @@ func TestWorkerStart(t *testing.T) {
pool.Dialer = mockDialer(mockSocket) pool.Dialer = mockDialer(mockSocket)
var wg sync.WaitGroup var wg sync.WaitGroup
wg.Add(1) wg.Go(func() {
go func() {
w.Start(pool) w.Start(pool)
wg.Done() })
}()
honeybeetest.Eventually(t, func() bool { honeybeetest.Eventually(t, func() bool {
select { select {
@@ -91,11 +89,9 @@ func TestWorkerStart(t *testing.T) {
pool.Dialer = mockDialer(mockSocket) pool.Dialer = mockDialer(mockSocket)
var wg sync.WaitGroup var wg sync.WaitGroup
wg.Add(1) wg.Go(func() {
go func() {
w.Start(pool) w.Start(pool)
wg.Done() })
}()
honeybeetest.Eventually(t, func() bool { honeybeetest.Eventually(t, func() bool {
select { select {
@@ -144,11 +140,9 @@ func TestWorkerStart(t *testing.T) {
pool.Dialer = mockDialer(mockSocket) pool.Dialer = mockDialer(mockSocket)
var wg sync.WaitGroup var wg sync.WaitGroup
wg.Add(1) wg.Go(func() {
go func() {
w.Start(pool) w.Start(pool)
wg.Done() })
}()
honeybeetest.Eventually(t, func() bool { honeybeetest.Eventually(t, func() bool {
select { select {
@@ -184,11 +178,9 @@ func TestWorkerStart(t *testing.T) {
pool.Dialer = mockDialer(mockSocket) pool.Dialer = mockDialer(mockSocket)
var wg sync.WaitGroup var wg sync.WaitGroup
wg.Add(1) wg.Go(func() {
go func() {
w.Start(pool) w.Start(pool)
wg.Done() })
}()
honeybeetest.Eventually(t, func() bool { honeybeetest.Eventually(t, func() bool {
select { select {
@@ -230,11 +222,9 @@ func TestWorkerStart(t *testing.T) {
pool.Dialer = mockDialer(mockSocket) pool.Dialer = mockDialer(mockSocket)
var wg sync.WaitGroup var wg sync.WaitGroup
wg.Add(1) wg.Go(func() {
go func() {
w.Start(pool) w.Start(pool)
wg.Done() })
}()
honeybeetest.Eventually(t, func() bool { honeybeetest.Eventually(t, func() bool {
select { select {
@@ -278,11 +268,9 @@ func TestWorkerStart(t *testing.T) {
pool.Dialer = mockDialer(mockSocket) pool.Dialer = mockDialer(mockSocket)
var wg sync.WaitGroup var wg sync.WaitGroup
wg.Add(1) wg.Go(func() {
go func() {
w.Start(pool) w.Start(pool)
wg.Done() })
}()
honeybeetest.Eventually(t, func() bool { honeybeetest.Eventually(t, func() bool {
select { select {