diff --git a/.eslintrc.js b/.eslintrc.js index fe5c1b5794..74318ab70e 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -78,6 +78,7 @@ module.exports = { "!matrix-js-sdk/src/matrix", "!matrix-js-sdk/src/crypto-api", "!matrix-js-sdk/src/types", + "!matrix-js-sdk/src/testing", "matrix-js-sdk/lib", "matrix-js-sdk/lib/", "matrix-js-sdk/lib/**", diff --git a/test/DecryptionFailureTracker-test.js b/test/DecryptionFailureTracker-test.js index 63b0489ee4..7fcb53d5d4 100644 --- a/test/DecryptionFailureTracker-test.js +++ b/test/DecryptionFailureTracker-test.js @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { MatrixEvent } from "matrix-js-sdk/src/matrix"; +import { mkDecryptionFailureMatrixEvent } from "matrix-js-sdk/src/testing"; import { DecryptionFailureTracker } from "../src/DecryptionFailureTracker"; @@ -26,20 +26,18 @@ class MockDecryptionError extends Error { } } -function createFailedDecryptionEvent() { - const event = new MatrixEvent({ - event_id: "event-id-" + Math.random().toString(16).slice(2), - content: { - algorithm: "m.megolm.v1.aes-sha2", - }, +async function createFailedDecryptionEvent() { + return await mkDecryptionFailureMatrixEvent({ + roomId: "!room:id", + sender: "@alice:example.com", + code: "UNKNOWN_ERROR", + msg: ":(", }); - event.setClearData(event.badEncryptedMessage(":(")); - return event; } describe("DecryptionFailureTracker", function () { - it("tracks a failed decryption for a visible event", function () { - const failedDecryptionEvent = createFailedDecryptionEvent(); + it("tracks a failed decryption for a visible event", async function () { + const failedDecryptionEvent = await createFailedDecryptionEvent(); let count = 0; const tracker = new DecryptionFailureTracker( @@ -61,8 +59,8 @@ describe("DecryptionFailureTracker", function () { expect(count).not.toBe(0, "should track a failure for an event that failed decryption"); }); - it("tracks a failed decryption with expected raw error for a visible event", function () { - const failedDecryptionEvent = createFailedDecryptionEvent(); + it("tracks a failed decryption with expected raw error for a visible event", async function () { + const failedDecryptionEvent = await createFailedDecryptionEvent(); let count = 0; let reportedRawCode = ""; @@ -89,8 +87,8 @@ describe("DecryptionFailureTracker", function () { expect(reportedRawCode).toBe("INBOUND_SESSION_MISMATCH_ROOM_ID", "Should add the rawCode to the event context"); }); - it("tracks a failed decryption for an event that becomes visible later", function () { - const failedDecryptionEvent = createFailedDecryptionEvent(); + it("tracks a failed decryption for an event that becomes visible later", async function () { + const failedDecryptionEvent = await createFailedDecryptionEvent(); let count = 0; const tracker = new DecryptionFailureTracker( @@ -112,8 +110,8 @@ describe("DecryptionFailureTracker", function () { expect(count).not.toBe(0, "should track a failure for an event that failed decryption"); }); - it("does not track a failed decryption for an event that never becomes visible", function () { - const failedDecryptionEvent = createFailedDecryptionEvent(); + it("does not track a failed decryption for an event that never becomes visible", async function () { + const failedDecryptionEvent = await createFailedDecryptionEvent(); let count = 0; const tracker = new DecryptionFailureTracker( @@ -133,8 +131,8 @@ describe("DecryptionFailureTracker", function () { expect(count).toBe(0, "should not track a failure for an event that never became visible"); }); - it("does not track a failed decryption where the event is subsequently successfully decrypted", () => { - const decryptedEvent = createFailedDecryptionEvent(); + it("does not track a failed decryption where the event is subsequently successfully decrypted", async () => { + const decryptedEvent = await createFailedDecryptionEvent(); const tracker = new DecryptionFailureTracker( (total) => { expect(true).toBe(false, "should not track an event that has since been decrypted correctly"); @@ -161,8 +159,8 @@ describe("DecryptionFailureTracker", function () { it( "does not track a failed decryption where the event is subsequently successfully decrypted " + "and later becomes visible", - () => { - const decryptedEvent = createFailedDecryptionEvent(); + async () => { + const decryptedEvent = await createFailedDecryptionEvent(); const tracker = new DecryptionFailureTracker( (total) => { expect(true).toBe(false, "should not track an event that has since been decrypted correctly"); @@ -187,9 +185,9 @@ describe("DecryptionFailureTracker", function () { }, ); - it("only tracks a single failure per event, despite multiple failed decryptions for multiple events", () => { - const decryptedEvent = createFailedDecryptionEvent(); - const decryptedEvent2 = createFailedDecryptionEvent(); + it("only tracks a single failure per event, despite multiple failed decryptions for multiple events", async () => { + const decryptedEvent = await createFailedDecryptionEvent(); + const decryptedEvent2 = await createFailedDecryptionEvent(); let count = 0; const tracker = new DecryptionFailureTracker( @@ -223,8 +221,8 @@ describe("DecryptionFailureTracker", function () { expect(count).toBe(2, count + " failures tracked, should only track a single failure per event"); }); - it("should not track a failure for an event that was tracked previously", () => { - const decryptedEvent = createFailedDecryptionEvent(); + it("should not track a failure for an event that was tracked previously", async () => { + const decryptedEvent = await createFailedDecryptionEvent(); let count = 0; const tracker = new DecryptionFailureTracker( @@ -251,11 +249,11 @@ describe("DecryptionFailureTracker", function () { expect(count).toBe(1, "should only track a single failure per event"); }); - it.skip("should not track a failure for an event that was tracked in a previous session", () => { + it.skip("should not track a failure for an event that was tracked in a previous session", async () => { // This test uses localStorage, clear it beforehand localStorage.clear(); - const decryptedEvent = createFailedDecryptionEvent(); + const decryptedEvent = await createFailedDecryptionEvent(); let count = 0; const tracker = new DecryptionFailureTracker( @@ -292,16 +290,16 @@ describe("DecryptionFailureTracker", function () { expect(count).toBe(1, count + " failures tracked, should only track a single failure per event"); }); - it("should count different error codes separately for multiple failures with different error codes", () => { + it("should count different error codes separately for multiple failures with different error codes", async () => { const counts = {}; const tracker = new DecryptionFailureTracker( (total, errorCode) => (counts[errorCode] = (counts[errorCode] || 0) + total), (error) => (error === "UnknownError" ? "UnknownError" : "OlmKeysNotSentError"), ); - const decryptedEvent1 = createFailedDecryptionEvent(); - const decryptedEvent2 = createFailedDecryptionEvent(); - const decryptedEvent3 = createFailedDecryptionEvent(); + const decryptedEvent1 = await createFailedDecryptionEvent(); + const decryptedEvent2 = await createFailedDecryptionEvent(); + const decryptedEvent3 = await createFailedDecryptionEvent(); const error1 = new MockDecryptionError("UnknownError"); const error2 = new MockDecryptionError("OlmKeysNotSentError"); @@ -325,16 +323,16 @@ describe("DecryptionFailureTracker", function () { expect(counts["OlmKeysNotSentError"]).toBe(2, "should track two OlmKeysNotSentError"); }); - it("should aggregate error codes correctly", () => { + it("should aggregate error codes correctly", async () => { const counts = {}; const tracker = new DecryptionFailureTracker( (total, errorCode) => (counts[errorCode] = (counts[errorCode] || 0) + total), (errorCode) => "OlmUnspecifiedError", ); - const decryptedEvent1 = createFailedDecryptionEvent(); - const decryptedEvent2 = createFailedDecryptionEvent(); - const decryptedEvent3 = createFailedDecryptionEvent(); + const decryptedEvent1 = await createFailedDecryptionEvent(); + const decryptedEvent2 = await createFailedDecryptionEvent(); + const decryptedEvent3 = await createFailedDecryptionEvent(); const error1 = new MockDecryptionError("ERROR_CODE_1"); const error2 = new MockDecryptionError("ERROR_CODE_2"); @@ -359,14 +357,14 @@ describe("DecryptionFailureTracker", function () { ); }); - it("should remap error codes correctly", () => { + it("should remap error codes correctly", async () => { const counts = {}; const tracker = new DecryptionFailureTracker( (total, errorCode) => (counts[errorCode] = (counts[errorCode] || 0) + total), (errorCode) => Array.from(errorCode).reverse().join(""), ); - const decryptedEvent = createFailedDecryptionEvent(); + const decryptedEvent = await createFailedDecryptionEvent(); const error = new MockDecryptionError("ERROR_CODE_1"); diff --git a/test/components/views/rooms/EventTile-test.tsx b/test/components/views/rooms/EventTile-test.tsx index ce89a9fe8f..31203d4f85 100644 --- a/test/components/views/rooms/EventTile-test.tsx +++ b/test/components/views/rooms/EventTile-test.tsx @@ -30,20 +30,13 @@ import { } from "matrix-js-sdk/src/matrix"; import { EventEncryptionInfo, EventShieldColour, EventShieldReason } from "matrix-js-sdk/src/crypto-api"; import { TooltipProvider } from "@vector-im/compound-web"; +import { mkEncryptedMatrixEvent } from "matrix-js-sdk/src/testing"; import EventTile, { EventTileProps } from "../../../../src/components/views/rooms/EventTile"; import MatrixClientContext from "../../../../src/contexts/MatrixClientContext"; import RoomContext, { TimelineRenderingType } from "../../../../src/contexts/RoomContext"; import { MatrixClientPeg } from "../../../../src/MatrixClientPeg"; -import { - filterConsole, - flushPromises, - getRoomContext, - mkEncryptedEvent, - mkEvent, - mkMessage, - stubClient, -} from "../../../test-utils"; +import { filterConsole, flushPromises, getRoomContext, mkEvent, mkMessage, stubClient } from "../../../test-utils"; import { mkThread } from "../../../test-utils/threads"; import DMRoomMap from "../../../../src/utils/DMRoomMap"; import dis from "../../../../src/dispatcher/dispatcher"; @@ -225,11 +218,11 @@ describe("EventTile", () => { }); it("shows a warning for an event from an unverified device", async () => { - mxEvent = await mkEncryptedEvent({ + mxEvent = await mkEncryptedMatrixEvent({ plainContent: { msgtype: "m.text", body: "msg1" }, plainType: "m.room.message", - user: "@alice:example.org", - room: room.roomId, + sender: "@alice:example.org", + roomId: room.roomId, }); eventToEncryptionInfoMap.set(mxEvent.getId()!, { shieldColour: EventShieldColour.RED, @@ -250,11 +243,11 @@ describe("EventTile", () => { }); it("shows no shield for a verified event", async () => { - mxEvent = await mkEncryptedEvent({ + mxEvent = await mkEncryptedMatrixEvent({ plainContent: { msgtype: "m.text", body: "msg1" }, plainType: "m.room.message", - user: "@alice:example.org", - room: room.roomId, + sender: "@alice:example.org", + roomId: room.roomId, }); eventToEncryptionInfoMap.set(mxEvent.getId()!, { shieldColour: EventShieldColour.NONE, @@ -279,11 +272,11 @@ describe("EventTile", () => { [EventShieldReason.AUTHENTICITY_NOT_GUARANTEED, "can't be guaranteed"], [EventShieldReason.MISMATCHED_SENDER_KEY, "Encrypted by an unverified session"], ])("shows the correct reason code for %i (%s)", async (reasonCode: EventShieldReason, expectedText: string) => { - mxEvent = await mkEncryptedEvent({ + mxEvent = await mkEncryptedMatrixEvent({ plainContent: { msgtype: "m.text", body: "msg1" }, plainType: "m.room.message", - user: "@alice:example.org", - room: room.roomId, + sender: "@alice:example.org", + roomId: room.roomId, }); eventToEncryptionInfoMap.set(mxEvent.getId()!, { shieldColour: EventShieldColour.GREY, @@ -337,11 +330,11 @@ describe("EventTile", () => { it("should update the warning when the event is edited", async () => { // we start out with an event from the trusted device - mxEvent = await mkEncryptedEvent({ + mxEvent = await mkEncryptedMatrixEvent({ plainContent: { msgtype: "m.text", body: "msg1" }, plainType: "m.room.message", - user: "@alice:example.org", - room: room.roomId, + sender: "@alice:example.org", + roomId: room.roomId, }); eventToEncryptionInfoMap.set(mxEvent.getId()!, { shieldColour: EventShieldColour.NONE, @@ -360,11 +353,11 @@ describe("EventTile", () => { expect(container.getElementsByClassName("mx_EventTile_e2eIcon")).toHaveLength(0); // then we replace the event with one from the unverified device - const replacementEvent = await mkEncryptedEvent({ + const replacementEvent = await mkEncryptedMatrixEvent({ plainContent: { msgtype: "m.text", body: "msg1" }, plainType: "m.room.message", - user: "@alice:example.org", - room: room.roomId, + sender: "@alice:example.org", + roomId: room.roomId, }); eventToEncryptionInfoMap.set(replacementEvent.getId()!, { shieldColour: EventShieldColour.RED, @@ -388,11 +381,11 @@ describe("EventTile", () => { jest.spyOn(client, "isRoomEncrypted").mockReturnValue(true); // we start out with an event from the trusted device - mxEvent = await mkEncryptedEvent({ + mxEvent = await mkEncryptedMatrixEvent({ plainContent: { msgtype: "m.text", body: "msg1" }, plainType: "m.room.message", - user: "@alice:example.org", - room: room.roomId, + sender: "@alice:example.org", + roomId: room.roomId, }); eventToEncryptionInfoMap.set(mxEvent.getId()!, { diff --git a/test/test-utils/test-utils.ts b/test/test-utils/test-utils.ts index 933d324dd5..7bf83c2d10 100644 --- a/test/test-utils/test-utils.ts +++ b/test/test-utils/test-utils.ts @@ -36,7 +36,6 @@ import { IPushRules, RelationType, JoinRule, - IEventDecryptionResult, OidcClientConfig, } from "matrix-js-sdk/src/matrix"; import { KnownMembership } from "matrix-js-sdk/src/types"; @@ -404,50 +403,6 @@ export function mkEvent(opts: MakeEventProps): MatrixEvent { return mxEvent; } -/** - * Create an m.room.encrypted event - * - * @param opts - Values for the event - * @param opts.room - The ID of the room for the event - * @param opts.user - The sender of the event - * @param opts.plainType - The type the event will have, once it has been decrypted - * @param opts.plainContent - The content the event will have, once it has been decrypted - */ -export async function mkEncryptedEvent(opts: { - room: Room["roomId"]; - user: User["userId"]; - plainType: string; - plainContent: IContent; -}): Promise { - // we construct an event which has been decrypted by stubbing out CryptoBackend.decryptEvent and then - // calling MatrixEvent.attemptDecryption. - - const mxEvent = mkEvent({ - type: "m.room.encrypted", - room: opts.room, - user: opts.user, - event: true, - content: {}, - }); - - const decryptionResult: IEventDecryptionResult = { - claimedEd25519Key: "", - clearEvent: { - type: opts.plainType, - content: opts.plainContent, - }, - forwardingCurve25519KeyChain: [], - senderCurve25519Key: "", - untrusted: false, - }; - - const mockCrypto = { - decryptEvent: async (_ev): Promise => decryptionResult, - } as Parameters[0]; - await mxEvent.attemptDecryption(mockCrypto); - return mxEvent; -} - /** * Create an m.room.member event. * @param {Object} opts Values for the membership.