mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-05-07 00:37:36 +00:00
139 lines
4 KiB
Go
139 lines
4 KiB
Go
package websocket
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/rs/zerolog"
|
|
"github.com/rs/zerolog/log"
|
|
)
|
|
|
|
func TestBroadcastStateLogsStructuredContextWhenSequencerFull(t *testing.T) {
|
|
hub := NewHub(nil)
|
|
|
|
for i := 0; i < cap(hub.broadcastSeq); i++ {
|
|
hub.broadcastSeq <- Message{Type: "rawData"}
|
|
}
|
|
|
|
var buf bytes.Buffer
|
|
restore := setWebSocketTestLogger(&buf, zerolog.WarnLevel)
|
|
defer restore()
|
|
|
|
hub.BroadcastState(map[string]string{"state": "full"})
|
|
|
|
entry := singleWebSocketLogEntry(t, buf.String())
|
|
assertWebSocketLogStringField(t, entry, "component", websocketHubComponent)
|
|
assertWebSocketLogStringField(t, entry, "action", "enqueue_state_dropped")
|
|
assertWebSocketLogStringField(t, entry, "channel", "broadcast_seq")
|
|
assertWebSocketLogIntField(t, entry, "channel_depth", cap(hub.broadcastSeq))
|
|
assertWebSocketLogIntField(t, entry, "channel_capacity", cap(hub.broadcastSeq))
|
|
}
|
|
|
|
func TestBroadcastStateToTenantLogsStructuredContextWhenQueueFull(t *testing.T) {
|
|
hub := NewHub(nil)
|
|
|
|
for i := 0; i < cap(hub.tenantBroadcast); i++ {
|
|
hub.tenantBroadcast <- TenantBroadcast{OrgID: "org-fill", Message: Message{Type: "rawData"}}
|
|
}
|
|
|
|
var buf bytes.Buffer
|
|
restore := setWebSocketTestLogger(&buf, zerolog.WarnLevel)
|
|
defer restore()
|
|
|
|
hub.BroadcastStateToTenant("org-123", map[string]string{"state": "full"})
|
|
|
|
entry := singleWebSocketLogEntry(t, buf.String())
|
|
assertWebSocketLogStringField(t, entry, "component", websocketHubComponent)
|
|
assertWebSocketLogStringField(t, entry, "action", "enqueue_tenant_state_dropped")
|
|
assertWebSocketLogStringField(t, entry, "org_id", "org-123")
|
|
assertWebSocketLogStringField(t, entry, "channel", "tenant_broadcast")
|
|
assertWebSocketLogIntField(t, entry, "channel_depth", cap(hub.tenantBroadcast))
|
|
assertWebSocketLogIntField(t, entry, "channel_capacity", cap(hub.tenantBroadcast))
|
|
}
|
|
|
|
func TestBroadcastMessageLogsStructuredContextWhenQueueFull(t *testing.T) {
|
|
hub := NewHub(nil)
|
|
|
|
for i := 0; i < cap(hub.broadcast); i++ {
|
|
hub.broadcast <- []byte("occupied")
|
|
}
|
|
|
|
var buf bytes.Buffer
|
|
restore := setWebSocketTestLogger(&buf, zerolog.WarnLevel)
|
|
defer restore()
|
|
|
|
hub.BroadcastMessage(Message{
|
|
Type: "custom",
|
|
Data: map[string]string{"status": "ok"},
|
|
})
|
|
|
|
entry := singleWebSocketLogEntry(t, buf.String())
|
|
assertWebSocketLogStringField(t, entry, "component", websocketHubComponent)
|
|
assertWebSocketLogStringField(t, entry, "action", "enqueue_broadcast_dropped")
|
|
assertWebSocketLogStringField(t, entry, "message_type", "custom")
|
|
assertWebSocketLogStringField(t, entry, "channel", "broadcast")
|
|
assertWebSocketLogIntField(t, entry, "channel_depth", cap(hub.broadcast))
|
|
assertWebSocketLogIntField(t, entry, "channel_capacity", cap(hub.broadcast))
|
|
}
|
|
|
|
func setWebSocketTestLogger(buf *bytes.Buffer, level zerolog.Level) func() {
|
|
original := log.Logger
|
|
log.Logger = zerolog.New(buf).Level(level)
|
|
return func() {
|
|
log.Logger = original
|
|
}
|
|
}
|
|
|
|
func singleWebSocketLogEntry(t *testing.T, raw string) map[string]any {
|
|
t.Helper()
|
|
|
|
lines := strings.Split(strings.TrimSpace(raw), "\n")
|
|
if len(lines) != 1 {
|
|
t.Fatalf("expected exactly one log line, got %d: %q", len(lines), raw)
|
|
}
|
|
|
|
var parsed map[string]any
|
|
if err := json.Unmarshal([]byte(lines[0]), &parsed); err != nil {
|
|
t.Fatalf("failed to parse log entry: %v", err)
|
|
}
|
|
|
|
return parsed
|
|
}
|
|
|
|
func assertWebSocketLogStringField(t *testing.T, entry map[string]any, field, want string) {
|
|
t.Helper()
|
|
|
|
raw, ok := entry[field]
|
|
if !ok {
|
|
t.Fatalf("missing field %q in log entry: %+v", field, entry)
|
|
}
|
|
|
|
got, ok := raw.(string)
|
|
if !ok {
|
|
t.Fatalf("field %q has non-string value %#v", field, raw)
|
|
}
|
|
|
|
if got != want {
|
|
t.Fatalf("field %q = %q, want %q", field, got, want)
|
|
}
|
|
}
|
|
|
|
func assertWebSocketLogIntField(t *testing.T, entry map[string]any, field string, want int) {
|
|
t.Helper()
|
|
|
|
raw, ok := entry[field]
|
|
if !ok {
|
|
t.Fatalf("missing field %q in log entry: %+v", field, entry)
|
|
}
|
|
|
|
got, ok := raw.(float64)
|
|
if !ok {
|
|
t.Fatalf("field %q has non-numeric value %#v", field, raw)
|
|
}
|
|
|
|
if int(got) != want {
|
|
t.Fatalf("field %q = %d, want %d", field, int(got), want)
|
|
}
|
|
}
|