test(matrix): add coverage for deduped action helpers

This commit is contained in:
Peter Steinberger
2026-02-18 16:17:50 +00:00
parent f5c3702191
commit 98fac87a9e
3 changed files with 198 additions and 0 deletions

View File

@@ -0,0 +1,15 @@
import { describe, expect, it } from "vitest";
import { resolveMatrixActionLimit } from "./limits.js";
describe("resolveMatrixActionLimit", () => {
it("uses fallback for non-finite values", () => {
expect(resolveMatrixActionLimit(undefined, 20)).toBe(20);
expect(resolveMatrixActionLimit(Number.NaN, 20)).toBe(20);
});
it("normalizes finite numbers to positive integers", () => {
expect(resolveMatrixActionLimit(7.9, 20)).toBe(7);
expect(resolveMatrixActionLimit(0, 20)).toBe(1);
expect(resolveMatrixActionLimit(-3, 20)).toBe(1);
});
});

View File

@@ -0,0 +1,74 @@
import type { MatrixClient } from "@vector-im/matrix-bot-sdk";
import { describe, expect, it, vi } from "vitest";
import { listMatrixPins, pinMatrixMessage, unpinMatrixMessage } from "./pins.js";
function createPinsClient(seedPinned: string[], knownBodies: Record<string, string> = {}) {
let pinned = [...seedPinned];
const getRoomStateEvent = vi.fn(async () => ({ pinned: [...pinned] }));
const sendStateEvent = vi.fn(
async (_roomId: string, _type: string, _key: string, payload: any) => {
pinned = [...payload.pinned];
},
);
const getEvent = vi.fn(async (_roomId: string, eventId: string) => {
const body = knownBodies[eventId];
if (!body) {
throw new Error("missing");
}
return {
event_id: eventId,
sender: "@alice:example.org",
type: "m.room.message",
origin_server_ts: 123,
content: { msgtype: "m.text", body },
};
});
return {
client: {
getRoomStateEvent,
sendStateEvent,
getEvent,
stop: vi.fn(),
} as unknown as MatrixClient,
getPinned: () => pinned,
sendStateEvent,
};
}
describe("matrix pins actions", () => {
it("pins a message once even when asked twice", async () => {
const { client, getPinned, sendStateEvent } = createPinsClient(["$a"]);
const first = await pinMatrixMessage("!room:example.org", "$b", { client });
const second = await pinMatrixMessage("!room:example.org", "$b", { client });
expect(first.pinned).toEqual(["$a", "$b"]);
expect(second.pinned).toEqual(["$a", "$b"]);
expect(getPinned()).toEqual(["$a", "$b"]);
expect(sendStateEvent).toHaveBeenCalledTimes(2);
});
it("unpinds only the selected message id", async () => {
const { client, getPinned } = createPinsClient(["$a", "$b", "$c"]);
const result = await unpinMatrixMessage("!room:example.org", "$b", { client });
expect(result.pinned).toEqual(["$a", "$c"]);
expect(getPinned()).toEqual(["$a", "$c"]);
});
it("lists pinned ids and summarizes only resolvable events", async () => {
const { client } = createPinsClient(["$a", "$missing"], { $a: "hello" });
const result = await listMatrixPins("!room:example.org", { client });
expect(result.pinned).toEqual(["$a", "$missing"]);
expect(result.events).toEqual([
expect.objectContaining({
eventId: "$a",
body: "hello",
}),
]);
});
});

View File

@@ -0,0 +1,109 @@
import type { MatrixClient } from "@vector-im/matrix-bot-sdk";
import { describe, expect, it, vi } from "vitest";
import { listMatrixReactions, removeMatrixReactions } from "./reactions.js";
function createReactionsClient(params: {
chunk: Array<{
event_id?: string;
sender?: string;
key?: string;
}>;
userId?: string | null;
}) {
const doRequest = vi.fn(async (_method: string, _path: string, _query: any) => ({
chunk: params.chunk.map((item) => ({
event_id: item.event_id ?? "",
sender: item.sender ?? "",
content: item.key
? {
"m.relates_to": {
rel_type: "m.annotation",
event_id: "$target",
key: item.key,
},
}
: {},
})),
}));
const getUserId = vi.fn(async () => params.userId ?? null);
const redactEvent = vi.fn(async () => undefined);
return {
client: {
doRequest,
getUserId,
redactEvent,
stop: vi.fn(),
} as unknown as MatrixClient,
doRequest,
redactEvent,
};
}
describe("matrix reaction actions", () => {
it("aggregates reactions by key and unique sender", async () => {
const { client, doRequest } = createReactionsClient({
chunk: [
{ event_id: "$1", sender: "@alice:example.org", key: "👍" },
{ event_id: "$2", sender: "@bob:example.org", key: "👍" },
{ event_id: "$3", sender: "@alice:example.org", key: "👎" },
{ event_id: "$4", sender: "@bot:example.org" },
],
userId: "@bot:example.org",
});
const result = await listMatrixReactions("!room:example.org", "$msg", { client, limit: 2.9 });
expect(doRequest).toHaveBeenCalledWith(
"GET",
expect.stringContaining("/rooms/!room%3Aexample.org/relations/%24msg/"),
expect.objectContaining({ limit: 2 }),
);
expect(result).toEqual(
expect.arrayContaining([
expect.objectContaining({
key: "👍",
count: 2,
users: expect.arrayContaining(["@alice:example.org", "@bob:example.org"]),
}),
expect.objectContaining({
key: "👎",
count: 1,
users: ["@alice:example.org"],
}),
]),
);
});
it("removes only current-user reactions matching emoji filter", async () => {
const { client, redactEvent } = createReactionsClient({
chunk: [
{ event_id: "$1", sender: "@me:example.org", key: "👍" },
{ event_id: "$2", sender: "@me:example.org", key: "👎" },
{ event_id: "$3", sender: "@other:example.org", key: "👍" },
],
userId: "@me:example.org",
});
const result = await removeMatrixReactions("!room:example.org", "$msg", {
client,
emoji: "👍",
});
expect(result).toEqual({ removed: 1 });
expect(redactEvent).toHaveBeenCalledTimes(1);
expect(redactEvent).toHaveBeenCalledWith("!room:example.org", "$1");
});
it("returns removed=0 when current user id is unavailable", async () => {
const { client, redactEvent } = createReactionsClient({
chunk: [{ event_id: "$1", sender: "@me:example.org", key: "👍" }],
userId: null,
});
const result = await removeMatrixReactions("!room:example.org", "$msg", { client });
expect(result).toEqual({ removed: 0 });
expect(redactEvent).not.toHaveBeenCalled();
});
});