You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-07-31 15:24:23 +03:00
MatrixClient.setAccountData
: await remote echo. (#4695)
* Rewrite `deleteAccountData` test use fetch-mock rather than whatever this was * `MatrixClient.setAccountData`: await remote echo Wait for the echo to come back from the server before we assume the account data has been successfully set * Update integration tests Fix up the integ tests which call `setAccountData` and now need a sync response. * Address review comment
This commit is contained in:
committed by
GitHub
parent
30d9b0518f
commit
c537a361fb
@ -47,6 +47,7 @@ import {
|
||||
ClientPrefix,
|
||||
ConditionKind,
|
||||
ContentHelpers,
|
||||
createClient,
|
||||
Direction,
|
||||
EventTimeline,
|
||||
EventTimelineSet,
|
||||
@ -81,6 +82,8 @@ import { KnownMembership } from "../../src/@types/membership";
|
||||
import { type RoomMessageEventContent } from "../../src/@types/events";
|
||||
import { mockOpenIdConfiguration } from "../test-utils/oidc.ts";
|
||||
import { type CryptoBackend } from "../../src/common-crypto/CryptoBackend";
|
||||
import { SyncResponder } from "../test-utils/SyncResponder.ts";
|
||||
import { mockInitialApiRequests } from "../test-utils/mockEndpoints.ts";
|
||||
|
||||
jest.useFakeTimers();
|
||||
|
||||
@ -125,6 +128,18 @@ type WrappedRoom = Room & {
|
||||
_state: Map<string, any>;
|
||||
};
|
||||
|
||||
/** A list of methods to run after the current test */
|
||||
const afterTestHooks: (() => Promise<void> | void)[] = [];
|
||||
|
||||
afterEach(async () => {
|
||||
fetchMock.reset();
|
||||
jest.restoreAllMocks();
|
||||
for (const hook of afterTestHooks) {
|
||||
await hook();
|
||||
}
|
||||
afterTestHooks.length = 0;
|
||||
});
|
||||
|
||||
describe("convertQueryDictToMap", () => {
|
||||
it("returns an empty map when dict is undefined", () => {
|
||||
expect(convertQueryDictToMap(undefined)).toEqual(new Map());
|
||||
@ -165,6 +180,11 @@ describe("MatrixClient", function () {
|
||||
const userId = "@alice:bar";
|
||||
const identityServerUrl = "https://identity.server";
|
||||
const identityServerDomain = "identity.server";
|
||||
|
||||
/**
|
||||
* @deprecated this is hard to use correctly; better to create a regular client with {@link createClient}
|
||||
* and use fetch-mock.
|
||||
*/
|
||||
let client: MatrixClient;
|
||||
let store: Store;
|
||||
let scheduler: MatrixScheduler;
|
||||
@ -2259,7 +2279,7 @@ describe("MatrixClient", function () {
|
||||
});
|
||||
|
||||
describe("checkTurnServers", () => {
|
||||
beforeAll(() => {
|
||||
beforeEach(() => {
|
||||
mocked(supportsMatrixCall).mockReturnValue(true);
|
||||
});
|
||||
|
||||
@ -2659,10 +2679,164 @@ describe("MatrixClient", function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe("delete account data", () => {
|
||||
afterEach(() => {
|
||||
jest.spyOn(featureUtils, "buildFeatureSupportMap").mockRestore();
|
||||
describe("setAccountData", () => {
|
||||
const TEST_HOMESERVER_URL = "https://alice-server.com";
|
||||
|
||||
/** Create and start a MatrixClient, connected to the `TEST_HOMESERVER_URL` */
|
||||
async function setUpClient(): Promise<MatrixClient> {
|
||||
// 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, userId);
|
||||
|
||||
const client = createClient({ baseUrl: TEST_HOMESERVER_URL, userId });
|
||||
await client.startClient();
|
||||
|
||||
// Remember to stop the client again, to stop it spamming logs and HTTP requests
|
||||
afterTestHooks.push(() => client.stopClient());
|
||||
return client;
|
||||
}
|
||||
|
||||
it("falls back to raw request if called before the client is started", async () => {
|
||||
// GIVEN a bunch of setup
|
||||
const client = createClient({ baseUrl: TEST_HOMESERVER_URL, userId });
|
||||
|
||||
const eventType = "im.vector.test";
|
||||
const content = { a: 1 };
|
||||
const testresponse = { test: 1 };
|
||||
|
||||
// ... including an expected REST request
|
||||
const url = new URL(
|
||||
`/_matrix/client/v3/user/${encodeURIComponent(client.getSafeUserId())}/account_data/${eventType}`,
|
||||
TEST_HOMESERVER_URL,
|
||||
).toString();
|
||||
fetchMock.put({ url, name: "put-account-data" }, testresponse);
|
||||
|
||||
// suppress the expected warning on the console
|
||||
jest.spyOn(console, "warn").mockImplementation();
|
||||
|
||||
// WHEN we call `setAccountData` ...
|
||||
const result = await client.setAccountData(eventType, content);
|
||||
|
||||
// THEN, method should have returned the right thing
|
||||
expect(result).toEqual(testresponse);
|
||||
|
||||
// and the REST call should have happened, and had the correct content
|
||||
const lastCall = fetchMock.lastCall("put-account-data");
|
||||
expect(lastCall).toBeDefined();
|
||||
expect(lastCall?.[1]?.body).toEqual(JSON.stringify(content));
|
||||
|
||||
// and a warning should have been logged
|
||||
// eslint-disable-next-line no-console
|
||||
expect(console.warn).toHaveBeenCalledWith(
|
||||
expect.stringContaining("Calling `setAccountData` before the client is started"),
|
||||
);
|
||||
});
|
||||
|
||||
it("makes a request to the server, and waits for the /sync response", async () => {
|
||||
// GIVEN a bunch of setup
|
||||
const syncResponder = new SyncResponder(TEST_HOMESERVER_URL);
|
||||
const client = await setUpClient();
|
||||
|
||||
const eventType = "im.vector.test";
|
||||
const content = { a: 1 };
|
||||
const testresponse = { test: 1 };
|
||||
|
||||
// ... including an expected REST request
|
||||
const url = new URL(
|
||||
`/_matrix/client/v3/user/${encodeURIComponent(client.getSafeUserId())}/account_data/${eventType}`,
|
||||
TEST_HOMESERVER_URL,
|
||||
).toString();
|
||||
fetchMock.put({ url, name: "put-account-data" }, testresponse);
|
||||
|
||||
// WHEN we call `setAccountData` ...
|
||||
const setProm = client.setAccountData(eventType, content);
|
||||
|
||||
// THEN, the REST call should have happened, and had the correct content
|
||||
const lastCall = fetchMock.lastCall("put-account-data");
|
||||
expect(lastCall).toBeDefined();
|
||||
expect(lastCall?.[1]?.body).toEqual(JSON.stringify(content));
|
||||
|
||||
// Even after waiting a bit, the method should not yet have returned
|
||||
await jest.advanceTimersByTimeAsync(10);
|
||||
let finished = false;
|
||||
setProm.finally(() => (finished = true));
|
||||
expect(finished).toBeFalsy();
|
||||
|
||||
// ... and `getAccountData` still returns the wrong thing
|
||||
expect(client.getAccountData(eventType)).not.toBeDefined();
|
||||
|
||||
// WHEN the update arrives over /sync
|
||||
const content2 = { a: 2 };
|
||||
|
||||
syncResponder.sendOrQueueSyncResponse({
|
||||
account_data: { events: [{ type: eventType, content: content2 }] },
|
||||
});
|
||||
|
||||
// THEN the method should complete, and `getAccountData` returns the new data
|
||||
expect(await setProm).toEqual(testresponse);
|
||||
expect(client.getAccountData(eventType)?.event).toEqual({ type: eventType, content: content2 });
|
||||
});
|
||||
|
||||
it("does nothing if the data matches what is there", async () => {
|
||||
// GIVEN a running matrix client ...
|
||||
const syncResponder = new SyncResponder(TEST_HOMESERVER_URL);
|
||||
const client = await setUpClient();
|
||||
|
||||
// ... which has previously received the account data over /sync
|
||||
const eventType = "im.vector.test";
|
||||
const content = { a: 1, b: 2 };
|
||||
|
||||
// Keys in a different order, to check that doesn't matter.
|
||||
const syncedContent = { b: 2, a: 1 };
|
||||
syncResponder.sendOrQueueSyncResponse({
|
||||
account_data: { events: [{ type: eventType, content: syncedContent }] },
|
||||
});
|
||||
await jest.advanceTimersByTimeAsync(1);
|
||||
|
||||
// Check that getAccountData is ready
|
||||
expect(client.getAccountData(eventType)?.event).toEqual({ type: eventType, content: syncedContent });
|
||||
|
||||
// WHEN we call `setAccountData` ...
|
||||
await client.setAccountData(eventType, content);
|
||||
|
||||
// THEN there should be no REST call
|
||||
expect(fetchMock.calls(/account_data/).length).toEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("delete account data", () => {
|
||||
const TEST_HOMESERVER_URL = "https://alice-server.com";
|
||||
|
||||
/** Create and start a MatrixClient, connected to the `TEST_HOMESERVER_URL` */
|
||||
async function setUpClient(versionsResponse: object = { versions: ["1"] }): Promise<MatrixClient> {
|
||||
// anything that we don't have a specific matcher for silently returns a 404
|
||||
fetchMock.catch(404);
|
||||
fetchMock.config.warnOnFallback = false;
|
||||
|
||||
fetchMock.getOnce(new URL("/_matrix/client/versions", TEST_HOMESERVER_URL).toString(), versionsResponse, {
|
||||
overwriteRoutes: true,
|
||||
});
|
||||
fetchMock.getOnce(
|
||||
new URL("/_matrix/client/v3/pushrules/", TEST_HOMESERVER_URL).toString(),
|
||||
{},
|
||||
{ overwriteRoutes: true },
|
||||
);
|
||||
fetchMock.postOnce(
|
||||
new URL(`/_matrix/client/v3/user/${encodeURIComponent(userId)}/filter`, TEST_HOMESERVER_URL).toString(),
|
||||
{ filter_id: "fid" },
|
||||
{ overwriteRoutes: true },
|
||||
);
|
||||
|
||||
const client = createClient({ baseUrl: TEST_HOMESERVER_URL, userId });
|
||||
await client.startClient();
|
||||
|
||||
// Remember to stop the client again, to stop it spamming logs and HTTP requests
|
||||
afterTestHooks.push(() => client.stopClient());
|
||||
return client;
|
||||
}
|
||||
|
||||
it("makes correct request when deletion is supported by server in unstable versions", async () => {
|
||||
const eventType = "im.vector.test";
|
||||
const versionsResponse = {
|
||||
@ -2671,17 +2845,17 @@ describe("MatrixClient", function () {
|
||||
"org.matrix.msc3391": true,
|
||||
},
|
||||
};
|
||||
const requestSpy = jest.spyOn(client.http, "authedRequest").mockResolvedValue(versionsResponse);
|
||||
const unstablePrefix = "/_matrix/client/unstable/org.matrix.msc3391";
|
||||
const path = `/user/${encodeURIComponent(userId)}/account_data/${eventType}`;
|
||||
const client = await setUpClient(versionsResponse);
|
||||
|
||||
const url = new URL(
|
||||
`/_matrix/client/unstable/org.matrix.msc3391/user/${encodeURIComponent(userId)}/account_data/${eventType}`,
|
||||
TEST_HOMESERVER_URL,
|
||||
).toString();
|
||||
fetchMock.delete({ url, name: "delete-data" }, {});
|
||||
|
||||
// populate version support
|
||||
await client.getVersions();
|
||||
await client.deleteAccountData(eventType);
|
||||
|
||||
expect(requestSpy).toHaveBeenCalledWith(Method.Delete, path, undefined, undefined, {
|
||||
prefix: unstablePrefix,
|
||||
});
|
||||
expect(fetchMock.calls("delete-data").length).toEqual(1);
|
||||
});
|
||||
|
||||
it("makes correct request when deletion is supported by server based on matrix version", async () => {
|
||||
@ -2690,34 +2864,43 @@ describe("MatrixClient", function () {
|
||||
// so mock the support map to fake stable support
|
||||
const stableSupportedDeletionMap = new Map();
|
||||
stableSupportedDeletionMap.set(featureUtils.Feature.AccountDataDeletion, featureUtils.ServerSupport.Stable);
|
||||
jest.spyOn(featureUtils, "buildFeatureSupportMap").mockResolvedValue(new Map());
|
||||
const requestSpy = jest.spyOn(client.http, "authedRequest").mockImplementation(() => Promise.resolve());
|
||||
const path = `/user/${encodeURIComponent(userId)}/account_data/${eventType}`;
|
||||
jest.spyOn(featureUtils, "buildFeatureSupportMap").mockResolvedValue(stableSupportedDeletionMap);
|
||||
|
||||
const client = await setUpClient();
|
||||
|
||||
const url = new URL(
|
||||
`/_matrix/client/v3/user/${encodeURIComponent(userId)}/account_data/${eventType}`,
|
||||
TEST_HOMESERVER_URL,
|
||||
).toString();
|
||||
fetchMock.delete({ url, name: "delete-data" }, {});
|
||||
|
||||
// populate version support
|
||||
await client.getVersions();
|
||||
await client.deleteAccountData(eventType);
|
||||
|
||||
expect(requestSpy).toHaveBeenCalledWith(Method.Delete, path, undefined, undefined, undefined);
|
||||
expect(fetchMock.calls("delete-data").length).toEqual(1);
|
||||
});
|
||||
|
||||
it("makes correct request when deletion is not supported by server", async () => {
|
||||
const eventType = "im.vector.test";
|
||||
const versionsResponse = {
|
||||
versions: ["1"],
|
||||
unstable_features: {
|
||||
"org.matrix.msc3391": false,
|
||||
},
|
||||
};
|
||||
const requestSpy = jest.spyOn(client.http, "authedRequest").mockResolvedValue(versionsResponse);
|
||||
const path = `/user/${encodeURIComponent(userId)}/account_data/${eventType}`;
|
||||
|
||||
// populate version support
|
||||
await client.getVersions();
|
||||
await client.deleteAccountData(eventType);
|
||||
const syncResponder = new SyncResponder(TEST_HOMESERVER_URL);
|
||||
const client = await setUpClient();
|
||||
|
||||
const url = new URL(
|
||||
`/_matrix/client/v3/user/${encodeURIComponent(userId)}/account_data/${eventType}`,
|
||||
TEST_HOMESERVER_URL,
|
||||
).toString();
|
||||
fetchMock.put({ url, name: "put-account-data" }, {});
|
||||
|
||||
const setProm = client.deleteAccountData(eventType);
|
||||
syncResponder.sendOrQueueSyncResponse({
|
||||
account_data: { events: [{ type: eventType, content: {} }] },
|
||||
});
|
||||
await setProm;
|
||||
|
||||
// account data updated with empty content
|
||||
expect(requestSpy).toHaveBeenCalledWith(Method.Put, path, undefined, {});
|
||||
const lastCall = fetchMock.lastCall("put-account-data");
|
||||
expect(lastCall).toBeDefined();
|
||||
expect(lastCall?.[1]?.body).toEqual("{}");
|
||||
});
|
||||
});
|
||||
|
||||
|
Reference in New Issue
Block a user