package handlers import ( "encoding/json" "net/http" "net/http/httptest" "testing" "veza-backend-api/internal/config" "github.com/gin-gonic/gin" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func newWebRTCRouter(cfg *config.Config) *gin.Engine { gin.SetMode(gin.TestMode) r := gin.New() r.GET("/config/webrtc", GetWebRTCConfig(cfg)) return r } func decodeWebRTC(t *testing.T, w *httptest.ResponseRecorder) WebRTCConfigResponse { t.Helper() var resp WebRTCConfigResponse require.NoError(t, json.NewDecoder(w.Body).Decode(&resp)) return resp } func TestGetWebRTCConfig_StunOnlyWhenNoTurn(t *testing.T) { cfg := &config.Config{ WebRTCStunURLs: []string{"stun:stun.example.org:3478"}, } r := newWebRTCRouter(cfg) w := httptest.NewRecorder() req := httptest.NewRequest(http.MethodGet, "/config/webrtc", nil) r.ServeHTTP(w, req) require.Equal(t, http.StatusOK, w.Code) resp := decodeWebRTC(t, w) require.Len(t, resp.IceServers, 1) assert.Equal(t, []string{"stun:stun.example.org:3478"}, resp.IceServers[0].URLs) assert.Empty(t, resp.IceServers[0].Username) assert.Empty(t, resp.IceServers[0].Credential) } func TestGetWebRTCConfig_EmitsTurnBlockWhenFullyConfigured(t *testing.T) { cfg := &config.Config{ WebRTCStunURLs: []string{"stun:stun.example.org:3478"}, WebRTCTurnURLs: []string{"turn:turn.veza.local:3478", "turns:turn.veza.local:5349"}, WebRTCTurnUsername: "vezauser", WebRTCTurnCredential: "shhhhh", } r := newWebRTCRouter(cfg) w := httptest.NewRecorder() req := httptest.NewRequest(http.MethodGet, "/config/webrtc", nil) r.ServeHTTP(w, req) require.Equal(t, http.StatusOK, w.Code) resp := decodeWebRTC(t, w) require.Len(t, resp.IceServers, 2) assert.Equal(t, []string{"stun:stun.example.org:3478"}, resp.IceServers[0].URLs) assert.Equal(t, []string{"turn:turn.veza.local:3478", "turns:turn.veza.local:5349"}, resp.IceServers[1].URLs) assert.Equal(t, "vezauser", resp.IceServers[1].Username) assert.Equal(t, "shhhhh", resp.IceServers[1].Credential) } func TestGetWebRTCConfig_OmitsTurnBlockWhenHalfConfigured(t *testing.T) { // Half-configured TURN (URLs but no creds) is worse than missing — // the browser would surface auth failures instead of falling back // cleanly. The handler omits the block in that case. cases := map[string]*config.Config{ "missing username": { WebRTCStunURLs: []string{"stun:s.test:3478"}, WebRTCTurnURLs: []string{"turn:t.test:3478"}, WebRTCTurnCredential: "creds", }, "missing credential": { WebRTCStunURLs: []string{"stun:s.test:3478"}, WebRTCTurnURLs: []string{"turn:t.test:3478"}, WebRTCTurnUsername: "user", }, "missing urls": { WebRTCStunURLs: []string{"stun:s.test:3478"}, WebRTCTurnUsername: "user", WebRTCTurnCredential: "creds", }, } for name, cfg := range cases { t.Run(name, func(t *testing.T) { r := newWebRTCRouter(cfg) w := httptest.NewRecorder() req := httptest.NewRequest(http.MethodGet, "/config/webrtc", nil) r.ServeHTTP(w, req) require.Equal(t, http.StatusOK, w.Code) resp := decodeWebRTC(t, w) assert.Len(t, resp.IceServers, 1, "only the STUN entry should be present when TURN is partial") assert.Equal(t, []string{"stun:s.test:3478"}, resp.IceServers[0].URLs) }) } } func TestGetWebRTCConfig_EmptyConfigReturnsEmptyIceServers(t *testing.T) { cfg := &config.Config{} // no STUN, no TURN r := newWebRTCRouter(cfg) w := httptest.NewRecorder() req := httptest.NewRequest(http.MethodGet, "/config/webrtc", nil) r.ServeHTTP(w, req) require.Equal(t, http.StatusOK, w.Code) resp := decodeWebRTC(t, w) assert.Empty(t, resp.IceServers) }