From 1b64a5fe4a102c44b70544d305b86361d2b0a88e Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 19 Aug 2022 18:27:56 +0100 Subject: [PATCH 1/2] Add unit tests for hangup / reject Fixes https://github.com/vector-im/element-call/issues/537 --- spec/test-utils/webrtc.ts | 6 ++ spec/unit/webrtc/call.spec.ts | 122 +++++++++++++++++++++++++++------- 2 files changed, 103 insertions(+), 25 deletions(-) diff --git a/spec/test-utils/webrtc.ts b/spec/test-utils/webrtc.ts index 9ed142fcc00..80ccbef2668 100644 --- a/spec/test-utils/webrtc.ts +++ b/spec/test-utils/webrtc.ts @@ -109,6 +109,12 @@ export class MockRTCPeerConnection { sdp: DUMMY_SDP, }); } + createAnswer() { + return Promise.resolve({ + type: 'answer', + sdp: DUMMY_SDP, + }); + } setRemoteDescription() { return Promise.resolve(); } diff --git a/spec/unit/webrtc/call.spec.ts b/spec/unit/webrtc/call.spec.ts index 338e968b190..6dd4c622af5 100644 --- a/spec/unit/webrtc/call.spec.ts +++ b/spec/unit/webrtc/call.spec.ts @@ -25,7 +25,9 @@ import { installWebRTCMocks, } from "../../test-utils/webrtc"; import { CallFeed } from "../../../src/webrtc/callFeed"; -import { EventType } from "../../../src"; +import { EventType, MatrixEvent } from "../../../src"; + +const FAKE_ROOM_ID = "!foo:bar"; const startVoiceCall = async (client: TestClient, call: MatrixCall): Promise => { const callPromise = call.placeVoiceCall(); @@ -43,6 +45,31 @@ const startVideoCall = async (client: TestClient, call: MatrixCall): Promise { + const callPromise = call.initWithInvite({ + getContent: jest.fn().mockReturnValue({ + version, + call_id: "call_id", + party_id: "remote_party_id", + offer: { + sdp: DUMMY_SDP, + }, + }), + getSender: () => "@test:foo", + getLocalAge: () => null, + } as unknown as MatrixEvent); + call.getFeeds().push(new CallFeed({ + client: client.client, + userId: "remote_user_id", + // @ts-ignore Mock + stream: new MockMediaStream("remote_stream_id", [new MockMediaStreamTrack("remote_tack_id")]), + id: "remote_feed_id", + purpose: SDPStreamMetadataPurpose.Usermedia, + })); + await client.httpBackend.flush(""); + await callPromise; +}; + describe('Call', function() { let client; let call; @@ -60,7 +87,7 @@ describe('Call', function() { client = new TestClient("@alice:foo", "somedevice", "token", undefined, {}); // We just stub out sendEvent: we're not interested in testing the client's // event sending code here - client.client.sendEvent = () => {}; + client.client.sendEvent = jest.fn(); client.client.mediaHandler = new MockMediaHandler; client.client.getMediaHandler = () => client.client.mediaHandler; client.httpBackend.when("GET", "/voip/turnServer").respond(200, {}); @@ -74,7 +101,7 @@ describe('Call', function() { call = new MatrixCall({ client: client.client, - roomId: '!foo:bar', + roomId: FAKE_ROOM_ID, }); // call checks one of these is wired up call.on('error', () => {}); @@ -594,28 +621,7 @@ describe('Call', function() { }); it("should end call after receiving a select event with a different party id", async () => { - const callPromise = call.initWithInvite({ - getContent: () => ({ - version: 1, - call_id: "call_id", - party_id: "remote_party_id", - offer: { - sdp: DUMMY_SDP, - }, - }), - getSender: () => "@test:foo", - getLocalAge: () => null, - }); - call.feeds.push(new CallFeed({ - client, - userId: "remote_user_id", - // @ts-ignore Mock - stream: new MockMediaStream("remote_stream_id", [new MockMediaStreamTrack("remote_tack_id")]), - id: "remote_feed_id", - purpose: SDPStreamMetadataPurpose.Usermedia, - })); - await client.httpBackend.flush(); - await callPromise; + await fakeIncomingCall(client, call); const callHangupCallback = jest.fn(); call.on(CallEvent.Hangup, callHangupCallback); @@ -845,4 +851,70 @@ describe('Call', function() { }); }); }); + + describe("rejecting calls", () => { + it("sends hangup event when rejecting v0 calls", async () => { + await fakeIncomingCall(client, call, 0); + + call.reject(); + + expect(client.client.sendEvent).toHaveBeenCalledWith( + FAKE_ROOM_ID, + EventType.CallHangup, + expect.objectContaining({ + call_id: call.callId, + }), + ); + }); + + it("sends reject event when rejecting v1 calls", async () => { + await fakeIncomingCall(client, call, "1"); + + call.reject(); + + expect(client.client.sendEvent).toHaveBeenCalledWith( + FAKE_ROOM_ID, + EventType.CallReject, + expect.objectContaining({ + call_id: call.callId, + }), + ); + }); + + it("does not reject a call that has already been answered", async () => { + await fakeIncomingCall(client, call, "1"); + + await call.answer(); + + client.client.sendEvent.mockReset(); + + let caught = false; + try { + call.reject(); + } catch (e) { + caught = true; + } + + expect(caught).toEqual(true); + expect(client.client.sendEvent).not.toHaveBeenCalled(); + }); + + it("hangs up a call", async () => { + await fakeIncomingCall(client, call, "1"); + + await call.answer(); + + client.client.sendEvent.mockReset(); + + call.hangup(); + + expect(client.client.sendEvent).toHaveBeenCalledWith( + FAKE_ROOM_ID, + EventType.CallHangup, + expect.objectContaining({ + call_id: call.callId, + }), + ); + }); + }); }); From 4f54507c5d67862d452116a7ffdfe7d7576b4682 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 19 Aug 2022 18:52:22 +0100 Subject: [PATCH 2/2] Fix some bugs where we carried on with the call after it had been ended --- spec/unit/webrtc/call.spec.ts | 2 ++ src/webrtc/call.ts | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/spec/unit/webrtc/call.spec.ts b/spec/unit/webrtc/call.spec.ts index 6dd4c622af5..1cedd5c7cc4 100644 --- a/spec/unit/webrtc/call.spec.ts +++ b/spec/unit/webrtc/call.spec.ts @@ -897,6 +897,8 @@ describe('Call', function() { expect(caught).toEqual(true); expect(client.client.sendEvent).not.toHaveBeenCalled(); + + call.hangup(); }); it("hangs up a call", async () => { diff --git a/src/webrtc/call.ts b/src/webrtc/call.ts index 43ef311882f..8e14e06a880 100644 --- a/src/webrtc/call.ts +++ b/src/webrtc/call.ts @@ -1569,6 +1569,10 @@ export class MatrixCall extends TypedEventEmitter