Feature(Go): Web auth, WebSocket signaling and live device list (Phase 3)
This commit is contained in:
152
server/go/hub/hub_test.go
Normal file
152
server/go/hub/hub_test.go
Normal file
@@ -0,0 +1,152 @@
|
||||
package hub
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestHubRegisterListUnregister(t *testing.T) {
|
||||
h := New()
|
||||
if got := h.Count(); got != 0 {
|
||||
t.Fatalf("empty hub: want Count=0, got %d", got)
|
||||
}
|
||||
|
||||
h.Register(&Device{ID: "a", Name: "Alice", ConnectedAt: time.Now()})
|
||||
h.Register(&Device{ID: "b", Name: "Bob", ConnectedAt: time.Now()})
|
||||
if got := h.Count(); got != 2 {
|
||||
t.Fatalf("after 2 registers: want Count=2, got %d", got)
|
||||
}
|
||||
|
||||
list := h.ListDevices()
|
||||
if len(list) != 2 {
|
||||
t.Fatalf("want 2 devices in list, got %d", len(list))
|
||||
}
|
||||
|
||||
h.Unregister("a")
|
||||
if got := h.Count(); got != 1 {
|
||||
t.Fatalf("after unregister: want Count=1, got %d", got)
|
||||
}
|
||||
|
||||
// Unregister non-existent ID is a no-op
|
||||
h.Unregister("ghost")
|
||||
if got := h.Count(); got != 1 {
|
||||
t.Fatalf("after no-op unregister: want Count=1, got %d", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHubNilAndEmptyIgnored(t *testing.T) {
|
||||
h := New()
|
||||
h.Register(nil)
|
||||
h.Register(&Device{ID: ""})
|
||||
h.Unregister("")
|
||||
if got := h.Count(); got != 0 {
|
||||
t.Fatalf("nil/empty register should be no-op, got Count=%d", got)
|
||||
}
|
||||
}
|
||||
|
||||
type captureHandler struct {
|
||||
mu sync.Mutex
|
||||
online []string
|
||||
offline []string
|
||||
updates []string // formatted "id:rtt"
|
||||
}
|
||||
|
||||
func (c *captureHandler) OnDeviceOnline(d DeviceInfo) {
|
||||
c.mu.Lock()
|
||||
c.online = append(c.online, d.ID)
|
||||
c.mu.Unlock()
|
||||
}
|
||||
|
||||
func (c *captureHandler) OnDeviceOffline(id string) {
|
||||
c.mu.Lock()
|
||||
c.offline = append(c.offline, id)
|
||||
c.mu.Unlock()
|
||||
}
|
||||
|
||||
func (c *captureHandler) OnDeviceUpdate(id string, rtt int, _ string) {
|
||||
c.mu.Lock()
|
||||
c.updates = append(c.updates, fmt.Sprintf("%s:%d", id, rtt))
|
||||
c.mu.Unlock()
|
||||
}
|
||||
|
||||
func TestHubSubscribeEvents(t *testing.T) {
|
||||
h := New()
|
||||
c := &captureHandler{}
|
||||
unsub := h.Subscribe(c)
|
||||
|
||||
h.Register(&Device{ID: "x", Name: "x"})
|
||||
h.Register(&Device{ID: "y", Name: "y"})
|
||||
h.Unregister("x")
|
||||
h.Unregister("nonexistent") // no event
|
||||
|
||||
if len(c.online) != 2 || c.online[0] != "x" || c.online[1] != "y" {
|
||||
t.Fatalf("online events: %+v", c.online)
|
||||
}
|
||||
if len(c.offline) != 1 || c.offline[0] != "x" {
|
||||
t.Fatalf("offline events: %+v", c.offline)
|
||||
}
|
||||
|
||||
unsub()
|
||||
h.Register(&Device{ID: "z"})
|
||||
if len(c.online) != 2 {
|
||||
t.Fatalf("after unsubscribe should not receive events: %+v", c.online)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHubUpdateLive(t *testing.T) {
|
||||
h := New()
|
||||
c := &captureHandler{}
|
||||
h.Subscribe(c)
|
||||
|
||||
h.Register(&Device{ID: "x", Name: "x"})
|
||||
h.UpdateLive("x", 42, "Notepad")
|
||||
h.UpdateLive("ghost", 999, "should be ignored") // unknown id, no event
|
||||
|
||||
if len(c.updates) != 1 || c.updates[0] != "x:42" {
|
||||
t.Fatalf("updates: %+v", c.updates)
|
||||
}
|
||||
|
||||
list := h.ListDevices()
|
||||
if list[0].RTT != 42 || list[0].ActiveWindow != "Notepad" {
|
||||
t.Fatalf("live fields not applied: %+v", list[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestHubRegisterOverwrites(t *testing.T) {
|
||||
h := New()
|
||||
h.Register(&Device{ID: "x", Name: "first"})
|
||||
h.Register(&Device{ID: "x", Name: "second"})
|
||||
list := h.ListDevices()
|
||||
if len(list) != 1 || list[0].Name != "second" {
|
||||
t.Fatalf("re-register should overwrite, got %+v", list)
|
||||
}
|
||||
}
|
||||
|
||||
// Race detector should not fire under `go test -race ./hub/...`.
|
||||
func TestHubConcurrent(t *testing.T) {
|
||||
h := New()
|
||||
const goroutines = 50
|
||||
const opsPer = 100
|
||||
|
||||
var wg sync.WaitGroup
|
||||
for g := range goroutines {
|
||||
wg.Add(1)
|
||||
go func(g int) {
|
||||
defer wg.Done()
|
||||
for i := range opsPer {
|
||||
id := fmt.Sprintf("g%d-%d", g, i)
|
||||
h.Register(&Device{ID: id, Name: id, ConnectedAt: time.Now()})
|
||||
_ = h.ListDevices()
|
||||
_ = h.Count()
|
||||
h.Unregister(id)
|
||||
}
|
||||
}(g)
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
if got := h.Count(); got != 0 {
|
||||
t.Fatalf("after all unregisters: want 0, got %d", got)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user