Skip to content

Commit ae9a26d

Browse files
committed
Make it easier to mock call memberships for specific user IDs
1 parent b8903dd commit ae9a26d

File tree

6 files changed

+47
-72
lines changed

6 files changed

+47
-72
lines changed

spec/unit/matrixrtc/MatrixRTCSession.spec.ts

Lines changed: 20 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@ limitations under the License.
1616

1717
import { encodeBase64, EventType, MatrixClient, type MatrixError, type MatrixEvent, type Room } from "../../../src";
1818
import { KnownMembership } from "../../../src/@types/membership";
19-
import { DEFAULT_EXPIRE_DURATION, type SessionMembershipData } from "../../../src/matrixrtc/CallMembership";
19+
import { DEFAULT_EXPIRE_DURATION } from "../../../src/matrixrtc/CallMembership";
2020
import { MatrixRTCSession, MatrixRTCSessionEvent } from "../../../src/matrixrtc/MatrixRTCSession";
2121
import { type EncryptionKeysEventContent } from "../../../src/matrixrtc/types";
2222
import { secureRandomString } from "../../../src/randomstring";
23-
import { makeMockEvent, makeMockRoom, makeMockRoomState, membershipTemplate, makeKey } from "./mocks";
23+
import { makeMockEvent, makeMockRoom, membershipTemplate, makeKey, type MembershipData, mockRoomState } from "./mocks";
2424

2525
const mockFocus = { type: "mock" };
2626

@@ -47,7 +47,7 @@ describe("MatrixRTCSession", () => {
4747

4848
describe("roomSessionForRoom", () => {
4949
it("creates a room-scoped session from room state", () => {
50-
const mockRoom = makeMockRoom(membershipTemplate);
50+
const mockRoom = makeMockRoom([membershipTemplate]);
5151

5252
sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom);
5353
expect(sess?.memberships.length).toEqual(1);
@@ -74,7 +74,7 @@ describe("MatrixRTCSession", () => {
7474
});
7575

7676
it("ignores memberships events of members not in the room", () => {
77-
const mockRoom = makeMockRoom(membershipTemplate);
77+
const mockRoom = makeMockRoom([membershipTemplate]);
7878
mockRoom.hasMembershipState = (state) => state === KnownMembership.Join;
7979
sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom);
8080
expect(sess?.memberships.length).toEqual(0);
@@ -205,10 +205,11 @@ describe("MatrixRTCSession", () => {
205205
const mockFocus = { type: "livekit", livekit_service_url: "https://test.org" };
206206
const joinSessionConfig = {};
207207

208-
const sessionMembershipData: SessionMembershipData = {
208+
const sessionMembershipData: MembershipData = {
209209
call_id: "",
210210
scope: "m.room",
211211
application: "m.call",
212+
user_id: "@mock:user.example",
212213
device_id: "AAAAAAA_session",
213214
focus_active: mockFocus,
214215
foci_preferred: [mockFocus],
@@ -236,8 +237,8 @@ describe("MatrixRTCSession", () => {
236237
client._unstable_sendDelayedStateEvent = sendDelayedStateMock;
237238
});
238239

239-
async function testSession(membershipData: SessionMembershipData): Promise<void> {
240-
sess = MatrixRTCSession.roomSessionForRoom(client, makeMockRoom(membershipData));
240+
async function testSession(membershipData: MembershipData): Promise<void> {
241+
sess = MatrixRTCSession.roomSessionForRoom(client, makeMockRoom([membershipData]));
241242

242243
sess.joinRoomSession([mockFocus], mockFocus, joinSessionConfig);
243244
await Promise.race([sentStateEvent, new Promise((resolve) => setTimeout(resolve, 500))]);
@@ -432,7 +433,7 @@ describe("MatrixRTCSession", () => {
432433

433434
describe("onMembershipsChanged", () => {
434435
it("does not emit if no membership changes", () => {
435-
const mockRoom = makeMockRoom(membershipTemplate);
436+
const mockRoom = makeMockRoom([membershipTemplate]);
436437
sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom);
437438

438439
const onMembershipsChanged = jest.fn();
@@ -443,13 +444,13 @@ describe("MatrixRTCSession", () => {
443444
});
444445

445446
it("emits on membership changes", () => {
446-
const mockRoom = makeMockRoom(membershipTemplate);
447+
const mockRoom = makeMockRoom([membershipTemplate]);
447448
sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom);
448449

449450
const onMembershipsChanged = jest.fn();
450451
sess.on(MatrixRTCSessionEvent.MembershipsChanged, onMembershipsChanged);
451452

452-
mockRoom.getLiveTimeline().getState = jest.fn().mockReturnValue(makeMockRoomState([], mockRoom.roomId));
453+
mockRoomState(mockRoom, []);
453454
sess.onRTCSessionMemberUpdate();
454455

455456
expect(onMembershipsChanged).toHaveBeenCalled();
@@ -635,18 +636,14 @@ describe("MatrixRTCSession", () => {
635636
expect(sess!.statistics.counters.roomEventEncryptionKeysSent).toEqual(1);
636637

637638
// member2 leaves triggering key rotation
638-
mockRoom.getLiveTimeline().getState = jest
639-
.fn()
640-
.mockReturnValue(makeMockRoomState([membershipTemplate], mockRoom.roomId));
639+
mockRoomState(mockRoom, [membershipTemplate]);
641640
sess.onRTCSessionMemberUpdate();
642641

643642
// member2 re-joins which should trigger an immediate re-send
644643
const keysSentPromise2 = new Promise<EncryptionKeysEventContent>((resolve) => {
645644
sendEventMock.mockImplementation((_roomId, _evType, payload) => resolve(payload));
646645
});
647-
mockRoom.getLiveTimeline().getState = jest
648-
.fn()
649-
.mockReturnValue(makeMockRoomState([membershipTemplate, member2], mockRoom.roomId));
646+
mockRoomState(mockRoom, [membershipTemplate, member2]);
650647
sess.onRTCSessionMemberUpdate();
651648
// but, that immediate resend is throttled so we need to wait a bit
652649
jest.advanceTimersByTime(1000);
@@ -697,9 +694,7 @@ describe("MatrixRTCSession", () => {
697694
device_id: "BBBBBBB",
698695
});
699696

700-
mockRoom.getLiveTimeline().getState = jest
701-
.fn()
702-
.mockReturnValue(makeMockRoomState([membershipTemplate, member2], mockRoom.roomId));
697+
mockRoomState(mockRoom, [membershipTemplate, member2]);
703698
sess.onRTCSessionMemberUpdate();
704699

705700
await keysSentPromise2;
@@ -724,9 +719,7 @@ describe("MatrixRTCSession", () => {
724719
});
725720

726721
const mockRoom = makeMockRoom([member1, member2]);
727-
mockRoom.getLiveTimeline().getState = jest
728-
.fn()
729-
.mockReturnValue(makeMockRoomState([member1, member2], mockRoom.roomId));
722+
mockRoomState(mockRoom, [member1, member2]);
730723

731724
sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom);
732725
sess.joinRoomSession([mockFocus], mockFocus, { manageMediaKeys: true });
@@ -773,10 +766,6 @@ describe("MatrixRTCSession", () => {
773766
};
774767

775768
const mockRoom = makeMockRoom([member1, member2]);
776-
mockRoom.getLiveTimeline().getState = jest
777-
.fn()
778-
.mockReturnValue(makeMockRoomState([member1, member2], mockRoom.roomId));
779-
780769
sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom);
781770
sess.joinRoomSession([mockFocus], mockFocus, { manageMediaKeys: true });
782771

@@ -806,6 +795,7 @@ describe("MatrixRTCSession", () => {
806795

807796
// update created_ts
808797
member2.created_ts = 5000;
798+
mockRoomState(mockRoom, [member1, member2]);
809799

810800
const keysSentPromise2 = new Promise((resolve) => {
811801
sendEventMock.mockImplementation(resolve);
@@ -869,9 +859,7 @@ describe("MatrixRTCSession", () => {
869859
sendEventMock.mockImplementation((_roomId, _evType, payload) => resolve(payload));
870860
});
871861

872-
mockRoom.getLiveTimeline().getState = jest
873-
.fn()
874-
.mockReturnValue(makeMockRoomState([membershipTemplate], mockRoom.roomId));
862+
mockRoomState(mockRoom, [membershipTemplate]);
875863
sess.onRTCSessionMemberUpdate();
876864

877865
jest.advanceTimersByTime(KEY_DELAY);
@@ -900,7 +888,7 @@ describe("MatrixRTCSession", () => {
900888
it("wraps key index around to 0 when it reaches the maximum", async () => {
901889
// this should give us keys with index [0...255, 0, 1]
902890
const membersToTest = 258;
903-
const members: SessionMembershipData[] = [];
891+
const members: MembershipData[] = [];
904892
for (let i = 0; i < membersToTest; i++) {
905893
members.push(Object.assign({}, membershipTemplate, { device_id: `DEVICE${i}` }));
906894
}
@@ -920,11 +908,7 @@ describe("MatrixRTCSession", () => {
920908
sess.joinRoomSession([mockFocus], mockFocus, { manageMediaKeys: true });
921909
} else {
922910
// otherwise update the state reducing the membership each time in order to trigger key rotation
923-
mockRoom.getLiveTimeline().getState = jest
924-
.fn()
925-
.mockReturnValue(
926-
makeMockRoomState(members.slice(0, membersToTest - i), mockRoom.roomId),
927-
);
911+
mockRoomState(mockRoom, members.slice(0, membersToTest - i));
928912
}
929913

930914
sess!.onRTCSessionMemberUpdate();
@@ -965,9 +949,7 @@ describe("MatrixRTCSession", () => {
965949
device_id: "BBBBBBB",
966950
});
967951

968-
mockRoom.getLiveTimeline().getState = jest
969-
.fn()
970-
.mockReturnValue(makeMockRoomState([membershipTemplate, member2], mockRoom.roomId));
952+
mockRoomState(mockRoom, [membershipTemplate, member2]);
971953
sess.onRTCSessionMemberUpdate();
972954

973955
await new Promise((resolve) => {

spec/unit/matrixrtc/MatrixRTCSessionManager.spec.ts

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,10 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
import { type Mock } from "jest-mock";
18-
1917
import { ClientEvent, EventTimeline, MatrixClient } from "../../../src";
2018
import { RoomStateEvent } from "../../../src/models/room-state";
2119
import { MatrixRTCSessionManagerEvents } from "../../../src/matrixrtc/MatrixRTCSessionManager";
22-
import { makeMockRoom, makeMockRoomState, membershipTemplate } from "./mocks";
20+
import { makeMockRoom, membershipTemplate, mockRoomState } from "./mocks";
2321

2422
describe("MatrixRTCSessionManager", () => {
2523
let client: MatrixClient;
@@ -52,19 +50,16 @@ describe("MatrixRTCSessionManager", () => {
5250
it("Fires event when session ends", () => {
5351
const onEnded = jest.fn();
5452
client.matrixRTC.on(MatrixRTCSessionManagerEvents.SessionEnded, onEnded);
55-
const room1 = makeMockRoom(membershipTemplate);
53+
const room1 = makeMockRoom([membershipTemplate]);
5654
jest.spyOn(client, "getRooms").mockReturnValue([room1]);
5755
jest.spyOn(client, "getRoom").mockReturnValue(room1);
5856

5957
client.emit(ClientEvent.Room, room1);
6058

61-
(room1.getLiveTimeline as Mock).mockReturnValue({
62-
getState: jest.fn().mockReturnValue(makeMockRoomState([{}], room1.roomId)),
63-
});
59+
mockRoomState(room1, [{ user_id: membershipTemplate.user_id }]);
6460

6561
const roomState = room1.getLiveTimeline().getState(EventTimeline.FORWARDS)!;
6662
const membEvent = roomState.getStateEvents("")[0];
67-
6863
client.emit(RoomStateEvent.Events, membEvent, roomState, null);
6964

7065
expect(onEnded).toHaveBeenCalledWith(room1.roomId, client.matrixRTC.getActiveRoomSession(room1));

spec/unit/matrixrtc/MembershipManager.spec.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ describe.each([
8080
// Default to fake timers.
8181
jest.useFakeTimers();
8282
client = makeMockClient("@alice:example.org", "AAAAAAA");
83-
room = makeMockRoom(membershipTemplate);
83+
room = makeMockRoom([membershipTemplate]);
8484
// Provide a default mock that is like the default "non error" server behaviour.
8585
(client._unstable_sendDelayedStateEvent as Mock<any>).mockResolvedValue({ delay_id: "id" });
8686
(client._unstable_updateDelayedEvent as Mock<any>).mockResolvedValue(undefined);
@@ -403,11 +403,11 @@ describe.each([
403403
type: "livekit",
404404
},
405405
],
406-
device_id: client.getDeviceId(),
406+
user_id: client.getUserId()!,
407+
device_id: client.getDeviceId()!,
407408
created_ts: 1000,
408409
},
409410
room.roomId,
410-
client.getUserId()!,
411411
),
412412
);
413413
expect(manager.getActiveFocus()).toStrictEqual(focus);
@@ -449,7 +449,7 @@ describe.each([
449449

450450
await manager.onRTCSessionMemberUpdate([
451451
mockCallMembership(membershipTemplate, room.roomId),
452-
mockCallMembership(myMembership as SessionMembershipData, room.roomId, client.getUserId() ?? undefined),
452+
mockCallMembership({ ...myMembership as SessionMembershipData, user_id: client.getUserId()! }, room.roomId),
453453
]);
454454

455455
await jest.advanceTimersByTimeAsync(1);
@@ -766,7 +766,7 @@ describe.each([
766766

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

771771
const membershipManager = new MembershipManager(undefined, room, client, () => undefined, logger);
772772

spec/unit/matrixrtc/RoomAndToDeviceTransport.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ describe("RoomAndToDeviceTransport", () => {
8888
});
8989
it("only sends to device keys when sending a key", async () => {
9090
transport.start();
91-
await transport.sendKey("1235", 0, [mockCallMembership(membershipTemplate, roomId, "@alice:example.org")]);
91+
await transport.sendKey("1235", 0, [mockCallMembership({ ...membershipTemplate, user_id: '@alice:example.org' }, roomId)]);
9292
expect(toDeviceSendKeySpy).toHaveBeenCalledTimes(1);
9393
expect(roomSendKeySpy).toHaveBeenCalledTimes(0);
9494
expect(transport.enabled.room).toBeFalsy();
@@ -118,7 +118,7 @@ describe("RoomAndToDeviceTransport", () => {
118118
expect(transport.enabled.room).toBeTruthy();
119119
expect(transport.enabled.toDevice).toBeFalsy();
120120

121-
await transport.sendKey("1235", 0, [mockCallMembership(membershipTemplate, roomId, "@alice:example.org")]);
121+
await transport.sendKey("1235", 0, [mockCallMembership({ ...membershipTemplate, user_id: '@alice:example.org' }, roomId)]);
122122
expect(sendEventMock).toHaveBeenCalledTimes(1);
123123
expect(roomSendKeySpy).toHaveBeenCalledTimes(1);
124124
expect(toDeviceSendKeySpy).toHaveBeenCalledTimes(0);

spec/unit/matrixrtc/ToDeviceKeyTransport.spec.ts

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -62,19 +62,16 @@ describe("ToDeviceKeyTransport", () => {
6262
const keyIndex = 2;
6363
await transport.sendKey(keyBase64Encoded, keyIndex, [
6464
mockCallMembership(
65-
Object.assign({}, membershipTemplate, { device_id: "BOBDEVICE" }),
65+
{ ...membershipTemplate, user_id: "@bob:example.org", device_id: "BOBDEVICE" },
6666
roomId,
67-
"@bob:example.org",
6867
),
6968
mockCallMembership(
70-
Object.assign({}, membershipTemplate, { device_id: "CARLDEVICE" }),
69+
{ ...membershipTemplate, user_id: "@carl:example.org", device_id: "CARLDEVICE" },
7170
roomId,
72-
"@carl:example.org",
7371
),
7472
mockCallMembership(
75-
Object.assign({}, membershipTemplate, { device_id: "MATDEVICE" }),
73+
{ ...membershipTemplate, user_id: "@mat:example.org", device_id: "MATDEVICE" },
7674
roomId,
77-
"@mat:example.org",
7875
),
7976
]);
8077

@@ -154,9 +151,8 @@ describe("ToDeviceKeyTransport", () => {
154151
const keyIndex = 2;
155152
await transport.sendKey(keyBase64Encoded, keyIndex, [
156153
mockCallMembership(
157-
Object.assign({}, membershipTemplate, { device_id: "MYDEVICE" }),
154+
{ ...membershipTemplate, user_id: '@alice:example.org', device_id: 'MYDEVICE' },
158155
roomId,
159-
"@alice:example.org",
160156
),
161157
]);
162158

spec/unit/matrixrtc/mocks.ts

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,12 @@ import { EventType, type Room, RoomEvent, type MatrixClient, type MatrixEvent }
2020
import { CallMembership, type SessionMembershipData } from "../../../src/matrixrtc/CallMembership";
2121
import { secureRandomString } from "../../../src/randomstring";
2222

23-
type MembershipData = SessionMembershipData[] | SessionMembershipData | {};
23+
export type MembershipData = (SessionMembershipData | {}) & { user_id: string };
2424

25-
export const membershipTemplate: SessionMembershipData = {
25+
export const membershipTemplate: SessionMembershipData & { user_id: string } = {
2626
application: "m.call",
2727
call_id: "",
28+
user_id: "@mock:user.example",
2829
device_id: "AAAAAAA",
2930
scope: "m.room",
3031
focus_active: { type: "livekit", focus_selection: "oldest_membership" },
@@ -68,7 +69,7 @@ export function makeMockClient(userId: string, deviceId: string): MockClient {
6869
}
6970

7071
export function makeMockRoom(
71-
membershipData: MembershipData,
72+
membershipData: MembershipData[],
7273
): Room & { emitTimelineEvent: (event: MatrixEvent) => void } {
7374
const roomId = secureRandomString(8);
7475
// Caching roomState here so it does not get recreated when calling `getLiveTimeline.getState()`
@@ -87,10 +88,8 @@ export function makeMockRoom(
8788
});
8889
}
8990

90-
export function makeMockRoomState(membershipData: MembershipData, roomId: string) {
91-
const events = Array.isArray(membershipData)
92-
? membershipData.map((m) => mockRTCEvent(m, roomId))
93-
: [mockRTCEvent(membershipData, roomId)];
91+
function makeMockRoomState(membershipData: MembershipData[], roomId: string) {
92+
const events = membershipData.map((m) => mockRTCEvent(m, roomId));
9493
const keysAndEvents = events.map((e) => {
9594
const data = e.getContent() as SessionMembershipData;
9695
return [`_${e.sender?.userId}_${data.device_id}`];
@@ -120,6 +119,10 @@ export function makeMockRoomState(membershipData: MembershipData, roomId: string
120119
};
121120
}
122121

122+
export function mockRoomState(room: Room, membershipData: MembershipData[]): void {
123+
room.getLiveTimeline().getState = jest.fn().mockReturnValue(makeMockRoomState(membershipData, room.roomId));
124+
}
125+
123126
export function makeMockEvent(
124127
type: string,
125128
sender: string,
@@ -138,13 +141,12 @@ export function makeMockEvent(
138141
} as unknown as MatrixEvent;
139142
}
140143

141-
export function mockRTCEvent(membershipData: MembershipData, roomId: string, customSender?: string): MatrixEvent {
142-
const sender = customSender ?? "@mock:user.example";
144+
export function mockRTCEvent({ user_id: sender, ...membershipData }: MembershipData, roomId: string): MatrixEvent {
143145
return makeMockEvent(EventType.GroupCallMemberPrefix, sender, roomId, membershipData);
144146
}
145147

146-
export function mockCallMembership(membershipData: MembershipData, roomId: string, sender?: string): CallMembership {
147-
return new CallMembership(mockRTCEvent(membershipData, roomId, sender), membershipData);
148+
export function mockCallMembership(membershipData: MembershipData, roomId: string): CallMembership {
149+
return new CallMembership(mockRTCEvent(membershipData, roomId), membershipData);
148150
}
149151

150152
export function makeKey(id: number, key: string): { key: string; index: number } {

0 commit comments

Comments
 (0)