diff --git a/hub_test.go b/hub_test.go index 71c771f..feb16a1 100644 --- a/hub_test.go +++ b/hub_test.go @@ -2449,6 +2449,179 @@ func TestClientSendOfferPermissions(t *testing.T) { } } +func TestClientSendOfferPermissionsAudioOnly(t *testing.T) { + hub, _, _, server, shutdown := CreateHubForTest(t) + defer shutdown() + + mcu, err := NewTestMCU() + if err != nil { + t.Fatal(err) + } else if err := mcu.Start(); err != nil { + t.Fatal(err) + } + defer mcu.Stop() + + hub.SetMcu(mcu) + + ctx, cancel := context.WithTimeout(context.Background(), testTimeout) + defer cancel() + + client1 := NewTestClient(t, server, hub) + defer client1.CloseWithBye() + + if err := client1.SendHello(testDefaultUserId + "1"); err != nil { + t.Fatal(err) + } + + hello1, err := client1.RunUntilHello(ctx) + if err != nil { + t.Fatal(err) + } + + // Join room by id. + roomId := "test-room" + if room, err := client1.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 err := client1.RunUntilJoined(ctx, hello1.Hello); err != nil { + t.Error(err) + } + + session1 := hub.GetSessionByPublicId(hello1.Hello.SessionId).(*ClientSession) + if session1 == nil { + t.Fatalf("Session %s does not exist", hello1.Hello.SessionId) + } + + // Client is allowed to send audio only. + session1.SetPermissions([]Permission{PERMISSION_MAY_PUBLISH_AUDIO}) + + // Client may not send an offer with audio and video. + if err := client1.SendMessage(MessageClientMessageRecipient{ + Type: "session", + SessionId: hello1.Hello.SessionId, + }, MessageClientMessageData{ + Type: "offer", + Sid: "54321", + RoomType: "video", + Payload: map[string]interface{}{ + "sdp": MockSdpOfferAudioAndVideo, + }, + }); err != nil { + t.Fatal(err) + } + + if msg, err := client1.RunUntilMessage(ctx); err != nil { + t.Fatal(err) + } else { + if err := checkMessageError(msg, "not_allowed"); err != nil { + t.Fatal(err) + } + } + + // Client may send an offer (audio only). + if err := client1.SendMessage(MessageClientMessageRecipient{ + Type: "session", + SessionId: hello1.Hello.SessionId, + }, MessageClientMessageData{ + Type: "offer", + Sid: "54321", + RoomType: "video", + Payload: map[string]interface{}{ + "sdp": MockSdpOfferAudioOnly, + }, + }); err != nil { + t.Fatal(err) + } + + // The test MCU doesn't support clients yet, so an error will be returned + // to the client trying to send the offer. + if msg, err := client1.RunUntilMessage(ctx); err != nil { + t.Fatal(err) + } else { + if err := checkMessageError(msg, "client_not_found"); err != nil { + t.Fatal(err) + } + } +} + +func TestClientSendOfferPermissionsAudioVideo(t *testing.T) { + hub, _, _, server, shutdown := CreateHubForTest(t) + defer shutdown() + + mcu, err := NewTestMCU() + if err != nil { + t.Fatal(err) + } else if err := mcu.Start(); err != nil { + t.Fatal(err) + } + defer mcu.Stop() + + hub.SetMcu(mcu) + + ctx, cancel := context.WithTimeout(context.Background(), testTimeout) + defer cancel() + + client1 := NewTestClient(t, server, hub) + defer client1.CloseWithBye() + + if err := client1.SendHello(testDefaultUserId + "1"); err != nil { + t.Fatal(err) + } + + hello1, err := client1.RunUntilHello(ctx) + if err != nil { + t.Fatal(err) + } + + // Join room by id. + roomId := "test-room" + if room, err := client1.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 err := client1.RunUntilJoined(ctx, hello1.Hello); err != nil { + t.Error(err) + } + + session1 := hub.GetSessionByPublicId(hello1.Hello.SessionId).(*ClientSession) + if session1 == nil { + t.Fatalf("Session %s does not exist", hello1.Hello.SessionId) + } + + // Client is allowed to send audio and video. + session1.SetPermissions([]Permission{PERMISSION_MAY_PUBLISH_AUDIO, PERMISSION_MAY_PUBLISH_VIDEO}) + + // Client may send an offer (audio and video). + if err := client1.SendMessage(MessageClientMessageRecipient{ + Type: "session", + SessionId: hello1.Hello.SessionId, + }, MessageClientMessageData{ + Type: "offer", + Sid: "54321", + RoomType: "video", + Payload: map[string]interface{}{ + "sdp": MockSdpOfferAudioAndVideo, + }, + }); err != nil { + t.Fatal(err) + } + + // The test MCU doesn't support clients yet, so an error will be returned + // to the client trying to send the offer. + if msg, err := client1.RunUntilMessage(ctx); err != nil { + t.Fatal(err) + } else { + if err := checkMessageError(msg, "client_not_found"); err != nil { + t.Fatal(err) + } + } +} + func TestClientRequestOfferNotInRoom(t *testing.T) { hub, _, _, server, shutdown := CreateHubForTest(t) defer shutdown() diff --git a/mock_data_test.go b/mock_data_test.go new file mode 100644 index 0000000..ccf35fa --- /dev/null +++ b/mock_data_test.go @@ -0,0 +1,84 @@ +package signaling + +const ( + // See https://tools.ietf.org/id/draft-ietf-rtcweb-sdp-08.html#rfc.section.5.2.1 + MockSdpOfferAudioOnly = `v=0 +o=- 20518 0 IN IP4 0.0.0.0 +s=- +t=0 0 +a=group:BUNDLE audio-D.ietf-mmusic-sdp-bundle-negotiation +a=ice-options:trickle-D.ietf-mmusic-trickle-ice +m=audio 54609 UDP/TLS/RTP/SAVPF 109 0 8 +c=IN IP4 192.168.0.1 +a=mid:audio +a=msid:ma ta +a=sendrecv +a=rtpmap:109 opus/48000/2 +a=rtpmap:0 PCMU/8000 +a=rtpmap:8 PCMA/8000 +a=maxptime:120 +a=ice-ufrag:074c6550 +a=ice-pwd:a28a397a4c3f31747d1ee3474af08a068 +a=fingerprint:sha-256 19:E2:1C:3B:4B:9F:81:E6:B8:5C:F4:A5:A8:D8:73:04:BB:05:2F:70:9F:04:A9:0E:05:E9:26:33:E8:70:88:A2 +a=setup:actpass +a=tls-id:1 +a=rtcp-mux +a=rtcp:60065 IN IP4 192.168.0.1 +a=rtcp-rsize +a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level +a=extmap:2 urn:ietf:params:rtp-hdrext:sdes:mid +a=candidate:0 1 UDP 2122194687 192.0.2.4 61665 typ host +a=candidate:1 1 UDP 1685987071 192.168.0.1 54609 typ srflx raddr 192.0.2.4 rport 61665 +a=candidate:0 2 UDP 2122194687 192.0.2.4 61667 typ host +a=candidate:1 2 UDP 1685987071 192.168.0.1 60065 typ srflx raddr 192.0.2.4 rport 61667 +a=end-of-candidates +` + + // See https://tools.ietf.org/id/draft-ietf-rtcweb-sdp-08.html#rfc.section.5.2.2.1 + MockSdpOfferAudioAndVideo = `v=0 +o=- 20518 0 IN IP4 0.0.0.0 +s=- +t=0 0 +a=group:BUNDLE audio-D.ietf-mmusic-sdp-bundle-negotiation +a=ice-options:trickle-D.ietf-mmusic-trickle-ice +m=audio 54609 UDP/TLS/RTP/SAVPF 109 0 8 +c=IN IP4 192.168.0.1 +a=mid:audio +a=msid:ma ta +a=sendrecv +a=rtpmap:109 opus/48000/2 +a=rtpmap:0 PCMU/8000 +a=rtpmap:8 PCMA/8000 +a=maxptime:120 +a=ice-ufrag:074c6550 +a=ice-pwd:a28a397a4c3f31747d1ee3474af08a068 +a=fingerprint:sha-256 19:E2:1C:3B:4B:9F:81:E6:B8:5C:F4:A5:A8:D8:73:04:BB:05:2F:70:9F:04:A9:0E:05:E9:26:33:E8:70:88:A2 +a=setup:actpass +a=tls-id:1 +a=rtcp-mux +a=rtcp:60065 IN IP4 192.168.0.1 +a=rtcp-rsize +a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level +a=extmap:2 urn:ietf:params:rtp-hdrext:sdes:mid +a=candidate:0 1 UDP 2122194687 192.0.2.4 61665 typ host +a=candidate:1 1 UDP 1685987071 192.168.0.1 54609 typ srflx raddr 192.0.2.4 rport 61665 +a=candidate:0 2 UDP 2122194687 192.0.2.4 61667 typ host +a=candidate:1 2 UDP 1685987071 192.168.0.1 60065 typ srflx raddr 192.0.2.4 rport 61667 +a=end-of-candidates +m=video 54609 UDP/TLS/RTP/SAVPF 99 120 +c=IN IP4 192.168.0.1 +a=mid:video +a=msid:ma tb +a=sendrecv +a=rtpmap:99 H264/90000 +a=fmtp:99 profile-level-id=4d0028;packetization-mode=1 +a=rtpmap:120 VP8/90000 +a=rtcp-fb:99 nack +a=rtcp-fb:99 nack pli +a=rtcp-fb:99 ccm fir +a=rtcp-fb:120 nack +a=rtcp-fb:120 nack pli +a=rtcp-fb:120 ccm fir +a=extmap:2 urn:ietf:params:rtp-hdrext:sdes:mid +` +)