datarhei-dragonfork-core/app/webrtc/handler_stats_test.go
Zac Gaetano 8557a1c65e
Some checks failed
ci / vet + build (push) Failing after 5m7s
ci / race tests (push) Has been skipped
ci / WebRTC smoke (5-viewer fanout) (push) Has been skipped
ci / WebRTC latency p95 gate (push) Has been skipped
webrtc: add GET /webrtc/stats endpoint and SetWHIPHandler (issue #24)
2026-05-10 21:28:24 -04:00

127 lines
3.8 KiB
Go

package webrtc
import (
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"github.com/labstack/echo/v4"
)
// TestStatsHandler_EmptySubsystem verifies that GET /webrtc/stats returns
// a well-formed JSON body with all-zero counts when no streams or peers
// are active and no WHIP handler is linked.
func TestStatsHandler_EmptySubsystem(t *testing.T) {
h := NewHandler(newTestSubsystem(t), 0)
e := echo.New()
req := httptest.NewRequest(http.MethodGet, "/webrtc/stats", nil)
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
if err := h.StatsHandler(c); err != nil {
t.Fatalf("StatsHandler returned error: %v", err)
}
if rec.Code != http.StatusOK {
t.Fatalf("expected 200, got %d: %s", rec.Code, rec.Body.String())
}
var stats WebRTCStats
if err := json.Unmarshal(rec.Body.Bytes(), &stats); err != nil {
t.Fatalf("invalid JSON: %v\nbody: %s", err, rec.Body.String())
}
if stats.ActiveStreams != 0 {
t.Errorf("ActiveStreams: want 0, got %d", stats.ActiveStreams)
}
if stats.ActivePeers != 0 {
t.Errorf("ActivePeers: want 0, got %d", stats.ActivePeers)
}
if stats.ActivePublishers != 0 {
t.Errorf("ActivePublishers: want 0, got %d", stats.ActivePublishers)
}
if stats.UDPPortsInUse != 0 {
t.Errorf("UDPPortsInUse: want 0, got %d", stats.UDPPortsInUse)
}
}
// TestStatsHandler_WithWHIPHandler verifies that SetWHIPHandler links the
// WHIP publisher count into the stats response.
func TestStatsHandler_WithWHIPHandler(t *testing.T) {
sub := newTestSubsystem(t)
h := NewHandler(sub, 0)
// Link a real WHIPHandler so that StatsHandler calls PublisherCount().
wh := NewWHIPHandler(sub, 0)
h.SetWHIPHandler(wh)
e := echo.New()
req := httptest.NewRequest(http.MethodGet, "/webrtc/stats", nil)
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
if err := h.StatsHandler(c); err != nil {
t.Fatalf("StatsHandler returned error: %v", err)
}
var stats WebRTCStats
if err := json.Unmarshal(rec.Body.Bytes(), &stats); err != nil {
t.Fatalf("invalid JSON: %v", err)
}
// With no active publishers the count should be 0 — validates the
// link does not panic and that PublisherCount() is being called.
if stats.ActivePublishers != 0 {
t.Errorf("ActivePublishers: want 0, got %d", stats.ActivePublishers)
}
}
// TestStatsHandler_NilSub verifies that a nil Subsystem (possible during
// early wiring) does not panic and returns zeros.
func TestStatsHandler_NilSub(t *testing.T) {
h := NewHandler(nil, 0)
e := echo.New()
req := httptest.NewRequest(http.MethodGet, "/webrtc/stats", nil)
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
if err := h.StatsHandler(c); err != nil {
t.Fatalf("StatsHandler returned error: %v", err)
}
if rec.Code != http.StatusOK {
t.Fatalf("expected 200, got %d", rec.Code)
}
var stats WebRTCStats
if err := json.Unmarshal(rec.Body.Bytes(), &stats); err != nil {
t.Fatalf("invalid JSON: %v", err)
}
if stats.ActiveStreams != 0 || stats.UDPPortsInUse != 0 {
t.Errorf("expected all zeros with nil sub, got %+v", stats)
}
}
// TestStatsHandler_JSONFieldNames verifies the JSON key names match the
// contract defined in the issue so consumer scripts don't break.
func TestStatsHandler_JSONFieldNames(t *testing.T) {
h := NewHandler(newTestSubsystem(t), 0)
e := echo.New()
req := httptest.NewRequest(http.MethodGet, "/webrtc/stats", nil)
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
if err := h.StatsHandler(c); err != nil {
t.Fatalf("StatsHandler returned error: %v", err)
}
var raw map[string]interface{}
if err := json.Unmarshal(rec.Body.Bytes(), &raw); err != nil {
t.Fatalf("invalid JSON: %v", err)
}
for _, key := range []string{"active_streams", "active_peers", "active_publishers", "udp_ports_in_use"} {
if _, ok := raw[key]; !ok {
t.Errorf("JSON response missing required field %q", key)
}
}
}