checker-stun-turn/checker/discover_test.go
Pierre-Olivier Mercier 7c7706fe3f Initial commit
Adds a happyDomain checker that probes STUN/TURN servers end-to-end:
DNS/SRV discovery, UDP/TCP/TLS/DTLS dial, STUN binding + reflexive-addr
sanity, open-relay detection, authenticated TURN Allocate (long-term
creds or REST-API HMAC), public-relay check, CreatePermission + Send
round-trip through the relay, and optional ChannelBind.

Failing sub-tests carry a remediation string (`Fix`) that the HTML
report surfaces as a yellow headline callout and inline next to each
row. Mapping covers the most common coturn misconfigurations
(external-ip, relay-ip, lt-cred-mech, min-port/max-port, cert issues,
401 nonce drift, 441/442/486/508 allocation errors).

Implements sdk.EndpointDiscoverer (checker/discovery.go): every
stuns:/turns:/DTLS endpoint observed during Collect is published as a
DiscoveredEndpoint{Type: "tls"|"dtls"} so a downstream TLS checker can
verify certificates without re-parsing the observation.

Backed by pion/stun/v3 + pion/turn/v4 + pion/dtls/v3; SDK pinned to a
local replace until the EndpointDiscoverer interface ships in a tagged
release.
2026-04-26 19:55:05 +07:00

66 lines
2.1 KiB
Go

package checker
import "testing"
func TestParseURI(t *testing.T) {
cases := []struct {
in string
host string
port uint16
transport Transport
secure bool
isTURN bool
wantErr bool
}{
{"stun:turn.example.com", "turn.example.com", 3478, TransportUDP, false, false, false},
{"stun:turn.example.com:3478", "turn.example.com", 3478, TransportUDP, false, false, false},
{"stuns:turn.example.com:5349", "turn.example.com", 5349, TransportTLS, true, false, false},
{"turn:turn.example.com:3478?transport=udp", "turn.example.com", 3478, TransportUDP, false, true, false},
{"turn:turn.example.com:3478?transport=tcp", "turn.example.com", 3478, TransportTCP, false, true, false},
{"turns:turn.example.com:5349?transport=tcp", "turn.example.com", 5349, TransportTLS, true, true, false},
{"turns:turn.example.com?transport=dtls", "turn.example.com", 5349, TransportDTLS, true, true, false},
{"http://example.com", "", 0, "", false, false, true},
{"stun:", "", 0, "", false, false, true},
}
for _, tc := range cases {
ep, err := parseURI(tc.in)
if tc.wantErr {
if err == nil {
t.Errorf("%q: expected error, got nil", tc.in)
}
continue
}
if err != nil {
t.Errorf("%q: unexpected error: %v", tc.in, err)
continue
}
if ep.Host != tc.host || ep.Port != tc.port || ep.Transport != tc.transport ||
ep.Secure != tc.secure || ep.IsTURN != tc.isTURN {
t.Errorf("%q: got %+v, want host=%s port=%d transport=%s secure=%v isTURN=%v",
tc.in, ep, tc.host, tc.port, tc.transport, tc.secure, tc.isTURN)
}
}
}
func TestParseTransports(t *testing.T) {
got := parseTransports("udp, TLS ,dtls")
want := []Transport{TransportUDP, TransportTLS, TransportDTLS}
if len(got) != len(want) {
t.Fatalf("got %v want %v", got, want)
}
for i := range got {
if got[i] != want[i] {
t.Fatalf("index %d: got %s want %s", i, got[i], want[i])
}
}
}
func TestRestAPICredentials(t *testing.T) {
c := restAPICredentials("topsecret", "alice", "example.com", 0)
if c.Username == "" || c.Password == "" {
t.Fatalf("empty creds: %+v", c)
}
if c.Realm != "example.com" {
t.Fatalf("realm mismatch: %s", c.Realm)
}
}