From 63680fac199262426234da09192593eb0e02e086 Mon Sep 17 00:00:00 2001 From: abdussamedulutas Date: Wed, 17 Jun 2026 08:13:22 +0300 Subject: [PATCH] =?UTF-8?q?#31:=20WebRTC=20signaling=20paritesi=20?= =?UTF-8?q?=E2=80=94=20engine=20relay=20testi?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sinyalleşme (offer/answer/ICE) ayrı bir engine kavramı değil; SDK bunları {type:':rtcpack:', payload} olarak pack/to üzerinden tüneller (Node ile aynı, sunucuda RTC handler'ı yok). Engine bu paketleri her iki yönde de payload'ı incelemeden aynen taşır. Test: TestWebRTCSignalingRelay. Co-Authored-By: Claude Opus 4.8 --- internal/services/datatransfer_test.go | 45 ++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/internal/services/datatransfer_test.go b/internal/services/datatransfer_test.go index eacd87b..2984b36 100644 --- a/internal/services/datatransfer_test.go +++ b/internal/services/datatransfer_test.go @@ -6,6 +6,7 @@ import ( "testing" "git.saqut.com/saqut/mwse/internal/testutil" + "git.saqut.com/saqut/mwse/internal/ws" ) // findReply scans the captured frames for a [payload, id] frame whose id slot is @@ -100,6 +101,50 @@ func TestTunnelLargePayloadIntact(t *testing.T) { } } +// TestWebRTCSignalingRelay is the #31 parity test. WebRTC signaling is not a +// distinct engine concept: the SDK tunnels offer/answer/ICE as opaque +// {type:":rtcpack:", payload:{...}} packs over pack/to. The engine must carry +// them through unchanged, in both directions, without inspecting the RTC payload. +func TestWebRTCSignalingRelay(t *testing.T) { + hub := newHub() + a, fa := connect(hub, "a") + b, fb := connect(hub, "b") + + signal := func(from *ws.Client, to string, rtc map[string]any) { + hub.Handle(from, msg("pack/to", "to", to, + "pack", map[string]any{"type": ":rtcpack:", "payload": rtc})) + } + + // A → B: SDP offer. + signal(a, "b", map[string]any{"type": "offer", "value": map[string]any{"sdp": "v=0...offer"}}) + offerPack := asMap(t, waitSignal(t, fb, "pack")["pack"]) + if offerPack["type"] != ":rtcpack:" { + t.Fatalf("offer relayed as %v, want :rtcpack:", offerPack["type"]) + } + if rtc := asMap(t, offerPack["payload"]); rtc["type"] != "offer" { + t.Fatalf("inner signaling type = %v, want offer", rtc["type"]) + } + + // B → A: SDP answer. + signal(b, "a", map[string]any{"type": "answer", "value": map[string]any{"sdp": "v=0...answer"}}) + answerPack := asMap(t, waitSignal(t, fa, "pack")["pack"]) + if rtc := asMap(t, answerPack["payload"]); rtc["type"] != "answer" { + t.Fatalf("inner signaling type = %v, want answer", rtc["type"]) + } + + // A → B: ICE candidate — payload carried verbatim. + cand := map[string]any{"candidate": "candidate:842163049 1 udp ...", "sdpMLineIndex": float64(0)} + signal(a, "b", map[string]any{"type": "icecandidate", "value": cand}) + waitFor(t, func() bool { + for _, raw := range fb.Writes() { + if strings.Contains(string(raw), "icecandidate") { + return true + } + } + return false + }) +} + // assertNoAddressLeak fails if a relayed payload exposes anything resembling the // sender's real network identity. func assertNoAddressLeak(t *testing.T, payload map[string]any) {