From 560b3da1559c7878ac8bbeb1d5e589061f8e1499 Mon Sep 17 00:00:00 2001 From: Michael Weimann Date: Thu, 9 Feb 2023 17:15:41 +0100 Subject: [PATCH 1/4] Examine all m.direct rooms to find a DM --- src/utils/DMRoomMap.ts | 10 +++++ src/utils/dm/findDMForUser.ts | 4 +- test/LegacyCallHandler-test.ts | 16 ++----- test/utils/DMRoomMap-test.ts | 54 +++++++++++++++++++++++ test/utils/dm/findDMForUser-test.ts | 67 ++++++++++++++++++----------- 5 files changed, 112 insertions(+), 39 deletions(-) create mode 100644 test/utils/DMRoomMap-test.ts diff --git a/src/utils/DMRoomMap.ts b/src/utils/DMRoomMap.ts index 9d6755b4ece..24f67c2ff26 100644 --- a/src/utils/DMRoomMap.ts +++ b/src/utils/DMRoomMap.ts @@ -192,6 +192,16 @@ export default class DMRoomMap { .reduce((obj, r) => (obj[r.userId] = r.room) && obj, {}); } + /** + * @returns all room Ids from m.direct + */ + public getRoomIds(): Set { + return Object.values(this.mDirectEvent).reduce((prevRoomIds: Set, roomIds: string[]): Set => { + roomIds.forEach((roomId) => prevRoomIds.add(roomId)); + return prevRoomIds; + }, new Set()); + } + private getUserToRooms(): { [key: string]: string[] } { if (!this.userToRooms) { const userToRooms = this.mDirectEvent; diff --git a/src/utils/dm/findDMForUser.ts b/src/utils/dm/findDMForUser.ts index babf8bd2afd..3aa0cb8b9aa 100644 --- a/src/utils/dm/findDMForUser.ts +++ b/src/utils/dm/findDMForUser.ts @@ -29,8 +29,8 @@ import { getFunctionalMembers } from "../room/getFunctionalMembers"; * @returns {Room} Room if found */ export function findDMForUser(client: MatrixClient, userId: string): Room { - const roomIds = DMRoomMap.shared().getDMRoomsForUserId(userId); - const rooms = roomIds.map((id) => client.getRoom(id)); + const roomIds = DMRoomMap.shared().getRoomIds(); + const rooms = Array.from(roomIds).map((id) => client.getRoom(id)); const suitableDMRooms = rooms .filter((r) => { // Validate that we are joined and the other person is also joined. We'll also make sure diff --git a/test/LegacyCallHandler-test.ts b/test/LegacyCallHandler-test.ts index 3e21780dabd..074ba7e3994 100644 --- a/test/LegacyCallHandler-test.ts +++ b/test/LegacyCallHandler-test.ts @@ -225,20 +225,10 @@ describe("LegacyCallHandler", () => { return null; } }, - getDMRoomsForUserId: (userId: string) => { - if (userId === NATIVE_ALICE) { - return [NATIVE_ROOM_ALICE]; - } else if (userId === NATIVE_BOB) { - return [NATIVE_ROOM_BOB]; - } else if (userId === NATIVE_CHARLIE) { - return [NATIVE_ROOM_CHARLIE]; - } else if (userId === VIRTUAL_BOB) { - return [VIRTUAL_ROOM_BOB]; - } else { - return []; - } + getRoomIds: () => { + return [NATIVE_ROOM_ALICE, NATIVE_ROOM_BOB, NATIVE_ROOM_CHARLIE, VIRTUAL_ROOM_BOB]; }, - } as DMRoomMap; + } as unknown as DMRoomMap; DMRoomMap.setShared(dmRoomMap); pstnLookup = null; diff --git a/test/utils/DMRoomMap-test.ts b/test/utils/DMRoomMap-test.ts new file mode 100644 index 00000000000..9a3ddd951cd --- /dev/null +++ b/test/utils/DMRoomMap-test.ts @@ -0,0 +1,54 @@ +/* +Copyright 2023 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { mocked, Mocked } from "jest-mock"; +import { EventType, IContent, MatrixClient } from "matrix-js-sdk/src/matrix"; + +import DMRoomMap from "../../src/utils/DMRoomMap"; +import { mkEvent, stubClient } from "../test-utils"; + +describe("DMRoomMap", () => { + const roomId1 = "!room1:example.com"; + const roomId2 = "!room2:example.com"; + const roomId3 = "!room3:example.com"; + const roomId4 = "!room4:example.com"; + + const mDirectContent = { + "user@example.com": [roomId1, roomId2], + "@user:example.com": [roomId1, roomId3, roomId4], + "@user2:example.com": [] as string[], + } satisfies IContent; + + let client: Mocked; + let dmRoomMap: DMRoomMap; + + beforeEach(() => { + client = mocked(stubClient()); + + const mDirectEvent = mkEvent({ + event: true, + type: EventType.Direct, + user: client.getSafeUserId(), + content: mDirectContent, + }); + client.getAccountData.mockReturnValue(mDirectEvent); + dmRoomMap = new DMRoomMap(client); + }); + + it("getRoomIds should return the room Ids", () => { + expect(dmRoomMap.getRoomIds()).toEqual(new Set([roomId1, roomId2, roomId3, roomId4])); + }); +}); diff --git a/test/utils/dm/findDMForUser-test.ts b/test/utils/dm/findDMForUser-test.ts index c1acad95e74..3e08b97d391 100644 --- a/test/utils/dm/findDMForUser-test.ts +++ b/test/utils/dm/findDMForUser-test.ts @@ -15,10 +15,10 @@ limitations under the License. */ import { mocked } from "jest-mock"; -import { MatrixClient, Room } from "matrix-js-sdk/src/matrix"; +import { EventType, MatrixClient, Room } from "matrix-js-sdk/src/matrix"; import DMRoomMap from "../../../src/utils/DMRoomMap"; -import { createTestClient, makeMembershipEvent } from "../../test-utils"; +import { createTestClient, makeMembershipEvent, mkEvent } from "../../test-utils"; import { LocalRoom } from "../../../src/models/LocalRoom"; import { findDMForUser } from "../../../src/utils/dm/findDMForUser"; import { getFunctionalMembers } from "../../../src/utils/room/getFunctionalMembers"; @@ -39,6 +39,19 @@ describe("findDMForUser", () => { let dmRoomMap: DMRoomMap; let mockClient: MatrixClient; + const setUpMDirect = (mDirect: { [key: string]: string[] }) => { + const mDirectEvent = mkEvent({ + event: true, + type: EventType.Direct, + user: mockClient.getSafeUserId(), + content: mDirect, + }); + mocked(mockClient).getAccountData.mockReturnValue(mDirectEvent); + + dmRoomMap = new DMRoomMap(mockClient); + jest.spyOn(DMRoomMap, "shared").mockReturnValue(dmRoomMap); + }; + beforeEach(() => { mockClient = createTestClient(); @@ -87,24 +100,15 @@ describe("findDMForUser", () => { [room5.roomId]: room5, }[roomId]; }); + }); - dmRoomMap = { - getDMRoomForIdentifiers: jest.fn(), - getDMRoomsForUserId: jest.fn(), - } as unknown as DMRoomMap; - jest.spyOn(DMRoomMap, "shared").mockReturnValue(dmRoomMap); - mocked(dmRoomMap.getDMRoomsForUserId).mockReturnValue([ - room1.roomId, - room2.roomId, - room3.roomId, - room4.roomId, - room5.roomId, - ]); + afterAll(() => { + jest.restoreAllMocks(); }); describe("for an empty DM room list", () => { beforeEach(() => { - mocked(dmRoomMap.getDMRoomsForUserId).mockReturnValue([]); + setUpMDirect({}); }); it("should return undefined", () => { @@ -112,17 +116,32 @@ describe("findDMForUser", () => { }); }); - it("should find a room ordered by last activity 1", () => { - room1.getLastActiveTimestamp = () => 2; - room3.getLastActiveTimestamp = () => 1; + describe("when there are soom rooms", () => { + beforeEach(() => { + setUpMDirect({ + [userId1]: [room1.roomId, room2.roomId, room3.roomId, room4.roomId, room5.roomId], + }); + }); + + it("should find a room ordered by last activity 1", () => { + room1.getLastActiveTimestamp = () => 2; + room3.getLastActiveTimestamp = () => 1; - expect(findDMForUser(mockClient, userId1)).toBe(room1); - }); + expect(findDMForUser(mockClient, userId1)).toBe(room1); + }); - it("should find a room ordered by last activity 2", () => { - room1.getLastActiveTimestamp = () => 1; - room3.getLastActiveTimestamp = () => 2; + it("should find a room ordered by last activity 2", () => { + room1.getLastActiveTimestamp = () => 1; + room3.getLastActiveTimestamp = () => 2; - expect(findDMForUser(mockClient, userId1)).toBe(room3); + expect(findDMForUser(mockClient, userId1)).toBe(room3); + }); + + it("should find a room for a user without an m.direct entry but a DM-like room exists", () => { + room1.getLastActiveTimestamp = () => 1; + room3.getLastActiveTimestamp = () => 2; + + expect(findDMForUser(mockClient, userId2)).toBe(room3); + }); }); }); From 087d976e131d213864644bf28281f995f80ca8b0 Mon Sep 17 00:00:00 2001 From: Michael Weimann Date: Fri, 10 Feb 2023 10:06:31 +0100 Subject: [PATCH 2/4] Implement as fallback --- src/utils/dm/findDMForUser.ts | 42 +++++++++----- test/LegacyCallHandler-test.ts | 14 ++++- test/utils/dm/findDMForUser-test.ts | 87 +++++++++++++++-------------- 3 files changed, 87 insertions(+), 56 deletions(-) diff --git a/src/utils/dm/findDMForUser.ts b/src/utils/dm/findDMForUser.ts index 3aa0cb8b9aa..9c3f483972d 100644 --- a/src/utils/dm/findDMForUser.ts +++ b/src/utils/dm/findDMForUser.ts @@ -21,17 +21,8 @@ import { isLocalRoom } from "../localRoom/isLocalRoom"; import { isJoinedOrNearlyJoined } from "../membership"; import { getFunctionalMembers } from "../room/getFunctionalMembers"; -/** - * Tries to find a DM room with a specific user. - * - * @param {MatrixClient} client - * @param {string} userId ID of the user to find the DM for - * @returns {Room} Room if found - */ -export function findDMForUser(client: MatrixClient, userId: string): Room { - const roomIds = DMRoomMap.shared().getRoomIds(); - const rooms = Array.from(roomIds).map((id) => client.getRoom(id)); - const suitableDMRooms = rooms +function extractSuitableRoom(rooms: Room[], userId: string): Room | undefined { + const suitableRooms = rooms .filter((r) => { // Validate that we are joined and the other person is also joined. We'll also make sure // that the room also looks like a DM (until we have canonical DMs to tell us). For now, @@ -54,7 +45,32 @@ export function findDMForUser(client: MatrixClient, userId: string): Room { .sort((r1, r2) => { return r2.getLastActiveTimestamp() - r1.getLastActiveTimestamp(); }); - if (suitableDMRooms.length) { - return suitableDMRooms[0]; + + if (suitableRooms.length) { + return suitableRooms[0]; } + + return undefined; +} + +/** + * Tries to find a DM room with a specific user. + * + * @param {MatrixClient} client + * @param {string} userId ID of the user to find the DM for + * @returns {Room | undefined} Room if found + */ +export function findDMForUser(client: MatrixClient, userId: string): Room | undefined { + const roomIdsForUserId = DMRoomMap.shared().getDMRoomsForUserId(userId); + const roomsForUserId = roomIdsForUserId.map((id) => client.getRoom(id)); + const suitableRoomForUserId = extractSuitableRoom(roomsForUserId, userId); + + if (suitableRoomForUserId) { + return suitableRoomForUserId; + } + + // Try to find in all rooms as a fallback + const allRoomIds = DMRoomMap.shared().getRoomIds(); + const allRooms = Array.from(allRoomIds).map((id) => client.getRoom(id)); + return extractSuitableRoom(allRooms, userId); } diff --git a/test/LegacyCallHandler-test.ts b/test/LegacyCallHandler-test.ts index 074ba7e3994..515402c7950 100644 --- a/test/LegacyCallHandler-test.ts +++ b/test/LegacyCallHandler-test.ts @@ -225,8 +225,18 @@ describe("LegacyCallHandler", () => { return null; } }, - getRoomIds: () => { - return [NATIVE_ROOM_ALICE, NATIVE_ROOM_BOB, NATIVE_ROOM_CHARLIE, VIRTUAL_ROOM_BOB]; + getDMRoomsForUserId: (userId: string) => { + if (userId === NATIVE_ALICE) { + return [NATIVE_ROOM_ALICE]; + } else if (userId === NATIVE_BOB) { + return [NATIVE_ROOM_BOB]; + } else if (userId === NATIVE_CHARLIE) { + return [NATIVE_ROOM_CHARLIE]; + } else if (userId === VIRTUAL_BOB) { + return [VIRTUAL_ROOM_BOB]; + } else { + return []; + } }, } as unknown as DMRoomMap; DMRoomMap.setShared(dmRoomMap); diff --git a/test/utils/dm/findDMForUser-test.ts b/test/utils/dm/findDMForUser-test.ts index 3e08b97d391..964dcd93a5d 100644 --- a/test/utils/dm/findDMForUser-test.ts +++ b/test/utils/dm/findDMForUser-test.ts @@ -15,10 +15,10 @@ limitations under the License. */ import { mocked } from "jest-mock"; -import { EventType, MatrixClient, Room } from "matrix-js-sdk/src/matrix"; +import { MatrixClient, Room } from "matrix-js-sdk/src/matrix"; import DMRoomMap from "../../../src/utils/DMRoomMap"; -import { createTestClient, makeMembershipEvent, mkEvent } from "../../test-utils"; +import { createTestClient, makeMembershipEvent } from "../../test-utils"; import { LocalRoom } from "../../../src/models/LocalRoom"; import { findDMForUser } from "../../../src/utils/dm/findDMForUser"; import { getFunctionalMembers } from "../../../src/utils/room/getFunctionalMembers"; @@ -30,28 +30,17 @@ jest.mock("../../../src/utils/room/getFunctionalMembers", () => ({ describe("findDMForUser", () => { const userId1 = "@user1:example.com"; const userId2 = "@user2:example.com"; + const userId3 = "@user3:example.com"; const botId = "@bot:example.com"; let room1: Room; let room2: LocalRoom; let room3: Room; let room4: Room; let room5: Room; + let room6: Room; let dmRoomMap: DMRoomMap; let mockClient: MatrixClient; - const setUpMDirect = (mDirect: { [key: string]: string[] }) => { - const mDirectEvent = mkEvent({ - event: true, - type: EventType.Direct, - user: mockClient.getSafeUserId(), - content: mDirect, - }); - mocked(mockClient).getAccountData.mockReturnValue(mDirectEvent); - - dmRoomMap = new DMRoomMap(mockClient); - jest.spyOn(DMRoomMap, "shared").mockReturnValue(dmRoomMap); - }; - beforeEach(() => { mockClient = createTestClient(); @@ -91,6 +80,14 @@ describe("findDMForUser", () => { room5 = new Room("!room5:example.com", mockClient, userId1); room5.getLastActiveTimestamp = () => 100; + // room not correctly stored in userId → room map; should be found by the "all rooms" fallback + room6 = new Room("!room6:example.com", mockClient, userId1); + room6.getMyMembership = () => "join"; + room6.currentState.setStateEvents([ + makeMembershipEvent(room6.roomId, userId1, "join"), + makeMembershipEvent(room6.roomId, userId3, "join"), + ]); + mocked(mockClient.getRoom).mockImplementation((roomId: string) => { return { [room1.roomId]: room1, @@ -98,17 +95,33 @@ describe("findDMForUser", () => { [room3.roomId]: room3, [room4.roomId]: room4, [room5.roomId]: room5, + [room6.roomId]: room6, }[roomId]; }); - }); - afterAll(() => { - jest.restoreAllMocks(); + dmRoomMap = { + getDMRoomForIdentifiers: jest.fn(), + getDMRoomsForUserId: jest.fn(), + getRoomIds: jest + .fn() + .mockReturnValue( + new Set([room1.roomId, room2.roomId, room3.roomId, room4.roomId, room5.roomId, room6.roomId]), + ), + } as unknown as DMRoomMap; + jest.spyOn(DMRoomMap, "shared").mockReturnValue(dmRoomMap); + mocked(dmRoomMap.getDMRoomsForUserId).mockImplementation((userId: string) => { + if (userId === userId1) { + return [room1.roomId, room2.roomId, room3.roomId, room4.roomId, room5.roomId]; + } + + return []; + }); }); describe("for an empty DM room list", () => { beforeEach(() => { - setUpMDirect({}); + mocked(dmRoomMap.getDMRoomsForUserId).mockReturnValue([]); + mocked(dmRoomMap.getRoomIds).mockReturnValue(new Set()); }); it("should return undefined", () => { @@ -116,32 +129,24 @@ describe("findDMForUser", () => { }); }); - describe("when there are soom rooms", () => { - beforeEach(() => { - setUpMDirect({ - [userId1]: [room1.roomId, room2.roomId, room3.roomId, room4.roomId, room5.roomId], - }); - }); + it("should find a room ordered by last activity 1", () => { + room1.getLastActiveTimestamp = () => 2; + room3.getLastActiveTimestamp = () => 1; - it("should find a room ordered by last activity 1", () => { - room1.getLastActiveTimestamp = () => 2; - room3.getLastActiveTimestamp = () => 1; - - expect(findDMForUser(mockClient, userId1)).toBe(room1); - }); + expect(findDMForUser(mockClient, userId1)).toBe(room1); + }); - it("should find a room ordered by last activity 2", () => { - room1.getLastActiveTimestamp = () => 1; - room3.getLastActiveTimestamp = () => 2; + it("should find a room ordered by last activity 2", () => { + room1.getLastActiveTimestamp = () => 1; + room3.getLastActiveTimestamp = () => 2; - expect(findDMForUser(mockClient, userId1)).toBe(room3); - }); + expect(findDMForUser(mockClient, userId1)).toBe(room3); + }); - it("should find a room for a user without an m.direct entry but a DM-like room exists", () => { - room1.getLastActiveTimestamp = () => 1; - room3.getLastActiveTimestamp = () => 2; + it("should find a room by the 'all rooms' fallback", () => { + room1.getLastActiveTimestamp = () => 1; + room6.getLastActiveTimestamp = () => 2; - expect(findDMForUser(mockClient, userId2)).toBe(room3); - }); + expect(findDMForUser(mockClient, userId3)).toBe(room6); }); }); From 5cafe56a8b051eb26d4f14d52130c866878b209f Mon Sep 17 00:00:00 2001 From: Michael Weimann Date: Fri, 10 Feb 2023 10:45:24 +0100 Subject: [PATCH 3/4] Fix strict types --- src/utils/dm/findDMForUser.ts | 8 ++++--- test/utils/dm/findDMForUser-test.ts | 37 ++++++++++++++++++----------- 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/src/utils/dm/findDMForUser.ts b/src/utils/dm/findDMForUser.ts index 9c3f483972d..3f6fcbfca08 100644 --- a/src/utils/dm/findDMForUser.ts +++ b/src/utils/dm/findDMForUser.ts @@ -35,7 +35,7 @@ function extractSuitableRoom(rooms: Room[], userId: string): Room | undefined { const functionalUsers = getFunctionalMembers(r); const members = r.currentState.getMembers(); const joinedMembers = members.filter( - (m) => !functionalUsers.includes(m.userId) && isJoinedOrNearlyJoined(m.membership), + (m) => !functionalUsers.includes(m.userId) && m.membership && isJoinedOrNearlyJoined(m.membership), ); const otherMember = joinedMembers.find((m) => m.userId === userId); return otherMember && joinedMembers.length === 2; @@ -62,7 +62,7 @@ function extractSuitableRoom(rooms: Room[], userId: string): Room | undefined { */ export function findDMForUser(client: MatrixClient, userId: string): Room | undefined { const roomIdsForUserId = DMRoomMap.shared().getDMRoomsForUserId(userId); - const roomsForUserId = roomIdsForUserId.map((id) => client.getRoom(id)); + const roomsForUserId = roomIdsForUserId.map((id) => client.getRoom(id)).filter((r): r is Room => r !== null); const suitableRoomForUserId = extractSuitableRoom(roomsForUserId, userId); if (suitableRoomForUserId) { @@ -71,6 +71,8 @@ export function findDMForUser(client: MatrixClient, userId: string): Room | unde // Try to find in all rooms as a fallback const allRoomIds = DMRoomMap.shared().getRoomIds(); - const allRooms = Array.from(allRoomIds).map((id) => client.getRoom(id)); + const allRooms = Array.from(allRoomIds) + .map((id) => client.getRoom(id)) + .filter((r): r is Room => r !== null); return extractSuitableRoom(allRooms, userId); } diff --git a/test/utils/dm/findDMForUser-test.ts b/test/utils/dm/findDMForUser-test.ts index 964dcd93a5d..60c5b342f48 100644 --- a/test/utils/dm/findDMForUser-test.ts +++ b/test/utils/dm/findDMForUser-test.ts @@ -38,6 +38,7 @@ describe("findDMForUser", () => { let room4: Room; let room5: Room; let room6: Room; + const room7Id = "!room7:example.com"; let dmRoomMap: DMRoomMap; let mockClient: MatrixClient; @@ -89,29 +90,37 @@ describe("findDMForUser", () => { ]); mocked(mockClient.getRoom).mockImplementation((roomId: string) => { - return { - [room1.roomId]: room1, - [room2.roomId]: room2, - [room3.roomId]: room3, - [room4.roomId]: room4, - [room5.roomId]: room5, - [room6.roomId]: room6, - }[roomId]; + return ( + { + [room1.roomId]: room1, + [room2.roomId]: room2, + [room3.roomId]: room3, + [room4.roomId]: room4, + [room5.roomId]: room5, + [room6.roomId]: room6, + }[roomId] || null + ); }); dmRoomMap = { getDMRoomForIdentifiers: jest.fn(), getDMRoomsForUserId: jest.fn(), - getRoomIds: jest - .fn() - .mockReturnValue( - new Set([room1.roomId, room2.roomId, room3.roomId, room4.roomId, room5.roomId, room6.roomId]), - ), + getRoomIds: jest.fn().mockReturnValue( + new Set([ + room1.roomId, + room2.roomId, + room3.roomId, + room4.roomId, + room5.roomId, + room6.roomId, + room7Id, // this room does not exist in client + ]), + ), } as unknown as DMRoomMap; jest.spyOn(DMRoomMap, "shared").mockReturnValue(dmRoomMap); mocked(dmRoomMap.getDMRoomsForUserId).mockImplementation((userId: string) => { if (userId === userId1) { - return [room1.roomId, room2.roomId, room3.roomId, room4.roomId, room5.roomId]; + return [room1.roomId, room2.roomId, room3.roomId, room4.roomId, room5.roomId, room7Id]; } return []; From 957e4783aa0427d7794b2fab5c10fe2837f7c491 Mon Sep 17 00:00:00 2001 From: Michael Weimann Date: Fri, 10 Feb 2023 11:03:03 +0100 Subject: [PATCH 4/4] Fix more strict type issues --- src/utils/dm/findDMRoom.ts | 2 +- test/utils/dm/findDMRoom-test.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/utils/dm/findDMRoom.ts b/src/utils/dm/findDMRoom.ts index d8cbb56d905..7196b42feea 100644 --- a/src/utils/dm/findDMRoom.ts +++ b/src/utils/dm/findDMRoom.ts @@ -29,7 +29,7 @@ import { findDMForUser } from "./findDMForUser"; */ export function findDMRoom(client: MatrixClient, targets: Member[]): Room | null { const targetIds = targets.map((t) => t.userId); - let existingRoom: Room; + let existingRoom: Room | undefined; if (targetIds.length === 1) { existingRoom = findDMForUser(client, targetIds[0]); } else { diff --git a/test/utils/dm/findDMRoom-test.ts b/test/utils/dm/findDMRoom-test.ts index b7ed77f582f..8ce301cee86 100644 --- a/test/utils/dm/findDMRoom-test.ts +++ b/test/utils/dm/findDMRoom-test.ts @@ -53,8 +53,8 @@ describe("findDMRoom", () => { expect(findDMRoom(mockClient, [member1])).toBe(room1); }); - it("should return null for a single target without a room", () => { - mocked(findDMForUser).mockReturnValue(null); + it("should return undefined for a single target without a room", () => { + mocked(findDMForUser).mockReturnValue(undefined); expect(findDMRoom(mockClient, [member1])).toBeNull(); });