Files
free-turn-proxy/internal/config/json_test.go

184 lines
5.7 KiB
Go

package config
import (
"encoding/json"
"reflect"
"strings"
"testing"
"time"
"github.com/samosvalishe/free-turn-proxy/internal/transport/kcpmux"
"github.com/samosvalishe/free-turn-proxy/internal/uri"
)
const minimalJSON = `{"peer":"1.2.3.4:5000","vk":{"links":["https://vk.ru/call/join/CODE"]}}`
func TestParseClientJSONFillsDefaults(t *testing.T) {
c, err := ParseClientJSON([]byte(minimalJSON), "")
if err != nil {
t.Fatalf("ParseClientJSON() error = %v", err)
}
want := Defaults()
want.Proxy.Peer = "1.2.3.4:5000"
want.VK.Links = []string{"CODE"}
if !reflect.DeepEqual(*c, want) {
t.Errorf("parsed config differs from defaults:\n got %+v\nwant %+v", *c, want)
}
}
func TestParseClientJSONAppliesFields(t *testing.T) {
in := `{
"peer": "5.6.7.8:56000",
"clientId": "abc",
"turn": {"n": 3, "transport": "udp"},
"proxy": {"listen": "127.0.0.1:1080"},
"vk": {"links": ["A", "B"], "streamsPerCred": 4, "manualCaptcha": true, "platform": "mobile"},
"obf": {"profile": "rtpopus3", "key": "` + testObfKey + `"},
"dns": {"mode": "doh", "servers": ["1.1.1.1", "8.8.8.8"]},
"log": {"debug": true}
}`
c, err := ParseClientJSON([]byte(in), "")
if err != nil {
t.Fatalf("ParseClientJSON() error = %v", err)
}
if c.TURN.N != 3 || !c.TURN.TransportUDP {
t.Errorf("TURN = %+v", c.TURN)
}
if c.Proxy.Listen != "127.0.0.1:1080" {
t.Errorf("Proxy = %+v", c.Proxy)
}
if len(c.VK.Links) != 2 || c.VK.Platform != PlatformMobile || !c.VK.ManualCaptcha {
t.Errorf("VK = %+v", c.VK)
}
if c.Obf.Profile != ObfProfileRTPOpus3 || len(c.Obf.Key) != 32 {
t.Errorf("Obf = %+v", c.Obf)
}
if c.DNS.Mode != DNSModeDoH || len(c.DNS.Servers) != 2 {
t.Errorf("DNS = %+v", c.DNS)
}
if !c.Log.Debug || c.ClientID != "abc" {
t.Errorf("Log/ClientID = %+v / %q", c.Log, c.ClientID)
}
}
func TestParseClientJSONRunsValidate(t *testing.T) {
in := `{"peer":"1.2.3.4:5000","vk":{"links":["A"]},` +
`"obf":{"profile":"none","timingMs":20}}`
_, err := ParseClientJSON([]byte(in), "")
if err == nil || !strings.Contains(err.Error(), "-obf-timing") {
t.Fatalf("ParseClientJSON() error = %v, want obf-timing rule", err)
}
}
func TestParseClientJSONRejectsUnknownField(t *testing.T) {
in := `{"peer":"1.2.3.4:5000","vk":{"links":["A"]},"treads":12}`
_, err := ParseClientJSON([]byte(in), "")
if err == nil || !strings.Contains(err.Error(), "treads") {
t.Fatalf("ParseClientJSON() error = %v, want unknown field error", err)
}
}
func TestParseClientJSONRejectsMalformed(t *testing.T) {
if _, err := ParseClientJSON([]byte("{"), ""); err == nil {
t.Fatal("ParseClientJSON() error = nil for malformed input")
}
}
func TestParseClientJSONOverlayURI(t *testing.T) {
node := &uri.Config{Version: 1, Provider: ProviderVK, Peer: "9.9.9.9:56000", N: 5}
c, err := ParseClientJSON([]byte(minimalJSON), node.String())
if err != nil {
t.Fatalf("ParseClientJSON() error = %v", err)
}
if c.Proxy.Peer != "9.9.9.9:56000" || c.TURN.N != 5 {
t.Errorf("overlay not applied: peer %q, n %d", c.Proxy.Peer, c.TURN.N)
}
if len(c.VK.Links) != 1 || c.VK.Links[0] != "CODE" {
t.Errorf("links overwritten by overlay: %v", c.VK.Links)
}
}
func TestParseClientJSONRejectsBadOverlay(t *testing.T) {
if _, err := ParseClientJSON([]byte(minimalJSON), "freeturn://not-base64!!"); err == nil {
t.Fatal("ParseClientJSON() error = nil for broken URI")
}
}
func TestPeekSubURLJSON(t *testing.T) {
if got := PeekSubURLJSON([]byte(`{"subUrl":"https://example.org/sub"}`)); got != "https://example.org/sub" {
t.Errorf("PeekSubURLJSON() = %q", got)
}
if got := PeekSubURLJSON([]byte(minimalJSON)); got != "" {
t.Errorf("PeekSubURLJSON() = %q, want empty", got)
}
if got := PeekSubURLJSON([]byte("{")); got != "" {
t.Errorf("PeekSubURLJSON() on malformed = %q, want empty", got)
}
}
func TestDefaultClientJSONRoundTrip(t *testing.T) {
var dto ClientJSON
if err := json.Unmarshal([]byte(DefaultClientJSON()), &dto); err != nil {
t.Fatalf("DefaultClientJSON() is not valid JSON: %v", err)
}
if !reflect.DeepEqual(dto, defaultClientJSON()) {
t.Errorf("round-trip changed defaults:\n got %+v\nwant %+v", dto, defaultClientJSON())
}
dto.Peer = "1.2.3.4:5000"
dto.VK.Links = []string{"CODE"}
b, err := json.Marshal(dto)
if err != nil {
t.Fatal(err)
}
c, err := ParseClientJSON(b, "")
if err != nil {
t.Fatalf("ParseClientJSON(DefaultClientJSON + required) error = %v", err)
}
if c.Obf.Timing != 0 || c.TURN.N != DefaultStreams {
t.Errorf("unexpected config from defaults: %+v", c)
}
}
func TestClientJSONTimingMsMapsToDuration(t *testing.T) {
in := `{"peer":"1.2.3.4:5000","vk":{"links":["A"]},` +
`"obf":{"profile":"rtpopus","key":"` + testObfKey + `","timingMs":25}}`
c, err := ParseClientJSON([]byte(in), "")
if err != nil {
t.Fatal(err)
}
if c.Obf.Timing != 25*time.Millisecond {
t.Errorf("Obf.Timing = %v, want 25ms", c.Obf.Timing)
}
}
// Старый JSON без секций mode/kcp обязан читаться: поля optional, дефолт - udp.
func TestParseClientJSONLegacyWithoutMode(t *testing.T) {
data := `{"peer":"1.2.3.4:5000","vk":{"links":["abcdef"]}}`
c, err := ParseClientJSON([]byte(data), "")
if err != nil {
t.Fatalf("ParseClientJSON() error = %v", err)
}
if c.Proxy.Mode != ProxyModeUDP {
t.Errorf("Proxy.Mode = %q, want udp", c.Proxy.Mode)
}
if c.KCP.Profile != kcpmux.DefaultProfile() {
t.Errorf("KCP profile = %+v, want default", c.KCP.Profile)
}
}
func TestParseClientJSONTCPMode(t *testing.T) {
data := `{"peer":"1.2.3.4:5000","vk":{"links":["abcdef"]},"proxy":{"mode":"tcp"}}`
c, err := ParseClientJSON([]byte(data), "")
if err != nil {
t.Fatalf("ParseClientJSON() error = %v", err)
}
if c.Proxy.Mode != ProxyModeTCP {
t.Errorf("Proxy.Mode = %q, want tcp", c.Proxy.Mode)
}
}