271 lines
6.5 KiB
Go
271 lines
6.5 KiB
Go
package push
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestHubDeliversSessionTargetedEvent(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
hub := NewHub(4)
|
|
target, err := hub.Register(StreamBinding{
|
|
UserID: "user-123",
|
|
DeviceSessionID: "device-session-1",
|
|
})
|
|
require.NoError(t, err)
|
|
otherSession, err := hub.Register(StreamBinding{
|
|
UserID: "user-123",
|
|
DeviceSessionID: "device-session-2",
|
|
})
|
|
require.NoError(t, err)
|
|
unrelatedUser, err := hub.Register(StreamBinding{
|
|
UserID: "user-999",
|
|
DeviceSessionID: "device-session-3",
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
hub.Publish(Event{
|
|
UserID: "user-123",
|
|
DeviceSessionID: "device-session-1",
|
|
EventType: "fleet.updated",
|
|
EventID: "event-1",
|
|
PayloadBytes: []byte("payload-1"),
|
|
})
|
|
|
|
assertEvent(t, target.Events(), Event{
|
|
UserID: "user-123",
|
|
DeviceSessionID: "device-session-1",
|
|
EventType: "fleet.updated",
|
|
EventID: "event-1",
|
|
PayloadBytes: []byte("payload-1"),
|
|
})
|
|
assertNoEvent(t, otherSession.Events())
|
|
assertNoEvent(t, unrelatedUser.Events())
|
|
}
|
|
|
|
func TestHubDeliversUserTargetedEventToAllUserSessions(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
hub := NewHub(4)
|
|
first, err := hub.Register(StreamBinding{
|
|
UserID: "user-123",
|
|
DeviceSessionID: "device-session-1",
|
|
})
|
|
require.NoError(t, err)
|
|
second, err := hub.Register(StreamBinding{
|
|
UserID: "user-123",
|
|
DeviceSessionID: "device-session-2",
|
|
})
|
|
require.NoError(t, err)
|
|
unrelated, err := hub.Register(StreamBinding{
|
|
UserID: "user-999",
|
|
DeviceSessionID: "device-session-3",
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
hub.Publish(Event{
|
|
UserID: "user-123",
|
|
EventType: "fleet.updated",
|
|
EventID: "event-1",
|
|
PayloadBytes: []byte("payload-1"),
|
|
RequestID: "request-1",
|
|
TraceID: "trace-1",
|
|
})
|
|
|
|
want := Event{
|
|
UserID: "user-123",
|
|
EventType: "fleet.updated",
|
|
EventID: "event-1",
|
|
PayloadBytes: []byte("payload-1"),
|
|
RequestID: "request-1",
|
|
TraceID: "trace-1",
|
|
}
|
|
assertEvent(t, first.Events(), want)
|
|
assertEvent(t, second.Events(), want)
|
|
assertNoEvent(t, unrelated.Events())
|
|
}
|
|
|
|
func TestSubscriptionCloseUnregistersStream(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
hub := NewHub(4)
|
|
subscription, err := hub.Register(StreamBinding{
|
|
UserID: "user-123",
|
|
DeviceSessionID: "device-session-1",
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
subscription.Close()
|
|
|
|
select {
|
|
case <-subscription.Done():
|
|
case <-time.After(time.Second):
|
|
require.FailNow(t, "subscription did not close")
|
|
}
|
|
|
|
hub.Publish(Event{
|
|
UserID: "user-123",
|
|
EventType: "fleet.updated",
|
|
EventID: "event-1",
|
|
PayloadBytes: []byte("payload-1"),
|
|
})
|
|
|
|
assertNoEvent(t, subscription.Events())
|
|
assert.NoError(t, subscription.Err())
|
|
}
|
|
|
|
func TestHubOverflowClosesOnlySlowSubscription(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
hub := NewHub(1)
|
|
slow, err := hub.Register(StreamBinding{
|
|
UserID: "user-123",
|
|
DeviceSessionID: "device-session-1",
|
|
})
|
|
require.NoError(t, err)
|
|
fast, err := hub.Register(StreamBinding{
|
|
UserID: "user-123",
|
|
DeviceSessionID: "device-session-2",
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
hub.Publish(Event{
|
|
UserID: "user-123",
|
|
EventType: "fleet.updated",
|
|
EventID: "event-1",
|
|
PayloadBytes: []byte("payload-1"),
|
|
})
|
|
assertEvent(t, fast.Events(), Event{
|
|
UserID: "user-123",
|
|
EventType: "fleet.updated",
|
|
EventID: "event-1",
|
|
PayloadBytes: []byte("payload-1"),
|
|
})
|
|
|
|
hub.Publish(Event{
|
|
UserID: "user-123",
|
|
EventType: "fleet.updated",
|
|
EventID: "event-2",
|
|
PayloadBytes: []byte("payload-2"),
|
|
})
|
|
|
|
select {
|
|
case <-slow.Done():
|
|
case <-time.After(time.Second):
|
|
require.FailNow(t, "slow subscription did not close after overflow")
|
|
}
|
|
|
|
assert.ErrorIs(t, slow.Err(), ErrSubscriptionOverflow)
|
|
assertEvent(t, fast.Events(), Event{
|
|
UserID: "user-123",
|
|
EventType: "fleet.updated",
|
|
EventID: "event-2",
|
|
PayloadBytes: []byte("payload-2"),
|
|
})
|
|
}
|
|
|
|
func TestHubRevokeDeviceSessionClosesOnlyMatchingSubscriptions(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
hub := NewHub(4)
|
|
targetOne, err := hub.Register(StreamBinding{
|
|
UserID: "user-123",
|
|
DeviceSessionID: "device-session-1",
|
|
})
|
|
require.NoError(t, err)
|
|
targetTwo, err := hub.Register(StreamBinding{
|
|
UserID: "user-456",
|
|
DeviceSessionID: "device-session-1",
|
|
})
|
|
require.NoError(t, err)
|
|
otherSession, err := hub.Register(StreamBinding{
|
|
UserID: "user-123",
|
|
DeviceSessionID: "device-session-2",
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
hub.RevokeDeviceSession("device-session-1")
|
|
|
|
select {
|
|
case <-targetOne.Done():
|
|
case <-time.After(time.Second):
|
|
require.FailNow(t, "first matching subscription did not close after revoke")
|
|
}
|
|
|
|
select {
|
|
case <-targetTwo.Done():
|
|
case <-time.After(time.Second):
|
|
require.FailNow(t, "second matching subscription did not close after revoke")
|
|
}
|
|
|
|
assert.ErrorIs(t, targetOne.Err(), ErrSubscriptionRevoked)
|
|
assert.ErrorIs(t, targetTwo.Err(), ErrSubscriptionRevoked)
|
|
|
|
select {
|
|
case <-otherSession.Done():
|
|
require.FailNow(t, "unrelated session subscription closed after revoke")
|
|
case <-time.After(50 * time.Millisecond):
|
|
}
|
|
|
|
hub.Publish(Event{
|
|
UserID: "user-123",
|
|
DeviceSessionID: "device-session-2",
|
|
EventType: "fleet.updated",
|
|
EventID: "event-1",
|
|
PayloadBytes: []byte("payload-1"),
|
|
})
|
|
|
|
assertEvent(t, otherSession.Events(), Event{
|
|
UserID: "user-123",
|
|
DeviceSessionID: "device-session-2",
|
|
EventType: "fleet.updated",
|
|
EventID: "event-1",
|
|
PayloadBytes: []byte("payload-1"),
|
|
})
|
|
}
|
|
|
|
func TestHubRevokeDeviceSessionIgnoresUnknownOrEmptySession(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
hub := NewHub(4)
|
|
subscription, err := hub.Register(StreamBinding{
|
|
UserID: "user-123",
|
|
DeviceSessionID: "device-session-1",
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
hub.RevokeDeviceSession("")
|
|
hub.RevokeDeviceSession("missing-session")
|
|
|
|
select {
|
|
case <-subscription.Done():
|
|
require.FailNow(t, "subscription closed for empty or unknown session revoke")
|
|
case <-time.After(50 * time.Millisecond):
|
|
}
|
|
}
|
|
|
|
func assertEvent(t *testing.T, eventCh <-chan Event, want Event) {
|
|
t.Helper()
|
|
|
|
select {
|
|
case got := <-eventCh:
|
|
assert.Equal(t, want, got)
|
|
case <-time.After(time.Second):
|
|
require.FailNow(t, "event was not delivered")
|
|
}
|
|
}
|
|
|
|
func assertNoEvent(t *testing.T, eventCh <-chan Event) {
|
|
t.Helper()
|
|
|
|
select {
|
|
case got := <-eventCh:
|
|
require.FailNowf(t, "unexpected event delivered", "%+v", got)
|
|
case <-time.After(50 * time.Millisecond):
|
|
}
|
|
}
|