Wrote runReader tests, runSession body
This commit is contained in:
+75
-11
@@ -54,9 +54,20 @@ func NewWorker(
|
|||||||
return w, nil
|
return w, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *Worker) Start(
|
||||||
|
ctx WorkerContext,
|
||||||
|
wg *sync.WaitGroup,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Worker) Stop() {
|
||||||
|
w.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
func (w *Worker) Send(data []byte) error {
|
func (w *Worker) Send(data []byte) error {
|
||||||
conn := w.conn.Load()
|
conn := w.conn.Load()
|
||||||
if conn == nil {
|
if conn == nil {
|
||||||
|
// connection not established by session
|
||||||
return NewWorkerError(w.id, ErrConnectionUnavailable)
|
return NewWorkerError(w.id, ErrConnectionUnavailable)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,16 +85,6 @@ func (w *Worker) Send(data []byte) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Worker) Start(
|
|
||||||
ctx WorkerContext,
|
|
||||||
wg *sync.WaitGroup,
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Worker) Stop() {
|
|
||||||
w.cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Worker) runSession(
|
func (w *Worker) runSession(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
wctx WorkerContext,
|
wctx WorkerContext,
|
||||||
@@ -92,9 +93,72 @@ func (w *Worker) runSession(
|
|||||||
dial chan<- struct{},
|
dial chan<- struct{},
|
||||||
|
|
||||||
keepalive <-chan struct{},
|
keepalive <-chan struct{},
|
||||||
outbound <-chan []byte,
|
|
||||||
newConn <-chan *transport.Connection,
|
newConn <-chan *transport.Connection,
|
||||||
) {
|
) {
|
||||||
|
for {
|
||||||
|
// request new connection
|
||||||
|
select {
|
||||||
|
case dial <- struct{}{}:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
// obtain new connection
|
||||||
|
var conn *transport.Connection
|
||||||
|
preConn:
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
case <-keepalive:
|
||||||
|
select {
|
||||||
|
case dial <- struct{}{}:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
case conn = <-newConn:
|
||||||
|
break preConn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set up new connection
|
||||||
|
w.conn.Store(conn)
|
||||||
|
wctx.Events <- PoolEvent{ID: w.id, Kind: EventConnected}
|
||||||
|
|
||||||
|
// set up session
|
||||||
|
sessionDone := make(chan struct{})
|
||||||
|
var once sync.Once
|
||||||
|
onStop := func() {
|
||||||
|
once.Do(func() { close(sessionDone) })
|
||||||
|
}
|
||||||
|
|
||||||
|
// start session
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(2)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
w.runReader(conn, messages, sessionDone, onStop)
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
w.runStopMonitor(ctx, conn, keepalive, sessionDone, onStop)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// complete session
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
// tear down connection
|
||||||
|
w.conn.Store(nil)
|
||||||
|
wctx.Events <- PoolEvent{ID: w.id, Kind: EventDisconnected}
|
||||||
|
|
||||||
|
// exit if worker is shutting down
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
// refresh session
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Worker) runReader(
|
func (w *Worker) runReader(
|
||||||
|
|||||||
+218
-68
@@ -6,14 +6,231 @@ import (
|
|||||||
"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"
|
||||||
"git.wisehodl.dev/jay/go-honeybee/types"
|
"git.wisehodl.dev/jay/go-honeybee/types"
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"sync"
|
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestRunSession(t *testing.T) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRunReader(t *testing.T) {
|
||||||
|
t.Run("message arrives with correct data and non-zero receivedAt", func(t *testing.T) {
|
||||||
|
conn, _, incomingData, _ := setupWorkerTestConnection(t)
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
messages := make(chan receivedMessage, 1)
|
||||||
|
heartbeat := make(chan struct{})
|
||||||
|
sessionDone := make(chan struct{})
|
||||||
|
onStop := func() {}
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
w := &Worker{
|
||||||
|
ctx: ctx,
|
||||||
|
cancel: cancel,
|
||||||
|
id: "wss://test",
|
||||||
|
heartbeat: heartbeat,
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
for range heartbeat {
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
go w.runReader(conn, messages, sessionDone, onStop)
|
||||||
|
|
||||||
|
before := time.Now()
|
||||||
|
incomingData <- honeybeetest.MockIncomingData{
|
||||||
|
MsgType: websocket.TextMessage,
|
||||||
|
Data: []byte("hello"),
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Eventually(t, func() bool {
|
||||||
|
select {
|
||||||
|
case msg := <-messages:
|
||||||
|
return string(msg.data) == "hello" && msg.receivedAt.After(before)
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}, honeybeetest.TestTimeout, honeybeetest.TestTick)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("heartbeat receives one signal per message", func(t *testing.T) {
|
||||||
|
conn, _, incomingData, _ := setupWorkerTestConnection(t)
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
messages := make(chan receivedMessage, 10)
|
||||||
|
heartbeat := make(chan struct{})
|
||||||
|
sessionDone := make(chan struct{})
|
||||||
|
onStop := func() {}
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
w := &Worker{
|
||||||
|
ctx: ctx,
|
||||||
|
cancel: cancel,
|
||||||
|
id: "wss://test",
|
||||||
|
heartbeat: heartbeat,
|
||||||
|
}
|
||||||
|
|
||||||
|
received := atomic.Int32{}
|
||||||
|
go func() {
|
||||||
|
for range heartbeat {
|
||||||
|
received.Add(1)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
for range messages {
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
go w.runReader(conn, messages, sessionDone, onStop)
|
||||||
|
|
||||||
|
const count = 3
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
incomingData <- honeybeetest.MockIncomingData{
|
||||||
|
MsgType: websocket.TextMessage,
|
||||||
|
Data: []byte(fmt.Sprintf("msg-%d", i)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Eventually(t, func() bool {
|
||||||
|
return received.Load() == count
|
||||||
|
}, honeybeetest.TestTimeout, honeybeetest.TestTick)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("incoming channel close calls conn.Close and onStop", func(t *testing.T) {
|
||||||
|
conn, _, incomingData, _ := setupWorkerTestConnection(t)
|
||||||
|
|
||||||
|
messages := make(chan receivedMessage, 1)
|
||||||
|
heartbeat := make(chan struct{})
|
||||||
|
sessionDone := make(chan struct{})
|
||||||
|
onStopCalled := atomic.Bool{}
|
||||||
|
onStop := func() { onStopCalled.Store(true) }
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
w := &Worker{
|
||||||
|
ctx: ctx,
|
||||||
|
id: "wss://test",
|
||||||
|
heartbeat: heartbeat,
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
for range heartbeat {
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
for range messages {
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
go w.runReader(conn, messages, sessionDone, onStop)
|
||||||
|
|
||||||
|
// simulate remote close
|
||||||
|
incomingData <- honeybeetest.MockIncomingData{Err: io.EOF}
|
||||||
|
|
||||||
|
assert.Eventually(t, func() bool {
|
||||||
|
return connClosed(conn)
|
||||||
|
}, honeybeetest.TestTimeout, honeybeetest.TestTick)
|
||||||
|
|
||||||
|
assert.True(t, onStopCalled.Load())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("sessionDone close calls conn.Close and onStop", func(t *testing.T) {
|
||||||
|
conn, _, _, _ := setupWorkerTestConnection(t)
|
||||||
|
|
||||||
|
messages := make(chan receivedMessage, 1)
|
||||||
|
heartbeat := make(chan struct{})
|
||||||
|
sessionDone := make(chan struct{})
|
||||||
|
onStopCalled := atomic.Bool{}
|
||||||
|
onStop := func() { onStopCalled.Store(true) }
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
w := &Worker{
|
||||||
|
ctx: ctx,
|
||||||
|
id: "wss://test",
|
||||||
|
heartbeat: heartbeat,
|
||||||
|
}
|
||||||
|
go w.runReader(conn, messages, sessionDone, onStop)
|
||||||
|
|
||||||
|
close(sessionDone)
|
||||||
|
|
||||||
|
assert.Eventually(t, func() bool {
|
||||||
|
return connClosed(conn)
|
||||||
|
}, honeybeetest.TestTimeout, honeybeetest.TestTick)
|
||||||
|
|
||||||
|
assert.True(t, onStopCalled.Load())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRunStopMonitor(t *testing.T) {
|
||||||
|
t.Run("keepalive signal calls conn.Close and onStop", func(t *testing.T) {
|
||||||
|
conn, _, _, _ := setupWorkerTestConnection(t)
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
keepalive := make(chan struct{}, 1)
|
||||||
|
sessionDone := make(chan struct{})
|
||||||
|
onStopCalled := atomic.Bool{}
|
||||||
|
onStop := func() { onStopCalled.Store(true) }
|
||||||
|
|
||||||
|
w := &Worker{id: "wss://test"}
|
||||||
|
go w.runStopMonitor(ctx, conn, keepalive, sessionDone, onStop)
|
||||||
|
|
||||||
|
keepalive <- struct{}{}
|
||||||
|
|
||||||
|
assert.Eventually(t, func() bool {
|
||||||
|
return connClosed(conn)
|
||||||
|
}, honeybeetest.TestTimeout, honeybeetest.TestTick)
|
||||||
|
|
||||||
|
assert.True(t, onStopCalled.Load())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("ctx.Done calls conn.Close and onStop", func(t *testing.T) {
|
||||||
|
conn, _, _, _ := setupWorkerTestConnection(t)
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
|
keepalive := make(chan struct{})
|
||||||
|
sessionDone := make(chan struct{})
|
||||||
|
onStopCalled := atomic.Bool{}
|
||||||
|
onStop := func() { onStopCalled.Store(true) }
|
||||||
|
|
||||||
|
w := &Worker{id: "wss://test"}
|
||||||
|
go w.runStopMonitor(ctx, conn, keepalive, sessionDone, onStop)
|
||||||
|
|
||||||
|
cancel()
|
||||||
|
|
||||||
|
assert.Eventually(t, func() bool {
|
||||||
|
return connClosed(conn)
|
||||||
|
}, honeybeetest.TestTimeout, honeybeetest.TestTick)
|
||||||
|
|
||||||
|
assert.True(t, onStopCalled.Load())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("sessionDone close calls conn.Close and onStop", func(t *testing.T) {
|
||||||
|
conn, _, _, _ := setupWorkerTestConnection(t)
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
keepalive := make(chan struct{})
|
||||||
|
sessionDone := make(chan struct{})
|
||||||
|
onStopCalled := atomic.Bool{}
|
||||||
|
onStop := func() { onStopCalled.Store(true) }
|
||||||
|
|
||||||
|
w := &Worker{id: "wss://test"}
|
||||||
|
go w.runStopMonitor(ctx, conn, keepalive, sessionDone, onStop)
|
||||||
|
|
||||||
|
close(sessionDone)
|
||||||
|
|
||||||
|
assert.Eventually(t, func() bool {
|
||||||
|
return connClosed(conn)
|
||||||
|
}, honeybeetest.TestTimeout, honeybeetest.TestTick)
|
||||||
|
|
||||||
|
assert.True(t, onStopCalled.Load())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestRunForwarder(t *testing.T) {
|
func TestRunForwarder(t *testing.T) {
|
||||||
t.Run("message passes through to inbox", func(t *testing.T) {
|
t.Run("message passes through to inbox", func(t *testing.T) {
|
||||||
messages := make(chan receivedMessage, 1)
|
messages := make(chan receivedMessage, 1)
|
||||||
@@ -180,73 +397,6 @@ func TestRunKeepalive(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRunStopMonitor(t *testing.T) {
|
|
||||||
t.Run("keepalive signal calls conn.Close and onStop", func(t *testing.T) {
|
|
||||||
conn, _, _, _ := setupWorkerTestConnection(t)
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
keepalive := make(chan struct{}, 1)
|
|
||||||
sessionDone := make(chan struct{})
|
|
||||||
onStopCalled := atomic.Bool{}
|
|
||||||
onStop := func() { onStopCalled.Store(true) }
|
|
||||||
|
|
||||||
w := &Worker{id: "wss://test"}
|
|
||||||
go w.runStopMonitor(ctx, conn, keepalive, sessionDone, onStop)
|
|
||||||
|
|
||||||
keepalive <- struct{}{}
|
|
||||||
|
|
||||||
assert.Eventually(t, func() bool {
|
|
||||||
return connClosed(conn)
|
|
||||||
}, honeybeetest.TestTimeout, honeybeetest.TestTick)
|
|
||||||
|
|
||||||
assert.True(t, onStopCalled.Load())
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("ctx.Done calls conn.Close and onStop", func(t *testing.T) {
|
|
||||||
conn, _, _, _ := setupWorkerTestConnection(t)
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
|
|
||||||
keepalive := make(chan struct{})
|
|
||||||
sessionDone := make(chan struct{})
|
|
||||||
onStopCalled := atomic.Bool{}
|
|
||||||
onStop := func() { onStopCalled.Store(true) }
|
|
||||||
|
|
||||||
w := &Worker{id: "wss://test"}
|
|
||||||
go w.runStopMonitor(ctx, conn, keepalive, sessionDone, onStop)
|
|
||||||
|
|
||||||
cancel()
|
|
||||||
|
|
||||||
assert.Eventually(t, func() bool {
|
|
||||||
return connClosed(conn)
|
|
||||||
}, honeybeetest.TestTimeout, honeybeetest.TestTick)
|
|
||||||
|
|
||||||
assert.True(t, onStopCalled.Load())
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("sessionDone close calls conn.Close and onStop", func(t *testing.T) {
|
|
||||||
conn, _, _, _ := setupWorkerTestConnection(t)
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
keepalive := make(chan struct{})
|
|
||||||
sessionDone := make(chan struct{})
|
|
||||||
onStopCalled := atomic.Bool{}
|
|
||||||
onStop := func() { onStopCalled.Store(true) }
|
|
||||||
|
|
||||||
w := &Worker{id: "wss://test"}
|
|
||||||
go w.runStopMonitor(ctx, conn, keepalive, sessionDone, onStop)
|
|
||||||
|
|
||||||
close(sessionDone)
|
|
||||||
|
|
||||||
assert.Eventually(t, func() bool {
|
|
||||||
return connClosed(conn)
|
|
||||||
}, honeybeetest.TestTimeout, honeybeetest.TestTick)
|
|
||||||
|
|
||||||
assert.True(t, onStopCalled.Load())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunDialer(t *testing.T) {
|
func TestRunDialer(t *testing.T) {
|
||||||
t.Run("successful dial delivers connection to newConn", func(t *testing.T) {
|
t.Run("successful dial delivers connection to newConn", func(t *testing.T) {
|
||||||
w := &Worker{id: "wss://test"}
|
w := &Worker{id: "wss://test"}
|
||||||
|
|||||||
Reference in New Issue
Block a user