test: share matrix event fixtures

This commit is contained in:
Peter Steinberger
2026-04-20 16:22:01 +01:00
parent 938a78f9bf
commit e75ae8b3db
2 changed files with 99 additions and 103 deletions

View File

@@ -2,6 +2,25 @@ import type { MatrixEvent } from "matrix-js-sdk/lib/matrix.js";
import { describe, expect, it } from "vitest";
import { buildHttpError, matrixEventToRaw, parseMxc } from "./event-helpers.js";
const makeEditedMessageEvent = (): MatrixEvent =>
({
getId: () => "$root",
getSender: () => "@alice:example.org",
getType: () => "m.room.message",
getTs: () => 1000,
getOriginalContent: () => ({ body: "original", msgtype: "m.text" }),
getContent: () => ({
body: "@bot edited",
"m.mentions": { user_ids: ["@bot:example.org"] },
msgtype: "m.text",
}),
getUnsigned: () => ({
"m.relations": {
"m.replace": { event_id: "$edit" },
},
}),
}) as unknown as MatrixEvent;
describe("event-helpers", () => {
it("parses mxc URIs", () => {
expect(parseMxc("mxc://server.example/media-id")).toEqual({
@@ -59,25 +78,7 @@ describe("event-helpers", () => {
});
it("serializes current content by default for read APIs", () => {
const event = {
getId: () => "$root",
getSender: () => "@alice:example.org",
getType: () => "m.room.message",
getTs: () => 1000,
getOriginalContent: () => ({ body: "original", msgtype: "m.text" }),
getContent: () => ({
body: "@bot edited",
"m.mentions": { user_ids: ["@bot:example.org"] },
msgtype: "m.text",
}),
getUnsigned: () => ({
"m.relations": {
"m.replace": { event_id: "$edit" },
},
}),
} as unknown as MatrixEvent;
expect(matrixEventToRaw(event)).toMatchObject({
expect(matrixEventToRaw(makeEditedMessageEvent())).toMatchObject({
content: {
body: "@bot edited",
"m.mentions": { user_ids: ["@bot:example.org"] },
@@ -87,25 +88,7 @@ describe("event-helpers", () => {
});
it("can serialize original content for inbound trigger filtering", () => {
const event = {
getId: () => "$root",
getSender: () => "@alice:example.org",
getType: () => "m.room.message",
getTs: () => 1000,
getOriginalContent: () => ({ body: "original", msgtype: "m.text" }),
getContent: () => ({
body: "@bot edited",
"m.mentions": { user_ids: ["@bot:example.org"] },
msgtype: "m.text",
}),
getUnsigned: () => ({
"m.relations": {
"m.replace": { event_id: "$edit" },
},
}),
} as unknown as MatrixEvent;
expect(matrixEventToRaw(event, { contentMode: "original" })).toMatchObject({
expect(matrixEventToRaw(makeEditedMessageEvent(), { contentMode: "original" })).toMatchObject({
content: { body: "original", msgtype: "m.text" },
unsigned: {
"m.relations": {

View File

@@ -4,6 +4,43 @@ import { EventType } from "./types.js";
const { resolveMatrixRoomId, normalizeThreadId } = await import("./targets.js");
const BOT_USER_ID = "@bot:example.org";
const makeMappedDirectClient = (params: {
userId: string;
roomId: string;
botId?: string;
extra?: Record<string, unknown>;
}) =>
({
getAccountData: vi.fn().mockResolvedValue({
[params.userId]: [params.roomId],
}),
getUserId: vi.fn().mockResolvedValue(params.botId ?? BOT_USER_ID),
getJoinedRooms: vi.fn(),
getJoinedRoomMembers: vi.fn().mockResolvedValue([params.botId ?? BOT_USER_ID, params.userId]),
setAccountData: vi.fn(),
...params.extra,
}) as unknown as MatrixClient;
const makeFallbackDirectClient = (params: {
userId: string;
roomIds: string[];
botId?: string;
members?: string[];
extra?: Record<string, unknown>;
}) =>
({
getAccountData: vi.fn().mockRejectedValue(new Error("nope")),
getUserId: vi.fn().mockResolvedValue(params.botId ?? BOT_USER_ID),
getJoinedRooms: vi.fn().mockResolvedValue(params.roomIds),
getJoinedRoomMembers: vi
.fn()
.mockResolvedValue(params.members ?? [params.botId ?? BOT_USER_ID, params.userId]),
setAccountData: vi.fn().mockResolvedValue(undefined),
...params.extra,
}) as unknown as MatrixClient;
beforeEach(() => {
vi.clearAllMocks();
});
@@ -11,15 +48,7 @@ beforeEach(() => {
describe("resolveMatrixRoomId", () => {
it("uses m.direct when available", async () => {
const userId = "@user:example.org";
const client = {
getAccountData: vi.fn().mockResolvedValue({
[userId]: ["!room:example.org"],
}),
getUserId: vi.fn().mockResolvedValue("@bot:example.org"),
getJoinedRooms: vi.fn(),
getJoinedRoomMembers: vi.fn().mockResolvedValue(["@bot:example.org", userId]),
setAccountData: vi.fn(),
} as unknown as MatrixClient;
const client = makeMappedDirectClient({ userId, roomId: "!room:example.org" });
const roomId = await resolveMatrixRoomId(client, userId);
@@ -31,19 +60,12 @@ describe("resolveMatrixRoomId", () => {
it("falls back to joined rooms and persists m.direct", async () => {
const userId = "@fallback:example.org";
const roomId = "!room:example.org";
const setAccountData = vi.fn().mockResolvedValue(undefined);
const client = {
getAccountData: vi.fn().mockRejectedValue(new Error("nope")),
getUserId: vi.fn().mockResolvedValue("@bot:example.org"),
getJoinedRooms: vi.fn().mockResolvedValue([roomId]),
getJoinedRoomMembers: vi.fn().mockResolvedValue(["@bot:example.org", userId]),
setAccountData,
} as unknown as MatrixClient;
const client = makeFallbackDirectClient({ userId, roomIds: [roomId] });
const resolved = await resolveMatrixRoomId(client, userId);
expect(resolved).toBe(roomId);
expect(setAccountData).toHaveBeenCalledWith(
expect(client.setAccountData).toHaveBeenCalledWith(
EventType.Direct,
expect.objectContaining({ [userId]: [roomId] }),
);
@@ -51,20 +73,19 @@ describe("resolveMatrixRoomId", () => {
it("prefers joined rooms marked direct in local member state over plain strict rooms", async () => {
const userId = "@fallback:example.org";
const client = {
getAccountData: vi.fn().mockRejectedValue(new Error("nope")),
getUserId: vi.fn().mockResolvedValue("@bot:example.org"),
getJoinedRooms: vi.fn().mockResolvedValue(["!fallback:example.org", "!explicit:example.org"]),
getJoinedRoomMembers: vi.fn().mockResolvedValue(["@bot:example.org", userId]),
getRoomStateEvent: vi
.fn()
.mockImplementation(async (roomId: string, _eventType: string, stateKey: string) =>
roomId === "!explicit:example.org" && stateKey === "@bot:example.org"
? { is_direct: true }
: {},
),
setAccountData: vi.fn().mockResolvedValue(undefined),
} as unknown as MatrixClient;
const client = makeFallbackDirectClient({
userId,
roomIds: ["!fallback:example.org", "!explicit:example.org"],
extra: {
getRoomStateEvent: vi
.fn()
.mockImplementation(async (roomId: string, _eventType: string, stateKey: string) =>
roomId === "!explicit:example.org" && stateKey === BOT_USER_ID
? { is_direct: true }
: {},
),
},
});
const resolved = await resolveMatrixRoomId(client, userId);
@@ -77,20 +98,19 @@ describe("resolveMatrixRoomId", () => {
it("ignores remote member-state direct flags when resolving a direct room", async () => {
const userId = "@fallback:example.org";
const client = {
getAccountData: vi.fn().mockRejectedValue(new Error("nope")),
getUserId: vi.fn().mockResolvedValue("@bot:example.org"),
getJoinedRooms: vi
.fn()
.mockResolvedValue(["!fallback:example.org", "!remote-marked:example.org"]),
getJoinedRoomMembers: vi.fn().mockResolvedValue(["@bot:example.org", userId]),
getRoomStateEvent: vi
.fn()
.mockImplementation(async (roomId: string, _eventType: string, stateKey: string) =>
roomId === "!remote-marked:example.org" && stateKey === userId ? { is_direct: true } : {},
),
setAccountData: vi.fn().mockResolvedValue(undefined),
} as unknown as MatrixClient;
const client = makeFallbackDirectClient({
userId,
roomIds: ["!fallback:example.org", "!remote-marked:example.org"],
extra: {
getRoomStateEvent: vi
.fn()
.mockImplementation(async (roomId: string, _eventType: string, stateKey: string) =>
roomId === "!remote-marked:example.org" && stateKey === userId
? { is_direct: true }
: {},
),
},
});
const resolved = await resolveMatrixRoomId(client, userId);
@@ -126,15 +146,11 @@ describe("resolveMatrixRoomId", () => {
it("does not fall back to larger shared rooms for direct-user sends", async () => {
const userId = "@group:example.org";
const roomId = "!group:example.org";
const client = {
getAccountData: vi.fn().mockRejectedValue(new Error("nope")),
getUserId: vi.fn().mockResolvedValue("@bot:example.org"),
getJoinedRooms: vi.fn().mockResolvedValue([roomId]),
getJoinedRoomMembers: vi
.fn()
.mockResolvedValue(["@bot:example.org", userId, "@extra:example.org"]),
setAccountData: vi.fn().mockResolvedValue(undefined),
} as unknown as MatrixClient;
const client = makeFallbackDirectClient({
userId,
roomIds: [roomId],
members: [BOT_USER_ID, userId, "@extra:example.org"],
});
await expect(resolveMatrixRoomId(client, userId)).rejects.toThrow(
`No direct room found for ${userId} (m.direct missing)`,
@@ -145,16 +161,13 @@ describe("resolveMatrixRoomId", () => {
it("accepts nested Matrix user target prefixes", async () => {
const userId = "@prefixed:example.org";
const roomId = "!prefixed-room:example.org";
const client = {
getAccountData: vi.fn().mockResolvedValue({
[userId]: [roomId],
}),
getUserId: vi.fn().mockResolvedValue("@bot:example.org"),
getJoinedRooms: vi.fn(),
getJoinedRoomMembers: vi.fn().mockResolvedValue(["@bot:example.org", userId]),
setAccountData: vi.fn(),
resolveRoom: vi.fn(),
} as unknown as MatrixClient;
const client = makeMappedDirectClient({
userId,
roomId,
extra: {
resolveRoom: vi.fn(),
},
});
const resolved = await resolveMatrixRoomId(client, `matrix:user:${userId}`);