Skip to content

Commit

Permalink
Test for inserting events into the timeline in timestamp order (#3391)
Browse files Browse the repository at this point in the history
* Move mkReaction into test-utils so it can be used by other code

* Basic test for inserting messages into the thread timeline
  • Loading branch information
andybalaam authored May 24, 2023
1 parent 063d69e commit 4732098
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 31 deletions.
42 changes: 41 additions & 1 deletion spec/test-utils/test-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import "../olm-loader";

import { logger } from "../../src/logger";
import { IContent, IEvent, IEventRelation, IUnsigned, MatrixEvent, MatrixEventEvent } from "../../src/models/event";
import { ClientEvent, EventType, IPusher, MatrixClient, MsgType } from "../../src";
import { ClientEvent, EventType, IPusher, MatrixClient, MsgType, RelationType } from "../../src";
import { SyncState } from "../../src/sync";
import { eventMapperFor } from "../../src/event-mapper";

Expand Down Expand Up @@ -258,6 +258,9 @@ export interface IMessageOpts {
* @param opts.user - The user ID for the event.
* @param opts.msg - Optional. The content.body for the event.
* @param opts.event - True to make a MatrixEvent.
* @param opts.relatesTo - An IEventRelation relating this to another event.
* @param opts.ts - The timestamp of the event.
* @param opts.event - True to make a MatrixEvent.
* @param client - If passed along with opts.event=true will be used to set up re-emitters.
* @returns The event
*/
Expand Down Expand Up @@ -297,6 +300,7 @@ interface IReplyMessageOpts extends IMessageOpts {
* @param opts.room - The room ID for the event.
* @param opts.user - The user ID for the event.
* @param opts.msg - Optional. The content.body for the event.
* @param opts.ts - The timestamp of the event.
* @param opts.replyToMessage - The replied message
* @param opts.event - True to make a MatrixEvent.
* @param client - If passed along with opts.event=true will be used to set up re-emitters.
Expand Down Expand Up @@ -330,6 +334,42 @@ export function mkReplyMessage(
return mkEvent(eventOpts, client);
}

/**
* Create a reaction event.
*
* @param target - the event we are reacting to.
* @param client - the MatrixClient
* @param userId - the userId of the sender
* @param roomId - the id of the room we are in
* @param ts - The timestamp of the event.
* @returns The event
*/
export function mkReaction(
target: MatrixEvent,
client: MatrixClient,
userId: string,
roomId: string,
ts?: number,
): MatrixEvent {
return mkEvent(
{
event: true,
type: EventType.Reaction,
user: userId,
room: roomId,
content: {
"m.relates_to": {
rel_type: RelationType.Annotation,
event_id: target.getId()!,
key: Math.random().toString(),
},
},
ts,
},
client,
);
}

/**
* A mock implementation of webstorage
*/
Expand Down
54 changes: 52 additions & 2 deletions spec/unit/models/thread.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,20 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

import { mocked } from "jest-mock";

import { MatrixClient, PendingEventOrdering } from "../../../src/client";
import { Room } from "../../../src/models/room";
import { Thread, THREAD_RELATION_TYPE, ThreadEvent } from "../../../src/models/thread";
import { Thread, THREAD_RELATION_TYPE, ThreadEvent, FeatureSupport } from "../../../src/models/thread";
import { mkThread } from "../../test-utils/thread";
import { TestClient } from "../../TestClient";
import { emitPromise, mkMessage, mock } from "../../test-utils/test-utils";
import { emitPromise, mkMessage, mkReaction, mock } from "../../test-utils/test-utils";
import { Direction, EventStatus, MatrixEvent } from "../../../src";
import { ReceiptType } from "../../../src/@types/read_receipts";
import { getMockClientWithEventEmitter, mockClientMethodsUser } from "../../test-utils/client";
import { ReEmitter } from "../../../src/ReEmitter";
import { Feature, ServerSupport } from "../../../src/feature";
import { eventMapperFor } from "../../../src/event-mapper";

describe("Thread", () => {
describe("constructor", () => {
Expand Down Expand Up @@ -424,4 +427,51 @@ describe("Thread", () => {
expect(mock).toHaveBeenCalledWith("b1", "f1");
});
});

describe("insertEventIntoTimeline", () => {
it("Inserts a reply in timestamp order", () => {
// Assumption: no server side support because if we have it, events
// can only be added to the timeline after the thread has been
// initialised, and we are not properly initialising it here.
expect(Thread.hasServerSideSupport).toBe(FeatureSupport.None);

const client = createClientWithEventMapper();
const userId = "user1";
const room = new Room("room1", client, userId);

// Given a thread with a root plus 5 messages
const { thread, events } = mkThread({
room,
client,
authorId: userId,
participantUserIds: ["@bob:hs", "@chia:hs", "@dv:hs"],
length: 6,
ts: 100, // Events will be at ts 100, 101, 102, 103, 104 and 105
});

// When we insert a reply to the second thread message
const replyEvent = mkReaction(events[2], client, userId, room.roomId, 104);
thread.insertEventIntoTimeline(replyEvent);

// Then the reply is inserted based on its timestamp
expect(thread.events.map((ev) => ev.getId())).toEqual([
events[0].getId(),
events[1].getId(),
events[2].getId(),
events[3].getId(),
events[4].getId(),
replyEvent.getId(),
events[5].getId(),
]);
});

function createClientWithEventMapper(): MatrixClient {
const client = mock(MatrixClient, "MatrixClient");
client.reEmitter = mock(ReEmitter, "ReEmitter");
client.canSupport = new Map();
jest.spyOn(client, "getEventMapper").mockReturnValue(eventMapperFor(client, {}));
mocked(client.supportsThreads).mockReturnValue(true);
return client;
}
});
});
38 changes: 10 additions & 28 deletions spec/unit/room.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,24 +137,6 @@ describe("Room", function () {
room.client,
);

const mkReaction = (target: MatrixEvent) =>
utils.mkEvent(
{
event: true,
type: EventType.Reaction,
user: userA,
room: roomId,
content: {
"m.relates_to": {
rel_type: RelationType.Annotation,
event_id: target.getId()!,
key: Math.random().toString(),
},
},
},
room.client,
);

const mkRedaction = (target: MatrixEvent) =>
utils.mkEvent(
{
Expand Down Expand Up @@ -2674,7 +2656,7 @@ describe("Room", function () {
threadResponse1.localTimestamp += 1000;
const threadResponse2 = mkThreadResponse(threadRoot);
threadResponse2.localTimestamp += 2000;
const threadResponse2Reaction = mkReaction(threadResponse2);
const threadResponse2Reaction = utils.mkReaction(threadResponse2, room.client, userA, roomId);

room.client.fetchRoomEvent = (eventId: string) =>
Promise.resolve({
Expand Down Expand Up @@ -2714,7 +2696,7 @@ describe("Room", function () {
threadResponse1.localTimestamp += 1000;
const threadResponse2 = mkThreadResponse(threadRoot);
threadResponse2.localTimestamp += 2000;
const threadResponse2Reaction = mkReaction(threadResponse2);
const threadResponse2Reaction = utils.mkReaction(threadResponse2, room.client, userA, roomId);

room.client.fetchRoomEvent = (eventId: string) =>
Promise.resolve({
Expand Down Expand Up @@ -2862,8 +2844,8 @@ describe("Room", function () {
const randomMessage = mkMessage();
const threadRoot = mkMessage();
const threadResponse1 = mkThreadResponse(threadRoot);
const threadReaction1 = mkReaction(threadRoot);
const threadReaction2 = mkReaction(threadRoot);
const threadReaction1 = utils.mkReaction(threadRoot, room.client, userA, roomId);
const threadReaction2 = utils.mkReaction(threadRoot, room.client, userA, roomId);
const threadReaction2Redaction = mkRedaction(threadReaction2);

const roots = new Set([threadRoot.getId()!]);
Expand Down Expand Up @@ -2900,8 +2882,8 @@ describe("Room", function () {
it("thread response and its relations&redactions should be only in thread timeline", () => {
const threadRoot = mkMessage();
const threadResponse1 = mkThreadResponse(threadRoot);
const threadReaction1 = mkReaction(threadResponse1);
const threadReaction2 = mkReaction(threadResponse1);
const threadReaction1 = utils.mkReaction(threadResponse1, room.client, userA, roomId);
const threadReaction2 = utils.mkReaction(threadResponse1, room.client, userA, roomId);
const threadReaction2Redaction = mkRedaction(threadReaction2);

const roots = new Set([threadRoot.getId()!]);
Expand All @@ -2922,8 +2904,8 @@ describe("Room", function () {
const threadRoot = mkMessage();
const threadResponse1 = mkThreadResponse(threadRoot);
const reply1 = mkReply(threadResponse1);
const reaction1 = mkReaction(reply1);
const reaction2 = mkReaction(reply1);
const reaction1 = utils.mkReaction(reply1, room.client, userA, roomId);
const reaction2 = utils.mkReaction(reply1, room.client, userA, roomId);
const reaction2Redaction = mkRedaction(reply1);

const roots = new Set([threadRoot.getId()!]);
Expand Down Expand Up @@ -2957,9 +2939,9 @@ describe("Room", function () {
it("should aggregate relations in thread event timeline set", async () => {
Thread.setServerSideSupport(FeatureSupport.Stable);
const threadRoot = mkMessage();
const rootReaction = mkReaction(threadRoot);
const rootReaction = utils.mkReaction(threadRoot, room.client, userA, roomId);
const threadResponse = mkThreadResponse(threadRoot);
const threadReaction = mkReaction(threadResponse);
const threadReaction = utils.mkReaction(threadResponse, room.client, userA, roomId);

const events = [threadRoot, rootReaction, threadResponse, threadReaction];

Expand Down

0 comments on commit 4732098

Please sign in to comment.