Skip to content

Commit a3bbc49

Browse files
toger5hughns
andauthored
MatrixRTC: MembershipManager test cases and deprecation of MatrixRTCSession.room (#4713)
* WIP doodles on MembershipManager test cases * . * initial membership manager test setup. * Updates from discussion * revert renaming comments * remove unused import * fix leave delayed event resend test. It was missing a flush. * comment out and remove unused variables * es lint * use jsdom instead of node test environment * remove unused variables * remove unused export * temp * review * fixup tests * more review * remove wait for expect dependency * flatten tests and add comments * add more leave test cases * use defer * remove @jest/environment dependency * Cleanup awaits and Make mock types more correct. Make every mock return a Promise if the real implementation does return a pormise. * remove flush promise dependency * add linting to matrixrtc tests * Add fix async lints and use matrix rtc logger for test environment. * prettier * change to MatrixRTCSession logger * make accessing the full room deprecated * remove deprecated usage of full room * Clean up the deprecation --------- Co-authored-by: Hugh Nimmo-Smith <hughns@matrix.org>
1 parent d81929d commit a3bbc49

File tree

9 files changed

+758
-151
lines changed

9 files changed

+758
-151
lines changed

.eslintrc.cjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ module.exports = {
156156
},
157157
{
158158
// Enable stricter promise rules for the MatrixRTC codebase
159-
files: ["src/matrixrtc/**/*.ts"],
159+
files: ["src/matrixrtc/**/*.ts", "spec/unit/matrixrtc/*.ts"],
160160
rules: {
161161
// Encourage proper usage of Promises:
162162
"@typescript-eslint/no-floating-promises": "error",

spec/unit/matrixrtc/MatrixRTCSession.spec.ts

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

17-
import { encodeBase64, EventType, MatrixClient, MatrixError, type MatrixEvent, type Room } from "../../../src";
17+
import { encodeBase64, EventType, MatrixClient, type MatrixError, type MatrixEvent, type Room } from "../../../src";
1818
import { KnownMembership } from "../../../src/@types/membership";
1919
import { DEFAULT_EXPIRE_DURATION, type SessionMembershipData } 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 { flushPromises } from "../../test-utils/flushPromises";
2423
import { makeMockRoom, makeMockRoomState, membershipTemplate } from "./mocks";
2524

2625
const mockFocus = { type: "mock" };
@@ -37,10 +36,10 @@ describe("MatrixRTCSession", () => {
3736
client.getDeviceId = jest.fn().mockReturnValue("AAAAAAA");
3837
});
3938

40-
afterEach(() => {
39+
afterEach(async () => {
4140
client.stopClient();
4241
client.matrixRTC.stop();
43-
if (sess) sess.stop();
42+
if (sess) await sess.stop();
4443
sess = undefined;
4544
});
4645

@@ -322,11 +321,9 @@ describe("MatrixRTCSession", () => {
322321
let sendStateEventMock: jest.Mock;
323322
let sendDelayedStateMock: jest.Mock;
324323
let sendEventMock: jest.Mock;
325-
let updateDelayedEventMock: jest.Mock;
326324

327325
let sentStateEvent: Promise<void>;
328326
let sentDelayedState: Promise<void>;
329-
let updatedDelayedEvent: Promise<void>;
330327

331328
beforeEach(() => {
332329
sentStateEvent = new Promise((resolve) => {
@@ -340,15 +337,12 @@ describe("MatrixRTCSession", () => {
340337
};
341338
});
342339
});
343-
updatedDelayedEvent = new Promise((r) => {
344-
updateDelayedEventMock = jest.fn(r);
345-
});
346340
sendEventMock = jest.fn();
347341
client.sendStateEvent = sendStateEventMock;
348342
client._unstable_sendDelayedStateEvent = sendDelayedStateMock;
349343
client.sendEvent = sendEventMock;
350344

351-
client._unstable_updateDelayedEvent = updateDelayedEventMock;
345+
client._unstable_updateDelayedEvent = jest.fn();
352346

353347
mockRoom = makeMockRoom([]);
354348
sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom);
@@ -432,120 +426,6 @@ describe("MatrixRTCSession", () => {
432426
expect(client._unstable_sendDelayedStateEvent).toHaveBeenCalledTimes(1);
433427
jest.useRealTimers();
434428
});
435-
436-
describe("calls", () => {
437-
const activeFocusConfig = { type: "livekit", livekit_service_url: "https://active.url" };
438-
const activeFocus = { type: "livekit", focus_selection: "oldest_membership" };
439-
440-
async function testJoin(useOwnedStateEvents: boolean): Promise<void> {
441-
if (useOwnedStateEvents) {
442-
mockRoom.getVersion = jest.fn().mockReturnValue("org.matrix.msc3757.default");
443-
}
444-
445-
jest.useFakeTimers();
446-
447-
// preparing the delayed disconnect should handle the delay being too long
448-
const sendDelayedStateExceedAttempt = new Promise<void>((resolve) => {
449-
const error = new MatrixError({
450-
"errcode": "M_UNKNOWN",
451-
"org.matrix.msc4140.errcode": "M_MAX_DELAY_EXCEEDED",
452-
"org.matrix.msc4140.max_delay": 7500,
453-
});
454-
sendDelayedStateMock.mockImplementationOnce(() => {
455-
resolve();
456-
return Promise.reject(error);
457-
});
458-
});
459-
460-
const userStateKey = `${!useOwnedStateEvents ? "_" : ""}@alice:example.org_AAAAAAA`;
461-
// preparing the delayed disconnect should handle ratelimiting
462-
const sendDelayedStateAttempt = new Promise<void>((resolve) => {
463-
const error = new MatrixError({ errcode: "M_LIMIT_EXCEEDED" });
464-
sendDelayedStateMock.mockImplementationOnce(() => {
465-
resolve();
466-
return Promise.reject(error);
467-
});
468-
});
469-
470-
// setting the membership state should handle ratelimiting (also with a retry-after value)
471-
const sendStateEventAttempt = new Promise<void>((resolve) => {
472-
const error = new MatrixError(
473-
{ errcode: "M_LIMIT_EXCEEDED" },
474-
429,
475-
undefined,
476-
undefined,
477-
new Headers({ "Retry-After": "1" }),
478-
);
479-
sendStateEventMock.mockImplementationOnce(() => {
480-
resolve();
481-
return Promise.reject(error);
482-
});
483-
});
484-
485-
sess!.joinRoomSession([activeFocusConfig], activeFocus, {
486-
membershipServerSideExpiryTimeout: 9000,
487-
});
488-
489-
await sendDelayedStateExceedAttempt.then(); // needed to resolve after the send attempt catches
490-
await sendDelayedStateAttempt;
491-
const callProps = (d: number) => {
492-
return [mockRoom!.roomId, { delay: d }, "org.matrix.msc3401.call.member", {}, userStateKey];
493-
};
494-
expect(client._unstable_sendDelayedStateEvent).toHaveBeenNthCalledWith(1, ...callProps(9000));
495-
expect(client._unstable_sendDelayedStateEvent).toHaveBeenNthCalledWith(2, ...callProps(7500));
496-
497-
jest.advanceTimersByTime(5000);
498-
499-
await sendStateEventAttempt.then(); // needed to resolve after resendIfRateLimited catches
500-
jest.advanceTimersByTime(1000);
501-
502-
await sentStateEvent;
503-
expect(client.sendStateEvent).toHaveBeenCalledWith(
504-
mockRoom!.roomId,
505-
EventType.GroupCallMemberPrefix,
506-
{
507-
application: "m.call",
508-
scope: "m.room",
509-
call_id: "",
510-
expires: 14400000,
511-
device_id: "AAAAAAA",
512-
foci_preferred: [activeFocusConfig],
513-
focus_active: activeFocus,
514-
} satisfies SessionMembershipData,
515-
userStateKey,
516-
);
517-
await sentDelayedState;
518-
519-
// should have prepared the heartbeat to keep delaying the leave event while still connected
520-
await updatedDelayedEvent;
521-
expect(client._unstable_updateDelayedEvent).toHaveBeenCalledTimes(1);
522-
523-
// ensures that we reach the code that schedules the timeout for the next delay update before we advance the timers.
524-
await flushPromises();
525-
jest.advanceTimersByTime(5000);
526-
// should update delayed disconnect
527-
expect(client._unstable_updateDelayedEvent).toHaveBeenCalledTimes(2);
528-
529-
jest.useRealTimers();
530-
}
531-
532-
it("sends a membership event with session payload when joining a call", async () => {
533-
await testJoin(false);
534-
});
535-
536-
it("does not prefix the state key with _ for rooms that support user-owned state events", async () => {
537-
await testJoin(true);
538-
});
539-
});
540-
541-
it("does nothing if join called when already joined", async () => {
542-
sess!.joinRoomSession([mockFocus], mockFocus);
543-
await sentStateEvent;
544-
expect(client.sendStateEvent).toHaveBeenCalledTimes(1);
545-
546-
sess!.joinRoomSession([mockFocus], mockFocus);
547-
expect(client.sendStateEvent).toHaveBeenCalledTimes(1);
548-
});
549429
});
550430

551431
describe("onMembershipsChanged", () => {
@@ -616,9 +496,9 @@ describe("MatrixRTCSession", () => {
616496
sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom);
617497
});
618498

619-
afterEach(() => {
499+
afterEach(async () => {
620500
// stop the timers
621-
sess!.leaveRoomSession();
501+
await sess!.leaveRoomSession();
622502
});
623503

624504
it("creates a key when joining", () => {
@@ -715,7 +595,7 @@ describe("MatrixRTCSession", () => {
715595
}
716596
});
717597

718-
it("cancels key send event that fail", async () => {
598+
it("cancels key send event that fail", () => {
719599
const eventSentinel = {} as unknown as MatrixEvent;
720600

721601
client.cancelPendingEvent = jest.fn();

spec/unit/matrixrtc/MatrixRTCSessionManager.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import { makeMockRoom, makeMockRoomState, membershipTemplate } from "./mocks";
3232
describe("MatrixRTCSessionManager", () => {
3333
let client: MatrixClient;
3434

35-
beforeEach(async () => {
35+
beforeEach(() => {
3636
client = new MatrixClient({ baseUrl: "base_url" });
3737
client.matrixRTC.start();
3838
});

0 commit comments

Comments
 (0)