Allow internal clients to set / change the "inCall" flags.

This commit is contained in:
Joachim Bauch 2023-02-15 11:10:13 +01:00
parent 1997a8eecb
commit 2f6e2ba87c
No known key found for this signature in database
GPG Key ID: 77C1D22D53E15F02
9 changed files with 427 additions and 39 deletions

View File

@ -423,7 +423,7 @@ func (m *HelloClientMessage) CheckValid() error {
}
const (
// Features for all clients.
// Features to send to all clients.
ServerFeatureMcu = "mcu"
ServerFeatureSimulcast = "simulcast"
ServerFeatureUpdateSdp = "update-sdp"
@ -434,8 +434,11 @@ const (
ServerFeatureHelloV2 = "hello-v2"
ServerFeatureSwitchTo = "switchto"
// Features for internal clients only.
// Features to send to internal clients only.
ServerFeatureInternalVirtualSessions = "virtual-sessions"
// Possible client features from the "hello" request.
ClientFeatureInternalInCall = "internal-incall"
)
var (
@ -628,6 +631,7 @@ type AddSessionInternalClientMessage struct {
UserId string `json:"userid,omitempty"`
User *json.RawMessage `json:"user,omitempty"`
Flags uint32 `json:"flags,omitempty"`
InCall *int `json:"incall,omitempty"`
Options *AddSessionOptions `json:"options,omitempty"`
}
@ -639,7 +643,8 @@ func (m *AddSessionInternalClientMessage) CheckValid() error {
type UpdateSessionInternalClientMessage struct {
CommonSessionInternalClientMessage
Flags *uint32 `json:"flags,omitempty"`
Flags *uint32 `json:"flags,omitempty"`
InCall *int `json:"incall,omitempty"`
}
func (m *UpdateSessionInternalClientMessage) CheckValid() error {
@ -656,6 +661,14 @@ func (m *RemoveSessionInternalClientMessage) CheckValid() error {
return m.CommonSessionInternalClientMessage.CheckValid()
}
type InCallInternalClientMessage struct {
InCall int `json:"incall"`
}
func (m *InCallInternalClientMessage) CheckValid() error {
return nil
}
type InternalClientMessage struct {
Type string `json:"type"`
@ -664,6 +677,8 @@ type InternalClientMessage struct {
UpdateSession *UpdateSessionInternalClientMessage `json:"updatesession,omitempty"`
RemoveSession *RemoveSessionInternalClientMessage `json:"removesession,omitempty"`
InCall *InCallInternalClientMessage `json:"incall,omitempty"`
}
func (m *InternalClientMessage) CheckValid() error {
@ -686,6 +701,12 @@ func (m *InternalClientMessage) CheckValid() error {
} else if err := m.RemoveSession.CheckValid(); err != nil {
return err
}
case "incall":
if m.InCall == nil {
return fmt.Errorf("incall missing")
} else if err := m.InCall.CheckValid(); err != nil {
return err
}
}
return nil
}

View File

@ -48,6 +48,7 @@ var (
type ClientSession struct {
roomJoinTime int64
inCall uint32
hub *Hub
events AsyncEvents
@ -106,6 +107,9 @@ func NewClientSession(hub *Hub, privateId string, publicId string, data *Session
if s.clientType == HelloClientTypeInternal {
s.backendUrl = hello.Auth.internalParams.Backend
s.parsedBackendUrl = hello.Auth.internalParams.parsedBackend
if !s.HasFeature(ClientFeatureInternalInCall) {
s.SetInCall(FlagInCall | FlagWithAudio)
}
} else {
s.backendUrl = hello.Auth.Url
s.parsedBackendUrl = hello.Auth.parsedUrl
@ -155,6 +159,28 @@ func (s *ClientSession) ClientType() string {
return s.clientType
}
// GetInCall is only used for internal clients.
func (s *ClientSession) GetInCall() int {
return int(atomic.LoadUint32(&s.inCall))
}
func (s *ClientSession) SetInCall(inCall int) bool {
if inCall < 0 {
inCall = 0
}
for {
old := atomic.LoadUint32(&s.inCall)
if old == uint32(inCall) {
return false
}
if atomic.CompareAndSwapUint32(&s.inCall, old, uint32(inCall)) {
return true
}
}
}
func (s *ClientSession) GetFeatures() []string {
return s.features
}

View File

@ -238,7 +238,7 @@ func TestBandwidth_Backend(t *testing.T) {
params := TestBackendClientAuthParams{
UserId: testDefaultUserId,
}
if err := client.SendHelloParams(server.URL+"/one", HelloVersionV1, "client", params); err != nil {
if err := client.SendHelloParams(server.URL+"/one", HelloVersionV1, "client", nil, params); err != nil {
t.Fatal(err)
}

22
hub.go
View File

@ -1797,7 +1797,7 @@ func (h *Hub) processInternalMsg(client *Client, message *ClientMessage) {
request := NewBackendClientRoomRequest(room.Id(), msg.UserId, publicSessionId)
request.Room.ActorId = msg.Options.ActorId
request.Room.ActorType = msg.Options.ActorType
request.Room.InCall = FlagInCall | FlagWithPhone
request.Room.InCall = sess.GetInCall()
var response BackendClientResponse
if err := h.backend.PerformJSONRequest(ctx, session.ParsedBackendUrl(), request, &response); err != nil {
@ -1856,18 +1856,23 @@ func (h *Hub) processInternalMsg(client *Client, message *ClientMessage) {
sess := h.sessions[sid]
h.mu.Unlock()
if sess != nil {
update := false
var changed SessionChangeFlag
if virtualSession, ok := sess.(*VirtualSession); ok {
if msg.Flags != nil {
if virtualSession.SetFlags(*msg.Flags) {
update = true
changed |= SessionChangeFlags
}
}
if msg.InCall != nil {
if virtualSession.SetInCall(*msg.InCall) {
changed |= SessionChangeInCall
}
}
} else {
log.Printf("Ignore update request for non-virtual session %s", sess.PublicId())
}
if update {
room.NotifySessionChanged(sess)
if changed != 0 {
room.NotifySessionChanged(sess, changed)
}
}
case "removesession":
@ -1898,6 +1903,13 @@ func (h *Hub) processInternalMsg(client *Client, message *ClientMessage) {
sess.Close()
}
}
case "incall":
msg := msg.InCall
if session.SetInCall(msg.InCall) {
if room := session.GetRoom(); room != nil {
room.NotifySessionChanged(session, SessionChangeInCall)
}
}
default:
log.Printf("Ignore unsupported internal message %+v from %s", msg, session.PublicId())
return

View File

@ -848,7 +848,7 @@ func TestExpectClientHelloUnsupportedVersion(t *testing.T) {
params := TestBackendClientAuthParams{
UserId: testDefaultUserId,
}
if err := client.SendHelloParams(server.URL, "0.0", "", params); err != nil {
if err := client.SendHelloParams(server.URL, "0.0", "", nil, params); err != nil {
t.Fatal(err)
}
@ -1254,7 +1254,7 @@ func TestClientHelloSessionLimit(t *testing.T) {
params1 := TestBackendClientAuthParams{
UserId: testDefaultUserId,
}
if err := client.SendHelloParams(server1.URL+"/one", HelloVersionV1, "client", params1); err != nil {
if err := client.SendHelloParams(server1.URL+"/one", HelloVersionV1, "client", nil, params1); err != nil {
t.Fatal(err)
}
@ -1279,7 +1279,7 @@ func TestClientHelloSessionLimit(t *testing.T) {
params2 := TestBackendClientAuthParams{
UserId: testDefaultUserId + "2",
}
if err := client2.SendHelloParams(server1.URL+"/one", HelloVersionV1, "client", params2); err != nil {
if err := client2.SendHelloParams(server1.URL+"/one", HelloVersionV1, "client", nil, params2); err != nil {
t.Fatal(err)
}
@ -1295,7 +1295,7 @@ func TestClientHelloSessionLimit(t *testing.T) {
}
// The client can connect to a different backend.
if err := client2.SendHelloParams(server1.URL+"/two", HelloVersionV1, "client", params2); err != nil {
if err := client2.SendHelloParams(server1.URL+"/two", HelloVersionV1, "client", nil, params2); err != nil {
t.Fatal(err)
}
@ -1322,7 +1322,7 @@ func TestClientHelloSessionLimit(t *testing.T) {
params3 := TestBackendClientAuthParams{
UserId: testDefaultUserId + "3",
}
if err := client3.SendHelloParams(server1.URL+"/one", HelloVersionV1, "client", params3); err != nil {
if err := client3.SendHelloParams(server1.URL+"/one", HelloVersionV1, "client", nil, params3); err != nil {
t.Fatal(err)
}
@ -1926,7 +1926,7 @@ func TestClientHelloClient_V3Api(t *testing.T) {
}
// The "/api/v1/signaling/" URL will be changed to use "v3" as the "signaling-v3"
// feature is returned by the capabilities endpoint.
if err := client.SendHelloParams(server.URL+"/ocs/v2.php/apps/spreed/api/v1/signaling/backend", HelloVersionV1, "client", params); err != nil {
if err := client.SendHelloParams(server.URL+"/ocs/v2.php/apps/spreed/api/v1/signaling/backend", HelloVersionV1, "client", nil, params); err != nil {
t.Fatal(err)
}
@ -4269,7 +4269,7 @@ func TestNoSendBetweenSessionsOnDifferentBackends(t *testing.T) {
params1 := TestBackendClientAuthParams{
UserId: "user1",
}
if err := client1.SendHelloParams(server.URL+"/one", HelloVersionV1, "client", params1); err != nil {
if err := client1.SendHelloParams(server.URL+"/one", HelloVersionV1, "client", nil, params1); err != nil {
t.Fatal(err)
}
hello1, err := client1.RunUntilHello(ctx)
@ -4283,7 +4283,7 @@ func TestNoSendBetweenSessionsOnDifferentBackends(t *testing.T) {
params2 := TestBackendClientAuthParams{
UserId: "user2",
}
if err := client2.SendHelloParams(server.URL+"/two", HelloVersionV1, "client", params2); err != nil {
if err := client2.SendHelloParams(server.URL+"/two", HelloVersionV1, "client", nil, params2); err != nil {
t.Fatal(err)
}
hello2, err := client2.RunUntilHello(ctx)
@ -4339,7 +4339,7 @@ func TestNoSameRoomOnDifferentBackends(t *testing.T) {
params1 := TestBackendClientAuthParams{
UserId: "user1",
}
if err := client1.SendHelloParams(server.URL+"/one", HelloVersionV1, "client", params1); err != nil {
if err := client1.SendHelloParams(server.URL+"/one", HelloVersionV1, "client", nil, params1); err != nil {
t.Fatal(err)
}
hello1, err := client1.RunUntilHello(ctx)
@ -4353,7 +4353,7 @@ func TestNoSameRoomOnDifferentBackends(t *testing.T) {
params2 := TestBackendClientAuthParams{
UserId: "user2",
}
if err := client2.SendHelloParams(server.URL+"/two", HelloVersionV1, "client", params2); err != nil {
if err := client2.SendHelloParams(server.URL+"/two", HelloVersionV1, "client", nil, params2); err != nil {
t.Fatal(err)
}
hello2, err := client2.RunUntilHello(ctx)

60
room.go
View File

@ -44,6 +44,13 @@ const (
FlagWithPhone = 8
)
type SessionChangeFlag int
const (
SessionChangeFlags SessionChangeFlag = 1
SessionChangeInCall SessionChangeFlag = 2
)
var (
updateActiveSessionsInterval = 10 * time.Second
)
@ -571,7 +578,7 @@ func (r *Room) addInternalSessions(users []map[string]interface{}) []map[string]
}
for session := range r.internalSessions {
users = append(users, map[string]interface{}{
"inCall": FlagInCall | FlagWithAudio,
"inCall": session.(*ClientSession).GetInCall(),
"sessionId": session.PublicId(),
"lastPing": now,
"internal": true,
@ -579,7 +586,7 @@ func (r *Room) addInternalSessions(users []map[string]interface{}) []map[string]
}
for session := range r.virtualSessions {
users = append(users, map[string]interface{}{
"inCall": FlagInCall | FlagWithPhone,
"inCall": session.GetInCall(),
"sessionId": session.PublicId(),
"lastPing": now,
"virtual": true,
@ -816,18 +823,51 @@ func (r *Room) NotifySessionResumed(session *ClientSession) {
session.SendMessage(message)
}
func (r *Room) NotifySessionChanged(session Session) {
if session.ClientType() != HelloClientTypeVirtual {
func (r *Room) NotifySessionChanged(session Session, flags SessionChangeFlag) {
if flags&SessionChangeFlags != 0 && session.ClientType() == HelloClientTypeVirtual {
// Only notify if a virtual session has changed.
return
if virtual, ok := session.(*VirtualSession); ok {
r.publishSessionFlagsChanged(virtual)
}
}
virtual, ok := session.(*VirtualSession)
if !ok {
return
}
if flags&SessionChangeInCall != 0 {
joinLeave := 0
if clientSession, ok := session.(*ClientSession); ok {
if clientSession.GetInCall()&FlagInCall != 0 {
joinLeave = 1
} else {
joinLeave = 2
}
} else if virtual, ok := session.(*VirtualSession); ok {
if virtual.GetInCall()&FlagInCall != 0 {
joinLeave = 1
} else {
joinLeave = 2
}
}
r.publishSessionFlagsChanged(virtual)
if joinLeave != 0 {
if joinLeave == 1 {
r.mu.Lock()
if !r.inCallSessions[session] {
r.inCallSessions[session] = true
log.Printf("Session %s joined call %s", session.PublicId(), r.id)
}
r.mu.Unlock()
} else if joinLeave == 2 {
r.mu.Lock()
delete(r.inCallSessions, session)
r.mu.Unlock()
if clientSession, ok := session.(*ClientSession); ok {
clientSession.LeaveCall()
}
}
// TODO: Check if we could send a smaller update message with only the changed session.
r.publishUsersChangedWithInternal()
}
}
}
func (r *Room) publishUsersChangedWithInternal() {

View File

@ -385,7 +385,7 @@ func (c *TestClient) SendHelloV1(userid string) error {
params := TestBackendClientAuthParams{
UserId: userid,
}
return c.SendHelloParams(c.server.URL, HelloVersionV1, "", params)
return c.SendHelloParams(c.server.URL, HelloVersionV1, "", nil, params)
}
func (c *TestClient) SendHelloV2(userid string) error {
@ -434,7 +434,7 @@ func (c *TestClient) SendHelloV2WithTimes(userid string, issuedAt time.Time, exp
params := HelloV2AuthParams{
Token: tokenString,
}
return c.SendHelloParams(c.server.URL, HelloVersionV2, "", params)
return c.SendHelloParams(c.server.URL, HelloVersionV2, "", nil, params)
}
func (c *TestClient) SendHelloResume(resumeId string) error {
@ -453,10 +453,14 @@ func (c *TestClient) SendHelloClient(userid string) error {
params := TestBackendClientAuthParams{
UserId: userid,
}
return c.SendHelloParams(c.server.URL, HelloVersionV1, "client", params)
return c.SendHelloParams(c.server.URL, HelloVersionV1, "client", nil, params)
}
func (c *TestClient) SendHelloInternal() error {
return c.SendHelloInternalWithFeatures(nil)
}
func (c *TestClient) SendHelloInternalWithFeatures(features []string) error {
random := newRandomString(48)
mac := hmac.New(sha256.New, testInternalSecret)
mac.Write([]byte(random)) // nolint
@ -468,10 +472,10 @@ func (c *TestClient) SendHelloInternal() error {
Token: token,
Backend: backend,
}
return c.SendHelloParams("", HelloVersionV1, "internal", params)
return c.SendHelloParams("", HelloVersionV1, "internal", features, params)
}
func (c *TestClient) SendHelloParams(url string, version string, clientType string, params interface{}) error {
func (c *TestClient) SendHelloParams(url string, version string, clientType string, features []string, params interface{}) error {
data, err := json.Marshal(params)
if err != nil {
c.t.Fatal(err)
@ -481,7 +485,8 @@ func (c *TestClient) SendHelloParams(url string, version string, clientType stri
Id: "1234",
Type: "hello",
Hello: &HelloClientMessage{
Version: version,
Version: version,
Features: features,
Auth: HelloClientMessageAuth{
Type: clientType,
Url: url,

View File

@ -38,6 +38,8 @@ const (
)
type VirtualSession struct {
inCall uint32
hub *Hub
session *ClientSession
privateId string
@ -75,6 +77,12 @@ func NewVirtualSession(session *ClientSession, privateId string, publicId string
return nil, err
}
if msg.InCall != nil {
result.SetInCall(*msg.InCall)
} else if !session.HasFeature(ClientFeatureInternalInCall) {
result.SetInCall(FlagInCall | FlagWithPhone)
}
return result, nil
}
@ -90,6 +98,27 @@ func (s *VirtualSession) ClientType() string {
return HelloClientTypeVirtual
}
func (s *VirtualSession) GetInCall() int {
return int(atomic.LoadUint32(&s.inCall))
}
func (s *VirtualSession) SetInCall(inCall int) bool {
if inCall < 0 {
inCall = 0
}
for {
old := atomic.LoadUint32(&s.inCall)
if old == uint32(inCall) {
return false
}
if atomic.CompareAndSwapUint32(&s.inCall, old, uint32(inCall)) {
return true
}
}
}
func (s *VirtualSession) Data() *SessionIdData {
return s.data
}

View File

@ -25,6 +25,7 @@ import (
"context"
"encoding/json"
"errors"
"fmt"
"testing"
)
@ -143,8 +144,8 @@ func TestVirtualSession(t *testing.T) {
t.Errorf("Expected session id %s, got %+v", sessionId, updateMsg.Users[0])
} else if virtual, ok := updateMsg.Users[0]["virtual"].(bool); !ok || !virtual {
t.Errorf("Expected virtual user, got %+v", updateMsg.Users[0])
} else if inCall, ok := updateMsg.Users[0]["inCall"].(float64); !ok || inCall == 0 {
t.Errorf("Expected user in call, got %+v", updateMsg.Users[0])
} else if inCall, ok := updateMsg.Users[0]["inCall"].(float64); !ok || inCall != (FlagInCall|FlagWithPhone) {
t.Errorf("Expected user in call with phone, got %+v", updateMsg.Users[0])
}
msg3, err := client.RunUntilMessage(ctx)
@ -313,6 +314,260 @@ func TestVirtualSession(t *testing.T) {
}
}
func checkHasEntryWithInCall(message *RoomEventServerMessage, sessionId string, entryType string, inCall int) error {
found := false
for _, entry := range message.Users {
if sid, ok := entry["sessionId"].(string); ok && sid == sessionId {
if value, ok := entry[entryType].(bool); !ok || !value {
return fmt.Errorf("Expected %s user, got %+v", entryType, entry)
}
if value, ok := entry["inCall"].(float64); !ok || int(value) != inCall {
return fmt.Errorf("Expected in call %d, got %+v", inCall, entry)
}
found = true
break
}
}
if !found {
return fmt.Errorf("No user with session id %s found, got %+v", sessionId, message)
}
return nil
}
func TestVirtualSessionCustomInCall(t *testing.T) {
hub, _, _, server := CreateHubForTest(t)
roomId := "the-room-id"
emptyProperties := json.RawMessage("{}")
backend := &Backend{
id: "compat",
compat: true,
}
room, err := hub.createRoom(roomId, &emptyProperties, backend)
if err != nil {
t.Fatalf("Could not create room: %s", err)
}
defer room.Close()
clientInternal := NewTestClient(t, server, hub)
defer clientInternal.CloseWithBye()
features := []string{
ClientFeatureInternalInCall,
}
if err := clientInternal.SendHelloInternalWithFeatures(features); err != nil {
t.Fatal(err)
}
client := NewTestClient(t, server, hub)
defer client.CloseWithBye()
if err := client.SendHello(testDefaultUserId); err != nil {
t.Fatal(err)
}
ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
defer cancel()
helloInternal, err := clientInternal.RunUntilHello(ctx)
if err != nil {
t.Error(err)
} else {
if helloInternal.Hello.UserId != "" {
t.Errorf("Expected empty user id, got %+v", helloInternal.Hello)
}
if helloInternal.Hello.SessionId == "" {
t.Errorf("Expected session id, got %+v", helloInternal.Hello)
}
if helloInternal.Hello.ResumeId == "" {
t.Errorf("Expected resume id, got %+v", helloInternal.Hello)
}
}
if room, err := clientInternal.JoinRoomWithRoomSession(ctx, roomId, ""); err != nil {
t.Fatal(err)
} else if room.Room.RoomId != roomId {
t.Fatalf("Expected room %s, got %s", roomId, room.Room.RoomId)
}
hello, err := client.RunUntilHello(ctx)
if err != nil {
t.Error(err)
}
if room, err := client.JoinRoom(ctx, roomId); err != nil {
t.Fatal(err)
} else if room.Room.RoomId != roomId {
t.Fatalf("Expected room %s, got %s", roomId, room.Room.RoomId)
}
if _, additional, err := clientInternal.RunUntilJoinedAndReturn(ctx, helloInternal.Hello, hello.Hello); err != nil {
t.Error(err)
} else if len(additional) != 1 {
t.Errorf("expected one additional message, got %+v", additional)
} else if additional[0].Type != "event" {
t.Errorf("expected event message, got %+v", additional[0])
} else if additional[0].Event.Target != "participants" {
t.Errorf("expected event participants message, got %+v", additional[0])
} else if additional[0].Event.Type != "update" {
t.Errorf("expected event participants update message, got %+v", additional[0])
} else if additional[0].Event.Update.Users[0]["sessionId"].(string) != helloInternal.Hello.SessionId {
t.Errorf("expected event update message for internal session, got %+v", additional[0])
} else if additional[0].Event.Update.Users[0]["inCall"].(float64) != 0 {
t.Errorf("expected event update message with session not in call, got %+v", additional[0])
}
if err := client.RunUntilJoined(ctx, helloInternal.Hello, hello.Hello); err != nil {
t.Error(err)
}
internalSessionId := "session1"
userId := "user1"
msgAdd := &ClientMessage{
Type: "internal",
Internal: &InternalClientMessage{
Type: "addsession",
AddSession: &AddSessionInternalClientMessage{
CommonSessionInternalClientMessage: CommonSessionInternalClientMessage{
SessionId: internalSessionId,
RoomId: roomId,
},
UserId: userId,
Flags: FLAG_MUTED_SPEAKING,
},
},
}
if err := clientInternal.WriteJSON(msgAdd); err != nil {
t.Fatal(err)
}
msg1, err := client.RunUntilMessage(ctx)
if err != nil {
t.Fatal(err)
}
// The public session id will be generated by the server, so don't check for it.
if err := client.checkMessageJoinedSession(msg1, "", userId); err != nil {
t.Fatal(err)
}
sessionId := msg1.Event.Join[0].SessionId
session := hub.GetSessionByPublicId(sessionId)
if session == nil {
t.Fatalf("Could not get virtual session %s", sessionId)
}
if session.ClientType() != HelloClientTypeVirtual {
t.Errorf("Expected client type %s, got %s", HelloClientTypeVirtual, session.ClientType())
}
if sid := session.(*VirtualSession).SessionId(); sid != internalSessionId {
t.Errorf("Expected internal session id %s, got %s", internalSessionId, sid)
}
// Also a participants update event will be triggered for the virtual user.
msg2, err := client.RunUntilMessage(ctx)
if err != nil {
t.Fatal(err)
}
updateMsg, err := checkMessageParticipantsInCall(msg2)
if err != nil {
t.Error(err)
} else if updateMsg.RoomId != roomId {
t.Errorf("Expected room %s, got %s", roomId, updateMsg.RoomId)
} else if len(updateMsg.Users) != 2 {
t.Errorf("Expected two users, got %+v", updateMsg.Users)
}
if err := checkHasEntryWithInCall(updateMsg, sessionId, "virtual", 0); err != nil {
t.Error(err)
}
if err := checkHasEntryWithInCall(updateMsg, helloInternal.Hello.SessionId, "internal", 0); err != nil {
t.Error(err)
}
msg3, err := client.RunUntilMessage(ctx)
if err != nil {
t.Fatal(err)
}
flagsMsg, err := checkMessageParticipantFlags(msg3)
if err != nil {
t.Error(err)
} else if flagsMsg.RoomId != roomId {
t.Errorf("Expected room %s, got %s", roomId, flagsMsg.RoomId)
} else if flagsMsg.SessionId != sessionId {
t.Errorf("Expected session id %s, got %s", sessionId, flagsMsg.SessionId)
} else if flagsMsg.Flags != FLAG_MUTED_SPEAKING {
t.Errorf("Expected flags %d, got %+v", FLAG_MUTED_SPEAKING, flagsMsg.Flags)
}
// The internal session can change its "inCall" flags
msgInCall := &ClientMessage{
Type: "internal",
Internal: &InternalClientMessage{
Type: "incall",
InCall: &InCallInternalClientMessage{
InCall: FlagInCall | FlagWithAudio,
},
},
}
if err := clientInternal.WriteJSON(msgInCall); err != nil {
t.Fatal(err)
}
msg4, err := client.RunUntilMessage(ctx)
if err != nil {
t.Fatal(err)
}
updateMsg2, err := checkMessageParticipantsInCall(msg4)
if err != nil {
t.Error(err)
} else if updateMsg2.RoomId != roomId {
t.Errorf("Expected room %s, got %s", roomId, updateMsg2.RoomId)
} else if len(updateMsg2.Users) != 2 {
t.Errorf("Expected two users, got %+v", updateMsg2.Users)
}
if err := checkHasEntryWithInCall(updateMsg2, sessionId, "virtual", 0); err != nil {
t.Error(err)
}
if err := checkHasEntryWithInCall(updateMsg2, helloInternal.Hello.SessionId, "internal", FlagInCall|FlagWithAudio); err != nil {
t.Error(err)
}
// The internal session can change the "inCall" flags of a virtual session
newInCall := FlagInCall | FlagWithPhone
msgInCall2 := &ClientMessage{
Type: "internal",
Internal: &InternalClientMessage{
Type: "updatesession",
UpdateSession: &UpdateSessionInternalClientMessage{
CommonSessionInternalClientMessage: CommonSessionInternalClientMessage{
SessionId: internalSessionId,
RoomId: roomId,
},
InCall: &newInCall,
},
},
}
if err := clientInternal.WriteJSON(msgInCall2); err != nil {
t.Fatal(err)
}
msg5, err := client.RunUntilMessage(ctx)
if err != nil {
t.Fatal(err)
}
updateMsg3, err := checkMessageParticipantsInCall(msg5)
if err != nil {
t.Error(err)
} else if updateMsg3.RoomId != roomId {
t.Errorf("Expected room %s, got %s", roomId, updateMsg3.RoomId)
} else if len(updateMsg3.Users) != 2 {
t.Errorf("Expected two users, got %+v", updateMsg3.Users)
}
if err := checkHasEntryWithInCall(updateMsg3, sessionId, "virtual", newInCall); err != nil {
t.Error(err)
}
if err := checkHasEntryWithInCall(updateMsg3, helloInternal.Hello.SessionId, "internal", FlagInCall|FlagWithAudio); err != nil {
t.Error(err)
}
}
func TestVirtualSessionCleanup(t *testing.T) {
hub, _, _, server := CreateHubForTest(t)
@ -427,8 +682,8 @@ func TestVirtualSessionCleanup(t *testing.T) {
t.Errorf("Expected session id %s, got %+v", sessionId, updateMsg.Users[0])
} else if virtual, ok := updateMsg.Users[0]["virtual"].(bool); !ok || !virtual {
t.Errorf("Expected virtual user, got %+v", updateMsg.Users[0])
} else if inCall, ok := updateMsg.Users[0]["inCall"].(float64); !ok || inCall == 0 {
t.Errorf("Expected user in call, got %+v", updateMsg.Users[0])
} else if inCall, ok := updateMsg.Users[0]["inCall"].(float64); !ok || inCall != (FlagInCall|FlagWithPhone) {
t.Errorf("Expected user in call with phone, got %+v", updateMsg.Users[0])
}
msg3, err := client.RunUntilMessage(ctx)