Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MatrixRTC: MembershipManager test cases and deprecation of MatrixRTCSession.room #4713

Merged
merged 33 commits into from
Feb 27, 2025
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
a404539
WIP doodles on MembershipManager test cases
hughns Jan 23, 2025
834d461
.
hughns Jan 23, 2025
6594860
initial membership manager test setup.
toger5 Feb 17, 2025
738016f
Updates from discussion
hughns Feb 17, 2025
ec375f3
revert renaming comments
toger5 Feb 17, 2025
8c8e97e
remove unused import
toger5 Feb 17, 2025
d9192f3
fix leave delayed event resend test.
toger5 Feb 17, 2025
0444816
comment out and remove unused variables
toger5 Feb 17, 2025
7125d45
es lint
toger5 Feb 17, 2025
3d46d05
use jsdom instead of node test environment
toger5 Feb 17, 2025
b0492ca
remove unused variables
toger5 Feb 17, 2025
c59f04d
remove unused export
toger5 Feb 17, 2025
e1fbdcd
temp
toger5 Feb 18, 2025
cd1321b
review
toger5 Feb 19, 2025
28f17be
fixup tests
toger5 Feb 19, 2025
a21c6e5
more review
toger5 Feb 19, 2025
8df56bb
remove wait for expect dependency
toger5 Feb 21, 2025
87bb597
flatten tests and add comments
toger5 Feb 21, 2025
4e60a35
add more leave test cases
toger5 Feb 24, 2025
4502083
use defer
toger5 Feb 24, 2025
e5e5434
Merge branch 'develop' into hughns/membershipmanager-test-cases
toger5 Feb 24, 2025
d9b22cb
Merge branch 'develop' into hughns/membershipmanager-test-cases
toger5 Feb 24, 2025
998e16b
remove @jest/environment dependency
toger5 Feb 26, 2025
72994f2
Cleanup awaits and Make mock types more correct.
toger5 Feb 26, 2025
ce24845
remove flush promise dependency
toger5 Feb 26, 2025
c50fa32
add linting to matrixrtc tests
toger5 Feb 26, 2025
94bb78f
Add fix async lints and use matrix rtc logger for test environment.
toger5 Feb 26, 2025
1016b05
prettier
toger5 Feb 26, 2025
218c6d9
change to MatrixRTCSession logger
toger5 Feb 26, 2025
177e2f4
make accessing the full room deprecated
toger5 Feb 26, 2025
9e60a36
remove deprecated usage of full room
toger5 Feb 26, 2025
c83429c
Merge branch 'develop' into hughns/membershipmanager-test-cases
toger5 Feb 26, 2025
74f76b4
Clean up the deprecation
hughns Feb 27, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
"@babel/preset-env": "^7.12.11",
"@babel/preset-typescript": "^7.12.7",
"@casualbot/jest-sonar-reporter": "2.2.7",
"@jest/environment": "^29.7.0",
"@peculiar/webcrypto": "^1.4.5",
"@stylistic/eslint-plugin": "^3.0.0",
"@types/bs58": "^4.0.1",
Expand Down Expand Up @@ -124,7 +125,8 @@
"typedoc-plugin-coverage": "^3.0.0",
"typedoc-plugin-mdn-links": "^4.0.0",
"typedoc-plugin-missing-exports": "^3.0.0",
"typescript": "^5.4.2"
"typescript": "^5.4.2",
"wait-for-expect": "^3.0.2"
},
"@casualbot/jest-sonar-reporter": {
"outputDirectory": "coverage",
Expand Down
124 changes: 2 additions & 122 deletions spec/unit/matrixrtc/MatrixRTCSession.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,12 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

import { encodeBase64, EventType, MatrixClient, MatrixError, type MatrixEvent, type Room } from "../../../src";
import { encodeBase64, EventType, MatrixClient, type MatrixError, type MatrixEvent, type Room } from "../../../src";
import { KnownMembership } from "../../../src/@types/membership";
import { DEFAULT_EXPIRE_DURATION, type SessionMembershipData } from "../../../src/matrixrtc/CallMembership";
import { MatrixRTCSession, MatrixRTCSessionEvent } from "../../../src/matrixrtc/MatrixRTCSession";
import { type EncryptionKeysEventContent } from "../../../src/matrixrtc/types";
import { secureRandomString } from "../../../src/randomstring";
import { flushPromises } from "../../test-utils/flushPromises";
import { makeMockRoom, makeMockRoomState, membershipTemplate } from "./mocks";

const mockFocus = { type: "mock" };
Expand Down Expand Up @@ -322,11 +321,9 @@ describe("MatrixRTCSession", () => {
let sendStateEventMock: jest.Mock;
let sendDelayedStateMock: jest.Mock;
let sendEventMock: jest.Mock;
let updateDelayedEventMock: jest.Mock;

let sentStateEvent: Promise<void>;
let sentDelayedState: Promise<void>;
let updatedDelayedEvent: Promise<void>;

beforeEach(() => {
sentStateEvent = new Promise((resolve) => {
Expand All @@ -340,15 +337,12 @@ describe("MatrixRTCSession", () => {
};
});
});
updatedDelayedEvent = new Promise((r) => {
updateDelayedEventMock = jest.fn(r);
});
sendEventMock = jest.fn();
client.sendStateEvent = sendStateEventMock;
client._unstable_sendDelayedStateEvent = sendDelayedStateMock;
client.sendEvent = sendEventMock;

client._unstable_updateDelayedEvent = updateDelayedEventMock;
client._unstable_updateDelayedEvent = jest.fn();

mockRoom = makeMockRoom([]);
sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom);
Expand Down Expand Up @@ -432,120 +426,6 @@ describe("MatrixRTCSession", () => {
expect(client._unstable_sendDelayedStateEvent).toHaveBeenCalledTimes(1);
jest.useRealTimers();
});

describe("calls", () => {
const activeFocusConfig = { type: "livekit", livekit_service_url: "https://active.url" };
const activeFocus = { type: "livekit", focus_selection: "oldest_membership" };

async function testJoin(useOwnedStateEvents: boolean): Promise<void> {
if (useOwnedStateEvents) {
mockRoom.getVersion = jest.fn().mockReturnValue("org.matrix.msc3757.default");
}

jest.useFakeTimers();

// preparing the delayed disconnect should handle the delay being too long
const sendDelayedStateExceedAttempt = new Promise<void>((resolve) => {
const error = new MatrixError({
"errcode": "M_UNKNOWN",
"org.matrix.msc4140.errcode": "M_MAX_DELAY_EXCEEDED",
"org.matrix.msc4140.max_delay": 7500,
});
sendDelayedStateMock.mockImplementationOnce(() => {
resolve();
return Promise.reject(error);
});
});

const userStateKey = `${!useOwnedStateEvents ? "_" : ""}@alice:example.org_AAAAAAA`;
// preparing the delayed disconnect should handle ratelimiting
const sendDelayedStateAttempt = new Promise<void>((resolve) => {
const error = new MatrixError({ errcode: "M_LIMIT_EXCEEDED" });
sendDelayedStateMock.mockImplementationOnce(() => {
resolve();
return Promise.reject(error);
});
});

// setting the membership state should handle ratelimiting (also with a retry-after value)
const sendStateEventAttempt = new Promise<void>((resolve) => {
const error = new MatrixError(
{ errcode: "M_LIMIT_EXCEEDED" },
429,
undefined,
undefined,
new Headers({ "Retry-After": "1" }),
);
sendStateEventMock.mockImplementationOnce(() => {
resolve();
return Promise.reject(error);
});
});

sess!.joinRoomSession([activeFocusConfig], activeFocus, {
membershipServerSideExpiryTimeout: 9000,
});

await sendDelayedStateExceedAttempt.then(); // needed to resolve after the send attempt catches
await sendDelayedStateAttempt;
const callProps = (d: number) => {
return [mockRoom!.roomId, { delay: d }, "org.matrix.msc3401.call.member", {}, userStateKey];
};
expect(client._unstable_sendDelayedStateEvent).toHaveBeenNthCalledWith(1, ...callProps(9000));
expect(client._unstable_sendDelayedStateEvent).toHaveBeenNthCalledWith(2, ...callProps(7500));

jest.advanceTimersByTime(5000);

await sendStateEventAttempt.then(); // needed to resolve after resendIfRateLimited catches
jest.advanceTimersByTime(1000);

await sentStateEvent;
expect(client.sendStateEvent).toHaveBeenCalledWith(
mockRoom!.roomId,
EventType.GroupCallMemberPrefix,
{
application: "m.call",
scope: "m.room",
call_id: "",
expires: 14400000,
device_id: "AAAAAAA",
foci_preferred: [activeFocusConfig],
focus_active: activeFocus,
} satisfies SessionMembershipData,
userStateKey,
);
await sentDelayedState;

// should have prepared the heartbeat to keep delaying the leave event while still connected
await updatedDelayedEvent;
expect(client._unstable_updateDelayedEvent).toHaveBeenCalledTimes(1);

// ensures that we reach the code that schedules the timeout for the next delay update before we advance the timers.
await flushPromises();
jest.advanceTimersByTime(5000);
// should update delayed disconnect
expect(client._unstable_updateDelayedEvent).toHaveBeenCalledTimes(2);

jest.useRealTimers();
}

it("sends a membership event with session payload when joining a call", async () => {
await testJoin(false);
});

it("does not prefix the state key with _ for rooms that support user-owned state events", async () => {
await testJoin(true);
});
});

it("does nothing if join called when already joined", async () => {
sess!.joinRoomSession([mockFocus], mockFocus);
await sentStateEvent;
expect(client.sendStateEvent).toHaveBeenCalledTimes(1);

sess!.joinRoomSession([mockFocus], mockFocus);
expect(client.sendStateEvent).toHaveBeenCalledTimes(1);
});
});

describe("onMembershipsChanged", () => {
Expand Down
Loading
Loading