4b89fb23c5
* deprecate membershipID -> memberId & memberId -> stateKey in membership manager The membership manager used the memberId label for the stateKey. But only the StickymembershipManager really has a configurable memberId. * participantId -> callMembershipIdentityParts The participantId is a termonology from livekit. We do not want it in here! We want the js-sdk to be mostly transport agnostic. We do the transition from the identity parts to the acutal livekit identity in Element call (`sha256(userId+deviceId+memberId)`) * update tests * Expose `kind` to decide if we use the hashed or non hashed livekit participants. * expose delayId from the matrixRTCSession for delayed event delegation. * rename if to mapKey * backandId computation as part of the js-sdk * review valere * valr + timo keysWithoutMatchingRTCMembership * fix legacy encryption manager * fix doc issue * fix doc * fix imports * Encryption Manager needs own rtcBackendIdentity to use The encryption manager needs to signal our own key fast, cannot wait for remote echo of rtc membership. So it needs to be able to compute the rtcBackendIdentity * fix test * Remove double `useHashedRtcBackendIdentity` assignment. rename variables. * little improvements This stops the usage from the matrix event outside the CallMemerbship constructor. * fix logger import * Add back deprecated API for compat * Make change to CallMembership constructor backward compatible * more backward compatible --------- Co-authored-by: Valere <bill.carson@valrsoft.com>
163 lines
7.7 KiB
TypeScript
163 lines
7.7 KiB
TypeScript
/*
|
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
|
|
|
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.
|
|
*/
|
|
|
|
import { ClientEvent, EventTimeline, MatrixClient, type Room, RoomStateEvent } from "../../../src";
|
|
import { MatrixRTCSessionManager, MatrixRTCSessionManagerEvents } from "../../../src/matrixrtc";
|
|
import { makeMockRoom, type MembershipData, membershipTemplate, mockRoomState, mockRTCEvent } from "./mocks";
|
|
import { logger } from "../../../src/logger";
|
|
import { flushPromises } from "../../test-utils/flushPromises";
|
|
|
|
describe.each([{ eventKind: "sticky" }, { eventKind: "memberState" }])(
|
|
"MatrixRTCSessionManager ($eventKind)",
|
|
({ eventKind }) => {
|
|
let client: MatrixClient;
|
|
|
|
async function sendLeaveMembership(room: Room, membershipData: MembershipData[]): Promise<void> {
|
|
if (eventKind === "memberState") {
|
|
mockRoomState(room, [{ user_id: membershipTemplate.user_id }]);
|
|
const roomState = room.getLiveTimeline().getState(EventTimeline.FORWARDS)!;
|
|
const membEvent = roomState.getStateEvents("org.matrix.msc3401.call.member")[0];
|
|
client.emit(RoomStateEvent.Events, membEvent, roomState, null);
|
|
} else {
|
|
membershipData.splice(0, 1, { user_id: membershipTemplate.user_id });
|
|
client.emit(ClientEvent.Event, mockRTCEvent(membershipData[0], room.roomId, 10000));
|
|
}
|
|
await flushPromises();
|
|
}
|
|
|
|
beforeEach(() => {
|
|
client = new MatrixClient({ baseUrl: "base_url" });
|
|
client.matrixRTC.start();
|
|
});
|
|
|
|
afterEach(() => {
|
|
client.stopClient();
|
|
client.matrixRTC.stop();
|
|
});
|
|
|
|
it("Fires event when session starts", async () => {
|
|
const onStarted = jest.fn();
|
|
client.matrixRTC.on(MatrixRTCSessionManagerEvents.SessionStarted, onStarted);
|
|
|
|
try {
|
|
const room1 = makeMockRoom([membershipTemplate], eventKind === "sticky");
|
|
jest.spyOn(client, "getRooms").mockReturnValue([room1]);
|
|
|
|
client.emit(ClientEvent.Room, room1);
|
|
await flushPromises();
|
|
expect(onStarted).toHaveBeenCalledWith(room1.roomId, client.matrixRTC.getActiveRoomSession(room1));
|
|
} finally {
|
|
client.matrixRTC.off(MatrixRTCSessionManagerEvents.SessionStarted, onStarted);
|
|
}
|
|
});
|
|
|
|
it("Doesn't fire event if unrelated sessions starts", () => {
|
|
const onStarted = jest.fn();
|
|
client.matrixRTC.on(MatrixRTCSessionManagerEvents.SessionStarted, onStarted);
|
|
|
|
try {
|
|
const room1 = makeMockRoom([{ ...membershipTemplate, application: "m.other" }], eventKind === "sticky");
|
|
jest.spyOn(client, "getRooms").mockReturnValue([room1]);
|
|
|
|
client.emit(ClientEvent.Room, room1);
|
|
expect(onStarted).not.toHaveBeenCalled();
|
|
} finally {
|
|
client.matrixRTC.off(MatrixRTCSessionManagerEvents.SessionStarted, onStarted);
|
|
}
|
|
});
|
|
|
|
it("Fires event when session ends", async () => {
|
|
const onEnded = jest.fn();
|
|
client.matrixRTC.on(MatrixRTCSessionManagerEvents.SessionEnded, onEnded);
|
|
const membershipData: MembershipData[] = [membershipTemplate];
|
|
const room1 = makeMockRoom(membershipData, eventKind === "sticky");
|
|
jest.spyOn(client, "getRooms").mockReturnValue([room1]);
|
|
jest.spyOn(client, "getRoom").mockReturnValue(room1);
|
|
client.emit(ClientEvent.Room, room1);
|
|
await flushPromises();
|
|
await sendLeaveMembership(room1, membershipData);
|
|
|
|
expect(onEnded).toHaveBeenCalledWith(room1.roomId, client.matrixRTC.getActiveRoomSession(room1));
|
|
});
|
|
|
|
it("Fires correctly with custom sessionDescription", async () => {
|
|
const onStarted = jest.fn();
|
|
const onEnded = jest.fn();
|
|
// create a session manager with a custom session description
|
|
const sessionManager = new MatrixRTCSessionManager(logger, client, {
|
|
id: "test",
|
|
application: "m.notCall",
|
|
});
|
|
|
|
// manually start the session manager (its not the default one started by the client)
|
|
sessionManager.start();
|
|
sessionManager.on(MatrixRTCSessionManagerEvents.SessionEnded, onEnded);
|
|
sessionManager.on(MatrixRTCSessionManagerEvents.SessionStarted, onStarted);
|
|
|
|
try {
|
|
// Create a session for applicaation m.other, we ignore this session ecause it lacks a call_id
|
|
const room1MembershipData: MembershipData[] = [{ ...membershipTemplate, application: "m.other" }];
|
|
const room1 = makeMockRoom(room1MembershipData, eventKind === "sticky");
|
|
jest.spyOn(client, "getRooms").mockReturnValue([room1]);
|
|
client.emit(ClientEvent.Room, room1);
|
|
await flushPromises();
|
|
expect(onStarted).not.toHaveBeenCalled();
|
|
onStarted.mockClear();
|
|
|
|
// Create a session for applicaation m.notCall. We expect this call to be tracked because it has a call_id
|
|
const room2MembershipData: MembershipData[] = [
|
|
{ ...membershipTemplate, application: "m.notCall", call_id: "test" },
|
|
];
|
|
const room2 = makeMockRoom(room2MembershipData, eventKind === "sticky");
|
|
jest.spyOn(client, "getRooms").mockReturnValue([room1, room2]);
|
|
client.emit(ClientEvent.Room, room2);
|
|
await flushPromises();
|
|
expect(onStarted).toHaveBeenCalled();
|
|
onStarted.mockClear();
|
|
|
|
// Stop room1's RTC session. Tracked.
|
|
jest.spyOn(client, "getRoom").mockReturnValue(room2);
|
|
await sendLeaveMembership(room2, room2MembershipData);
|
|
expect(onEnded).toHaveBeenCalled();
|
|
onEnded.mockClear();
|
|
|
|
// Stop room1's RTC session. Not tracked.
|
|
jest.spyOn(client, "getRoom").mockReturnValue(room1);
|
|
await sendLeaveMembership(room1, room1MembershipData);
|
|
expect(onEnded).not.toHaveBeenCalled();
|
|
} finally {
|
|
client.matrixRTC.off(MatrixRTCSessionManagerEvents.SessionStarted, onStarted);
|
|
client.matrixRTC.off(MatrixRTCSessionManagerEvents.SessionEnded, onEnded);
|
|
}
|
|
});
|
|
|
|
it("Doesn't fire event if unrelated sessions ends", async () => {
|
|
const onEnded = jest.fn();
|
|
client.matrixRTC.on(MatrixRTCSessionManagerEvents.SessionEnded, onEnded);
|
|
const membership: MembershipData[] = [{ ...membershipTemplate, application: "m.other_app" }];
|
|
const room1 = makeMockRoom(membership, eventKind === "sticky");
|
|
jest.spyOn(client, "getRooms").mockReturnValue([room1]);
|
|
jest.spyOn(client, "getRoom").mockReturnValue(room1);
|
|
|
|
client.emit(ClientEvent.Room, room1);
|
|
|
|
await sendLeaveMembership(room1, membership);
|
|
|
|
expect(onEnded).not.toHaveBeenCalledWith(room1.roomId, client.matrixRTC.getActiveRoomSession(room1));
|
|
});
|
|
},
|
|
);
|