Skip to content

Commit

Permalink
Replace deprecated TestClient with fetchMock (#3550)
Browse files Browse the repository at this point in the history
* replace deprecated TestClient with fetchMock

* add stop() api to BackupManager for clean shutdown

* fix merge

* code review cleaning

* lint

* Address review comments

* Remove unused `TestClient.expectKeyBackupQuery`

* clean up imports

---------

Co-authored-by: Richard van der Hoff <[email protected]>
Co-authored-by: Richard van der Hoff <[email protected]>
  • Loading branch information
3 people authored Jul 25, 2023
1 parent 0cf0569 commit 29b815b
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 90 deletions.
17 changes: 0 additions & 17 deletions spec/TestClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@ import { syncPromise } from "./test-utils/test-utils";
import { createClient, IStartClientOpts } from "../src/matrix";
import { ICreateClientOpts, IDownloadKeyResult, MatrixClient, PendingEventOrdering } from "../src/client";
import { MockStorageApi } from "./MockStorageApi";
import { encodeUri } from "../src/utils";
import { IKeyBackupSession } from "../src/crypto/keybackup";
import { IKeysUploadResponse, IUploadKeysRequest } from "../src/client";
import { ISyncResponder } from "./test-utils/SyncResponder";

Expand Down Expand Up @@ -214,21 +212,6 @@ export class TestClient implements IE2EKeyReceiver, ISyncResponder {
});
}

/**
* Set up expectations that the client will query key backups for a particular session
*/
public expectKeyBackupQuery(roomId: string, sessionId: string, status: number, response: IKeyBackupSession) {
this.httpBackend
.when(
"GET",
encodeUri("/room_keys/keys/$roomId/$sessionId", {
$roomId: roomId,
$sessionId: sessionId,
}),
)
.respond(status, response);
}

/**
* get the uploaded curve25519 device key
*
Expand Down
160 changes: 87 additions & 73 deletions spec/integ/crypto/megolm-backup.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,24 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

import { Account } from "@matrix-org/olm";
import fetchMock from "fetch-mock-jest";

import { logger } from "../../../src/logger";
import { decodeRecoveryKey } from "../../../src/crypto/recoverykey";
import { IKeyBackupInfo, IKeyBackupSession } from "../../../src/crypto/keybackup";
import { TestClient } from "../../TestClient";
import { IEvent } from "../../../src";
import { MatrixEvent, MatrixEventEvent } from "../../../src/models/event";
import { createClient, ICreateClientOpts, IEvent, MatrixClient } from "../../../src";
import { MatrixEventEvent } from "../../../src/models/event";
import { SyncResponder } from "../../test-utils/SyncResponder";
import { E2EKeyReceiver } from "../../test-utils/E2EKeyReceiver";
import { E2EKeyResponder } from "../../test-utils/E2EKeyResponder";
import { mockInitialApiRequests } from "../../test-utils/mockEndpoints";
import { syncPromise } from "../../test-utils/test-utils";

const ROOM_ID = "!ROOM:ID";

/** The homeserver url that we give to the test client, and where we intercept /sync, /keys, etc requests. */
const TEST_HOMESERVER_URL = "https://alice-server.com";

const SESSION_ID = "o+21hSjP+mgEmcfdslPsQdvzWnkdt0Wyo00Kp++R8Kc";

const ENCRYPTED_EVENT: Partial<IEvent> = {
Expand Down Expand Up @@ -70,57 +77,65 @@ const CURVE25519_BACKUP_INFO: IKeyBackupInfo = {
version: "1",
auth_data: {
public_key: "hSDwCYkwp1R0i33ctD73Wg2/Og0mOBr066SpjqqbTmo",
// Will be updated with correct value on the fly
signatures: {},
},
};

const RECOVERY_KEY = "EsTc LW2K PGiF wKEA 3As5 g5c4 BXwk qeeJ ZJV8 Q9fu gUMN UE4d";

/**
* start an Olm session with a given recipient
*/
function createOlmSession(olmAccount: Olm.Account, recipientTestClient: TestClient): Promise<Olm.Session> {
return recipientTestClient.awaitOneTimeKeyUpload().then((keys) => {
const otkId = Object.keys(keys)[0];
const otk = keys[otkId];

const session = new global.Olm.Session();
session.create_outbound(olmAccount, recipientTestClient.getDeviceKey(), otk.key);
return session;
});
}
const TEST_USER_ID = "@alice:localhost";
const TEST_DEVICE_ID = "xzcvb";

describe("megolm key backups", function () {
if (!global.Olm) {
logger.warn("not running megolm tests: Olm not present");
return;
}
const Olm = global.Olm;
let testOlmAccount: Olm.Account;
let aliceTestClient: TestClient;

const setupTestClient = (): [Account, TestClient] => {
const aliceTestClient = new TestClient("@alice:localhost", "xzcvb", "akjgkrgjs");
const testOlmAccount = new Olm.Account();
testOlmAccount!.create();
let aliceClient: MatrixClient;
/** an object which intercepts `/sync` requests on the test homeserver */
let syncResponder: SyncResponder;

/** an object which intercepts `/keys/upload` requests on the test homeserver */
let e2eKeyReceiver: E2EKeyReceiver;
/** an object which intercepts `/keys/query` requests on the test homeserver */
let e2eKeyResponder: E2EKeyResponder;

jest.useFakeTimers();

beforeEach(async () => {
// anything that we don't have a specific matcher for silently returns a 404
fetchMock.catch(404);
fetchMock.config.warnOnFallback = false;

mockInitialApiRequests(TEST_HOMESERVER_URL);
syncResponder = new SyncResponder(TEST_HOMESERVER_URL);
e2eKeyReceiver = new E2EKeyReceiver(TEST_HOMESERVER_URL);
e2eKeyResponder = new E2EKeyResponder(TEST_HOMESERVER_URL);
e2eKeyResponder.addKeyReceiver(TEST_USER_ID, e2eKeyReceiver);
});

return [testOlmAccount, aliceTestClient];
};
afterEach(async () => {
if (aliceClient !== undefined) {
await aliceClient.stopClient();
}

beforeAll(function () {
return Olm.init();
});
// Allow in-flight things to complete before we tear down the test
await jest.runAllTimersAsync();

beforeEach(async function () {
[testOlmAccount, aliceTestClient] = setupTestClient();
await aliceTestClient!.client.initCrypto();
aliceTestClient!.client.crypto!.backupManager.backupInfo = CURVE25519_BACKUP_INFO;
fetchMock.mockReset();
});

afterEach(function () {
return aliceTestClient!.stop();
});
async function initTestClient(opts: Partial<ICreateClientOpts> = {}): Promise<MatrixClient> {
const client = createClient({
baseUrl: TEST_HOMESERVER_URL,
userId: TEST_USER_ID,
accessToken: "akjgkrgjs",
deviceId: TEST_DEVICE_ID,
...opts,
});
await client.initCrypto();

return client;
}

it("Alice checks key backups when receiving a message she can't decrypt", function () {
it("Alice checks key backups when receiving a message she can't decrypt", async function () {
const syncResponse = {
next_batch: 1,
rooms: {
Expand All @@ -134,37 +149,36 @@ describe("megolm key backups", function () {
},
};

return aliceTestClient!
.start()
.then(() => {
return createOlmSession(testOlmAccount, aliceTestClient);
})
.then(() => {
const privkey = decodeRecoveryKey(RECOVERY_KEY);
return aliceTestClient!.client!.crypto!.storeSessionBackupPrivateKey(privkey);
})
.then(() => {
aliceTestClient!.httpBackend.when("GET", "/sync").respond(200, syncResponse);
aliceTestClient!.expectKeyBackupQuery(ROOM_ID, SESSION_ID, 200, CURVE25519_KEY_BACKUP_DATA);
return aliceTestClient!.httpBackend.flushAllExpected();
})
.then(function (): Promise<MatrixEvent> {
const room = aliceTestClient!.client.getRoom(ROOM_ID)!;
const event = room.getLiveTimeline().getEvents()[0];

if (event.getContent()) {
return Promise.resolve(event);
}

return new Promise((resolve, reject) => {
event.once(MatrixEventEvent.Decrypted, (ev) => {
logger.log(`${Date.now()} event ${event.getId()} now decrypted`);
resolve(ev);
});
});
})
.then((event) => {
expect(event.getContent()).toEqual("testytest");
fetchMock.get("express:/_matrix/client/v3/room_keys/keys/:room_id/:session_id", CURVE25519_KEY_BACKUP_DATA);

// mock for the outgoing key requests that will be sent
fetchMock.put("express:/_matrix/client/r0/sendToDevice/m.room_key_request/:txid", {});

// We'll need to add a signature to the backup data, so take a copy to avoid mutating global state.
const backupData = JSON.parse(JSON.stringify(CURVE25519_BACKUP_INFO));
fetchMock.get("path:/_matrix/client/v3/room_keys/version", backupData);

aliceClient = await initTestClient();
await aliceClient.crypto!.signObject(backupData.auth_data);
await aliceClient.crypto!.storeSessionBackupPrivateKey(decodeRecoveryKey(RECOVERY_KEY));
await aliceClient.crypto!.backupManager!.checkAndStart();

// start after saving the private key
await aliceClient.startClient();

syncResponder.sendOrQueueSyncResponse(syncResponse);
await syncPromise(aliceClient);

const room = aliceClient.getRoom(ROOM_ID)!;

const event = room.getLiveTimeline().getEvents()[0];
await new Promise((resolve, reject) => {
event.once(MatrixEventEvent.Decrypted, (ev) => {
logger.log(`${Date.now()} event ${event.getId()} now decrypted`);
resolve(ev);
});
});

expect(event.getContent()).toEqual("testytest");
});
});

0 comments on commit 29b815b

Please sign in to comment.