Squashed 'livekit-server/' content from commit 154b4d26
git-subtree-dir: livekit-server git-subtree-split: 154b4d26b769c68a03c096124094b97bf61a996f
This commit is contained in:
@@ -0,0 +1,396 @@
|
||||
// Copyright 2023 LiveKit, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/livekit/protocol/auth"
|
||||
"github.com/livekit/protocol/livekit"
|
||||
|
||||
"github.com/livekit/livekit-server/pkg/rtc"
|
||||
"github.com/livekit/livekit-server/pkg/testutils"
|
||||
"github.com/livekit/livekit-server/test/client"
|
||||
)
|
||||
|
||||
func TestMultiNodeRouting(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.SkipNow()
|
||||
return
|
||||
}
|
||||
_, _, finish := setupMultiNodeTest("TestMultiNodeRouting")
|
||||
defer finish()
|
||||
|
||||
// creating room on node 1
|
||||
_, err := roomClient.CreateRoom(contextWithToken(createRoomToken()), &livekit.CreateRoomRequest{
|
||||
Name: testRoom,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// one node connecting to node 1, and another connecting to node 2
|
||||
c1 := createRTCClient("c1", defaultServerPort, nil)
|
||||
c2 := createRTCClient("c2", secondServerPort, nil)
|
||||
waitUntilConnected(t, c1, c2)
|
||||
defer stopClients(c1, c2)
|
||||
|
||||
// c1 publishing, and c2 receiving
|
||||
t1, err := c1.AddStaticTrack("audio/opus", "audio", "webcam")
|
||||
require.NoError(t, err)
|
||||
if t1 != nil {
|
||||
defer t1.Stop()
|
||||
}
|
||||
|
||||
testutils.WithTimeout(t, func() string {
|
||||
if len(c2.SubscribedTracks()) == 0 {
|
||||
return "c2 received no tracks"
|
||||
}
|
||||
if len(c2.SubscribedTracks()[c1.ID()]) != 1 {
|
||||
return "c2 didn't receive track published by c1"
|
||||
}
|
||||
tr1 := c2.SubscribedTracks()[c1.ID()][0]
|
||||
streamID, _ := rtc.UnpackStreamID(tr1.StreamID())
|
||||
require.Equal(t, c1.ID(), streamID)
|
||||
return ""
|
||||
})
|
||||
|
||||
remoteC1 := c2.GetRemoteParticipant(c1.ID())
|
||||
require.Equal(t, "c1", remoteC1.Name)
|
||||
require.Equal(t, "metadatac1", remoteC1.Metadata)
|
||||
}
|
||||
|
||||
func TestConnectWithoutCreation(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.SkipNow()
|
||||
return
|
||||
}
|
||||
|
||||
_, _, finish := setupMultiNodeTest("TestConnectWithoutCreation")
|
||||
defer finish()
|
||||
|
||||
c1 := createRTCClient("c1", defaultServerPort, nil)
|
||||
waitUntilConnected(t, c1)
|
||||
|
||||
c1.Stop()
|
||||
}
|
||||
|
||||
// testing multiple scenarios rooms
|
||||
func TestMultinodePublishingUponJoining(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.SkipNow()
|
||||
return
|
||||
}
|
||||
_, _, finish := setupMultiNodeTest("TestMultinodePublishingUponJoining")
|
||||
defer finish()
|
||||
|
||||
scenarioPublishingUponJoining(t)
|
||||
}
|
||||
|
||||
func TestMultinodeReceiveBeforePublish(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.SkipNow()
|
||||
return
|
||||
}
|
||||
_, _, finish := setupMultiNodeTest("TestMultinodeReceiveBeforePublish")
|
||||
defer finish()
|
||||
|
||||
scenarioReceiveBeforePublish(t)
|
||||
}
|
||||
|
||||
// reconnecting to the same room, after one of the servers has gone away
|
||||
func TestMultinodeReconnectAfterNodeShutdown(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.SkipNow()
|
||||
return
|
||||
}
|
||||
|
||||
_, s2, finish := setupMultiNodeTest("TestMultinodeReconnectAfterNodeShutdown")
|
||||
defer finish()
|
||||
|
||||
// creating room on node 1
|
||||
_, err := roomClient.CreateRoom(contextWithToken(createRoomToken()), &livekit.CreateRoomRequest{
|
||||
Name: testRoom,
|
||||
NodeId: s2.Node().Id,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// one node connecting to node 1, and another connecting to node 2
|
||||
c1 := createRTCClient("c1", defaultServerPort, nil)
|
||||
c2 := createRTCClient("c2", secondServerPort, nil)
|
||||
|
||||
waitUntilConnected(t, c1, c2)
|
||||
stopClients(c1, c2)
|
||||
|
||||
// stop s2, and connect to room again
|
||||
s2.Stop(true)
|
||||
|
||||
time.Sleep(syncDelay)
|
||||
|
||||
c3 := createRTCClient("c3", defaultServerPort, nil)
|
||||
waitUntilConnected(t, c3)
|
||||
}
|
||||
|
||||
func TestMultinodeDataPublishing(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.SkipNow()
|
||||
return
|
||||
}
|
||||
|
||||
_, _, finish := setupMultiNodeTest("TestMultinodeDataPublishing")
|
||||
defer finish()
|
||||
|
||||
scenarioDataPublish(t)
|
||||
}
|
||||
|
||||
func TestMultiNodeJoinAfterClose(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.SkipNow()
|
||||
return
|
||||
}
|
||||
|
||||
_, _, finish := setupMultiNodeTest("TestMultiNodeJoinAfterClose")
|
||||
defer finish()
|
||||
|
||||
scenarioJoinClosedRoom(t)
|
||||
}
|
||||
|
||||
func TestMultiNodeCloseNonRTCRoom(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.SkipNow()
|
||||
return
|
||||
}
|
||||
|
||||
_, _, finish := setupMultiNodeTest("closeNonRTCRoom")
|
||||
defer finish()
|
||||
|
||||
closeNonRTCRoom(t)
|
||||
}
|
||||
|
||||
// ensure that token accurately reflects out of band updates
|
||||
func TestMultiNodeRefreshToken(t *testing.T) {
|
||||
_, _, finish := setupMultiNodeTest("TestMultiNodeJoinAfterClose")
|
||||
defer finish()
|
||||
|
||||
// a participant joining with full permissions
|
||||
c1 := createRTCClient("c1", defaultServerPort, nil)
|
||||
waitUntilConnected(t, c1)
|
||||
|
||||
// update permissions and metadata
|
||||
ctx := contextWithToken(adminRoomToken(testRoom))
|
||||
_, err := roomClient.UpdateParticipant(ctx, &livekit.UpdateParticipantRequest{
|
||||
Room: testRoom,
|
||||
Identity: "c1",
|
||||
Permission: &livekit.ParticipantPermission{
|
||||
CanPublish: false,
|
||||
CanSubscribe: true,
|
||||
},
|
||||
Metadata: "metadata",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
testutils.WithTimeout(t, func() string {
|
||||
if c1.RefreshToken() == "" {
|
||||
return "did not receive refresh token"
|
||||
}
|
||||
// parse token to ensure it's correct
|
||||
verifier, err := auth.ParseAPIToken(c1.RefreshToken())
|
||||
require.NoError(t, err)
|
||||
|
||||
grants, err := verifier.Verify(testApiSecret)
|
||||
require.NoError(t, err)
|
||||
|
||||
if grants.Metadata != "metadata" {
|
||||
return "metadata did not match"
|
||||
}
|
||||
if *grants.Video.CanPublish {
|
||||
return "canPublish should be false"
|
||||
}
|
||||
if *grants.Video.CanPublishData {
|
||||
return "canPublishData should be false"
|
||||
}
|
||||
if !*grants.Video.CanSubscribe {
|
||||
return "canSubscribe should be true"
|
||||
}
|
||||
return ""
|
||||
})
|
||||
}
|
||||
|
||||
// ensure that token accurately reflects out of band updates
|
||||
func TestMultiNodeUpdateAttributes(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.SkipNow()
|
||||
return
|
||||
}
|
||||
|
||||
_, _, finish := setupMultiNodeTest("TestMultiNodeUpdateAttributes")
|
||||
defer finish()
|
||||
|
||||
c1 := createRTCClient("au1", defaultServerPort, &client.Options{
|
||||
TokenCustomizer: func(token *auth.AccessToken, grants *auth.VideoGrant) {
|
||||
token.SetAttributes(map[string]string{
|
||||
"mykey": "au1",
|
||||
})
|
||||
},
|
||||
})
|
||||
c2 := createRTCClient("au2", secondServerPort, &client.Options{
|
||||
TokenCustomizer: func(token *auth.AccessToken, grants *auth.VideoGrant) {
|
||||
token.SetAttributes(map[string]string{
|
||||
"mykey": "au2",
|
||||
})
|
||||
grants.SetCanUpdateOwnMetadata(true)
|
||||
},
|
||||
})
|
||||
waitUntilConnected(t, c1, c2)
|
||||
|
||||
testutils.WithTimeout(t, func() string {
|
||||
rc2 := c1.GetRemoteParticipant(c2.ID())
|
||||
rc1 := c2.GetRemoteParticipant(c1.ID())
|
||||
if rc2 == nil || rc1 == nil {
|
||||
return "participants could not see each other"
|
||||
}
|
||||
if rc1.Attributes == nil || rc1.Attributes["mykey"] != "au1" {
|
||||
return "rc1's initial attributes are incorrect"
|
||||
}
|
||||
if rc2.Attributes == nil || rc2.Attributes["mykey"] != "au2" {
|
||||
return "rc2's initial attributes are incorrect"
|
||||
}
|
||||
return ""
|
||||
})
|
||||
|
||||
// this one should not go through
|
||||
_ = c1.SetAttributes(map[string]string{"mykey": "shouldnotchange"})
|
||||
_ = c2.SetAttributes(map[string]string{"secondkey": "au2"})
|
||||
|
||||
// updates using room API should succeed
|
||||
_, err := roomClient.UpdateParticipant(contextWithToken(adminRoomToken(testRoom)), &livekit.UpdateParticipantRequest{
|
||||
Room: testRoom,
|
||||
Identity: "au1",
|
||||
Attributes: map[string]string{
|
||||
"secondkey": "au1",
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
testutils.WithTimeout(t, func() string {
|
||||
rc1 := c2.GetRemoteParticipant(c1.ID())
|
||||
rc2 := c1.GetRemoteParticipant(c2.ID())
|
||||
if rc1.Attributes["secondkey"] != "au1" {
|
||||
return "au1's attribute update failed"
|
||||
}
|
||||
if rc2.Attributes["secondkey"] != "au2" {
|
||||
return "au2's attribute update failed"
|
||||
}
|
||||
if rc1.Attributes["mykey"] != "au1" {
|
||||
return "au1's mykey should not change"
|
||||
}
|
||||
if rc2.Attributes["mykey"] != "au2" {
|
||||
return "au2's mykey should not change"
|
||||
}
|
||||
return ""
|
||||
})
|
||||
}
|
||||
|
||||
func TestMultiNodeRevokePublishPermission(t *testing.T) {
|
||||
_, _, finish := setupMultiNodeTest("TestMultiNodeRevokePublishPermission")
|
||||
defer finish()
|
||||
|
||||
c1 := createRTCClient("c1", defaultServerPort, nil)
|
||||
c2 := createRTCClient("c2", secondServerPort, nil)
|
||||
waitUntilConnected(t, c1, c2)
|
||||
|
||||
// c1 publishes a track for c2
|
||||
writers := publishTracksForClients(t, c1)
|
||||
defer stopWriters(writers...)
|
||||
|
||||
testutils.WithTimeout(t, func() string {
|
||||
if len(c2.SubscribedTracks()[c1.ID()]) != 2 {
|
||||
return "c2 did not receive c1's tracks"
|
||||
}
|
||||
return ""
|
||||
})
|
||||
|
||||
// revoke permission
|
||||
ctx := contextWithToken(adminRoomToken(testRoom))
|
||||
_, err := roomClient.UpdateParticipant(ctx, &livekit.UpdateParticipantRequest{
|
||||
Room: testRoom,
|
||||
Identity: "c1",
|
||||
Permission: &livekit.ParticipantPermission{
|
||||
CanPublish: false,
|
||||
CanPublishData: true,
|
||||
CanSubscribe: true,
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// ensure c1 no longer has track published, c2 no longer see track under C1
|
||||
testutils.WithTimeout(t, func() string {
|
||||
if len(c1.GetPublishedTrackIDs()) != 0 {
|
||||
return "c1 did not unpublish tracks"
|
||||
}
|
||||
remoteC1 := c2.GetRemoteParticipant(c1.ID())
|
||||
if remoteC1 == nil {
|
||||
return "c2 doesn't know about c1"
|
||||
}
|
||||
if len(remoteC1.Tracks) != 0 {
|
||||
return "c2 still has c1's tracks"
|
||||
}
|
||||
return ""
|
||||
})
|
||||
}
|
||||
|
||||
func TestCloseDisconnectedParticipantOnSignalClose(t *testing.T) {
|
||||
_, _, finish := setupMultiNodeTest("TestCloseDisconnectedParticipantOnSignalClose")
|
||||
defer finish()
|
||||
|
||||
c1 := createRTCClient("c1", secondServerPort, nil)
|
||||
waitUntilConnected(t, c1)
|
||||
|
||||
c2 := createRTCClient("c2", defaultServerPort, &client.Options{
|
||||
SignalRequestInterceptor: func(msg *livekit.SignalRequest, next client.SignalRequestHandler) error {
|
||||
switch msg.Message.(type) {
|
||||
case *livekit.SignalRequest_Offer, *livekit.SignalRequest_Answer, *livekit.SignalRequest_Leave:
|
||||
return nil
|
||||
default:
|
||||
return next(msg)
|
||||
}
|
||||
},
|
||||
SignalResponseInterceptor: func(msg *livekit.SignalResponse, next client.SignalResponseHandler) error {
|
||||
switch msg.Message.(type) {
|
||||
case *livekit.SignalResponse_Offer, *livekit.SignalResponse_Answer:
|
||||
return nil
|
||||
default:
|
||||
return next(msg)
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
testutils.WithTimeout(t, func() string {
|
||||
if len(c1.RemoteParticipants()) != 1 {
|
||||
return "c1 did not see c2 join"
|
||||
}
|
||||
return ""
|
||||
})
|
||||
|
||||
c2.Stop()
|
||||
|
||||
testutils.WithTimeout(t, func() string {
|
||||
if len(c1.RemoteParticipants()) != 0 {
|
||||
return "c1 did not see c2 removed"
|
||||
}
|
||||
return ""
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user