1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-11-23 17:02:25 +03:00

Cleanup tests

This commit is contained in:
Half-Shot
2025-11-06 10:38:27 +00:00
parent 7b39170368
commit dcf7e87799
6 changed files with 273 additions and 127 deletions

View File

@@ -16,14 +16,14 @@ limitations under the License.
import { SessionMembershipData } from "../../../src/matrixrtc/membership/legacy"; import { SessionMembershipData } from "../../../src/matrixrtc/membership/legacy";
import { EventType, type MatrixEvent } from "../../../src"; import { EventType, type MatrixEvent } from "../../../src";
import { import { CallMembership, DEFAULT_EXPIRE_DURATION } from "../../../src/matrixrtc/CallMembership";
CallMembership,
DEFAULT_EXPIRE_DURATION,
} from "../../../src/matrixrtc/CallMembership";
import { sessionMembershipTemplate } from "./mocks"; import { sessionMembershipTemplate } from "./mocks";
import { RtcMembershipData } from "../../../src/matrixrtc/membership/rtc"; import { RtcMembershipData } from "../../../src/matrixrtc/membership/rtc";
function makeMockEvent(eventType: EventType.RTCMembership|EventType.GroupCallMemberPrefix, originTs = 0): MatrixEvent { function makeMockEvent(
eventType: EventType.RTCMembership | EventType.GroupCallMemberPrefix,
originTs = 0,
): MatrixEvent {
return { return {
getTs: jest.fn().mockReturnValue(originTs), getTs: jest.fn().mockReturnValue(originTs),
getSender: jest.fn().mockReturnValue("@alice:example.org"), getSender: jest.fn().mockReturnValue("@alice:example.org"),
@@ -54,24 +54,36 @@ describe("CallMembership", () => {
it("rejects membership with no device_id", () => { it("rejects membership with no device_id", () => {
expect(() => { expect(() => {
new CallMembership(makeMockEvent(EventType.GroupCallMemberPrefix), Object.assign({}, membershipTemplate, { device_id: undefined })); new CallMembership(
makeMockEvent(EventType.GroupCallMemberPrefix),
Object.assign({}, membershipTemplate, { device_id: undefined }),
);
}).toThrow(); }).toThrow();
}); });
it("rejects membership with no call_id", () => { it("rejects membership with no call_id", () => {
expect(() => { expect(() => {
new CallMembership(makeMockEvent(EventType.GroupCallMemberPrefix), Object.assign({}, membershipTemplate, { call_id: undefined })); new CallMembership(
makeMockEvent(EventType.GroupCallMemberPrefix),
Object.assign({}, membershipTemplate, { call_id: undefined }),
);
}).toThrow(); }).toThrow();
}); });
it("allow membership with no scope", () => { it("allow membership with no scope", () => {
expect(() => { expect(() => {
new CallMembership(makeMockEvent(EventType.GroupCallMemberPrefix), Object.assign({}, membershipTemplate, { scope: undefined })); new CallMembership(
makeMockEvent(EventType.GroupCallMemberPrefix),
Object.assign({}, membershipTemplate, { scope: undefined }),
);
}).not.toThrow(); }).not.toThrow();
}); });
it("uses event timestamp if no created_ts", () => { it("uses event timestamp if no created_ts", () => {
const membership = new CallMembership(makeMockEvent(EventType.GroupCallMemberPrefix, 12345), membershipTemplate); const membership = new CallMembership(
makeMockEvent(EventType.GroupCallMemberPrefix, 12345),
membershipTemplate,
);
expect(membership.createdTs()).toEqual(12345); expect(membership.createdTs()).toEqual(12345);
}); });
@@ -104,7 +116,10 @@ describe("CallMembership", () => {
describe("getTransport", () => { describe("getTransport", () => {
const mockFocus = { type: "this_is_a_mock_focus" }; const mockFocus = { type: "this_is_a_mock_focus" };
const oldestMembership = new CallMembership(makeMockEvent(EventType.GroupCallMemberPrefix), membershipTemplate); const oldestMembership = new CallMembership(
makeMockEvent(EventType.GroupCallMemberPrefix),
membershipTemplate,
);
it("gets the correct active transport with oldest_membership", () => { it("gets the correct active transport with oldest_membership", () => {
const membership = new CallMembership(makeMockEvent(EventType.GroupCallMemberPrefix), { const membership = new CallMembership(makeMockEvent(EventType.GroupCallMemberPrefix), {
...membershipTemplate, ...membershipTemplate,
@@ -193,23 +208,35 @@ describe("CallMembership", () => {
it("rejects membership with no slot_id", () => { it("rejects membership with no slot_id", () => {
expect(() => { expect(() => {
new CallMembership(makeMockEvent(EventType.RTCMembership), { ...membershipTemplate, slot_id: undefined }); new CallMembership(makeMockEvent(EventType.RTCMembership), {
...membershipTemplate,
slot_id: undefined,
});
}).toThrow(); }).toThrow();
}); });
it("rejects membership with invalid slot_id", () => { it("rejects membership with invalid slot_id", () => {
expect(() => { expect(() => {
new CallMembership(makeMockEvent(EventType.RTCMembership), { ...membershipTemplate, slot_id: "invalid_slot_id" }); new CallMembership(makeMockEvent(EventType.RTCMembership), {
...membershipTemplate,
slot_id: "invalid_slot_id",
});
}).toThrow(); }).toThrow();
}); });
it("accepts membership with valid slot_id", () => { it("accepts membership with valid slot_id", () => {
expect(() => { expect(() => {
new CallMembership(makeMockEvent(EventType.RTCMembership), { ...membershipTemplate, slot_id: "m.call#" }); new CallMembership(makeMockEvent(EventType.RTCMembership), {
...membershipTemplate,
slot_id: "m.call#",
});
}).not.toThrow(); }).not.toThrow();
}); });
it("rejects membership with no application", () => { it("rejects membership with no application", () => {
expect(() => { expect(() => {
new CallMembership(makeMockEvent(EventType.RTCMembership), { ...membershipTemplate, application: undefined }); new CallMembership(makeMockEvent(EventType.RTCMembership), {
...membershipTemplate,
application: undefined,
});
}).toThrow(); }).toThrow();
}); });
@@ -224,13 +251,19 @@ describe("CallMembership", () => {
it("rejects membership with no member", () => { it("rejects membership with no member", () => {
expect(() => { expect(() => {
new CallMembership(makeMockEvent(EventType.RTCMembership), { ...membershipTemplate, member: undefined }); new CallMembership(makeMockEvent(EventType.RTCMembership), {
...membershipTemplate,
member: undefined,
});
}).toThrow(); }).toThrow();
}); });
it("rejects membership with incorrect member", () => { it("rejects membership with incorrect member", () => {
expect(() => { expect(() => {
new CallMembership(makeMockEvent(EventType.RTCMembership), { ...membershipTemplate, member: { i: "test" } }); new CallMembership(makeMockEvent(EventType.RTCMembership), {
...membershipTemplate,
member: { i: "test" },
});
}).toThrow(); }).toThrow();
expect(() => { expect(() => {
new CallMembership(makeMockEvent(EventType.RTCMembership), { new CallMembership(makeMockEvent(EventType.RTCMembership), {
@@ -276,7 +309,10 @@ describe("CallMembership", () => {
}); });
}).not.toThrow(); }).not.toThrow();
expect(() => { expect(() => {
new CallMembership(makeMockEvent(EventType.RTCMembership), { ...membershipTemplate, msc4354_sticky_key: undefined }); new CallMembership(makeMockEvent(EventType.RTCMembership), {
...membershipTemplate,
msc4354_sticky_key: undefined,
});
}).toThrow(); }).toThrow();
expect(() => { expect(() => {
new CallMembership(makeMockEvent(EventType.RTCMembership), { new CallMembership(makeMockEvent(EventType.RTCMembership), {

View File

@@ -14,10 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import { import { isLivekitTransport, isLivekitTransportConfig } from "../../../src/matrixrtc/LivekitTransport";
isLivekitTransport,
isLivekitTransportConfig,
} from "../../../src/matrixrtc/LivekitTransport";
describe("LivekitFocus", () => { describe("LivekitFocus", () => {
it("isLivekitFocus", () => { it("isLivekitFocus", () => {

View File

@@ -36,7 +36,6 @@ import {
type MembershipData, type MembershipData,
mockRoomState, mockRoomState,
mockRTCEvent, mockRTCEvent,
testStickyDurationMs,
} from "./mocks"; } from "./mocks";
import { RTCEncryptionManager } from "../../../src/matrixrtc/RTCEncryptionManager.ts"; import { RTCEncryptionManager } from "../../../src/matrixrtc/RTCEncryptionManager.ts";
import { RoomStickyEventsEvent, type StickyMatrixEvent } from "../../../src/models/room-sticky-events.ts"; import { RoomStickyEventsEvent, type StickyMatrixEvent } from "../../../src/models/room-sticky-events.ts";
@@ -98,7 +97,9 @@ describe("MatrixRTCSession", () => {
"sessionForSlot listenForSticky=$listenForStickyEvents listenForMemberStateEvents=$listenForMemberStateEvents testCreateSticky=$testCreateSticky", "sessionForSlot listenForSticky=$listenForStickyEvents listenForMemberStateEvents=$listenForMemberStateEvents testCreateSticky=$testCreateSticky",
(testConfig) => { (testConfig) => {
it(`will ${testConfig.listenForMemberStateEvents ? "" : "NOT"} throw if the room does not have any state stored`, () => { it(`will ${testConfig.listenForMemberStateEvents ? "" : "NOT"} throw if the room does not have any state stored`, () => {
const membershipTemplate = testConfig.testCreateSticky ? rtcMembershipTemplate : sessionMembershipTemplate; const membershipTemplate = testConfig.testCreateSticky
? rtcMembershipTemplate
: sessionMembershipTemplate;
const mockRoom = makeMockRoom([membershipTemplate], testConfig.testCreateSticky); const mockRoom = makeMockRoom([membershipTemplate], testConfig.testCreateSticky);
mockRoom.getLiveTimeline.mockReturnValue({ mockRoom.getLiveTimeline.mockReturnValue({
getState: jest.fn().mockReturnValue(undefined), getState: jest.fn().mockReturnValue(undefined),
@@ -117,7 +118,9 @@ describe("MatrixRTCSession", () => {
}); });
it("creates a room-scoped session from room state", () => { it("creates a room-scoped session from room state", () => {
const membershipTemplate = testConfig.testCreateSticky ? rtcMembershipTemplate : sessionMembershipTemplate; const membershipTemplate = testConfig.testCreateSticky
? rtcMembershipTemplate
: sessionMembershipTemplate;
const mockRoom = makeMockRoom([membershipTemplate], testConfig.testCreateSticky); const mockRoom = makeMockRoom([membershipTemplate], testConfig.testCreateSticky);
sess = MatrixRTCSession.sessionForSlot( sess = MatrixRTCSession.sessionForSlot(
@@ -136,14 +139,16 @@ describe("MatrixRTCSession", () => {
}); });
it("ignores memberships where application is not m.call", () => { it("ignores memberships where application is not m.call", () => {
const testMembership = testConfig.testCreateSticky ? { const testMembership = testConfig.testCreateSticky
...rtcMembershipTemplate, ? {
slot_id: "not-m.call#", ...rtcMembershipTemplate,
application: { slot_id: "not-m.call#",
...rtcMembershipTemplate.application, application: {
type: "not-m.call", ...rtcMembershipTemplate.application,
}, type: "not-m.call",
} : { ...sessionMembershipTemplate, application: "not-m.call#" } },
}
: { ...sessionMembershipTemplate, application: "not-m.call#" };
const mockRoom = makeMockRoom([testMembership], testConfig.testCreateSticky); const mockRoom = makeMockRoom([testMembership], testConfig.testCreateSticky);
const sess = MatrixRTCSession.sessionForSlot( const sess = MatrixRTCSession.sessionForSlot(
client, client,
@@ -155,14 +160,16 @@ describe("MatrixRTCSession", () => {
}); });
it("ignores memberships where callId is not empty", () => { it("ignores memberships where callId is not empty", () => {
const testMembership = testConfig.testCreateSticky ? { const testMembership = testConfig.testCreateSticky
...rtcMembershipTemplate, ? {
slot_id: "m.call#foobar", ...rtcMembershipTemplate,
application: { slot_id: "m.call#foobar",
...rtcMembershipTemplate.application, application: {
"m.call.id": "foobar" ...rtcMembershipTemplate.application,
}, "m.call.id": "foobar",
} : { ...sessionMembershipTemplate, application: "m.call", call_id: "foobar" } },
}
: { ...sessionMembershipTemplate, application: "m.call", call_id: "foobar" };
const mockRoom = makeMockRoom([testMembership], testConfig.testCreateSticky); const mockRoom = makeMockRoom([testMembership], testConfig.testCreateSticky);
const sess = MatrixRTCSession.sessionForSlot( const sess = MatrixRTCSession.sessionForSlot(
client, client,
@@ -175,21 +182,29 @@ describe("MatrixRTCSession", () => {
it("ignores expired memberships events", () => { it("ignores expired memberships events", () => {
jest.useFakeTimers(); jest.useFakeTimers();
const expiredMembership = testConfig.testCreateSticky ? { const expiredMembership = testConfig.testCreateSticky
...rtcMembershipTemplate, ? {
slot_id: "m.call#foobar", ...rtcMembershipTemplate,
member: { slot_id: "m.call#foobar",
...rtcMembershipTemplate.member, member: {
claimed_device_id: "EXPIRED", ...rtcMembershipTemplate.member,
}, claimed_device_id: "EXPIRED",
application: { },
...rtcMembershipTemplate.application, application: {
"m.call.id": "foobar" ...rtcMembershipTemplate.application,
}, "m.call.id": "foobar",
__test_sticky_expiry: 3000, },
} : { ...sessionMembershipTemplate, device_id: "EXPIRED", expires: 3000 } __test_sticky_expiry: 3000,
}
: { ...sessionMembershipTemplate, device_id: "EXPIRED", expires: 3000 };
jest.setSystemTime(0); jest.setSystemTime(0);
const mockRoom = makeMockRoom([testConfig.testCreateSticky ? rtcMembershipTemplate : sessionMembershipTemplate, expiredMembership], testConfig.testCreateSticky); const mockRoom = makeMockRoom(
[
testConfig.testCreateSticky ? rtcMembershipTemplate : sessionMembershipTemplate,
expiredMembership,
],
testConfig.testCreateSticky,
);
jest.advanceTimersByTime(3000); jest.advanceTimersByTime(3000);
sess = MatrixRTCSession.sessionForSlot( sess = MatrixRTCSession.sessionForSlot(
@@ -204,7 +219,10 @@ describe("MatrixRTCSession", () => {
}); });
it("ignores memberships events of members not in the room", () => { it("ignores memberships events of members not in the room", () => {
const mockRoom = makeMockRoom([sessionMembershipTemplate], testConfig.testCreateSticky); const membershipTemplate = testConfig.testCreateSticky
? rtcMembershipTemplate
: sessionMembershipTemplate;
const mockRoom = makeMockRoom([membershipTemplate], testConfig.testCreateSticky);
mockRoom.hasMembershipState.mockImplementation((state) => state === KnownMembership.Join); mockRoom.hasMembershipState.mockImplementation((state) => state === KnownMembership.Join);
sess = MatrixRTCSession.sessionForSlot( sess = MatrixRTCSession.sessionForSlot(
client, client,
@@ -217,7 +235,10 @@ describe("MatrixRTCSession", () => {
it("ignores memberships events with no sender", () => { it("ignores memberships events with no sender", () => {
// Force the sender to be undefined. // Force the sender to be undefined.
const mockRoom = makeMockRoom([{ ...sessionMembershipTemplate, user_id: "" }], testConfig.testCreateSticky); const membershipTemplate = testConfig.testCreateSticky
? { ...rtcMembershipTemplate, member: { claimed_user_id: "" } }
: { ...sessionMembershipTemplate, user_id: "" };
const mockRoom = makeMockRoom([membershipTemplate], testConfig.testCreateSticky);
mockRoom.hasMembershipState.mockImplementation((state) => state === KnownMembership.Join); mockRoom.hasMembershipState.mockImplementation((state) => state === KnownMembership.Join);
sess = MatrixRTCSession.sessionForSlot( sess = MatrixRTCSession.sessionForSlot(
client, client,
@@ -231,19 +252,21 @@ describe("MatrixRTCSession", () => {
it("honours created_ts", () => { it("honours created_ts", () => {
jest.useFakeTimers(); jest.useFakeTimers();
jest.setSystemTime(500); jest.setSystemTime(500);
const expiredMembership = testConfig.testCreateSticky ? { const expiredMembership = testConfig.testCreateSticky
...rtcMembershipTemplate, ? {
slot_id: "m.call#foobar", ...rtcMembershipTemplate,
member: { slot_id: "m.call#",
...rtcMembershipTemplate.member, member: {
claimed_device_id: "EXPIRED", ...rtcMembershipTemplate.member,
}, claimed_device_id: "EXPIRED",
application: { },
...rtcMembershipTemplate.application, application: {
"m.call.id": "foobar" ...rtcMembershipTemplate.application,
}, "m.call.id": "",
__test_sticky_expiry: 1500, },
} : { ...sessionMembershipTemplate, device_id: "EXPIRED", created_ts: 500, expires: 1000 }; __test_sticky_expiry: 1000,
}
: { ...sessionMembershipTemplate, device_id: "EXPIRED", created_ts: 500, expires: 1000 };
const mockRoom = makeMockRoom([expiredMembership], testConfig.testCreateSticky); const mockRoom = makeMockRoom([expiredMembership], testConfig.testCreateSticky);
sess = MatrixRTCSession.sessionForSlot( sess = MatrixRTCSession.sessionForSlot(
client, client,
@@ -251,6 +274,7 @@ describe("MatrixRTCSession", () => {
callSession, callSession,
testConfig.createWithDefaults ? undefined : testConfig, testConfig.createWithDefaults ? undefined : testConfig,
); );
expect(sess.memberships[0]).toBeDefined();
expect(sess?.memberships[0].getAbsoluteExpiry()).toEqual(1500); expect(sess?.memberships[0].getAbsoluteExpiry()).toEqual(1500);
jest.useRealTimers(); jest.useRealTimers();
}); });
@@ -275,7 +299,10 @@ describe("MatrixRTCSession", () => {
getLocalAge: jest.fn().mockReturnValue(0), getLocalAge: jest.fn().mockReturnValue(0),
} as unknown as MatrixEvent; } as unknown as MatrixEvent;
const mockRoom = makeMockRoom([]); const mockRoom = makeMockRoom([]);
mockRoom.getLiveTimeline().getState(Direction.Forward)?.events.set(EventType.GroupCallMemberPrefix, new Map([[EventType.GroupCallMemberPrefix, event]])); mockRoom
.getLiveTimeline()
.getState(Direction.Forward)
?.events.set(EventType.GroupCallMemberPrefix, new Map([[EventType.GroupCallMemberPrefix, event]]));
sess = MatrixRTCSession.sessionForSlot( sess = MatrixRTCSession.sessionForSlot(
client, client,
mockRoom, mockRoom,
@@ -294,7 +321,10 @@ describe("MatrixRTCSession", () => {
getLocalAge: jest.fn().mockReturnValue(0), getLocalAge: jest.fn().mockReturnValue(0),
} as unknown as MatrixEvent; } as unknown as MatrixEvent;
const mockRoom = makeMockRoom([]); const mockRoom = makeMockRoom([]);
mockRoom.getLiveTimeline().getState(Direction.Forward)?.events.set(EventType.GroupCallMemberPrefix, new Map([[EventType.GroupCallMemberPrefix, event]])); mockRoom
.getLiveTimeline()
.getState(Direction.Forward)
?.events.set(EventType.GroupCallMemberPrefix, new Map([[EventType.GroupCallMemberPrefix, event]]));
sess = MatrixRTCSession.sessionForSlot( sess = MatrixRTCSession.sessionForSlot(
client, client,
mockRoom, mockRoom,
@@ -305,9 +335,16 @@ describe("MatrixRTCSession", () => {
}); });
it("ignores memberships with no device_id", () => { it("ignores memberships with no device_id", () => {
const testMembership = Object.assign({}, sessionMembershipTemplate); const membershipTemplate = testConfig.testCreateSticky
(testMembership.device_id as string | undefined) = undefined; ? {
const mockRoom = makeMockRoom([testMembership]); ...rtcMembershipTemplate,
member: {
...rtcMembershipTemplate.member,
claimed_device_id: undefined,
},
}
: { ...sessionMembershipTemplate, device_id: undefined };
const mockRoom = makeMockRoom([membershipTemplate]);
const sess = MatrixRTCSession.sessionForSlot( const sess = MatrixRTCSession.sessionForSlot(
client, client,
mockRoom, mockRoom,
@@ -317,9 +354,13 @@ describe("MatrixRTCSession", () => {
expect(sess.memberships).toHaveLength(0); expect(sess.memberships).toHaveLength(0);
}); });
it("ignores memberships with no call_id", () => { it("ignores memberships with a different slot description", () => {
const testMembership = Object.assign({}, sessionMembershipTemplate); const testMembership = testConfig.testCreateSticky
(testMembership.call_id as string | undefined) = undefined; ? {
...rtcMembershipTemplate,
slot_id: "m.call#fibble",
}
: { ...sessionMembershipTemplate, call_id: undefined };
const mockRoom = makeMockRoom([testMembership]); const mockRoom = makeMockRoom([testMembership]);
sess = MatrixRTCSession.sessionForSlot( sess = MatrixRTCSession.sessionForSlot(
client, client,
@@ -401,7 +442,7 @@ describe("MatrixRTCSession", () => {
expect(sess?.slotDescription.id).toEqual(""); expect(sess?.slotDescription.id).toEqual("");
}); });
it.skip("handles an incoming sticky event to an existing session", () => { it("handles an incoming sticky event to an existing session", () => {
const mockRoom = makeMockRoom([sessionMembershipTemplate], false, callSession, true); const mockRoom = makeMockRoom([sessionMembershipTemplate], false, callSession, true);
const stickyUserId = "@stickyev:user.example"; const stickyUserId = "@stickyev:user.example";
@@ -703,7 +744,10 @@ describe("MatrixRTCSession", () => {
// Simulate a join, including the update to the room state // Simulate a join, including the update to the room state
sess!.joinRoomSession([mockFocus], mockFocus, { notificationType: "ring" }); sess!.joinRoomSession([mockFocus], mockFocus, { notificationType: "ring" });
await Promise.race([sentStateEvent, new Promise((resolve) => setTimeout(resolve, 5000))]); await Promise.race([sentStateEvent, new Promise((resolve) => setTimeout(resolve, 5000))]);
mockRoomState(mockRoom, [sessionMembershipTemplate, { ...sessionMembershipTemplate, user_id: client.getUserId()! }]); mockRoomState(mockRoom, [
sessionMembershipTemplate,
{ ...sessionMembershipTemplate, user_id: client.getUserId()! },
]);
sess!.onRTCSessionMemberUpdate(); sess!.onRTCSessionMemberUpdate();
expect(client.sendEvent).not.toHaveBeenCalled(); expect(client.sendEvent).not.toHaveBeenCalled();
@@ -717,7 +761,10 @@ describe("MatrixRTCSession", () => {
// from someone else, starting the call before our own state event has been sent // from someone else, starting the call before our own state event has been sent
mockRoomState(mockRoom, [sessionMembershipTemplate]); mockRoomState(mockRoom, [sessionMembershipTemplate]);
sess!.onRTCSessionMemberUpdate(); sess!.onRTCSessionMemberUpdate();
mockRoomState(mockRoom, [sessionMembershipTemplate, { ...sessionMembershipTemplate, user_id: client.getUserId()! }]); mockRoomState(mockRoom, [
sessionMembershipTemplate,
{ ...sessionMembershipTemplate, user_id: client.getUserId()! },
]);
sess!.onRTCSessionMemberUpdate(); sess!.onRTCSessionMemberUpdate();
// We assume that the responsibility to send a notification, if any, lies with the other // We assume that the responsibility to send a notification, if any, lies with the other

View File

@@ -17,7 +17,14 @@ limitations under the License.
import { ClientEvent, EventTimeline, MatrixClient, type Room } from "../../../src"; import { ClientEvent, EventTimeline, MatrixClient, type Room } from "../../../src";
import { RoomStateEvent } from "../../../src/models/room-state"; import { RoomStateEvent } from "../../../src/models/room-state";
import { MatrixRTCSessionManager, MatrixRTCSessionManagerEvents } from "../../../src/matrixrtc/MatrixRTCSessionManager"; import { MatrixRTCSessionManager, MatrixRTCSessionManagerEvents } from "../../../src/matrixrtc/MatrixRTCSessionManager";
import { makeMockRoom, type MembershipData, sessionMembershipTemplate, mockRoomState, mockRTCEvent, rtcMembershipTemplate } from "./mocks"; import {
makeMockRoom,
type MembershipData,
sessionMembershipTemplate,
mockRoomState,
mockRTCEvent,
rtcMembershipTemplate,
} from "./mocks";
import { logger } from "../../../src/logger"; import { logger } from "../../../src/logger";
import { slotDescriptionToId } from "../../../src/matrixrtc"; import { slotDescriptionToId } from "../../../src/matrixrtc";
@@ -111,29 +118,49 @@ describe.each([{ eventKind: "sticky" }, { eventKind: "memberState" }])(
try { try {
// Create a session for applicaation m.other, we ignore this session because it has the wrong application type. // 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: { const room1MembershipData: MembershipData[] =
...rtcMembershipTemplate.application, eventKind === "sticky"
type: "m.call" ? [
}}] : [{ ...membershipTemplate, application: "m.call" }]; {
const room1 = makeMockRoom(room1MembershipData, eventKind === "sticky", { application: "m.call", id: ""}); ...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]); jest.spyOn(client, "getRooms").mockReturnValue([room1]);
client.emit(ClientEvent.Room, room1); client.emit(ClientEvent.Room, room1);
expect(onStarted).not.toHaveBeenCalled(); expect(onStarted).not.toHaveBeenCalled();
onStarted.mockClear(); onStarted.mockClear();
// Create a session for applicaation m.notCall. We expect this call to be tracked because it has a call_id // Create a session for applicaation m.notCall. We expect this call to be tracked because it has a call_id
const room2MembershipData: MembershipData[] = eventKind === "sticky" ? [ const room2MembershipData: MembershipData[] =
{ ...membershipTemplate, application: { eventKind === "sticky"
...rtcMembershipTemplate.application, ? [
type: slotDescription.application, {
}, slot_id: slotDescriptionToId(slotDescription) }, ...membershipTemplate,
] : [{ application: {
...membershipTemplate, ...rtcMembershipTemplate.application,
application: slotDescription.application, type: slotDescription.application,
call_id: slotDescription.id, },
}]; slot_id: slotDescriptionToId(slotDescription),
},
]
: [
{
...membershipTemplate,
application: slotDescription.application,
call_id: slotDescription.id,
},
];
const room2 = makeMockRoom(room2MembershipData, eventKind === "sticky", slotDescription); const room2 = makeMockRoom(room2MembershipData, eventKind === "sticky", slotDescription);
console.log({room2: room2.roomId}) console.log({ room2: room2.roomId });
jest.spyOn(client, "getRooms").mockReturnValue([room1, room2]); jest.spyOn(client, "getRooms").mockReturnValue([room1, room2]);
client.emit(ClientEvent.Room, room2); client.emit(ClientEvent.Room, room2);
expect(onStarted).toHaveBeenCalled(); expect(onStarted).toHaveBeenCalled();
@@ -158,10 +185,18 @@ describe.each([{ eventKind: "sticky" }, { eventKind: "memberState" }])(
it("Doesn't fire event if unrelated sessions ends", () => { it("Doesn't fire event if unrelated sessions ends", () => {
const onEnded = jest.fn(); const onEnded = jest.fn();
client.matrixRTC.on(MatrixRTCSessionManagerEvents.SessionEnded, onEnded); client.matrixRTC.on(MatrixRTCSessionManagerEvents.SessionEnded, onEnded);
const membership: MembershipData[] = eventKind === "sticky" ? [{ ...membershipTemplate, application: { const membership: MembershipData[] =
...rtcMembershipTemplate.application, eventKind === "sticky"
type: "m.other_app", ? [
}}] : [{ ...membershipTemplate, application: "m.other_app" }]; {
...membershipTemplate,
application: {
...rtcMembershipTemplate.application,
type: "m.other_app",
},
},
]
: [{ ...membershipTemplate, application: "m.other_app" }];
const room1 = makeMockRoom(membership, eventKind === "sticky"); const room1 = makeMockRoom(membership, eventKind === "sticky");
jest.spyOn(client, "getRooms").mockReturnValue([room1]); jest.spyOn(client, "getRooms").mockReturnValue([room1]);
jest.spyOn(client, "getRoom").mockReturnValue(room1); jest.spyOn(client, "getRoom").mockReturnValue(room1);

View File

@@ -25,11 +25,7 @@ import {
type Room, type Room,
MAX_STICKY_DURATION_MS, MAX_STICKY_DURATION_MS,
} from "../../../src"; } from "../../../src";
import { import { MembershipManagerEvent, Status, type Transport } from "../../../src/matrixrtc";
MembershipManagerEvent,
Status,
type Transport,
} from "../../../src/matrixrtc";
import { makeMockClient, makeMockRoom, sessionMembershipTemplate, mockCallMembership, type MockClient } from "./mocks"; import { makeMockClient, makeMockRoom, sessionMembershipTemplate, mockCallMembership, type MockClient } from "./mocks";
import { LegacyMembershipManager, StickyEventMembershipManager } from "../../../src/matrixrtc/MembershipManager.ts"; import { LegacyMembershipManager, StickyEventMembershipManager } from "../../../src/matrixrtc/MembershipManager.ts";
import { SessionMembershipData } from "../../../src/matrixrtc/membership/legacy.ts"; import { SessionMembershipData } from "../../../src/matrixrtc/membership/legacy.ts";
@@ -98,13 +94,12 @@ describe("MembershipManager", () => {
// There is no need to clean up mocks since we will recreate the client. // There is no need to clean up mocks since we will recreate the client.
}); });
describe("LegacyMembershipManager", () => { describe("LegacyMembershipManager", () => {
beforeEach(() => { beforeEach(() => {
// Provide a default mock that is like the default "non error" server behaviour. // 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_sendDelayedStateEvent as Mock<any>).mockResolvedValue({ delay_id: "id" });
(client._unstable_updateDelayedEvent as Mock<any>).mockResolvedValue(undefined); (client._unstable_updateDelayedEvent as Mock<any>).mockResolvedValue(undefined);
(client.sendStateEvent as Mock<any>).mockResolvedValue({ event_id: "id" }); (client.sendStateEvent as Mock<any>).mockResolvedValue({ event_id: "id" });
}) });
describe("isActivated()", () => { describe("isActivated()", () => {
it("defaults to false", () => { it("defaults to false", () => {
@@ -124,7 +119,9 @@ describe("MembershipManager", () => {
it("sends a membership event and schedules delayed leave when joining a call", async () => { it("sends a membership event and schedules delayed leave when joining a call", async () => {
// Spys/Mocks // Spys/Mocks
const updateDelayedEventHandle = createAsyncHandle<void>(client._unstable_updateDelayedEvent as Mock); const updateDelayedEventHandle = createAsyncHandle<void>(
client._unstable_updateDelayedEvent as Mock,
);
// Test // Test
const memberManager = new LegacyMembershipManager(undefined, room, client, callSession); const memberManager = new LegacyMembershipManager(undefined, room, client, callSession);
@@ -308,7 +305,12 @@ describe("MembershipManager", () => {
expect(client._unstable_sendDelayedStateEvent).toHaveBeenCalledTimes(2); expect(client._unstable_sendDelayedStateEvent).toHaveBeenCalledTimes(2);
}); });
it("uses delayedLeaveEventDelayMs from config", () => { it("uses delayedLeaveEventDelayMs from config", () => {
const manager = new LegacyMembershipManager({ delayedLeaveEventDelayMs: 123456 }, room, client, callSession); const manager = new LegacyMembershipManager(
{ delayedLeaveEventDelayMs: 123456 },
room,
client,
callSession,
);
manager.join([focus]); manager.join([focus]);
expect(client._unstable_sendDelayedStateEvent).toHaveBeenCalledWith( expect(client._unstable_sendDelayedStateEvent).toHaveBeenCalledWith(
room.roomId, room.roomId,
@@ -867,7 +869,10 @@ describe("MembershipManager", () => {
const manager = new LegacyMembershipManager({}, room, client, callSession); const manager = new LegacyMembershipManager({}, room, client, callSession);
manager.join([]); manager.join([]);
expect(manager.isActivated()).toEqual(true); expect(manager.isActivated()).toEqual(true);
const membership = mockCallMembership({ ...sessionMembershipTemplate, user_id: client.getUserId()! }, room.roomId); const membership = mockCallMembership(
{ ...sessionMembershipTemplate, user_id: client.getUserId()! },
room.roomId,
);
await manager.onRTCSessionMemberUpdate([membership]); await manager.onRTCSessionMemberUpdate([membership]);
await manager.updateCallIntent("video"); await manager.updateCallIntent("video");
expect(client.sendStateEvent).toHaveBeenCalledTimes(2); expect(client.sendStateEvent).toHaveBeenCalledTimes(2);

View File

@@ -20,7 +20,12 @@ import { type Mocked } from "jest-mock";
import { EventType, type Room, RoomEvent, type MatrixClient, type MatrixEvent } from "../../../src"; import { EventType, type Room, RoomEvent, type MatrixClient, type MatrixEvent } from "../../../src";
import { CallMembership } from "../../../src/matrixrtc/CallMembership"; import { CallMembership } from "../../../src/matrixrtc/CallMembership";
import { secureRandomString } from "../../../src/randomstring"; import { secureRandomString } from "../../../src/randomstring";
import { DefaultCallApplicationDescription, RtcSlotEventContent, SlotDescription, slotDescriptionToId } from "../../../src/matrixrtc"; import {
DefaultCallApplicationDescription,
RtcSlotEventContent,
SlotDescription,
slotDescriptionToId,
} from "../../../src/matrixrtc";
import { mkMatrixEvent } from "../../../src/testing"; import { mkMatrixEvent } from "../../../src/testing";
import type { SessionMembershipData } from "../../../src/matrixrtc/membership/legacy"; import type { SessionMembershipData } from "../../../src/matrixrtc/membership/legacy";
import type { RtcMembershipData } from "../../../src/matrixrtc/membership/rtc"; import type { RtcMembershipData } from "../../../src/matrixrtc/membership/rtc";
@@ -50,17 +55,17 @@ export const sessionMembershipTemplate: SessionMembershipData & { user_id: strin
], ],
}; };
export const rtcMembershipTemplate: RtcMembershipData&{user_id: string, __test_sticky_expiry?: number} = { export const rtcMembershipTemplate: RtcMembershipData & { user_id: string; __test_sticky_expiry?: number } = {
slot_id: "m.call#", slot_id: "m.call#",
application: { application: {
type: "m.call", "type": "m.call",
"m.call.id": "" "m.call.id": "",
}, },
user_id: "@mock:user.example", user_id: "@mock:user.example",
member: { member: {
claimed_user_id: "@mock:user.example", claimed_user_id: "@mock:user.example",
claimed_device_id: "AAAAAAA", claimed_device_id: "AAAAAAA",
id: "ea2MaingeeMo" id: "ea2MaingeeMo",
}, },
sticky_key: "ea2MaingeeMo", sticky_key: "ea2MaingeeMo",
rtc_transports: [ rtc_transports: [
@@ -78,7 +83,6 @@ export const rtcMembershipTemplate: RtcMembershipData&{user_id: string, __test_s
versions: [], versions: [],
}; };
export type MockClient = Pick< export type MockClient = Pick<
MatrixClient, MatrixClient,
| "getUserId" | "getUserId"
@@ -116,7 +120,11 @@ export function makeMockRoom(
): Mocked<Room & { emitTimelineEvent: (event: MatrixEvent) => void }> { ): Mocked<Room & { emitTimelineEvent: (event: MatrixEvent) => void }> {
const roomId = secureRandomString(8); const roomId = secureRandomString(8);
// Caching roomState here so it does not get recreated when calling `getLiveTimeline.getState()` // Caching roomState here so it does not get recreated when calling `getLiveTimeline.getState()`
const roomState = makeMockRoomState(useStickyEvents ? [] : membershipData, roomId, addRTCSlot ? slotDescription : undefined); const roomState = makeMockRoomState(
useStickyEvents ? [] : membershipData,
roomId,
addRTCSlot ? slotDescription : undefined,
);
const ts = Date.now(); const ts = Date.now();
const room = Object.assign(new EventEmitter(), { const room = Object.assign(new EventEmitter(), {
roomId: roomId, roomId: roomId,
@@ -128,7 +136,16 @@ export function makeMockRoom(
_unstable_getStickyEvents: jest _unstable_getStickyEvents: jest
.fn() .fn()
.mockImplementation(() => .mockImplementation(() =>
useStickyEvents ? membershipData.map((m) => mockRTCEvent(m, roomId, (m as typeof rtcMembershipTemplate).__test_sticky_expiry ?? testStickyDurationMs, ts)) : [], useStickyEvents
? membershipData.map((m) =>
mockRTCEvent(
m,
roomId,
(m as typeof rtcMembershipTemplate).__test_sticky_expiry ?? testStickyDurationMs,
ts,
),
)
: [],
) as any, ) as any,
}); });
return Object.assign(room, { return Object.assign(room, {
@@ -143,7 +160,7 @@ function makeMockRoomState(membershipData: MembershipData[], roomId: string, slo
const data = e.getContent() as SessionMembershipData; const data = e.getContent() as SessionMembershipData;
return [`_${e.sender?.userId}_${data.device_id}`]; return [`_${e.sender?.userId}_${data.device_id}`];
}); });
let slotEvent: MatrixEvent|undefined; let slotEvent: MatrixEvent | undefined;
if (slotDescription) { if (slotDescription) {
// Add a slot // Add a slot
@@ -181,18 +198,27 @@ function makeMockRoomState(membershipData: MembershipData[], roomId: string, slo
values: () => events, values: () => events,
}, },
], ],
...(slotEvent ? [[EventType.RTCSlot, { ...(slotEvent
size: () => true, ? [
has: (stateKey: string) => slotEvent.getStateKey() === stateKey, [
get: (stateKey: string) => slotEvent.getStateKey() === stateKey ? slotEvent : undefined, EventType.RTCSlot,
values: () => [slotEvent], {
}]] : []), size: () => true,
has: (stateKey: string) => slotEvent.getStateKey() === stateKey,
get: (stateKey: string) => (slotEvent.getStateKey() === stateKey ? slotEvent : undefined),
values: () => [slotEvent],
},
],
]
: []),
] as any), ] as any),
}; };
} }
export function mockRoomState(room: Room, membershipData: MembershipData[], slotDescription?: SlotDescription): void { export function mockRoomState(room: Room, membershipData: MembershipData[], slotDescription?: SlotDescription): void {
room.getLiveTimeline().getState = jest.fn().mockReturnValue(makeMockRoomState(membershipData, room.roomId, slotDescription)); room.getLiveTimeline().getState = jest
.fn()
.mockReturnValue(makeMockRoomState(membershipData, room.roomId, slotDescription));
} }
export function makeMockEvent( export function makeMockEvent(