1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-08-07 23:02:56 +03:00

Better fallback for the event localTimestamp calculation. (#3900)

* better fallback to localTimestamp calculation

Signed-off-by: Timo K <toger5@hotmail.de>

* make `isExpired` impl simpler to read

Signed-off-by: Timo K <toger5@hotmail.de>

* update tests

Signed-off-by: Timo K <toger5@hotmail.de>

* refactor to use localTimestamp in the mocks

Signed-off-by: Timo K <toger5@hotmail.de>

* Update src/models/event.ts

Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>

* Update src/models/event.ts

Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>

* Update and clarify comments.
So that the localTimestamp and localAge behavior is easier to understand.

Signed-off-by: Timo K <toger5@hotmail.de>

* Replace localTimestamp biding
with binding the whole roomState

Signed-off-by: Timo K <toger5@hotmail.de>

---------

Signed-off-by: Timo K <toger5@hotmail.de>
Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>
This commit is contained in:
Timo
2023-11-20 18:20:04 +01:00
committed by GitHub
parent 83ba0fbb49
commit e98ce78027
5 changed files with 54 additions and 62 deletions

View File

@@ -85,7 +85,7 @@ describe("CallMembership", () => {
it("considers memberships expired when local age large", () => { it("considers memberships expired when local age large", () => {
const fakeEvent = makeMockEvent(1000); const fakeEvent = makeMockEvent(1000);
fakeEvent.getLocalAge = jest.fn().mockReturnValue(6000); fakeEvent.localTimestamp = Date.now() - 6000;
const membership = new CallMembership(fakeEvent, membershipTemplate); const membership = new CallMembership(fakeEvent, membershipTemplate);
expect(membership.isExpired()).toEqual(true); expect(membership.isExpired()).toEqual(true);
}); });

View File

@@ -67,7 +67,7 @@ describe("MatrixRTCSession", () => {
const expiredMembership = Object.assign({}, membershipTemplate); const expiredMembership = Object.assign({}, membershipTemplate);
expiredMembership.expires = 1000; expiredMembership.expires = 1000;
expiredMembership.device_id = "EXPIRED"; expiredMembership.device_id = "EXPIRED";
const mockRoom = makeMockRoom([membershipTemplate, expiredMembership], () => 10000); const mockRoom = makeMockRoom([membershipTemplate, expiredMembership], 10000);
sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom); sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom);
expect(sess?.memberships.length).toEqual(1); expect(sess?.memberships.length).toEqual(1);
@@ -266,7 +266,7 @@ describe("MatrixRTCSession", () => {
const timeElapsed = 60 * 60 * 1000 - 1000; const timeElapsed = 60 * 60 * 1000 - 1000;
mockRoom.getLiveTimeline().getState(EventTimeline.FORWARDS)!.getStateEvents = jest mockRoom.getLiveTimeline().getState(EventTimeline.FORWARDS)!.getStateEvents = jest
.fn() .fn()
.mockReturnValue(mockRTCEvent(eventContent.memberships, mockRoom.roomId, () => timeElapsed)); .mockReturnValue(mockRTCEvent(eventContent.memberships, mockRoom.roomId, timeElapsed));
const eventReSentPromise = new Promise<Record<string, any>>((r) => { const eventReSentPromise = new Promise<Record<string, any>>((r) => {
resolveFn = (_roomId: string, _type: string, val: Record<string, any>) => { resolveFn = (_roomId: string, _type: string, val: Record<string, any>) => {
@@ -539,10 +539,8 @@ describe("MatrixRTCSession", () => {
it("emits an event at the time a membership event expires", () => { it("emits an event at the time a membership event expires", () => {
jest.useFakeTimers(); jest.useFakeTimers();
try { try {
let eventAge = 0;
const membership = Object.assign({}, membershipTemplate); const membership = Object.assign({}, membershipTemplate);
const mockRoom = makeMockRoom([membership], () => eventAge); const mockRoom = makeMockRoom([membership], 0);
sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom); sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom);
const membershipObject = sess.memberships[0]; const membershipObject = sess.memberships[0];
@@ -550,7 +548,6 @@ describe("MatrixRTCSession", () => {
const onMembershipsChanged = jest.fn(); const onMembershipsChanged = jest.fn();
sess.on(MatrixRTCSessionEvent.MembershipsChanged, onMembershipsChanged); sess.on(MatrixRTCSessionEvent.MembershipsChanged, onMembershipsChanged);
eventAge = 61 * 1000 * 1000;
jest.advanceTimersByTime(61 * 1000 * 1000); jest.advanceTimersByTime(61 * 1000 * 1000);
expect(onMembershipsChanged).toHaveBeenCalledWith([membershipObject], []); expect(onMembershipsChanged).toHaveBeenCalledWith([membershipObject], []);
@@ -561,47 +558,49 @@ describe("MatrixRTCSession", () => {
}); });
it("prunes expired memberships on update", () => { it("prunes expired memberships on update", () => {
client.sendStateEvent = jest.fn(); jest.useFakeTimers();
try {
client.sendStateEvent = jest.fn();
let eventAge = 0; const mockMemberships = [
const mockRoom = makeMockRoom(
[
Object.assign({}, membershipTemplate, { Object.assign({}, membershipTemplate, {
device_id: "OTHERDEVICE", device_id: "OTHERDEVICE",
expires: 1000, expires: 1000,
}), }),
], ];
() => eventAge, const mockRoomNoExpired = makeMockRoom(mockMemberships, 0);
);
sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom);
// sanity check sess = MatrixRTCSession.roomSessionForRoom(client, mockRoomNoExpired);
expect(sess.memberships).toHaveLength(1);
expect(sess.memberships[0].deviceId).toEqual("OTHERDEVICE");
eventAge = 10000; // sanity check
expect(sess.memberships).toHaveLength(1);
expect(sess.memberships[0].deviceId).toEqual("OTHERDEVICE");
sess.joinRoomSession([mockFocus]); jest.advanceTimersByTime(10000);
expect(client.sendStateEvent).toHaveBeenCalledWith( sess.joinRoomSession([mockFocus]);
mockRoom!.roomId,
EventType.GroupCallMemberPrefix, expect(client.sendStateEvent).toHaveBeenCalledWith(
{ mockRoomNoExpired!.roomId,
memberships: [ EventType.GroupCallMemberPrefix,
{ {
application: "m.call", memberships: [
scope: "m.room", {
call_id: "", application: "m.call",
device_id: "AAAAAAA", scope: "m.room",
expires: 3600000, call_id: "",
foci_active: [mockFocus], device_id: "AAAAAAA",
membershipID: expect.stringMatching(".*"), expires: 3600000,
}, foci_active: [mockFocus],
], membershipID: expect.stringMatching(".*"),
}, },
"@alice:example.org", ],
); },
"@alice:example.org",
);
} finally {
jest.useRealTimers();
}
}); });
it("fills in created_ts for other memberships on update", () => { it("fills in created_ts for other memberships on update", () => {

View File

@@ -18,41 +18,29 @@ import { EventType, MatrixEvent, Room } from "../../../src";
import { CallMembershipData } from "../../../src/matrixrtc/CallMembership"; import { CallMembershipData } from "../../../src/matrixrtc/CallMembership";
import { randomString } from "../../../src/randomstring"; import { randomString } from "../../../src/randomstring";
export function makeMockRoom( export function makeMockRoom(memberships: CallMembershipData[], localAge: number | null = null): Room {
memberships: CallMembershipData[],
getLocalAge: (() => number) | undefined = undefined,
): Room {
const roomId = randomString(8); const roomId = randomString(8);
// Caching roomState here so it does not get recreated when calling `getLiveTimeline.getState()`
const roomState = makeMockRoomState(memberships, roomId, localAge);
return { return {
roomId: roomId, roomId: roomId,
getLiveTimeline: jest.fn().mockReturnValue({ getLiveTimeline: jest.fn().mockReturnValue({
getState: jest.fn().mockReturnValue(makeMockRoomState(memberships, roomId, getLocalAge)), getState: jest.fn().mockReturnValue(roomState),
}), }),
} as unknown as Room; } as unknown as Room;
} }
export function makeMockRoomState( export function makeMockRoomState(memberships: CallMembershipData[], roomId: string, localAge: number | null = null) {
memberships: CallMembershipData[], const event = mockRTCEvent(memberships, roomId, localAge);
roomId: string,
getLocalAge: (() => number) | undefined,
) {
return { return {
getStateEvents: (_: string, stateKey: string) => { getStateEvents: (_: string, stateKey: string) => {
const event = mockRTCEvent(memberships, roomId, getLocalAge);
if (stateKey !== undefined) return event; if (stateKey !== undefined) return event;
return [event]; return [event];
}, },
}; };
} }
export function mockRTCEvent( export function mockRTCEvent(memberships: CallMembershipData[], roomId: string, localAge: number | null): MatrixEvent {
memberships: CallMembershipData[],
roomId: string,
getLocalAge: (() => number) | undefined,
): MatrixEvent {
const getLocalAgeFn = getLocalAge ?? (() => 10);
return { return {
getType: jest.fn().mockReturnValue(EventType.GroupCallMemberPrefix), getType: jest.fn().mockReturnValue(EventType.GroupCallMemberPrefix),
getContent: jest.fn().mockReturnValue({ getContent: jest.fn().mockReturnValue({
@@ -60,8 +48,7 @@ export function mockRTCEvent(
}), }),
getSender: jest.fn().mockReturnValue("@mock:user.example"), getSender: jest.fn().mockReturnValue("@mock:user.example"),
getTs: jest.fn().mockReturnValue(1000), getTs: jest.fn().mockReturnValue(1000),
getLocalAge: getLocalAgeFn, localTimestamp: Date.now() - (localAge ?? 10),
localTimestamp: Date.now(),
getRoomId: jest.fn().mockReturnValue(roomId), getRoomId: jest.fn().mockReturnValue(roomId),
sender: { sender: {
userId: "@mock:user.example", userId: "@mock:user.example",

View File

@@ -91,7 +91,7 @@ export class CallMembership {
} }
public isExpired(): boolean { public isExpired(): boolean {
return this.getAbsoluteExpiry() < this.parentEvent.getTs() + this.parentEvent.getLocalAge(); return this.getMsUntilExpiry() <= 0;
} }
public getActiveFoci(): Focus[] { public getActiveFoci(): Focus[] {

View File

@@ -404,7 +404,13 @@ export class MatrixEvent extends TypedEventEmitter<MatrixEventEmittedEvents, Mat
}); });
this.txnId = event.txn_id; this.txnId = event.txn_id;
this.localTimestamp = Date.now() - (this.getAge() ?? 0); // The localTimestamp is calculated using the age.
// Some events lack an `age` property, either because they are EDUs such as typing events,
// or due to server-side bugs such as https://github.com/matrix-org/synapse/issues/8429.
// The fallback in these cases will be to use the origin_server_ts.
// For EDUs, the origin_server_ts also is not defined so we use Date.now().
const age = this.getAge();
this.localTimestamp = age !== undefined ? Date.now() - age : this.getTs() ?? Date.now();
this.reEmitter = new TypedReEmitter(this); this.reEmitter = new TypedReEmitter(this);
} }