orama/pkg/turn/server_test.go
anonpenguin23 8ee606bfb1 feat: implement SFU and TURN server functionality
- Add signaling package with message types and structures for SFU communication.
- Implement client and server message serialization/deserialization tests.
- Enhance systemd manager to handle SFU and TURN services, including start/stop logic.
- Create TURN server configuration and main server logic with HMAC-SHA1 authentication.
- Add tests for TURN server credential generation and validation.
- Define systemd service files for SFU and TURN services.
2026-02-21 11:17:13 +02:00

226 lines
5.3 KiB
Go

package turn
import (
"fmt"
"testing"
"time"
)
func TestGenerateCredentials(t *testing.T) {
secret := "test-secret-key-32bytes-long!!!!"
namespace := "test-namespace"
ttl := 10 * time.Minute
username, password := GenerateCredentials(secret, namespace, ttl)
if username == "" {
t.Fatal("username should not be empty")
}
if password == "" {
t.Fatal("password should not be empty")
}
// Username should be "{timestamp}:{namespace}"
var ts int64
var ns string
n, err := fmt.Sscanf(username, "%d:%s", &ts, &ns)
if err != nil || n != 2 {
t.Fatalf("username format should be timestamp:namespace, got %q", username)
}
if ns != namespace {
t.Fatalf("namespace in username should be %q, got %q", namespace, ns)
}
// Timestamp should be ~10 minutes in the future
now := time.Now().Unix()
expectedExpiry := now + int64(ttl.Seconds())
if ts < expectedExpiry-2 || ts > expectedExpiry+2 {
t.Fatalf("expiry timestamp should be ~%d, got %d", expectedExpiry, ts)
}
}
func TestGeneratePassword(t *testing.T) {
secret := "test-secret"
username := "1234567890:test-ns"
password1 := GeneratePassword(secret, username)
password2 := GeneratePassword(secret, username)
// Same inputs should produce same output
if password1 != password2 {
t.Fatal("GeneratePassword should be deterministic")
}
// Different secret should produce different output
password3 := GeneratePassword("different-secret", username)
if password1 == password3 {
t.Fatal("different secrets should produce different passwords")
}
// Different username should produce different output
password4 := GeneratePassword(secret, "9999999999:other-ns")
if password1 == password4 {
t.Fatal("different usernames should produce different passwords")
}
}
func TestValidateCredentials(t *testing.T) {
secret := "test-secret-key"
namespace := "my-namespace"
ttl := 10 * time.Minute
// Generate valid credentials
username, password := GenerateCredentials(secret, namespace, ttl)
tests := []struct {
name string
secret string
username string
password string
namespace string
wantValid bool
}{
{
name: "valid credentials",
secret: secret,
username: username,
password: password,
namespace: namespace,
wantValid: true,
},
{
name: "wrong secret",
secret: "wrong-secret",
username: username,
password: password,
namespace: namespace,
wantValid: false,
},
{
name: "wrong password",
secret: secret,
username: username,
password: "wrongpassword",
namespace: namespace,
wantValid: false,
},
{
name: "wrong namespace",
secret: secret,
username: username,
password: password,
namespace: "other-namespace",
wantValid: false,
},
{
name: "expired credentials",
secret: secret,
username: fmt.Sprintf("%d:%s", time.Now().Unix()-60, namespace),
password: GeneratePassword(secret, fmt.Sprintf("%d:%s", time.Now().Unix()-60, namespace)),
namespace: namespace,
wantValid: false,
},
{
name: "malformed username - no colon",
secret: secret,
username: "badusername",
password: "whatever",
namespace: namespace,
wantValid: false,
},
{
name: "malformed username - non-numeric timestamp",
secret: secret,
username: "notanumber:my-namespace",
password: "whatever",
namespace: namespace,
wantValid: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := ValidateCredentials(tt.secret, tt.username, tt.password, tt.namespace)
if got != tt.wantValid {
t.Errorf("ValidateCredentials() = %v, want %v", got, tt.wantValid)
}
})
}
}
func TestConfigValidation(t *testing.T) {
tests := []struct {
name string
config Config
wantErrs int
}{
{
name: "valid config",
config: Config{
ListenAddr: "0.0.0.0:3478",
PublicIP: "1.2.3.4",
Realm: "dbrs.space",
AuthSecret: "secret123",
RelayPortStart: 49152,
RelayPortEnd: 50000,
Namespace: "test-ns",
},
wantErrs: 0,
},
{
name: "missing all fields",
config: Config{},
wantErrs: 6, // listen_addr, public_ip, realm, auth_secret, relay_port_range, namespace
},
{
name: "invalid public IP",
config: Config{
ListenAddr: "0.0.0.0:3478",
PublicIP: "not-an-ip",
Realm: "dbrs.space",
AuthSecret: "secret",
RelayPortStart: 49152,
RelayPortEnd: 50000,
Namespace: "test-ns",
},
wantErrs: 1,
},
{
name: "relay range too small",
config: Config{
ListenAddr: "0.0.0.0:3478",
PublicIP: "1.2.3.4",
Realm: "dbrs.space",
AuthSecret: "secret",
RelayPortStart: 49152,
RelayPortEnd: 49200,
Namespace: "test-ns",
},
wantErrs: 1,
},
{
name: "relay range inverted",
config: Config{
ListenAddr: "0.0.0.0:3478",
PublicIP: "1.2.3.4",
Realm: "dbrs.space",
AuthSecret: "secret",
RelayPortStart: 50000,
RelayPortEnd: 49152,
Namespace: "test-ns",
},
wantErrs: 1,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
errs := tt.config.Validate()
if len(errs) != tt.wantErrs {
t.Errorf("Validate() returned %d errors, want %d: %v", len(errs), tt.wantErrs, errs)
}
})
}
}