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