Skip to content

Commit 5bcd26e

Browse files
dbkrhughnst3chguytoger5
authored
Support MSC4222 state_after (#4487)
* WIP support for state_after * Fix sliding sync sdk / embedded tests * Allow both state & state_after to be undefined Since it must have allowed state to be undefined previously: the test had it as such. * Fix limited sync handling * Need to use state_after being undefined if state can be undefined anyway * Make sliding sync sdk tests pass * Remove deprecated interfaces & backwards-compat code * Remove useless assignment * Use updates unstable prefix * Clarify docs * Remove additional semi-backwards compatible overload * Update unstable prefixes * Iterate Signed-off-by: Michael Telatynski <[email protected]> * Iterate Signed-off-by: Michael Telatynski <[email protected]> * Fix test Signed-off-by: Michael Telatynski <[email protected]> * Iterate Signed-off-by: Michael Telatynski <[email protected]> * Iterate Signed-off-by: Michael Telatynski <[email protected]> * Add test for MSC4222 behaviour Signed-off-by: Michael Telatynski <[email protected]> * Improve coverage Signed-off-by: Michael Telatynski <[email protected]> * Iterate Signed-off-by: Michael Telatynski <[email protected]> * Fix tests Signed-off-by: Michael Telatynski <[email protected]> * Iterate Signed-off-by: Michael Telatynski <[email protected]> * Tidy Signed-off-by: Michael Telatynski <[email protected]> * Add comments to explain why things work as they are. * Fix sync accumulator for state_after sync handling Signed-off-by: Michael Telatynski <[email protected]> * Add tests Signed-off-by: Michael Telatynski <[email protected]> * Revert "Fix room state being updated with old (now overwritten) state and emitting for those updates. (#4242)" This reverts commit 957329b. * Fix Sync Accumulator toJSON putting start timeline state in state_after field Signed-off-by: Michael Telatynski <[email protected]> * Update tests Signed-off-by: Michael Telatynski <[email protected]> * Add test case Signed-off-by: Michael Telatynski <[email protected]> * Iterate Signed-off-by: Michael Telatynski <[email protected]> --------- Signed-off-by: Michael Telatynski <[email protected]> Co-authored-by: Hugh Nimmo-Smith <[email protected]> Co-authored-by: Michael Telatynski <[email protected]> Co-authored-by: Timo <[email protected]>
1 parent 66f099b commit 5bcd26e

32 files changed

+1348
-740
lines changed

spec/integ/crypto/crypto.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1327,7 +1327,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string,
13271327

13281328
const syncResponse = getSyncResponse(["@bob:xyz"]);
13291329
// Every 2 messages in the room, the session should be rotated
1330-
syncResponse.rooms[Category.Join][ROOM_ID].state.events[0].content = {
1330+
syncResponse.rooms[Category.Join][ROOM_ID].state!.events[0].content = {
13311331
algorithm: "m.megolm.v1.aes-sha2",
13321332
rotation_period_msgs: 2,
13331333
};
@@ -1383,7 +1383,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string,
13831383
const oneHourInMs = 60 * 60 * 1000;
13841384

13851385
// Every 1h the session should be rotated
1386-
syncResponse.rooms[Category.Join][ROOM_ID].state.events[0].content = {
1386+
syncResponse.rooms[Category.Join][ROOM_ID].state!.events[0].content = {
13871387
algorithm: "m.megolm.v1.aes-sha2",
13881388
rotation_period_ms: oneHourInMs,
13891389
};

spec/integ/matrix-client-event-timeline.spec.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1144,7 +1144,7 @@ describe("MatrixClient event timelines", function () {
11441144

11451145
const prom = emitPromise(room, ThreadEvent.Update);
11461146
// Assume we're seeing the reply while loading backlog
1147-
await room.addLiveEvents([THREAD_REPLY2]);
1147+
await room.addLiveEvents([THREAD_REPLY2], { addToState: false });
11481148
httpBackend
11491149
.when(
11501150
"GET",
@@ -1155,7 +1155,7 @@ describe("MatrixClient event timelines", function () {
11551155
});
11561156
await flushHttp(prom);
11571157
// but while loading the metadata, a new reply has arrived
1158-
await room.addLiveEvents([THREAD_REPLY3]);
1158+
await room.addLiveEvents([THREAD_REPLY3], { addToState: false });
11591159
const thread = room.getThread(THREAD_ROOT_UPDATED.event_id!)!;
11601160
// then the events should still be all in the right order
11611161
expect(thread.events.map((it) => it.getId())).toEqual([
@@ -1247,7 +1247,7 @@ describe("MatrixClient event timelines", function () {
12471247

12481248
const prom = emitPromise(room, ThreadEvent.Update);
12491249
// Assume we're seeing the reply while loading backlog
1250-
await room.addLiveEvents([THREAD_REPLY2]);
1250+
await room.addLiveEvents([THREAD_REPLY2], { addToState: false });
12511251
httpBackend
12521252
.when(
12531253
"GET",
@@ -1263,7 +1263,7 @@ describe("MatrixClient event timelines", function () {
12631263
});
12641264
await flushHttp(prom);
12651265
// but while loading the metadata, a new reply has arrived
1266-
await room.addLiveEvents([THREAD_REPLY3]);
1266+
await room.addLiveEvents([THREAD_REPLY3], { addToState: false });
12671267
const thread = room.getThread(THREAD_ROOT_UPDATED.event_id!)!;
12681268
// then the events should still be all in the right order
12691269
expect(thread.events.map((it) => it.getId())).toEqual([
@@ -1560,7 +1560,7 @@ describe("MatrixClient event timelines", function () {
15601560
thread.initialEventsFetched = true;
15611561
const prom = emitPromise(room, ThreadEvent.NewReply);
15621562
respondToEvent(THREAD_ROOT_UPDATED);
1563-
await room.addLiveEvents([THREAD_REPLY2]);
1563+
await room.addLiveEvents([THREAD_REPLY2], { addToState: false });
15641564
await httpBackend.flushAllExpected();
15651565
await prom;
15661566
expect(thread.length).toBe(2);
@@ -1685,7 +1685,7 @@ describe("MatrixClient event timelines", function () {
16851685
thread.initialEventsFetched = true;
16861686
const prom = emitPromise(room, ThreadEvent.Update);
16871687
respondToEvent(THREAD_ROOT_UPDATED);
1688-
await room.addLiveEvents([THREAD_REPLY_REACTION]);
1688+
await room.addLiveEvents([THREAD_REPLY_REACTION], { addToState: false });
16891689
await httpBackend.flushAllExpected();
16901690
await prom;
16911691
expect(thread.length).toBe(1); // reactions don't count towards the length of a thread

spec/integ/matrix-client-methods.spec.ts

Lines changed: 33 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -168,14 +168,17 @@ describe("MatrixClient", function () {
168168
type: "test",
169169
content: {},
170170
});
171-
room.addLiveEvents([
172-
utils.mkMembership({
173-
user: userId,
174-
room: roomId,
175-
mship: KnownMembership.Join,
176-
event: true,
177-
}),
178-
]);
171+
room.addLiveEvents(
172+
[
173+
utils.mkMembership({
174+
user: userId,
175+
room: roomId,
176+
mship: KnownMembership.Join,
177+
event: true,
178+
}),
179+
],
180+
{ addToState: true },
181+
);
179182
httpBackend.verifyNoOutstandingRequests();
180183
store.storeRoom(room);
181184

@@ -188,14 +191,17 @@ describe("MatrixClient", function () {
188191
const roomId = "!roomId:server";
189192
const roomAlias = "#my-fancy-room:server";
190193
const room = new Room(roomId, client, userId);
191-
room.addLiveEvents([
192-
utils.mkMembership({
193-
user: userId,
194-
room: roomId,
195-
mship: KnownMembership.Join,
196-
event: true,
197-
}),
198-
]);
194+
room.addLiveEvents(
195+
[
196+
utils.mkMembership({
197+
user: userId,
198+
room: roomId,
199+
mship: KnownMembership.Join,
200+
event: true,
201+
}),
202+
],
203+
{ addToState: true },
204+
);
199205
store.storeRoom(room);
200206

201207
// The method makes a request to resolve the alias
@@ -275,14 +281,17 @@ describe("MatrixClient", function () {
275281
content: {},
276282
});
277283

278-
room.addLiveEvents([
279-
utils.mkMembership({
280-
user: userId,
281-
room: roomId,
282-
mship: KnownMembership.Knock,
283-
event: true,
284-
}),
285-
]);
284+
room.addLiveEvents(
285+
[
286+
utils.mkMembership({
287+
user: userId,
288+
room: roomId,
289+
mship: KnownMembership.Knock,
290+
event: true,
291+
}),
292+
],
293+
{ addToState: true },
294+
);
286295

287296
httpBackend.verifyNoOutstandingRequests();
288297
store.storeRoom(room);

spec/integ/matrix-client-syncing.spec.ts

Lines changed: 163 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -556,7 +556,7 @@ describe("MatrixClient syncing", () => {
556556
});
557557

558558
it("should resolve incoming invites from /sync", () => {
559-
syncData.rooms.join[roomOne].state.events.push(
559+
syncData.rooms.join[roomOne].state!.events.push(
560560
utils.mkMembership({
561561
room: roomOne,
562562
mship: KnownMembership.Invite,
@@ -589,7 +589,7 @@ describe("MatrixClient syncing", () => {
589589
name: "The Ghost",
590590
}) as IMinimalEvent,
591591
];
592-
syncData.rooms.join[roomOne].state.events.push(
592+
syncData.rooms.join[roomOne].state!.events.push(
593593
utils.mkMembership({
594594
room: roomOne,
595595
mship: KnownMembership.Invite,
@@ -617,7 +617,7 @@ describe("MatrixClient syncing", () => {
617617
name: "The Ghost",
618618
}) as IMinimalEvent,
619619
];
620-
syncData.rooms.join[roomOne].state.events.push(
620+
syncData.rooms.join[roomOne].state!.events.push(
621621
utils.mkMembership({
622622
room: roomOne,
623623
mship: KnownMembership.Invite,
@@ -644,7 +644,7 @@ describe("MatrixClient syncing", () => {
644644
});
645645

646646
it("should no-op if resolveInvitesToProfiles is not set", () => {
647-
syncData.rooms.join[roomOne].state.events.push(
647+
syncData.rooms.join[roomOne].state!.events.push(
648648
utils.mkMembership({
649649
room: roomOne,
650650
mship: KnownMembership.Invite,
@@ -1373,6 +1373,114 @@ describe("MatrixClient syncing", () => {
13731373
expect(stateEventEmitCount).toEqual(2);
13741374
});
13751375
});
1376+
1377+
describe("msc4222", () => {
1378+
const roomOneSyncOne = {
1379+
"timeline": {
1380+
events: [
1381+
utils.mkMessage({
1382+
room: roomOne,
1383+
user: otherUserId,
1384+
msg: "hello",
1385+
}),
1386+
],
1387+
},
1388+
"org.matrix.msc4222.state_after": {
1389+
events: [
1390+
utils.mkEvent({
1391+
type: "m.room.name",
1392+
room: roomOne,
1393+
user: otherUserId,
1394+
content: {
1395+
name: "Initial room name",
1396+
},
1397+
}),
1398+
utils.mkMembership({
1399+
room: roomOne,
1400+
mship: KnownMembership.Join,
1401+
user: otherUserId,
1402+
}),
1403+
utils.mkMembership({
1404+
room: roomOne,
1405+
mship: KnownMembership.Join,
1406+
user: selfUserId,
1407+
}),
1408+
utils.mkEvent({
1409+
type: "m.room.create",
1410+
room: roomOne,
1411+
user: selfUserId,
1412+
content: {},
1413+
}),
1414+
],
1415+
},
1416+
};
1417+
const roomOneSyncTwo = {
1418+
"org.matrix.msc4222.state_after": {
1419+
events: [
1420+
utils.mkEvent({
1421+
type: "m.room.topic",
1422+
room: roomOne,
1423+
user: selfUserId,
1424+
content: { topic: "A new room topic" },
1425+
}),
1426+
],
1427+
},
1428+
"state": {
1429+
events: [
1430+
utils.mkEvent({
1431+
type: "m.room.name",
1432+
room: roomOne,
1433+
user: selfUserId,
1434+
content: { name: "A new room name" },
1435+
}),
1436+
],
1437+
},
1438+
};
1439+
1440+
it("should ignore state events in timeline when state_after is present", async () => {
1441+
httpBackend!.when("GET", "/sync").respond(200, {
1442+
rooms: {
1443+
join: { [roomOne]: roomOneSyncOne },
1444+
},
1445+
});
1446+
httpBackend!.when("GET", "/sync").respond(200, {
1447+
rooms: {
1448+
join: { [roomOne]: roomOneSyncTwo },
1449+
},
1450+
});
1451+
1452+
client!.startClient();
1453+
return Promise.all([httpBackend!.flushAllExpected(), awaitSyncEvent(2)]).then(() => {
1454+
const room = client!.getRoom(roomOne)!;
1455+
expect(room.name).toEqual("Initial room name");
1456+
expect(room.currentState.getStateEvents("m.room.topic", "")?.getContent().topic).toBe(
1457+
"A new room topic",
1458+
);
1459+
});
1460+
});
1461+
1462+
it("should respect state events in state_after for left rooms", async () => {
1463+
httpBackend!.when("GET", "/sync").respond(200, {
1464+
rooms: {
1465+
join: { [roomOne]: roomOneSyncOne },
1466+
},
1467+
});
1468+
httpBackend!.when("GET", "/sync").respond(200, {
1469+
rooms: {
1470+
leave: { [roomOne]: roomOneSyncTwo },
1471+
},
1472+
});
1473+
1474+
client!.startClient();
1475+
return Promise.all([httpBackend!.flushAllExpected(), awaitSyncEvent(2)]).then(() => {
1476+
const room = client!.getRoom(roomOne)!;
1477+
expect(room.name).toEqual("Initial room name");
1478+
expect(room.currentState.getStateEvents("m.room.topic", "")?.getContent().topic).toBe(
1479+
"A new room topic",
1480+
);
1481+
});
1482+
});
1483+
});
13761484
});
13771485

13781486
describe("timeline", () => {
@@ -2274,6 +2382,57 @@ describe("MatrixClient syncing", () => {
22742382
}),
22752383
]);
22762384
});
2385+
2386+
describe("msc4222", () => {
2387+
it("should respect state events in state_after for left rooms", async () => {
2388+
httpBackend!.when("POST", "/filter").respond(200, {
2389+
filter_id: "another_id",
2390+
});
2391+
2392+
httpBackend!.when("GET", "/sync").respond(200, {
2393+
rooms: {
2394+
leave: {
2395+
[roomOne]: {
2396+
"org.matrix.msc4222.state_after": {
2397+
events: [
2398+
utils.mkEvent({
2399+
type: "m.room.topic",
2400+
room: roomOne,
2401+
user: selfUserId,
2402+
content: { topic: "A new room topic" },
2403+
}),
2404+
],
2405+
},
2406+
"state": {
2407+
events: [
2408+
utils.mkEvent({
2409+
type: "m.room.name",
2410+
room: roomOne,
2411+
user: selfUserId,
2412+
content: { name: "A new room name" },
2413+
}),
2414+
],
2415+
},
2416+
},
2417+
},
2418+
},
2419+
});
2420+
2421+
const [[room]] = await Promise.all([
2422+
client!.syncLeftRooms(),
2423+
2424+
// first flush the filter request; this will make syncLeftRooms make its /sync call
2425+
httpBackend!.flush("/filter").then(() => {
2426+
return httpBackend!.flushAllExpected();
2427+
}),
2428+
]);
2429+
2430+
expect(room.name).toEqual("Empty room");
2431+
expect(room.currentState.getStateEvents("m.room.topic", "")?.getContent().topic).toBe(
2432+
"A new room topic",
2433+
);
2434+
});
2435+
});
22772436
});
22782437

22792438
describe("peek", () => {

spec/integ/matrix-client-unread-notifications.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ describe("MatrixClient syncing", () => {
128128

129129
const thread = mkThread({ room, client: client!, authorId: selfUserId, participantUserIds: [selfUserId] });
130130
const threadReply = thread.events.at(-1)!;
131-
await room.addLiveEvents([thread.rootEvent]);
131+
await room.addLiveEvents([thread.rootEvent], { addToState: false });
132132

133133
// Initialize read receipt datastructure before testing the reaction
134134
room.addReceiptToStructure(thread.rootEvent.getId()!, ReceiptType.Read, selfUserId, { ts: 1 }, false);

0 commit comments

Comments
 (0)