Use test helpers from js-sdk for encrypted events (#12364)

* Use test helpers from js-sdk for encrypted events

Rather than gut-wrenching into the matrix-js-sdk, let's use the newly-exported
test helpers to generate encrypted events.

* Fix up
This commit is contained in:
Richard van der Hoff 2024-03-22 15:48:29 +00:00 committed by GitHub
parent 8f22550f19
commit 64806d0490
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 57 additions and 110 deletions

View file

@ -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/**",

View file

@ -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");

View file

@ -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()!, {

View file

@ -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<MatrixEvent> {
// 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<IEventDecryptionResult> => decryptionResult,
} as Parameters<MatrixEvent["attemptDecryption"]>[0];
await mxEvent.attemptDecryption(mockCrypto);
return mxEvent;
}
/**
* Create an m.room.member event.
* @param {Object} opts Values for the membership.