feat: edge gateway service
This commit is contained in:
@@ -0,0 +1,270 @@
|
||||
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):
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user