package webrtc import ( "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "crypto/x509" "math/big" "testing" "time" "github.com/pions/webrtc/pkg/rtcerr" "github.com/stretchr/testify/assert" ) func TestNew(t *testing.T) { t.Run("Success", func(t *testing.T) { secretKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) assert.Nil(t, err) certificate, err := GenerateCertificate(secretKey) assert.Nil(t, err) pc, err := New(RTCConfiguration{ IceServers: []RTCIceServer{ { URLs: []string{ "stun:stun.l.google.com:19302", "turns:google.de?transport=tcp", }, Username: "unittest", Credential: RTCOAuthCredential{ MacKey: "WmtzanB3ZW9peFhtdm42NzUzNG0=", AccessToken: "AAwg3kPHWPfvk9bDFL936wYvkoctMADzQ==", }, CredentialType: RTCIceCredentialTypeOauth, }, }, IceTransportPolicy: RTCIceTransportPolicyRelay, BundlePolicy: RTCBundlePolicyMaxCompat, RtcpMuxPolicy: RTCRtcpMuxPolicyNegotiate, PeerIdentity: "unittest", Certificates: []RTCCertificate{*certificate}, IceCandidatePoolSize: 5, }) assert.Nil(t, err) assert.NotNil(t, pc) }) t.Run("Failure", func(t *testing.T) { testCases := []struct { initialize func() (*RTCPeerConnection, error) expectedErr error }{ {func() (*RTCPeerConnection, error) { secretKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) assert.Nil(t, err) certificate, err := NewRTCCertificate(secretKey, x509.Certificate{ Version: 2, SerialNumber: big.NewInt(1653), NotBefore: time.Now().AddDate(0, -2, 0), NotAfter: time.Now().AddDate(0, -1, 0), }) assert.Nil(t, err) return New(RTCConfiguration{ Certificates: []RTCCertificate{*certificate}, }) }, &rtcerr.InvalidAccessError{Err: ErrCertificateExpired}}, {func() (*RTCPeerConnection, error) { return New(RTCConfiguration{ IceServers: []RTCIceServer{ { URLs: []string{ "stun:stun.l.google.com:19302", "turns:google.de?transport=tcp", }, Username: "unittest", }, }, }) }, &rtcerr.InvalidAccessError{Err: ErrNoTurnCredencials}}, } for i, testCase := range testCases { _, err := testCase.initialize() assert.EqualError(t, err, testCase.expectedErr.Error(), "testCase: %d %v", i, testCase, ) } }) } func TestRTCPeerConnection_SetConfiguration(t *testing.T) { t.Run("Success", func(t *testing.T) { secretKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) assert.Nil(t, err) certificate, err := GenerateCertificate(secretKey) assert.Nil(t, err) pc, err := New(RTCConfiguration{ PeerIdentity: "unittest", Certificates: []RTCCertificate{*certificate}, IceCandidatePoolSize: 5, }) assert.Nil(t, err) err = pc.SetConfiguration(RTCConfiguration{ IceServers: []RTCIceServer{ { URLs: []string{ "stun:stun.l.google.com:19302", "turns:google.de?transport=tcp", }, Username: "unittest", Credential: RTCOAuthCredential{ MacKey: "WmtzanB3ZW9peFhtdm42NzUzNG0=", AccessToken: "AAwg3kPHWPfvk9bDFL936wYvkoctMADzQ==", }, CredentialType: RTCIceCredentialTypeOauth, }, }, IceTransportPolicy: RTCIceTransportPolicyAll, BundlePolicy: RTCBundlePolicyBalanced, RtcpMuxPolicy: RTCRtcpMuxPolicyRequire, PeerIdentity: "unittest", Certificates: []RTCCertificate{*certificate}, IceCandidatePoolSize: 5, }) assert.Nil(t, err) }) t.Run("Failure", func(t *testing.T) { testCases := []struct { initialize func() (*RTCPeerConnection, error) updatingConfig func() RTCConfiguration expectedErr error }{ {func() (*RTCPeerConnection, error) { pc, err := New(RTCConfiguration{}) assert.Nil(t, err) err = pc.Close() assert.Nil(t, err) return pc, err }, func() RTCConfiguration { return RTCConfiguration{} }, &rtcerr.InvalidStateError{Err: ErrConnectionClosed}}, {func() (*RTCPeerConnection, error) { return New(RTCConfiguration{}) }, func() RTCConfiguration { return RTCConfiguration{ PeerIdentity: "unittest", } }, &rtcerr.InvalidModificationError{Err: ErrModifyingPeerIdentity}}, {func() (*RTCPeerConnection, error) { return New(RTCConfiguration{}) }, func() RTCConfiguration { secretKey1, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) assert.Nil(t, err) certificate1, err := GenerateCertificate(secretKey1) assert.Nil(t, err) secretKey2, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) assert.Nil(t, err) certificate2, err := GenerateCertificate(secretKey2) assert.Nil(t, err) return RTCConfiguration{ Certificates: []RTCCertificate{*certificate1, *certificate2}, } }, &rtcerr.InvalidModificationError{Err: ErrModifyingCertificates}}, {func() (*RTCPeerConnection, error) { return New(RTCConfiguration{}) }, func() RTCConfiguration { secretKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) assert.Nil(t, err) certificate, err := GenerateCertificate(secretKey) assert.Nil(t, err) return RTCConfiguration{ Certificates: []RTCCertificate{*certificate}, } }, &rtcerr.InvalidModificationError{Err: ErrModifyingCertificates}}, {func() (*RTCPeerConnection, error) { return New(RTCConfiguration{}) }, func() RTCConfiguration { return RTCConfiguration{ BundlePolicy: RTCBundlePolicyMaxCompat, } }, &rtcerr.InvalidModificationError{Err: ErrModifyingBundlePolicy}}, {func() (*RTCPeerConnection, error) { return New(RTCConfiguration{}) }, func() RTCConfiguration { return RTCConfiguration{ RtcpMuxPolicy: RTCRtcpMuxPolicyNegotiate, } }, &rtcerr.InvalidModificationError{Err: ErrModifyingRtcpMuxPolicy}}, // TODO Unittest for IceCandidatePoolSize cannot be done now needs pc.LocalDescription() {func() (*RTCPeerConnection, error) { return New(RTCConfiguration{}) }, func() RTCConfiguration { return RTCConfiguration{ IceServers: []RTCIceServer{ { URLs: []string{ "stun:stun.l.google.com:19302", "turns:google.de?transport=tcp", }, Username: "unittest", }, }, } }, &rtcerr.InvalidAccessError{Err: ErrNoTurnCredencials}}, } for i, testCase := range testCases { pc, err := testCase.initialize() assert.Nil(t, err) err = pc.SetConfiguration(testCase.updatingConfig()) assert.EqualError(t, err, testCase.expectedErr.Error(), "testCase: %d %v", i, testCase, ) } }) } func TestRTCPeerConnection_GetConfiguration(t *testing.T) { pc, err := New(RTCConfiguration{}) assert.Nil(t, err) expected := RTCConfiguration{ IceServers: []RTCIceServer{}, IceTransportPolicy: RTCIceTransportPolicyAll, BundlePolicy: RTCBundlePolicyBalanced, RtcpMuxPolicy: RTCRtcpMuxPolicyRequire, Certificates: []RTCCertificate{}, IceCandidatePoolSize: 0, } actual := pc.GetConfiguration() assert.True(t, &expected != &actual) assert.Equal(t, expected.IceServers, actual.IceServers) assert.Equal(t, expected.IceTransportPolicy, actual.IceTransportPolicy) assert.Equal(t, expected.BundlePolicy, actual.BundlePolicy) assert.Equal(t, expected.RtcpMuxPolicy, actual.RtcpMuxPolicy) assert.NotEqual(t, len(expected.Certificates), len(actual.Certificates)) assert.Equal(t, expected.IceCandidatePoolSize, actual.IceCandidatePoolSize) } // TODO - This unittest needs to be completed when CreateDataChannel is complete // func TestRTCPeerConnection_CreateDataChannel(t *testing.T) { // pc, err := New(RTCConfiguration{}) // assert.Nil(t, err) // // _, err = pc.CreateDataChannel("data", &RTCDataChannelInit{ // // }) // assert.Nil(t, err) // } // TODO Fix this test const minimalOffer = `v=0 o=- 7193157174393298413 2 IN IP4 127.0.0.1 s=- t=0 0 a=group:BUNDLE video m=video 43858 UDP/TLS/RTP/SAVPF 96 c=IN IP4 172.17.0.1 a=candidate:3885250869 1 udp 1 127.0.0.1 1 typ host a=ice-ufrag:OgYk a=ice-pwd:G0ka4ts7hRhMLNljuuXzqnOF a=fingerprint:sha-256 D7:06:10:DE:69:66:B1:53:0E:02:33:45:63:F8:AF:78:B2:C7:CE:AF:8E:FD:E5:13:20:50:74:93:CD:B5:C8:69 a=setup:active a=mid:video a=sendrecv a=rtpmap:96 VP8/90000 ` func TestSetRemoteDescription(t *testing.T) { testCases := []struct { desc RTCSessionDescription }{ {RTCSessionDescription{Type: RTCSdpTypeOffer, Sdp: minimalOffer}}, } for i, testCase := range testCases { peerConn, err := New(RTCConfiguration{}) if err != nil { t.Errorf("Case %d: got error: %v", i, err) } err = peerConn.SetRemoteDescription(testCase.desc) if err != nil { t.Errorf("Case %d: got error: %v", i, err) } } } func ExampleNew_default() { if _, err := New(RTCConfiguration{}); err != nil { panic(err) } }