cleaned up terminology, added cancel and expire tests, fixed send outcome behavior
This commit is contained in:
+143
-60
@@ -2,13 +2,13 @@ package prism
|
||||
|
||||
import (
|
||||
"context"
|
||||
// "git.wisehodl.dev/jay/go-mana-component"
|
||||
"github.com/stretchr/testify/assert"
|
||||
// "sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
const testURL = "wss://test"
|
||||
|
||||
func TestPostmasterUnknownPeerSend(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
poolHasPeer := func(id string) (string, bool) { return id, true }
|
||||
@@ -17,8 +17,20 @@ func TestPostmasterUnknownPeerSend(t *testing.T) {
|
||||
|
||||
pm := NewPostmaster(ctx, poolHasPeer, poolEvents, poolSendFunc, nil)
|
||||
|
||||
_, err := pm.Send(ctx, "wss://test", []byte("[]"), func(LetterOutcome) {})
|
||||
assert.Error(t, err)
|
||||
called := make(chan LetterOutcome, 1)
|
||||
pm.Send(ctx, testURL, nil, func(o LetterOutcome) { called <- o })
|
||||
|
||||
var outcome LetterOutcome
|
||||
Eventually(t, func() bool {
|
||||
select {
|
||||
default:
|
||||
return false
|
||||
case outcome = <-called:
|
||||
return true
|
||||
}
|
||||
}, "should have received outcome")
|
||||
|
||||
assert.Equal(t, OutcomeRejected, outcome.Kind)
|
||||
}
|
||||
|
||||
func TestPostmasterSend(t *testing.T) {
|
||||
@@ -29,18 +41,13 @@ func TestPostmasterSend(t *testing.T) {
|
||||
|
||||
pm := NewPostmaster(ctx, poolHasPeer, poolEvents, poolSendFunc, nil)
|
||||
|
||||
poolEvents <- PoolEvent{
|
||||
ID: "wss://test", Kind: EventAdded, At: time.Now()}
|
||||
poolEvents <- PoolEvent{
|
||||
ID: "wss://test", Kind: EventConnected, At: time.Now()}
|
||||
poolEvents <- PoolEvent{ID: testURL, Kind: EventAdded, At: time.Now()}
|
||||
poolEvents <- PoolEvent{ID: testURL, Kind: EventConnected, At: time.Now()}
|
||||
|
||||
Eventually(t, func() bool { return len(pm.Peers()) > 0 },
|
||||
"should add peer")
|
||||
Eventually(t, func() bool { return len(pm.Peers()) > 0 }, "should add peer")
|
||||
|
||||
called := make(chan LetterOutcome, 1)
|
||||
_, err := pm.Send(
|
||||
ctx, "wss://test", []byte("[]"), func(o LetterOutcome) { called <- o })
|
||||
assert.NoError(t, err)
|
||||
pm.Send(ctx, testURL, nil, func(o LetterOutcome) { called <- o })
|
||||
|
||||
var outcome LetterOutcome
|
||||
Eventually(t, func() bool {
|
||||
@@ -55,6 +62,78 @@ func TestPostmasterSend(t *testing.T) {
|
||||
assert.Equal(t, OutcomeSent, outcome.Kind)
|
||||
}
|
||||
|
||||
func TestPostmasterCancelInFlight(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
poolHasPeer := func(id string) (string, bool) { return id, true }
|
||||
poolEvents := make(chan PoolEvent, 4)
|
||||
poolSendFunc := func(id string, data Envelope) error { return nil }
|
||||
|
||||
pm := NewPostmaster(ctx, poolHasPeer, poolEvents, poolSendFunc, nil)
|
||||
|
||||
poolEvents <- PoolEvent{ID: testURL, Kind: EventAdded, At: time.Now()}
|
||||
Eventually(t, func() bool { return len(pm.Peers()) > 0 }, "should add peer")
|
||||
|
||||
called := make(chan LetterOutcome, 1)
|
||||
cancel := pm.Send(ctx, testURL, nil, func(o LetterOutcome) { called <- o })
|
||||
|
||||
// wait for letter to queue
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
// cancel the letter using its callback
|
||||
cancel()
|
||||
|
||||
// connect the pool
|
||||
poolEvents <- PoolEvent{ID: testURL, Kind: EventConnected, At: time.Now()}
|
||||
|
||||
var outcome LetterOutcome
|
||||
Eventually(t, func() bool {
|
||||
select {
|
||||
default:
|
||||
return false
|
||||
case outcome = <-called:
|
||||
return true
|
||||
}
|
||||
}, "should have received outcome")
|
||||
|
||||
// letter should drain out of the queue and return cancelled
|
||||
assert.Equal(t, OutcomeCancelled, outcome.Kind)
|
||||
}
|
||||
|
||||
func TestPostmasterExpire(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
poolHasPeer := func(id string) (string, bool) { return id, true }
|
||||
poolEvents := make(chan PoolEvent, 4)
|
||||
poolSendFunc := func(id string, data Envelope) error { return nil }
|
||||
|
||||
pm := NewPostmaster(ctx, poolHasPeer, poolEvents, poolSendFunc, nil)
|
||||
|
||||
poolEvents <- PoolEvent{ID: testURL, Kind: EventAdded, At: time.Now()}
|
||||
Eventually(t, func() bool { return len(pm.Peers()) > 0 }, "should add peer")
|
||||
|
||||
called := make(chan LetterOutcome, 1)
|
||||
pm.Send(ctx, testURL, nil, func(o LetterOutcome) { called <- o },
|
||||
WithDeadline(1*time.Millisecond))
|
||||
|
||||
// wait for letter to queue and expire
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
// connect the pool
|
||||
poolEvents <- PoolEvent{ID: testURL, Kind: EventConnected, At: time.Now()}
|
||||
|
||||
var outcome LetterOutcome
|
||||
Eventually(t, func() bool {
|
||||
select {
|
||||
default:
|
||||
return false
|
||||
case outcome = <-called:
|
||||
return true
|
||||
}
|
||||
}, "should have received outcome")
|
||||
|
||||
// letter should drain out of the queue and return expired
|
||||
assert.Equal(t, OutcomeExpired, outcome.Kind)
|
||||
}
|
||||
|
||||
func TestPostmasterPeerRemoved(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
poolHasPeer := func(id string) (string, bool) { return id, true }
|
||||
@@ -64,27 +143,20 @@ func TestPostmasterPeerRemoved(t *testing.T) {
|
||||
pm := NewPostmaster(ctx, poolHasPeer, poolEvents, poolSendFunc, nil)
|
||||
|
||||
// add peer, but do not connect
|
||||
poolEvents <- PoolEvent{
|
||||
ID: "wss://test", Kind: EventAdded, At: time.Now()}
|
||||
Eventually(t, func() bool { return len(pm.Peers()) > 0 },
|
||||
"should add peer")
|
||||
poolEvents <- PoolEvent{ID: testURL, Kind: EventAdded, At: time.Now()}
|
||||
Eventually(t, func() bool { return len(pm.Peers()) > 0 }, "should add peer")
|
||||
|
||||
// send two letters
|
||||
outcomes := make([]LetterOutcome, 0, 2)
|
||||
called := make(chan LetterOutcome, 2)
|
||||
_, err := pm.Send(
|
||||
ctx, "wss://test", []byte("[]"), func(o LetterOutcome) { called <- o })
|
||||
assert.NoError(t, err)
|
||||
_, err = pm.Send(
|
||||
ctx, "wss://test", []byte("[]"), func(o LetterOutcome) { called <- o })
|
||||
assert.NoError(t, err)
|
||||
pm.Send(ctx, testURL, nil, func(o LetterOutcome) { called <- o })
|
||||
pm.Send(ctx, testURL, nil, func(o LetterOutcome) { called <- o })
|
||||
|
||||
// wait for them to hit the courier queue
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
// remove the peer
|
||||
poolEvents <- PoolEvent{
|
||||
ID: "wss://test", Kind: EventRemoved, At: time.Now()}
|
||||
poolEvents <- PoolEvent{ID: testURL, Kind: EventRemoved, At: time.Now()}
|
||||
|
||||
// expect each letter to return cancelled
|
||||
Eventually(t, func() bool {
|
||||
@@ -103,9 +175,19 @@ func TestPostmasterPeerRemoved(t *testing.T) {
|
||||
}
|
||||
|
||||
// subsequent sends should fail
|
||||
_, err = pm.Send(
|
||||
ctx, "wss://test", []byte("[]"), func(o LetterOutcome) { called <- o })
|
||||
assert.Error(t, err)
|
||||
pm.Send(ctx, testURL, nil, func(o LetterOutcome) { called <- o })
|
||||
|
||||
var outcome LetterOutcome
|
||||
Eventually(t, func() bool {
|
||||
select {
|
||||
default:
|
||||
return false
|
||||
case outcome = <-called:
|
||||
return true
|
||||
}
|
||||
}, "should have received outcome")
|
||||
|
||||
assert.Equal(t, OutcomeRejected, outcome.Kind)
|
||||
}
|
||||
|
||||
func TestPostmasterCourierCloseRace(t *testing.T) {
|
||||
@@ -117,42 +199,39 @@ func TestPostmasterCourierCloseRace(t *testing.T) {
|
||||
pm := NewPostmaster(ctx, poolHasPeer, poolEvents, poolSendFunc, nil)
|
||||
|
||||
// add peer, but do not connect
|
||||
poolEvents <- PoolEvent{
|
||||
ID: "wss://test", Kind: EventAdded, At: time.Now()}
|
||||
Eventually(t, func() bool { return len(pm.Peers()) > 0 },
|
||||
"should add peer")
|
||||
poolEvents <- PoolEvent{ID: testURL, Kind: EventAdded, At: time.Now()}
|
||||
Eventually(t, func() bool { return len(pm.Peers()) > 0 }, "should add peer")
|
||||
|
||||
// remove the peer
|
||||
poolEvents <- PoolEvent{
|
||||
ID: "wss://test", Kind: EventRemoved, At: time.Now()}
|
||||
poolEvents <- PoolEvent{ID: testURL, Kind: EventRemoved, At: time.Now()}
|
||||
|
||||
// send a letter
|
||||
time.Sleep(5 * time.Microsecond) // small wait lines up the race condition
|
||||
var outcome LetterOutcome
|
||||
var outcome *LetterOutcome
|
||||
called := make(chan LetterOutcome, 1)
|
||||
_, err := pm.Send(
|
||||
ctx, "wss://test", []byte("[]"), func(o LetterOutcome) { called <- o })
|
||||
pm.Send(ctx, testURL, nil, func(o LetterOutcome) { called <- o })
|
||||
|
||||
if err != nil {
|
||||
// the close won the race, the letter was not sent
|
||||
return
|
||||
}
|
||||
|
||||
// the letter might beat the courier close and return cancelled
|
||||
Eventually(t, func() bool {
|
||||
select {
|
||||
default:
|
||||
return false
|
||||
case outcome = <-called:
|
||||
case o := <-called:
|
||||
outcome = &o
|
||||
return true
|
||||
}
|
||||
}, "should have returned 1 outcomes")
|
||||
|
||||
if outcome.LetterID == 0 {
|
||||
if outcome == nil {
|
||||
t.Fatal("did not receive an outcome")
|
||||
}
|
||||
|
||||
assert.Equal(t, OutcomeCancelled, outcome.Kind)
|
||||
// depending on the race, the outcome could be:
|
||||
// close, then send: send is rejected by the postmaster
|
||||
// send, then close: send is cancelled by the courier
|
||||
assert.Contains(t,
|
||||
[]LetterOutcomeKind{OutcomeCancelled, OutcomeRejected},
|
||||
outcome.Kind,
|
||||
)
|
||||
}
|
||||
|
||||
func TestPostmasterClose(t *testing.T) {
|
||||
@@ -164,20 +243,14 @@ func TestPostmasterClose(t *testing.T) {
|
||||
pm := NewPostmaster(ctx, poolHasPeer, poolEvents, poolSendFunc, nil)
|
||||
|
||||
// add peer, but do not connect
|
||||
poolEvents <- PoolEvent{
|
||||
ID: "wss://test", Kind: EventAdded, At: time.Now()}
|
||||
Eventually(t, func() bool { return len(pm.Peers()) > 0 },
|
||||
"should add peer")
|
||||
poolEvents <- PoolEvent{ID: testURL, Kind: EventAdded, At: time.Now()}
|
||||
Eventually(t, func() bool { return len(pm.Peers()) > 0 }, "should add peer")
|
||||
|
||||
// send two letters
|
||||
outcomes := make([]LetterOutcome, 0, 2)
|
||||
called := make(chan LetterOutcome, 2)
|
||||
_, err := pm.Send(
|
||||
ctx, "wss://test", []byte("[]"), func(o LetterOutcome) { called <- o })
|
||||
assert.NoError(t, err)
|
||||
_, err = pm.Send(
|
||||
ctx, "wss://test", []byte("[]"), func(o LetterOutcome) { called <- o })
|
||||
assert.NoError(t, err)
|
||||
pm.Send(ctx, testURL, nil, func(o LetterOutcome) { called <- o })
|
||||
pm.Send(ctx, testURL, nil, func(o LetterOutcome) { called <- o })
|
||||
|
||||
// wait for them to hit the courier queue
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
@@ -201,8 +274,18 @@ func TestPostmasterClose(t *testing.T) {
|
||||
assert.Equal(t, OutcomeCancelled, outcomes[1].Kind)
|
||||
}
|
||||
|
||||
// subsequent sends should fail
|
||||
_, err = pm.Send(
|
||||
ctx, "wss://test", []byte("[]"), func(o LetterOutcome) { called <- o })
|
||||
assert.Error(t, err)
|
||||
// subsequent sends should be rejected
|
||||
pm.Send(ctx, testURL, nil, func(o LetterOutcome) { called <- o })
|
||||
|
||||
var outcome LetterOutcome
|
||||
Eventually(t, func() bool {
|
||||
select {
|
||||
default:
|
||||
return false
|
||||
case outcome = <-called:
|
||||
return true
|
||||
}
|
||||
}, "should have received outcome")
|
||||
|
||||
assert.Equal(t, OutcomeRejected, outcome.Kind)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user