Skip to content
91 changes: 46 additions & 45 deletions spec/unit/matrixrtc/CallMembership.spec.ts

Large diffs are not rendered by default.

11 changes: 0 additions & 11 deletions spec/unit/matrixrtc/LivekitTransport.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ limitations under the License.

import {
isLivekitTransport,
isLivekitFocusSelection,
isLivekitTransportConfig,
} from "../../../src/matrixrtc/LivekitTransport";

Expand All @@ -40,16 +39,6 @@ describe("LivekitFocus", () => {
isLivekitTransport({ type: "livekit", livekit_service_url: "http://test.com", other_alias: "test" }),
).toBeFalsy();
});
it("isLivekitFocusActive", () => {
expect(
isLivekitFocusSelection({
type: "livekit",
focus_selection: "oldest_membership",
}),
).toBeTruthy();
expect(isLivekitFocusSelection({ type: "livekit" })).toBeFalsy();
expect(isLivekitFocusSelection({ type: "not-livekit", focus_selection: "oldest_membership" })).toBeFalsy();
});
it("isLivekitFocusConfig", () => {
expect(
isLivekitTransportConfig({
Expand Down
249 changes: 112 additions & 137 deletions spec/unit/matrixrtc/MatrixRTCSession.spec.ts

Large diffs are not rendered by default.

46 changes: 32 additions & 14 deletions spec/unit/matrixrtc/MatrixRTCSessionManager.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@ limitations under the License.
import { ClientEvent, EventTimeline, MatrixClient, type Room } from "../../../src";
import { RoomStateEvent } from "../../../src/models/room-state";
import { MatrixRTCSessionManager, MatrixRTCSessionManagerEvents } from "../../../src/matrixrtc/MatrixRTCSessionManager";
import { makeMockRoom, type MembershipData, membershipTemplate, mockRoomState, mockRTCEvent } from "./mocks";
import { makeMockRoom, type MembershipData, sessionMembershipTemplate, mockRoomState, mockRTCEvent, rtcMembershipTemplate } from "./mocks";
import { logger } from "../../../src/logger";
import { slotDescriptionToId } from "../../../src/matrixrtc";

describe.each([{ eventKind: "sticky" }, { eventKind: "memberState" }])(
describe.each([{ eventKind: "sticky" }, /*{ eventKind: "memberState" }*/])(
"MatrixRTCSessionManager ($eventKind)",
({ eventKind }) => {
let client: MatrixClient;
let membershipTemplate: MembershipData;

function sendLeaveMembership(room: Room, membershipData: MembershipData[]): void {
if (eventKind === "memberState") {
Expand All @@ -40,6 +42,7 @@ describe.each([{ eventKind: "sticky" }, { eventKind: "memberState" }])(
beforeEach(() => {
client = new MatrixClient({ baseUrl: "base_url" });
client.matrixRTC.start();
membershipTemplate = eventKind ? rtcMembershipTemplate : sessionMembershipTemplate;
});

afterEach(() => {
Expand Down Expand Up @@ -91,34 +94,46 @@ describe.each([{ eventKind: "sticky" }, { eventKind: "memberState" }])(
expect(onEnded).toHaveBeenCalledWith(room1.roomId, client.matrixRTC.getActiveRoomSession(room1));
});

it("Fires correctly with custom sessionDescription", () => {
it("Fires correctly with custom slotDescription", () => {
const onStarted = jest.fn();
const onEnded = jest.fn();
// create a session manager with a custom session description
const sessionManager = new MatrixRTCSessionManager(logger, client, {
const slotDescription = {
id: "test",
application: "m.notCall",
});
};
// create a session manager with a custom session description
const sessionManager = new MatrixRTCSessionManager(logger, client, slotDescription);

// 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");
// Create a session for applicaation m.other, we ignore this session because it has the wrong application type.
const room1MembershipData: MembershipData[] = eventKind === "sticky" ? [{ ...membershipTemplate, application: {
...rtcMembershipTemplate.application,
type: "m.call"
}}] : [{ ...membershipTemplate, application: "m.call" }];
const room1 = makeMockRoom(room1MembershipData, eventKind === "sticky", { application: "m.call", id: ""});
jest.spyOn(client, "getRooms").mockReturnValue([room1]);
client.emit(ClientEvent.Room, room1);
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");
const room2MembershipData: MembershipData[] = eventKind === "sticky" ? [
{ ...membershipTemplate, application: {
...rtcMembershipTemplate.application,
type: slotDescription.application,
}, slot_id: slotDescriptionToId(slotDescription) },
] : [{
...membershipTemplate,
application: slotDescription.application,
call_id: slotDescription.id,
}];
const room2 = makeMockRoom(room2MembershipData, eventKind === "sticky", slotDescription);
console.log({room2: room2.roomId})
jest.spyOn(client, "getRooms").mockReturnValue([room1, room2]);
client.emit(ClientEvent.Room, room2);
expect(onStarted).toHaveBeenCalled();
Expand All @@ -143,7 +158,10 @@ describe.each([{ eventKind: "sticky" }, { eventKind: "memberState" }])(
it("Doesn't fire event if unrelated sessions ends", () => {
const onEnded = jest.fn();
client.matrixRTC.on(MatrixRTCSessionManagerEvents.SessionEnded, onEnded);
const membership: MembershipData[] = [{ ...membershipTemplate, application: "m.other_app" }];
const membership: MembershipData[] = eventKind === "sticky" ? [{ ...membershipTemplate, application: {
...rtcMembershipTemplate.application,
type: "m.other_app",
}}] : [{ ...membershipTemplate, application: "m.other_app" }];
const room1 = makeMockRoom(membership, eventKind === "sticky");
jest.spyOn(client, "getRooms").mockReturnValue([room1]);
jest.spyOn(client, "getRoom").mockReturnValue(room1);
Expand Down
31 changes: 15 additions & 16 deletions spec/unit/matrixrtc/MembershipManager.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,10 @@ import {
MembershipManagerEvent,
Status,
type Transport,
type SessionMembershipData,
type LivekitFocusSelection,
} from "../../../src/matrixrtc";
import { makeMockClient, makeMockRoom, membershipTemplate, mockCallMembership, type MockClient } from "./mocks";
import { makeMockClient, makeMockRoom, sessionMembershipTemplate, mockCallMembership, type MockClient } from "./mocks";
import { MembershipManager, StickyEventMembershipManager } from "../../../src/matrixrtc/MembershipManager.ts";
import { SessionMembershipData } from "src/matrixrtc/membership/legacy.ts";

/**
* Create a promise that will resolve once a mocked method is called.
Expand Down Expand Up @@ -76,21 +75,21 @@ const callSession = { id: "", application: "m.call" };
describe("MembershipManager", () => {
let client: MockClient;
let room: Room;
const focusActive: LivekitFocusSelection = {
const focusActive = Object.freeze({
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TIL. Looks like this needs some strict mode? How is it defined? or maybe it is default?

focus_selection: "oldest_membership",
type: "livekit",
};
const focus: Transport = {
});
const focus: Transport = Object.freeze({
type: "livekit",
livekit_service_url: "https://active.url",
livekit_alias: "!active:active.url",
};
});

beforeEach(() => {
// Default to fake timers.
jest.useFakeTimers();
client = makeMockClient("@alice:example.org", "AAAAAAA");
room = makeMockRoom([membershipTemplate]);
room = makeMockRoom([sessionMembershipTemplate]);
// Provide a default mock that is like the default "non error" server behaviour.
(client._unstable_sendDelayedStateEvent as Mock<any>).mockResolvedValue({ delay_id: "id" });
(client._unstable_updateDelayedEvent as Mock<any>).mockResolvedValue(undefined);
Expand Down Expand Up @@ -350,7 +349,7 @@ describe("MembershipManager", () => {
const { resolve } = createAsyncHandle(client._unstable_sendDelayedStateEvent);
await jest.advanceTimersByTimeAsync(RESTART_DELAY);
// first simulate the sync, then resolve sending the delayed event.
await manager.onRTCSessionMemberUpdate([mockCallMembership(membershipTemplate, room.roomId)]);
await manager.onRTCSessionMemberUpdate([mockCallMembership(sessionMembershipTemplate, room.roomId)]);
resolve({ delay_id: "id" });
// Let the scheduler run one iteration so that the new join gets sent
await jest.runOnlyPendingTimersAsync();
Expand Down Expand Up @@ -433,7 +432,7 @@ describe("MembershipManager", () => {
describe("onRTCSessionMemberUpdate()", () => {
it("does nothing if not joined", async () => {
const manager = new MembershipManager({}, room, client, callSession);
await manager.onRTCSessionMemberUpdate([mockCallMembership(membershipTemplate, room.roomId)]);
await manager.onRTCSessionMemberUpdate([mockCallMembership(sessionMembershipTemplate, room.roomId)]);
await jest.advanceTimersToNextTimerAsync();
expect(client.sendStateEvent).not.toHaveBeenCalled();
expect(client._unstable_sendDelayedStateEvent).not.toHaveBeenCalled();
Expand All @@ -450,7 +449,7 @@ describe("MembershipManager", () => {
(client._unstable_sendDelayedStateEvent as Mock).mockClear();

await manager.onRTCSessionMemberUpdate([
mockCallMembership(membershipTemplate, room.roomId),
mockCallMembership(sessionMembershipTemplate, room.roomId),
mockCallMembership(
{ ...(myMembership as SessionMembershipData), user_id: client.getUserId()! },
room.roomId,
Expand All @@ -473,7 +472,7 @@ describe("MembershipManager", () => {
(client._unstable_sendDelayedStateEvent as Mock).mockClear();

// Our own membership is removed:
await manager.onRTCSessionMemberUpdate([mockCallMembership(membershipTemplate, room.roomId)]);
await manager.onRTCSessionMemberUpdate([mockCallMembership(sessionMembershipTemplate, room.roomId)]);
await jest.advanceTimersByTimeAsync(1);
expect(client.sendStateEvent).toHaveBeenCalled();
expect(client._unstable_sendDelayedStateEvent).toHaveBeenCalled();
Expand All @@ -496,7 +495,7 @@ describe("MembershipManager", () => {

const { resolve } = createAsyncHandle(client._unstable_sendDelayedStateEvent);
await jest.advanceTimersByTimeAsync(10_000);
await manager.onRTCSessionMemberUpdate([mockCallMembership(membershipTemplate, room.roomId)]);
await manager.onRTCSessionMemberUpdate([mockCallMembership(sessionMembershipTemplate, room.roomId)]);
resolve({ delay_id: "id" });
await jest.advanceTimersByTimeAsync(10_000);

Expand Down Expand Up @@ -865,7 +864,7 @@ describe("MembershipManager", () => {
const manager = new MembershipManager({}, room, client, callSession);
manager.join([]);
expect(manager.isActivated()).toEqual(true);
const membership = mockCallMembership({ ...membershipTemplate, user_id: client.getUserId()! }, room.roomId);
const membership = mockCallMembership({ ...sessionMembershipTemplate, user_id: client.getUserId()! }, room.roomId);
await manager.onRTCSessionMemberUpdate([membership]);
await manager.updateCallIntent("video");
expect(client.sendStateEvent).toHaveBeenCalledTimes(2);
Expand All @@ -879,7 +878,7 @@ describe("MembershipManager", () => {
manager.join([]);
expect(manager.isActivated()).toEqual(true);
const membership = mockCallMembership(
{ ...membershipTemplate, "user_id": client.getUserId()!, "m.call.intent": "video" },
{ ...sessionMembershipTemplate, "user_id": client.getUserId()!, "m.call.intent": "video" },
room.roomId,
);
await manager.onRTCSessionMemberUpdate([membership]);
Expand Down Expand Up @@ -948,7 +947,7 @@ describe("MembershipManager", () => {

it("Should prefix log with MembershipManager used", () => {
const client = makeMockClient("@alice:example.org", "AAAAAAA");
const room = makeMockRoom([membershipTemplate]);
const room = makeMockRoom([sessionMembershipTemplate]);

const membershipManager = new MembershipManager(undefined, room, client, callSession);

Expand Down
4 changes: 2 additions & 2 deletions spec/unit/matrixrtc/RTCEncryptionManager.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { RTCEncryptionManager } from "../../../src/matrixrtc/RTCEncryptionManage
import { type CallMembership, type Statistics } from "../../../src/matrixrtc";
import { type ToDeviceKeyTransport } from "../../../src/matrixrtc/ToDeviceKeyTransport.ts";
import { KeyTransportEvents, type KeyTransportEventsHandlerMap } from "../../../src/matrixrtc/IKeyTransport.ts";
import { membershipTemplate, mockCallMembership } from "./mocks.ts";
import { sessionMembershipTemplate, mockCallMembership } from "./mocks.ts";
import { decodeBase64, TypedEventEmitter } from "../../../src";
import { RoomAndToDeviceTransport } from "../../../src/matrixrtc/RoomAndToDeviceKeyTransport.ts";
import { type RoomKeyTransport } from "../../../src/matrixrtc/RoomKeyTransport.ts";
Expand Down Expand Up @@ -864,7 +864,7 @@ describe("RTCEncryptionManager", () => {

function aCallMembership(userId: string, deviceId: string, ts: number = 1000): CallMembership {
return mockCallMembership(
{ ...membershipTemplate, user_id: userId, device_id: deviceId, created_ts: ts },
{ ...sessionMembershipTemplate, user_id: userId, device_id: deviceId, created_ts: ts },
"!room:id",
);
}
Expand Down
4 changes: 2 additions & 2 deletions spec/unit/matrixrtc/RoomKeyTransport.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

import { makeMockEvent, makeMockRoom, membershipTemplate, makeKey } from "./mocks";
import { makeMockEvent, makeMockRoom, sessionMembershipTemplate, makeKey } from "./mocks";
import { RoomKeyTransport } from "../../../src/matrixrtc/RoomKeyTransport";
import { KeyTransportEvents } from "../../../src/matrixrtc/IKeyTransport";
import { EventType, MatrixClient, RoomEvent } from "../../../src";
Expand Down Expand Up @@ -48,7 +48,7 @@ describe("RoomKeyTransport", () => {
roomEventEncryptionKeysReceivedTotalAge: 0,
},
};
room = makeMockRoom([membershipTemplate]);
room = makeMockRoom([sessionMembershipTemplate]);
client = new MatrixClient({ baseUrl: "base_url" });
client.matrixRTC.start();
transport = new RoomKeyTransport(room, client, statistics, {
Expand Down
Loading