mirror of
https://github.com/the-draupnir-project/Draupnir.git
synced 2026-04-04 13:25:43 +00:00
* Rename to Draupnir in Appropriate Places * Integrate Code review feedback on CHANGELOG.md
233 lines
7.0 KiB
TypeScript
233 lines
7.0 KiB
TypeScript
// Copyright 2022 - 2024 Gnuxie <Gnuxie@protonmail.com>
|
|
// Copyright 2021 - 2022 The Matrix.org Foundation C.I.C.
|
|
//
|
|
// SPDX-License-Identifier: AFL-3.0 AND Apache-2.0
|
|
//
|
|
// SPDX-FileAttributionText: <text>
|
|
// This modified file incorporates work from mjolnir
|
|
// https://github.com/matrix-org/mjolnir
|
|
// </text>
|
|
|
|
import { strict as assert } from "assert";
|
|
|
|
import { newTestUser } from "./clientHelper";
|
|
import { getMessagesByUserIn } from "../../src/utils";
|
|
import { TextMessageContent } from "matrix-protection-suite";
|
|
|
|
/**
|
|
* Ensure that Draupnir paginates only the necessary segment of the room timeline when backfilling.
|
|
*/
|
|
describe("Test: timeline pagination", function () {
|
|
it("does not paginate across the entire room history while backfilling.", async function () {
|
|
this.timeout(60000);
|
|
// Create a few users and a room.
|
|
const badUser = await newTestUser(this.config.homeserverUrl, {
|
|
name: { contains: "spammer" },
|
|
});
|
|
const badUserId = await badUser.getUserId();
|
|
const moderator = await newTestUser(this.config.homeserverUrl, {
|
|
name: { contains: "moderator" },
|
|
});
|
|
const targetRoom = await moderator.createRoom({
|
|
invite: [await badUser.getUserId()],
|
|
});
|
|
await badUser.joinRoom(targetRoom);
|
|
|
|
// send some irrelevant messages
|
|
await Promise.all(
|
|
[...Array(200).keys()].map((i) =>
|
|
moderator.sendMessage(targetRoom, {
|
|
msgtype: "m.text.",
|
|
body: `Irrelevant Message #${i}`,
|
|
})
|
|
)
|
|
);
|
|
// bad guy sends 5 messages
|
|
for (let i = 0; i < 5; i++) {
|
|
await badUser.sendMessage(targetRoom, {
|
|
msgtype: "m.text",
|
|
body: "Very Bad Stuff",
|
|
});
|
|
}
|
|
// send some irrelevant messages
|
|
await Promise.all(
|
|
[...Array(50).keys()].map((i) =>
|
|
moderator.sendMessage(targetRoom, {
|
|
msgtype: "m.text.",
|
|
body: `Irrelevant Message #${i}`,
|
|
})
|
|
)
|
|
);
|
|
// bad guy sends 1 extra message at the most recent edge of the timeline.
|
|
await badUser.sendMessage(targetRoom, {
|
|
msgtype: "m.text",
|
|
body: "Very Bad Stuff",
|
|
});
|
|
// then call this paignator and ensure that we don't go across the entire room history.
|
|
let cbCount = 0;
|
|
let eventCount = 0;
|
|
await getMessagesByUserIn(
|
|
moderator,
|
|
badUserId,
|
|
targetRoom,
|
|
1000,
|
|
function (events) {
|
|
cbCount += 1;
|
|
eventCount += events.length;
|
|
events.map((e) => {
|
|
assert.equal(
|
|
e.sender,
|
|
badUserId,
|
|
"All the events should be from the same sender"
|
|
);
|
|
});
|
|
}
|
|
);
|
|
assert.equal(
|
|
cbCount,
|
|
1,
|
|
"The callback only needs to be called once with all the messages because the events should be filtered."
|
|
);
|
|
assert.equal(
|
|
eventCount,
|
|
7,
|
|
"There shouldn't be any more events (1 member event and 6 messages), and they should all be from the same account."
|
|
);
|
|
});
|
|
it("does not call the callback with an empty array when there are no relevant events", async function () {
|
|
this.timeout(60000);
|
|
const badUser = await newTestUser(this.config.homeserverUrl, {
|
|
name: { contains: "spammer" },
|
|
});
|
|
const badUserId = await badUser.getUserId();
|
|
const moderator = await newTestUser(this.config.homeserverUrl, {
|
|
name: { contains: "moderator" },
|
|
});
|
|
const targetRoom = await moderator.createRoom();
|
|
// send some irrelevant messages
|
|
await Promise.all(
|
|
[...Array(200).keys()].map((i) =>
|
|
moderator.sendMessage(targetRoom, {
|
|
msgtype: "m.text.",
|
|
body: `Irrelevant Message #${i}`,
|
|
})
|
|
)
|
|
);
|
|
// The callback should not be called.
|
|
let cbCount = 0;
|
|
await getMessagesByUserIn(
|
|
moderator,
|
|
badUserId,
|
|
targetRoom,
|
|
1000,
|
|
(_events) => {
|
|
cbCount += 1;
|
|
}
|
|
);
|
|
assert.equal(cbCount, 0, "The callback should never get called");
|
|
});
|
|
it("The limit provided is respected", async function () {
|
|
this.timeout(60000);
|
|
const badUser = await newTestUser(this.config.homeserverUrl, {
|
|
name: { contains: "spammer" },
|
|
});
|
|
const badUserId = await badUser.getUserId();
|
|
const moderator = await newTestUser(this.config.homeserverUrl, {
|
|
name: { contains: "moderator" },
|
|
});
|
|
const targetRoom = await moderator.createRoom({
|
|
invite: [await badUser.getUserId()],
|
|
});
|
|
await badUser.joinRoom(targetRoom);
|
|
// send some bad person messages
|
|
// bad guy sends 5 messages at the start of the timeline
|
|
for (let i = 0; i < 5; i++) {
|
|
await badUser.sendMessage(targetRoom, {
|
|
msgtype: "m.text",
|
|
body: "Very Bad Stuff",
|
|
});
|
|
}
|
|
// send some irrelevant messages
|
|
await Promise.all(
|
|
[...Array(200).keys()].map((i) =>
|
|
moderator.sendMessage(targetRoom, {
|
|
msgtype: "m.text.",
|
|
body: `Irrelevant Message #${i}`,
|
|
})
|
|
)
|
|
);
|
|
let cbCount = 0;
|
|
await getMessagesByUserIn(
|
|
moderator,
|
|
"*spammer*",
|
|
targetRoom,
|
|
200,
|
|
(_events) => {
|
|
cbCount += 1;
|
|
}
|
|
);
|
|
// Remember that the limit is the number of events that getMessagesByUserIn has checked against the glob,
|
|
// not the number of events to provide to the callback.
|
|
// E.g. we don't want to paginate to the beginning of history just because less than 200 events match the glob,
|
|
// which is very likely if a user has only just started sending messages.
|
|
assert.equal(
|
|
cbCount,
|
|
0,
|
|
"The callback should never be called as the limit should be reached beforehand."
|
|
);
|
|
await getMessagesByUserIn(
|
|
moderator,
|
|
"*spammer*",
|
|
targetRoom,
|
|
205,
|
|
(events) => {
|
|
cbCount += 1;
|
|
events.map((e) => {
|
|
assert.equal(
|
|
e.sender,
|
|
badUserId,
|
|
"All the events should be from the same sender"
|
|
);
|
|
});
|
|
}
|
|
);
|
|
assert.equal(
|
|
cbCount,
|
|
1,
|
|
"The callback should be called once with events matching the glob."
|
|
);
|
|
});
|
|
it("Gives the events to the callback ordered by youngest first (even more important when the limit is reached halfway through a chunk).", async function () {
|
|
this.timeout(60000);
|
|
const moderator = await newTestUser(this.config.homeserverUrl, {
|
|
name: { contains: "moderator" },
|
|
});
|
|
const moderatorId = await moderator.getUserId();
|
|
const targetRoom = await moderator.createRoom();
|
|
for (let i = 0; i < 20; i++) {
|
|
await moderator.sendMessage(targetRoom, {
|
|
msgtype: "m.text.",
|
|
body: `${i}`,
|
|
});
|
|
}
|
|
await getMessagesByUserIn(
|
|
moderator,
|
|
moderatorId,
|
|
targetRoom,
|
|
5,
|
|
(events) => {
|
|
const messageNumbers = events.map((event) =>
|
|
parseInt((event.content as TextMessageContent).body, 10)
|
|
);
|
|
messageNumbers.map((n) => {
|
|
assert.equal(
|
|
n >= 15,
|
|
true,
|
|
"The youngest events should be given to the callback first."
|
|
);
|
|
});
|
|
}
|
|
);
|
|
});
|
|
});
|