You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-08-09 10:22:46 +03:00
* Remove deprecated calls in `webrtc/call.ts` * Throw error when legacy call was used * Remove `MatrixClient.initLegacyCrypto` (#4620) * Remove `MatrixClient.initLegacyCrypto` * Remove `MatrixClient.initLegacyCrypto` in README.md * Remove tests using `MatrixClient.initLegacyCrypto` * Remove legacy crypto support in `sync` api (#4622) * Remove deprecated `DeviceInfo` in `webrtc/call.ts` (#4654) * chore(legacy call): Remove `DeviceInfo` usage * refactor(legacy call): throw `GroupCallUnknownDeviceError` at the end of `initOpponentCrypto` * Remove deprecated methods and attributes of `MatrixClient` (#4659) * feat(legacy crypto)!: remove deprecated methods of `MatrixClient` * test(legacy crypto): update existing tests to not use legacy crypto - `Embedded.spec.ts`: casting since `encryptAndSendToDevices` is removed from `MatrixClient`. - `room.spec.ts`: remove deprecated usage of `MatrixClient.crypto` - `matrix-client.spec.ts` & `matrix-client-methods.spec.ts`: remove calls of deprecated methods of `MatrixClient` * test(legacy crypto): remove test files using `MatrixClient` deprecated methods * test(legacy crypto): update existing integ tests to run successfully * feat(legacy crypto!): remove `ICreateClientOpts.deviceToImport`. `ICreateClientOpts.deviceToImport` was used in the legacy cryto. The rust crypto doesn't support to import devices in this way. * feat(legacy crypto!): remove `{get,set}GlobalErrorOnUnknownDevices` `globalErrorOnUnknownDevices` is not used in the rust-crypto. The API is marked as unstable, we can remove it. * Remove usage of legacy crypto in `event.ts` (#4666) * feat(legacy crypto!): remove legacy crypto usage in `event.ts` * test(legacy crypto): update event.spec.ts to not use legacy crypto types * Remove legacy crypto export in `matrix.ts` (#4667) * feat(legacy crypto!): remove legacy crypto export in `matrix.ts` * test(legacy crypto): update `megolm-backup.spec.ts` to import directly `CryptoApi` * Remove usage of legacy crypto in integ tests (#4669) * Clean up legacy stores (#4663) * feat(legacy crypto!): keep legacy methods used in lib olm migration The rust cryto needs these legacy stores in order to do the migration from the legacy crypto to the rust crypto. We keep the following methods of the stores: - Used in `libolm_migration.ts`. - Needed in the legacy store tests. - Needed in the rust crypto test migration. * feat(legacy crypto): extract legacy crypto types in legacy stores In order to be able to delete the legacy crypto, these stores shouldn't rely on the legacy crypto. We need to extract the used types. * feat(crypto store): remove `CryptoStore` functions used only by tests * test(crypto store): use legacy `MemoryStore` type * Remove deprecated methods of `CryptoBackend` (#4671) * feat(CryptoBackend)!: remove deprecated methods * feat(rust-crypto)!: remove deprecated methods of `CryptoBackend` * test(rust-crypto): remove tests of deprecated methods of `CryptoBackend` * Remove usage of legacy crypto in `embedded.ts` (#4668) The interface of `encryptAndSendToDevices` changes because `DeviceInfo` is from the legacy crypto. In fact `encryptAndSendToDevices` only need pairs of userId and deviceId. * Remove legacy crypto files (#4672) * fix(legacy store): fix legacy store typing In https://github.com/matrix-org/matrix-js-sdk/pull/4663, the storeXXX methods were removed of the CryptoStore interface but they are used internally by IndexedDBCryptoStore. * feat(legacy crypto)!: remove content of `crypto/*` except legacy stores * test(legacy crypto): remove `spec/unit/crypto/*` except legacy store tests * refactor: remove unused types * doc: fix broken link * doc: remove link tag when typedoc is unable to find the CryptoApi * Clean up integ test after legacy crypto removal (#4682) * test(crypto): remove `newBackendOnly` test closure * test(crypto): fix duplicate test name * test(crypto): remove `oldBackendOnly` test closure * test(crypto): remove `rust-sdk` comparison * test(crypto): remove iteration on `CRYPTO_BACKEND` * test(crypto): remove old legacy comments and tests * test(crypto): fix documentations and removed unused expect * Restore broken link to `CryptoApi` (#4692) * chore: fix linting and formatting due to merge * Remove unused crypto type and missing doc (#4696) * chore(crypto): remove unused types * doc(crypto): add missing link * test(call): add test when crypto is enabled
2035 lines
77 KiB
TypeScript
2035 lines
77 KiB
TypeScript
/*
|
|
Copyright 2022 - 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 type HttpBackend from "matrix-mock-request";
|
|
import * as utils from "../test-utils/test-utils";
|
|
import { type IStoredClientOpts, MatrixClient } from "../../src/client";
|
|
import { MatrixEvent } from "../../src/models/event";
|
|
import {
|
|
Filter,
|
|
JoinRule,
|
|
type KnockRoomOpts,
|
|
MemoryStore,
|
|
Method,
|
|
Room,
|
|
type RoomSummary,
|
|
SERVICE_TYPES,
|
|
} from "../../src/matrix";
|
|
import { TestClient } from "../TestClient";
|
|
import { THREAD_RELATION_TYPE } from "../../src/models/thread";
|
|
import { type IFilterDefinition } from "../../src/filter";
|
|
import { type ISearchResults } from "../../src/@types/search";
|
|
import { type IStore } from "../../src/store";
|
|
import { SetPresence } from "../../src/sync";
|
|
import { KnownMembership } from "../../src/@types/membership";
|
|
|
|
describe("MatrixClient", function () {
|
|
const userId = "@alice:localhost";
|
|
const accessToken = "aseukfgwef";
|
|
const idServerDomain = "identity.localhost"; // not a real server
|
|
const identityAccessToken = "woop-i-am-a-secret";
|
|
let client: MatrixClient;
|
|
let httpBackend: HttpBackend;
|
|
let store: MemoryStore;
|
|
|
|
const defaultClientOpts: IStoredClientOpts = {
|
|
threadSupport: false,
|
|
};
|
|
const setupTests = (): [MatrixClient, HttpBackend, MemoryStore] => {
|
|
const store = new MemoryStore();
|
|
|
|
const testClient = new TestClient(userId, "aliceDevice", accessToken, undefined, {
|
|
store: store as IStore,
|
|
identityServer: {
|
|
getAccessToken: () => Promise.resolve(identityAccessToken),
|
|
},
|
|
idBaseUrl: `https://${idServerDomain}`,
|
|
});
|
|
|
|
return [testClient.client, testClient.httpBackend, store];
|
|
};
|
|
|
|
beforeEach(function () {
|
|
[client, httpBackend, store] = setupTests();
|
|
});
|
|
|
|
afterEach(function () {
|
|
httpBackend.verifyNoOutstandingExpectation();
|
|
return httpBackend.stop();
|
|
});
|
|
|
|
describe("uploadContent", function () {
|
|
const buf = Buffer.from("hello world");
|
|
const file = buf;
|
|
const opts = {
|
|
type: "text/plain",
|
|
name: "hi.txt",
|
|
};
|
|
|
|
it("should upload the file", function () {
|
|
httpBackend
|
|
.when("POST", "/_matrix/media/v3/upload")
|
|
.check(function (req) {
|
|
expect(req.rawData).toEqual(buf);
|
|
expect(req.queryParams?.filename).toEqual("hi.txt");
|
|
expect(req.headers["Authorization"]).toBe("Bearer " + accessToken);
|
|
expect(req.headers["Content-Type"]).toEqual("text/plain");
|
|
// @ts-ignore private property
|
|
expect(req.opts.json).toBeFalsy();
|
|
// @ts-ignore private property
|
|
expect(req.opts.timeout).toBe(undefined);
|
|
})
|
|
.respond(200, '{"content_uri": "content"}', true);
|
|
|
|
const prom = client.uploadContent(file, opts);
|
|
|
|
expect(prom).toBeTruthy();
|
|
|
|
const uploads = client.getCurrentUploads();
|
|
expect(uploads.length).toEqual(1);
|
|
expect(uploads[0].promise).toBe(prom);
|
|
expect(uploads[0].loaded).toEqual(0);
|
|
|
|
const prom2 = prom.then(function (response) {
|
|
expect(response.content_uri).toEqual("content");
|
|
|
|
const uploads = client.getCurrentUploads();
|
|
expect(uploads.length).toEqual(0);
|
|
});
|
|
|
|
httpBackend.flush("");
|
|
return prom2;
|
|
});
|
|
|
|
it("should parse errors into a MatrixError", function () {
|
|
httpBackend
|
|
.when("POST", "/_matrix/media/v3/upload")
|
|
.check(function (req) {
|
|
expect(req.rawData).toEqual(buf);
|
|
// @ts-ignore private property
|
|
expect(req.opts.json).toBeFalsy();
|
|
})
|
|
.respond(400, {
|
|
errcode: "M_SNAFU",
|
|
error: "broken",
|
|
});
|
|
|
|
const prom = client.uploadContent(file, opts).then(
|
|
function (response) {
|
|
throw Error("request not failed");
|
|
},
|
|
function (error) {
|
|
expect(error.httpStatus).toEqual(400);
|
|
expect(error.errcode).toEqual("M_SNAFU");
|
|
expect(error.message).toEqual("MatrixError: [400] broken");
|
|
},
|
|
);
|
|
|
|
httpBackend.flush("");
|
|
return prom;
|
|
});
|
|
|
|
it("should return a promise which can be cancelled", async () => {
|
|
const prom = client.uploadContent(file, opts);
|
|
|
|
const uploads = client.getCurrentUploads();
|
|
expect(uploads.length).toEqual(1);
|
|
expect(uploads[0].promise).toBe(prom);
|
|
expect(uploads[0].loaded).toEqual(0);
|
|
|
|
const r = client.cancelUpload(prom);
|
|
expect(r).toBe(true);
|
|
await expect(prom).rejects.toThrow("Aborted");
|
|
expect(client.getCurrentUploads()).toHaveLength(0);
|
|
});
|
|
});
|
|
|
|
describe("joinRoom", function () {
|
|
it("should no-op given the ID of a room you've already joined", async () => {
|
|
const roomId = "!foo:bar";
|
|
const room = new Room(roomId, client, userId);
|
|
client.fetchRoomEvent = () =>
|
|
Promise.resolve({
|
|
type: "test",
|
|
content: {},
|
|
});
|
|
room.addLiveEvents(
|
|
[
|
|
utils.mkMembership({
|
|
user: userId,
|
|
room: roomId,
|
|
mship: KnownMembership.Join,
|
|
event: true,
|
|
}),
|
|
],
|
|
{ addToState: true },
|
|
);
|
|
httpBackend.verifyNoOutstandingRequests();
|
|
store.storeRoom(room);
|
|
|
|
const joinPromise = client.joinRoom(roomId);
|
|
httpBackend.verifyNoOutstandingRequests();
|
|
expect(await joinPromise).toBe(room);
|
|
});
|
|
|
|
it("should no-op given the alias of a room you've already joined", async () => {
|
|
const roomId = "!roomId:server";
|
|
const roomAlias = "#my-fancy-room:server";
|
|
const room = new Room(roomId, client, userId);
|
|
room.addLiveEvents(
|
|
[
|
|
utils.mkMembership({
|
|
user: userId,
|
|
room: roomId,
|
|
mship: KnownMembership.Join,
|
|
event: true,
|
|
}),
|
|
],
|
|
{ addToState: true },
|
|
);
|
|
store.storeRoom(room);
|
|
|
|
// The method makes a request to resolve the alias
|
|
httpBackend.when("POST", "/join/" + encodeURIComponent(roomAlias)).respond(200, { room_id: roomId });
|
|
|
|
const joinPromise = client.joinRoom(roomAlias);
|
|
await httpBackend.flushAllExpected();
|
|
expect(await joinPromise).toBe(room);
|
|
});
|
|
|
|
it("should send request to inviteSignUrl if specified", async () => {
|
|
const roomId = "!roomId:server";
|
|
const inviteSignUrl = "https://id.server/sign/this/for/me";
|
|
const viaServers = ["a", "b", "c"];
|
|
const signature = {
|
|
sender: "sender",
|
|
mxid: "@sender:foo",
|
|
token: "token",
|
|
signatures: {},
|
|
};
|
|
|
|
httpBackend
|
|
.when("POST", inviteSignUrl)
|
|
.check((request) => {
|
|
expect(request.queryParams?.mxid).toEqual(client.getUserId());
|
|
})
|
|
.respond(200, signature);
|
|
httpBackend
|
|
.when("POST", "/join/" + encodeURIComponent(roomId))
|
|
.check((request) => {
|
|
expect(request.data.third_party_signed).toEqual(signature);
|
|
})
|
|
.respond(200, { room_id: roomId });
|
|
|
|
const prom = client.joinRoom(roomId, {
|
|
inviteSignUrl,
|
|
viaServers,
|
|
});
|
|
await httpBackend.flushAllExpected();
|
|
expect((await prom).roomId).toBe(roomId);
|
|
});
|
|
});
|
|
|
|
describe("knockRoom", function () {
|
|
const roomId = "!some-room-id:example.org";
|
|
const reason = "some reason";
|
|
const viaServers = "example.com";
|
|
|
|
type TestCase = [string, KnockRoomOpts];
|
|
const testCases: TestCase[] = [
|
|
["should knock a room", {}],
|
|
["should knock a room for a reason", { reason }],
|
|
["should knock a room via given servers", { viaServers }],
|
|
["should knock a room for a reason via given servers", { reason, viaServers }],
|
|
];
|
|
|
|
it.each(testCases)("%s", async (_, opts) => {
|
|
httpBackend
|
|
.when("POST", "/knock/" + encodeURIComponent(roomId))
|
|
.check((request) => {
|
|
expect(request.data).toEqual({ reason: opts.reason });
|
|
expect(request.queryParams).toEqual({ server_name: opts.viaServers, via: opts.viaServers });
|
|
})
|
|
.respond(200, { room_id: roomId });
|
|
|
|
const prom = client.knockRoom(roomId, opts);
|
|
await httpBackend.flushAllExpected();
|
|
expect((await prom).room_id).toBe(roomId);
|
|
});
|
|
|
|
it("should no-op if you've already knocked a room", function () {
|
|
const room = new Room(roomId, client, userId);
|
|
|
|
client.fetchRoomEvent = () =>
|
|
Promise.resolve({
|
|
type: "test",
|
|
content: {},
|
|
});
|
|
|
|
room.addLiveEvents(
|
|
[
|
|
utils.mkMembership({
|
|
user: userId,
|
|
room: roomId,
|
|
mship: KnownMembership.Knock,
|
|
event: true,
|
|
}),
|
|
],
|
|
{ addToState: true },
|
|
);
|
|
|
|
httpBackend.verifyNoOutstandingRequests();
|
|
store.storeRoom(room);
|
|
client.knockRoom(roomId);
|
|
httpBackend.verifyNoOutstandingRequests();
|
|
});
|
|
|
|
describe("errors", function () {
|
|
type TestCase = [number, { errcode: string; error?: string }, string];
|
|
const testCases: TestCase[] = [
|
|
[
|
|
403,
|
|
{ errcode: "M_FORBIDDEN", error: "You don't have permission to knock" },
|
|
"[M_FORBIDDEN: MatrixError: [403] You don't have permission to knock]",
|
|
],
|
|
[
|
|
500,
|
|
{ errcode: "INTERNAL_SERVER_ERROR" },
|
|
"[INTERNAL_SERVER_ERROR: MatrixError: [500] Unknown message]",
|
|
],
|
|
];
|
|
|
|
it.each(testCases)("should handle %s error", async (code, { errcode, error }, snapshot) => {
|
|
httpBackend.when("POST", "/knock/" + encodeURIComponent(roomId)).respond(code, { errcode, error });
|
|
|
|
const prom = client.knockRoom(roomId);
|
|
await Promise.all([
|
|
httpBackend.flushAllExpected(),
|
|
expect(prom).rejects.toMatchInlineSnapshot(snapshot),
|
|
]);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("getFilter", function () {
|
|
const filterId = "f1lt3r1d";
|
|
|
|
it("should return a filter from the store if allowCached", async () => {
|
|
const filter = Filter.fromJson(userId, filterId, {
|
|
event_format: "client",
|
|
});
|
|
store.storeFilter(filter);
|
|
const gotFilter = await client.getFilter(userId, filterId, true);
|
|
expect(gotFilter).toEqual(filter);
|
|
httpBackend.verifyNoOutstandingRequests();
|
|
});
|
|
|
|
it("should do an HTTP request if !allowCached even if one exists", async () => {
|
|
const httpFilterDefinition = {
|
|
event_format: "federation",
|
|
};
|
|
|
|
httpBackend
|
|
.when("GET", "/user/" + encodeURIComponent(userId) + "/filter/" + filterId)
|
|
.respond(200, httpFilterDefinition);
|
|
|
|
const storeFilter = Filter.fromJson(userId, filterId, {
|
|
event_format: "client",
|
|
});
|
|
store.storeFilter(storeFilter);
|
|
const [gotFilter] = await Promise.all([client.getFilter(userId, filterId, false), httpBackend.flush("")]);
|
|
expect(gotFilter.getDefinition()).toEqual(httpFilterDefinition);
|
|
});
|
|
|
|
it("should do an HTTP request if nothing is in the cache and then store it", async () => {
|
|
const httpFilterDefinition = {
|
|
event_format: "federation",
|
|
};
|
|
expect(store.getFilter(userId, filterId)).toBe(null);
|
|
|
|
httpBackend
|
|
.when("GET", "/user/" + encodeURIComponent(userId) + "/filter/" + filterId)
|
|
.respond(200, httpFilterDefinition);
|
|
const [gotFilter] = await Promise.all([client.getFilter(userId, filterId, true), httpBackend.flush("")]);
|
|
expect(gotFilter.getDefinition()).toEqual(httpFilterDefinition);
|
|
expect(store.getFilter(userId, filterId)).toBeTruthy();
|
|
});
|
|
});
|
|
|
|
describe("createFilter", function () {
|
|
const filterId = "f1llllllerid";
|
|
|
|
it("should do an HTTP request and then store the filter", async () => {
|
|
expect(store.getFilter(userId, filterId)).toBe(null);
|
|
|
|
const filterDefinition = {
|
|
event_format: "client" as IFilterDefinition["event_format"],
|
|
};
|
|
|
|
httpBackend
|
|
.when("POST", "/user/" + encodeURIComponent(userId) + "/filter")
|
|
.check(function (req) {
|
|
expect(req.data).toEqual(filterDefinition);
|
|
})
|
|
.respond(200, {
|
|
filter_id: filterId,
|
|
});
|
|
|
|
const [gotFilter] = await Promise.all([client.createFilter(filterDefinition), httpBackend.flush("")]);
|
|
expect(gotFilter.getDefinition()).toEqual(filterDefinition);
|
|
expect(store.getFilter(userId, filterId)).toEqual(gotFilter);
|
|
});
|
|
});
|
|
|
|
describe("searching", function () {
|
|
it("searchMessageText should perform a /search for room_events", function () {
|
|
const response = {
|
|
search_categories: {
|
|
room_events: {
|
|
count: 24,
|
|
results: [
|
|
{
|
|
rank: 0.1,
|
|
result: {
|
|
event_id: "$flibble:localhost",
|
|
type: "m.room.message",
|
|
user_id: "@alice:localhost",
|
|
room_id: "!feuiwhf:localhost",
|
|
content: {
|
|
body: "a result",
|
|
msgtype: "m.text",
|
|
},
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
};
|
|
|
|
client.searchMessageText({
|
|
query: "monkeys",
|
|
});
|
|
httpBackend
|
|
.when("POST", "/search")
|
|
.check(function (req) {
|
|
expect(req.data).toEqual({
|
|
search_categories: {
|
|
room_events: {
|
|
search_term: "monkeys",
|
|
},
|
|
},
|
|
});
|
|
})
|
|
.respond(200, response);
|
|
|
|
return httpBackend.flush("");
|
|
});
|
|
|
|
describe("should filter out context from different timelines (threads)", () => {
|
|
it("filters out thread replies when result is in the main timeline", async () => {
|
|
const response = {
|
|
search_categories: {
|
|
room_events: {
|
|
count: 24,
|
|
highlights: [],
|
|
results: [
|
|
{
|
|
rank: 0.1,
|
|
result: {
|
|
event_id: "$flibble:localhost",
|
|
type: "m.room.message",
|
|
sender: "@test:locahost",
|
|
origin_server_ts: 123,
|
|
user_id: "@alice:localhost",
|
|
room_id: "!feuiwhf:localhost",
|
|
content: {
|
|
body: "main timeline",
|
|
msgtype: "m.text",
|
|
},
|
|
},
|
|
context: {
|
|
profile_info: {},
|
|
events_after: [
|
|
{
|
|
event_id: "$ev-after:server",
|
|
type: "m.room.message",
|
|
sender: "@test:locahost",
|
|
origin_server_ts: 123,
|
|
user_id: "@alice:localhost",
|
|
room_id: "!feuiwhf:localhost",
|
|
content: {
|
|
"body": "thread reply",
|
|
"msgtype": "m.text",
|
|
"m.relates_to": {
|
|
event_id: "$some-thread:server",
|
|
rel_type: THREAD_RELATION_TYPE.name,
|
|
},
|
|
},
|
|
},
|
|
],
|
|
events_before: [
|
|
{
|
|
event_id: "$ev-before:server",
|
|
type: "m.room.message",
|
|
sender: "@test:locahost",
|
|
origin_server_ts: 123,
|
|
user_id: "@alice:localhost",
|
|
room_id: "!feuiwhf:localhost",
|
|
content: {
|
|
body: "main timeline again",
|
|
msgtype: "m.text",
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
};
|
|
|
|
const data: ISearchResults = {
|
|
results: [],
|
|
highlights: [],
|
|
};
|
|
client.processRoomEventsSearch(data, response);
|
|
|
|
expect(data.results).toHaveLength(1);
|
|
expect(data.results[0].context.getTimeline()).toHaveLength(2);
|
|
expect(data.results[0].context.getTimeline().find((e) => e.getId() === "$ev-after:server")).toBeFalsy();
|
|
});
|
|
|
|
it("filters out thread replies from threads other than the thread the result replied to", () => {
|
|
const response = {
|
|
search_categories: {
|
|
room_events: {
|
|
count: 24,
|
|
highlights: [],
|
|
results: [
|
|
{
|
|
rank: 0.1,
|
|
result: {
|
|
event_id: "$flibble:localhost",
|
|
type: "m.room.message",
|
|
sender: "@test:locahost",
|
|
origin_server_ts: 123,
|
|
user_id: "@alice:localhost",
|
|
room_id: "!feuiwhf:localhost",
|
|
content: {
|
|
"body": "thread 1 reply 1",
|
|
"msgtype": "m.text",
|
|
"m.relates_to": {
|
|
event_id: "$thread1:server",
|
|
rel_type: THREAD_RELATION_TYPE.name,
|
|
},
|
|
},
|
|
},
|
|
context: {
|
|
profile_info: {},
|
|
events_after: [
|
|
{
|
|
event_id: "$ev-after:server",
|
|
type: "m.room.message",
|
|
sender: "@test:locahost",
|
|
origin_server_ts: 123,
|
|
user_id: "@alice:localhost",
|
|
room_id: "!feuiwhf:localhost",
|
|
content: {
|
|
"body": "thread 2 reply 2",
|
|
"msgtype": "m.text",
|
|
"m.relates_to": {
|
|
event_id: "$thread2:server",
|
|
rel_type: THREAD_RELATION_TYPE.name,
|
|
},
|
|
},
|
|
},
|
|
],
|
|
events_before: [],
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
};
|
|
|
|
const data: ISearchResults = {
|
|
results: [],
|
|
highlights: [],
|
|
};
|
|
client.processRoomEventsSearch(data, response);
|
|
|
|
expect(data.results).toHaveLength(1);
|
|
expect(data.results[0].context.getTimeline()).toHaveLength(1);
|
|
expect(
|
|
data.results[0].context.getTimeline().find((e) => e.getId() === "$flibble:localhost"),
|
|
).toBeTruthy();
|
|
});
|
|
|
|
it("filters out main timeline events when result is a thread reply", () => {
|
|
const response = {
|
|
search_categories: {
|
|
room_events: {
|
|
count: 24,
|
|
highlights: [],
|
|
results: [
|
|
{
|
|
rank: 0.1,
|
|
result: {
|
|
event_id: "$flibble:localhost",
|
|
sender: "@test:locahost",
|
|
origin_server_ts: 123,
|
|
type: "m.room.message",
|
|
user_id: "@alice:localhost",
|
|
room_id: "!feuiwhf:localhost",
|
|
content: {
|
|
"body": "thread 1 reply 1",
|
|
"msgtype": "m.text",
|
|
"m.relates_to": {
|
|
event_id: "$thread1:server",
|
|
rel_type: THREAD_RELATION_TYPE.name,
|
|
},
|
|
},
|
|
},
|
|
context: {
|
|
events_after: [
|
|
{
|
|
event_id: "$ev-after:server",
|
|
sender: "@test:locahost",
|
|
origin_server_ts: 123,
|
|
type: "m.room.message",
|
|
user_id: "@alice:localhost",
|
|
room_id: "!feuiwhf:localhost",
|
|
content: {
|
|
body: "main timeline",
|
|
msgtype: "m.text",
|
|
},
|
|
},
|
|
],
|
|
events_before: [],
|
|
profile_info: {},
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
};
|
|
|
|
const data: ISearchResults = {
|
|
results: [],
|
|
highlights: [],
|
|
};
|
|
client.processRoomEventsSearch(data, response);
|
|
|
|
expect(data.results).toHaveLength(1);
|
|
expect(data.results[0].context.getTimeline()).toHaveLength(1);
|
|
expect(
|
|
data.results[0].context.getTimeline().find((e) => e.getId() === "$flibble:localhost"),
|
|
).toBeTruthy();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("partitionThreadedEvents", function () {
|
|
let room: Room;
|
|
beforeEach(() => {
|
|
room = new Room("!STrMRsukXHtqQdSeHa:matrix.org", client, userId);
|
|
});
|
|
|
|
it("returns empty arrays when given an empty arrays", function () {
|
|
const events: MatrixEvent[] = [];
|
|
const [timeline, threaded] = room.partitionThreadedEvents(events);
|
|
expect(timeline).toEqual([]);
|
|
expect(threaded).toEqual([]);
|
|
});
|
|
|
|
it("should not copy pre-thread in-timeline vote events onto both timelines", function () {
|
|
// @ts-ignore setting private property
|
|
client.clientOpts = {
|
|
...defaultClientOpts,
|
|
threadSupport: true,
|
|
};
|
|
|
|
const eventPollResponseReference = buildEventPollResponseReference();
|
|
const eventPollStartThreadRoot = buildEventPollStartThreadRoot();
|
|
const eventMessageInThread = buildEventMessageInThread(eventPollStartThreadRoot);
|
|
|
|
const events = [eventPollStartThreadRoot, eventMessageInThread, eventPollResponseReference];
|
|
// Vote has no threadId yet
|
|
// @ts-ignore private property
|
|
expect(eventPollResponseReference.threadId).toBeFalsy();
|
|
|
|
const [timeline, threaded] = room.partitionThreadedEvents(events);
|
|
|
|
expect(timeline).toEqual([
|
|
// The message that was sent in a thread is missing
|
|
eventPollStartThreadRoot,
|
|
eventPollResponseReference,
|
|
]);
|
|
|
|
// The vote event has been copied into the thread
|
|
const eventRefWithThreadId = withThreadId(eventPollResponseReference, eventPollStartThreadRoot.getId()!);
|
|
expect(eventRefWithThreadId.threadRootId).toBeTruthy();
|
|
|
|
expect(threaded).toEqual([eventPollStartThreadRoot, eventMessageInThread]);
|
|
});
|
|
|
|
it("should not copy pre-thread in-timeline reactions onto both timelines", function () {
|
|
// @ts-ignore setting private property
|
|
client.clientOpts = {
|
|
...defaultClientOpts,
|
|
threadSupport: true,
|
|
};
|
|
|
|
const eventPollStartThreadRoot = buildEventPollStartThreadRoot();
|
|
const eventMessageInThread = buildEventMessageInThread(eventPollStartThreadRoot);
|
|
const eventReaction = buildEventReaction(eventPollStartThreadRoot);
|
|
|
|
const events = [eventPollStartThreadRoot, eventMessageInThread, eventReaction];
|
|
|
|
const [timeline, threaded] = room.partitionThreadedEvents(events);
|
|
|
|
expect(timeline).toEqual([eventPollStartThreadRoot, eventReaction]);
|
|
|
|
expect(threaded).toEqual([eventPollStartThreadRoot, eventMessageInThread]);
|
|
});
|
|
|
|
it("should not copy post-thread in-timeline vote events onto both timelines", function () {
|
|
// @ts-ignore setting private property
|
|
client.clientOpts = {
|
|
...defaultClientOpts,
|
|
threadSupport: true,
|
|
};
|
|
|
|
const eventPollResponseReference = buildEventPollResponseReference();
|
|
const eventPollStartThreadRoot = buildEventPollStartThreadRoot();
|
|
const eventMessageInThread = buildEventMessageInThread(eventPollStartThreadRoot);
|
|
|
|
const events = [eventPollStartThreadRoot, eventPollResponseReference, eventMessageInThread];
|
|
|
|
const [timeline, threaded] = room.partitionThreadedEvents(events);
|
|
|
|
expect(timeline).toEqual([eventPollStartThreadRoot, eventPollResponseReference]);
|
|
|
|
expect(threaded).toEqual([eventPollStartThreadRoot, eventMessageInThread]);
|
|
});
|
|
|
|
it("should not copy post-thread in-timeline reactions onto both timelines", function () {
|
|
// @ts-ignore setting private property
|
|
client.clientOpts = {
|
|
...defaultClientOpts,
|
|
threadSupport: true,
|
|
};
|
|
|
|
const eventPollStartThreadRoot = buildEventPollStartThreadRoot();
|
|
const eventMessageInThread = buildEventMessageInThread(eventPollStartThreadRoot);
|
|
const eventReaction = buildEventReaction(eventPollStartThreadRoot);
|
|
|
|
const events = [eventPollStartThreadRoot, eventMessageInThread, eventReaction];
|
|
|
|
const [timeline, threaded] = room.partitionThreadedEvents(events);
|
|
|
|
expect(timeline).toEqual([eventPollStartThreadRoot, eventReaction]);
|
|
|
|
expect(threaded).toEqual([eventPollStartThreadRoot, eventMessageInThread]);
|
|
});
|
|
|
|
it("sends room state events to the main timeline only", function () {
|
|
// @ts-ignore setting private property
|
|
client.clientOpts = {
|
|
...defaultClientOpts,
|
|
threadSupport: true,
|
|
};
|
|
// This is based on recording the events in a real room:
|
|
|
|
const eventPollStartThreadRoot = buildEventPollStartThreadRoot();
|
|
const eventPollResponseReference = buildEventPollResponseReference();
|
|
const eventMessageInThread = buildEventMessageInThread(eventPollStartThreadRoot);
|
|
const eventRoomName = buildEventRoomName();
|
|
const eventEncryption = buildEventEncryption();
|
|
const eventGuestAccess = buildEventGuestAccess();
|
|
const eventHistoryVisibility = buildEventHistoryVisibility();
|
|
const eventJoinRules = buildEventJoinRules();
|
|
const eventPowerLevels = buildEventPowerLevels();
|
|
const eventMember = buildEventMember();
|
|
const eventCreate = buildEventCreate();
|
|
|
|
const events = [
|
|
eventPollStartThreadRoot,
|
|
eventPollResponseReference,
|
|
eventMessageInThread,
|
|
eventRoomName,
|
|
eventEncryption,
|
|
eventGuestAccess,
|
|
eventHistoryVisibility,
|
|
eventJoinRules,
|
|
eventPowerLevels,
|
|
eventMember,
|
|
eventCreate,
|
|
];
|
|
const [timeline, threaded] = room.partitionThreadedEvents(events);
|
|
|
|
expect(timeline).toEqual([
|
|
// The message that was sent in a thread is missing
|
|
eventPollStartThreadRoot,
|
|
eventPollResponseReference,
|
|
eventRoomName,
|
|
eventEncryption,
|
|
eventGuestAccess,
|
|
eventHistoryVisibility,
|
|
eventJoinRules,
|
|
eventPowerLevels,
|
|
eventMember,
|
|
eventCreate,
|
|
]);
|
|
|
|
// Thread should contain only stuff that happened in the thread - no room state events
|
|
expect(threaded).toEqual([eventPollStartThreadRoot, eventMessageInThread]);
|
|
});
|
|
|
|
it("sends redactions of reactions to thread responses to thread timeline only", () => {
|
|
// @ts-ignore setting private property
|
|
client.clientOpts = {
|
|
...defaultClientOpts,
|
|
threadSupport: true,
|
|
};
|
|
|
|
const threadRootEvent = buildEventPollStartThreadRoot();
|
|
const eventMessageInThread = buildEventMessageInThread(threadRootEvent);
|
|
const threadedReaction = buildEventReaction(eventMessageInThread);
|
|
const threadedReactionRedaction = buildEventRedaction(threadedReaction);
|
|
|
|
const events = [threadRootEvent, eventMessageInThread, threadedReaction, threadedReactionRedaction];
|
|
|
|
const [timeline, threaded] = room.partitionThreadedEvents(events);
|
|
|
|
expect(timeline).toEqual([threadRootEvent]);
|
|
|
|
expect(threaded).toEqual([
|
|
threadRootEvent,
|
|
eventMessageInThread,
|
|
threadedReaction,
|
|
threadedReactionRedaction,
|
|
]);
|
|
});
|
|
|
|
it("sends reply to reply to thread root outside of thread to main timeline only", () => {
|
|
// @ts-ignore setting private property
|
|
client.clientOpts = {
|
|
...defaultClientOpts,
|
|
threadSupport: true,
|
|
};
|
|
|
|
const threadRootEvent = buildEventPollStartThreadRoot();
|
|
const eventMessageInThread = buildEventMessageInThread(threadRootEvent);
|
|
const directReplyToThreadRoot = buildEventReply(threadRootEvent);
|
|
const replyToReply = buildEventReply(directReplyToThreadRoot);
|
|
|
|
const events = [threadRootEvent, eventMessageInThread, directReplyToThreadRoot, replyToReply];
|
|
|
|
const [timeline, threaded] = room.partitionThreadedEvents(events);
|
|
|
|
expect(timeline).toEqual([threadRootEvent, directReplyToThreadRoot, replyToReply]);
|
|
|
|
expect(threaded).toEqual([threadRootEvent, eventMessageInThread]);
|
|
});
|
|
|
|
it("sends reply to thread responses to main timeline only", () => {
|
|
// @ts-ignore setting private property
|
|
client.clientOpts = {
|
|
...defaultClientOpts,
|
|
threadSupport: true,
|
|
};
|
|
|
|
const threadRootEvent = buildEventPollStartThreadRoot();
|
|
const eventMessageInThread = buildEventMessageInThread(threadRootEvent);
|
|
const replyToThreadResponse = buildEventReply(eventMessageInThread);
|
|
|
|
const events = [threadRootEvent, eventMessageInThread, replyToThreadResponse];
|
|
|
|
const [timeline, threaded] = room.partitionThreadedEvents(events);
|
|
|
|
expect(timeline).toEqual([threadRootEvent]);
|
|
|
|
expect(threaded).toEqual([threadRootEvent, eventMessageInThread, replyToThreadResponse]);
|
|
});
|
|
});
|
|
|
|
describe("getThirdpartyUser", () => {
|
|
it("should hit the expected API endpoint", async () => {
|
|
const response = [
|
|
{
|
|
userid: "@Bob",
|
|
protocol: "irc",
|
|
fields: {},
|
|
},
|
|
];
|
|
|
|
const prom = client.getThirdpartyUser("irc", {});
|
|
httpBackend.when("GET", "/thirdparty/user/irc").respond(200, response);
|
|
await httpBackend.flush("");
|
|
expect(await prom).toStrictEqual(response);
|
|
});
|
|
});
|
|
|
|
describe("getThirdpartyLocation", () => {
|
|
it("should hit the expected API endpoint", async () => {
|
|
const response = [
|
|
{
|
|
alias: "#alias",
|
|
protocol: "irc",
|
|
fields: {},
|
|
},
|
|
];
|
|
|
|
const prom = client.getThirdpartyLocation("irc", {});
|
|
httpBackend.when("GET", "/thirdparty/location/irc").respond(200, response);
|
|
await httpBackend.flush("");
|
|
expect(await prom).toStrictEqual(response);
|
|
});
|
|
});
|
|
|
|
describe("getPushers", () => {
|
|
it("should hit the expected API endpoint", async () => {
|
|
const response = {
|
|
pushers: [],
|
|
};
|
|
|
|
const prom = client.getPushers();
|
|
httpBackend.when("GET", "/_matrix/client/versions").respond(200, {});
|
|
httpBackend.when("GET", "/pushers").respond(200, response);
|
|
await httpBackend.flush("");
|
|
expect(await prom).toStrictEqual(response);
|
|
});
|
|
});
|
|
|
|
describe("getKeyChanges", () => {
|
|
it("should hit the expected API endpoint", async () => {
|
|
const response = {
|
|
changed: [],
|
|
left: [],
|
|
};
|
|
|
|
const prom = client.getKeyChanges("old", "new");
|
|
httpBackend
|
|
.when("GET", "/keys/changes")
|
|
.check((req) => {
|
|
expect(req.queryParams?.from).toEqual("old");
|
|
expect(req.queryParams?.to).toEqual("new");
|
|
})
|
|
.respond(200, response);
|
|
await httpBackend.flush("");
|
|
expect(await prom).toStrictEqual(response);
|
|
});
|
|
});
|
|
|
|
describe("getDevices", () => {
|
|
it("should hit the expected API endpoint", async () => {
|
|
const response = {
|
|
devices: [],
|
|
};
|
|
|
|
const prom = client.getDevices();
|
|
httpBackend.when("GET", "/devices").respond(200, response);
|
|
await httpBackend.flush("");
|
|
expect(await prom).toStrictEqual(response);
|
|
});
|
|
});
|
|
|
|
describe("getDevice", () => {
|
|
it("should hit the expected API endpoint", async () => {
|
|
const response = {
|
|
device_id: "DEADBEEF",
|
|
display_name: "NotAPhone",
|
|
last_seen_ip: "127.0.0.1",
|
|
last_seen_ts: 1,
|
|
};
|
|
|
|
const prom = client.getDevice("DEADBEEF");
|
|
httpBackend.when("GET", "/devices/DEADBEEF").respond(200, response);
|
|
await httpBackend.flush("");
|
|
expect(await prom).toStrictEqual(response);
|
|
});
|
|
});
|
|
|
|
describe("getThreePids", () => {
|
|
it("should hit the expected API endpoint", async () => {
|
|
const response = {
|
|
threepids: [],
|
|
};
|
|
|
|
const prom = client.getThreePids();
|
|
httpBackend.when("GET", "/account/3pid").respond(200, response);
|
|
await httpBackend.flush("");
|
|
expect(await prom).toStrictEqual(response);
|
|
});
|
|
});
|
|
|
|
describe("deleteAlias", () => {
|
|
it("should hit the expected API endpoint", async () => {
|
|
const response = {};
|
|
const prom = client.deleteAlias("#foo:bar");
|
|
httpBackend.when("DELETE", "/directory/room/" + encodeURIComponent("#foo:bar")).respond(200, response);
|
|
await httpBackend.flush("");
|
|
expect(await prom).toStrictEqual(response);
|
|
});
|
|
});
|
|
|
|
describe("deleteRoomTag", () => {
|
|
it("should hit the expected API endpoint", async () => {
|
|
const response = {};
|
|
const prom = client.deleteRoomTag("!roomId:server", "u.tag");
|
|
const url = `/user/${encodeURIComponent(userId)}/rooms/${encodeURIComponent("!roomId:server")}/tags/u.tag`;
|
|
httpBackend.when("DELETE", url).respond(200, response);
|
|
await httpBackend.flush("");
|
|
expect(await prom).toStrictEqual(response);
|
|
});
|
|
});
|
|
|
|
describe("getRoomTags", () => {
|
|
it("should hit the expected API endpoint", async () => {
|
|
const response = {
|
|
tags: {
|
|
"u.tag": {
|
|
order: 0.5,
|
|
},
|
|
},
|
|
};
|
|
|
|
const prom = client.getRoomTags("!roomId:server");
|
|
const url = `/user/${encodeURIComponent(userId)}/rooms/${encodeURIComponent("!roomId:server")}/tags`;
|
|
httpBackend.when("GET", url).respond(200, response);
|
|
await httpBackend.flush("");
|
|
expect(await prom).toStrictEqual(response);
|
|
});
|
|
});
|
|
|
|
describe("requestRegisterEmailToken", () => {
|
|
it("should hit the expected API endpoint", async () => {
|
|
const response = {
|
|
sid: "random_sid",
|
|
submit_url: "https://foobar.matrix/_matrix/matrix",
|
|
};
|
|
|
|
const prom = client.requestRegisterEmailToken("bob@email", "secret", 1);
|
|
httpBackend
|
|
.when("POST", "/register/email/requestToken")
|
|
.check((req) => {
|
|
expect(req.data).toStrictEqual({
|
|
email: "bob@email",
|
|
client_secret: "secret",
|
|
send_attempt: 1,
|
|
});
|
|
})
|
|
.respond(200, response);
|
|
await httpBackend.flush("");
|
|
expect(await prom).toStrictEqual(response);
|
|
});
|
|
});
|
|
|
|
describe("inviteByThreePid", () => {
|
|
it("should supply an id_access_token", async () => {
|
|
const targetEmail = "gerald@example.org";
|
|
|
|
httpBackend
|
|
.when("POST", "/invite")
|
|
.check((req) => {
|
|
expect(req.data).toStrictEqual({
|
|
id_server: idServerDomain,
|
|
id_access_token: identityAccessToken,
|
|
medium: "email",
|
|
address: targetEmail,
|
|
});
|
|
})
|
|
.respond(200, {});
|
|
|
|
const prom = client.inviteByThreePid("!room:example.org", "email", targetEmail);
|
|
await httpBackend.flush("");
|
|
await prom; // returns empty object, so no validation needed
|
|
});
|
|
});
|
|
|
|
describe("createRoom", () => {
|
|
it("should populate id_access_token on 3pid invites", async () => {
|
|
const targetEmail = "gerald@example.org";
|
|
const response = {
|
|
room_id: "!room:localhost",
|
|
};
|
|
const input = {
|
|
invite_3pid: [
|
|
{
|
|
// we intentionally exclude the access token here, so it can be populated for us
|
|
id_server: idServerDomain,
|
|
medium: "email",
|
|
address: targetEmail,
|
|
},
|
|
],
|
|
};
|
|
|
|
httpBackend
|
|
.when("POST", "/createRoom")
|
|
.check((req) => {
|
|
expect(req.data).toMatchObject({
|
|
invite_3pid: expect.arrayContaining([
|
|
{
|
|
...input.invite_3pid[0],
|
|
id_access_token: identityAccessToken,
|
|
},
|
|
]),
|
|
});
|
|
expect(req.data.invite_3pid.length).toBe(1);
|
|
})
|
|
.respond(200, response);
|
|
|
|
const prom = client.createRoom(input);
|
|
await httpBackend.flush("");
|
|
expect(await prom).toStrictEqual(response);
|
|
});
|
|
});
|
|
|
|
describe("requestLoginToken", () => {
|
|
it("should hit the expected API endpoint with UIA", async () => {
|
|
const response = {};
|
|
const uiaData = {};
|
|
const prom = client.requestLoginToken(uiaData);
|
|
httpBackend.when("POST", "/v1/login/get_token", { auth: uiaData }).respond(200, response);
|
|
await httpBackend.flush("");
|
|
expect(await prom).toStrictEqual(response);
|
|
});
|
|
|
|
it("should hit the expected API endpoint without UIA", async () => {
|
|
const response = { login_token: "xyz", expires_in_ms: 5000 };
|
|
const prom = client.requestLoginToken();
|
|
httpBackend.when("POST", "/v1/login/get_token", {}).respond(200, response);
|
|
await httpBackend.flush("");
|
|
expect(await prom).toStrictEqual(response);
|
|
});
|
|
});
|
|
|
|
describe("logout", () => {
|
|
it("should abort pending requests when called with stopClient=true", async () => {
|
|
httpBackend.when("POST", "/logout").respond(200, {});
|
|
const fn = jest.fn();
|
|
client.http.request(Method.Get, "/test").catch(fn);
|
|
client.logout(true);
|
|
await httpBackend.flush(undefined);
|
|
expect(fn).toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe("sendHtmlEmote", () => {
|
|
it("should send valid html emote", async () => {
|
|
httpBackend
|
|
.when("PUT", "/send")
|
|
.check((req) => {
|
|
expect(req.data).toStrictEqual({
|
|
msgtype: "m.emote",
|
|
body: "Body",
|
|
formatted_body: "<h1>Body</h1>",
|
|
format: "org.matrix.custom.html",
|
|
});
|
|
})
|
|
.respond(200, { event_id: "$foobar" });
|
|
const prom = client.sendHtmlEmote("!room:server", "Body", "<h1>Body</h1>");
|
|
await httpBackend.flush(undefined);
|
|
await expect(prom).resolves.toStrictEqual({ event_id: "$foobar" });
|
|
});
|
|
});
|
|
|
|
describe("sendHtmlMessage", () => {
|
|
it("should send valid html message", async () => {
|
|
httpBackend
|
|
.when("PUT", "/send")
|
|
.check((req) => {
|
|
expect(req.data).toStrictEqual({
|
|
msgtype: "m.text",
|
|
body: "Body",
|
|
formatted_body: "<h1>Body</h1>",
|
|
format: "org.matrix.custom.html",
|
|
});
|
|
})
|
|
.respond(200, { event_id: "$foobar" });
|
|
const prom = client.sendHtmlMessage("!room:server", "Body", "<h1>Body</h1>");
|
|
await httpBackend.flush(undefined);
|
|
await expect(prom).resolves.toStrictEqual({ event_id: "$foobar" });
|
|
});
|
|
});
|
|
|
|
describe("forget", () => {
|
|
it("should remove from store by default", async () => {
|
|
const room = new Room("!roomId:server", client, userId);
|
|
client.store.storeRoom(room);
|
|
expect(client.store.getRooms()).toContain(room);
|
|
|
|
httpBackend.when("POST", "/forget").respond(200, {});
|
|
await Promise.all([client.forget(room.roomId), httpBackend.flushAllExpected()]);
|
|
expect(client.store.getRooms()).not.toContain(room);
|
|
});
|
|
});
|
|
|
|
describe("getCapabilities", () => {
|
|
it("should return cached capabilities if present", async () => {
|
|
const capsObject = {
|
|
"m.change_password": false,
|
|
};
|
|
|
|
httpBackend!.when("GET", "/versions").respond(200, {});
|
|
httpBackend!.when("GET", "/pushrules").respond(200, {});
|
|
httpBackend!.when("POST", "/filter").respond(200, { filter_id: "a filter id" });
|
|
httpBackend.when("GET", "/capabilities").respond(200, {
|
|
capabilities: capsObject,
|
|
});
|
|
|
|
client.startClient();
|
|
await httpBackend!.flushAllExpected();
|
|
|
|
expect(await client.getCapabilities()).toEqual(capsObject);
|
|
});
|
|
|
|
it("should fetch capabilities if cache not present", async () => {
|
|
const capsObject = {
|
|
"m.change_password": false,
|
|
};
|
|
|
|
httpBackend.when("GET", "/capabilities").respond(200, {
|
|
capabilities: capsObject,
|
|
});
|
|
|
|
const capsPromise = client.getCapabilities();
|
|
await httpBackend!.flushAllExpected();
|
|
|
|
expect(await capsPromise).toEqual(capsObject);
|
|
});
|
|
});
|
|
|
|
describe("getCachedCapabilities", () => {
|
|
it("should return cached capabilities or undefined", async () => {
|
|
const capsObject = {
|
|
"m.change_password": false,
|
|
};
|
|
|
|
httpBackend!.when("GET", "/versions").respond(200, {});
|
|
httpBackend!.when("GET", "/pushrules").respond(200, {});
|
|
httpBackend!.when("POST", "/filter").respond(200, { filter_id: "a filter id" });
|
|
httpBackend.when("GET", "/capabilities").respond(200, {
|
|
capabilities: capsObject,
|
|
});
|
|
|
|
expect(client.getCachedCapabilities()).toBeUndefined();
|
|
|
|
client.startClient();
|
|
|
|
await httpBackend!.flushAllExpected();
|
|
|
|
expect(client.getCachedCapabilities()).toEqual(capsObject);
|
|
});
|
|
});
|
|
|
|
describe("fetchCapabilities", () => {
|
|
const capsObject = {
|
|
"m.change_password": false,
|
|
};
|
|
|
|
beforeEach(() => {
|
|
httpBackend.when("GET", "/capabilities").respond(200, {
|
|
capabilities: capsObject,
|
|
});
|
|
});
|
|
|
|
afterEach(() => {
|
|
jest.useRealTimers();
|
|
});
|
|
|
|
it("should always fetch capabilities and then cache", async () => {
|
|
const prom = client.fetchCapabilities();
|
|
await httpBackend.flushAllExpected();
|
|
const caps = await prom;
|
|
|
|
expect(caps).toEqual(capsObject);
|
|
});
|
|
|
|
it("should write-through the cache", async () => {
|
|
httpBackend!.when("GET", "/versions").respond(200, {});
|
|
httpBackend!.when("GET", "/pushrules").respond(200, {});
|
|
httpBackend!.when("POST", "/filter").respond(200, { filter_id: "a filter id" });
|
|
|
|
client.startClient();
|
|
await httpBackend!.flushAllExpected();
|
|
|
|
expect(client.getCachedCapabilities()).toEqual(capsObject);
|
|
|
|
const newCapsObject = {
|
|
"m.change_password": true,
|
|
};
|
|
|
|
httpBackend.when("GET", "/capabilities").respond(200, {
|
|
capabilities: newCapsObject,
|
|
});
|
|
|
|
const prom = client.fetchCapabilities();
|
|
await httpBackend.flushAllExpected();
|
|
await prom;
|
|
|
|
expect(client.getCachedCapabilities()).toEqual(newCapsObject);
|
|
});
|
|
});
|
|
|
|
describe("getTerms", () => {
|
|
it("should return Identity Server terms", async () => {
|
|
httpBackend.when("GET", "/terms").respond(200, { foo: "bar" });
|
|
const prom = client.getTerms(SERVICE_TYPES.IS, "http://identity.server");
|
|
await httpBackend.flushAllExpected();
|
|
await expect(prom).resolves.toEqual({ foo: "bar" });
|
|
});
|
|
|
|
it("should return Integrations Manager terms", async () => {
|
|
httpBackend.when("GET", "/terms").respond(200, { foo: "bar" });
|
|
const prom = client.getTerms(SERVICE_TYPES.IM, "http://im.server");
|
|
await httpBackend.flushAllExpected();
|
|
await expect(prom).resolves.toEqual({ foo: "bar" });
|
|
});
|
|
});
|
|
|
|
describe("agreeToTerms", () => {
|
|
it("should send `user_accepts` via body of POST request", async () => {
|
|
const terms = ["https://vector.im/notice-1"];
|
|
|
|
httpBackend
|
|
.when("POST", "/terms")
|
|
.check((req) => {
|
|
expect(req.data.user_accepts).toStrictEqual(terms);
|
|
})
|
|
.respond(200, {});
|
|
|
|
const prom = client.agreeToTerms(SERVICE_TYPES.IS, "https://vector.im", "at", terms);
|
|
await httpBackend.flushAllExpected();
|
|
await prom;
|
|
});
|
|
});
|
|
|
|
describe("publicRooms", () => {
|
|
it("should use GET request if no server or filter is specified", () => {
|
|
httpBackend.when("GET", "/publicRooms").respond(200, {});
|
|
client.publicRooms({});
|
|
return httpBackend.flushAllExpected();
|
|
});
|
|
|
|
it("should use GET request if only server is specified", () => {
|
|
httpBackend
|
|
.when("GET", "/publicRooms")
|
|
.check((request) => {
|
|
expect(request.queryParams?.server).toBe("server1");
|
|
})
|
|
.respond(200, {});
|
|
client.publicRooms({ server: "server1" });
|
|
return httpBackend.flushAllExpected();
|
|
});
|
|
|
|
it("should use POST request if filter is specified", () => {
|
|
httpBackend
|
|
.when("POST", "/publicRooms")
|
|
.check((request) => {
|
|
expect(request.data.filter.generic_search_term).toBe("foobar");
|
|
})
|
|
.respond(200, {});
|
|
client.publicRooms({ filter: { generic_search_term: "foobar" } });
|
|
return httpBackend.flushAllExpected();
|
|
});
|
|
});
|
|
|
|
describe("login", () => {
|
|
it("should persist values to the client opts", async () => {
|
|
const token = "!token&";
|
|
const userId = "@m:t";
|
|
|
|
httpBackend.when("POST", "/login").respond(200, {
|
|
access_token: token,
|
|
user_id: userId,
|
|
});
|
|
const prom = client.login("fake.login", {});
|
|
await httpBackend.flushAllExpected();
|
|
const resp = await prom;
|
|
expect(resp.access_token).toBe(token);
|
|
expect(resp.user_id).toBe(userId);
|
|
expect(client.getUserId()).toBe(userId);
|
|
expect(client.http.opts.accessToken).toBe(token);
|
|
});
|
|
});
|
|
|
|
describe("registerWithIdentityServer", () => {
|
|
it("should pass data to POST request", async () => {
|
|
const token = {
|
|
access_token: "access_token",
|
|
token_type: "Bearer",
|
|
matrix_server_name: "server_name",
|
|
expires_in: 12345,
|
|
};
|
|
|
|
httpBackend
|
|
.when("POST", "/account/register")
|
|
.check((req) => {
|
|
expect(req.data).toStrictEqual(token);
|
|
})
|
|
.respond(200, {
|
|
access_token: "at",
|
|
token: "tt",
|
|
});
|
|
|
|
const prom = client.registerWithIdentityServer(token);
|
|
await httpBackend.flushAllExpected();
|
|
const resp = await prom;
|
|
expect(resp.access_token).toBe("at");
|
|
expect(resp.token).toBe("tt");
|
|
});
|
|
});
|
|
|
|
describe("setPowerLevel", () => {
|
|
it.each([
|
|
{
|
|
userId: "alice@localhost",
|
|
powerLevel: 100,
|
|
expectation: {
|
|
"alice@localhost": 100,
|
|
},
|
|
},
|
|
{
|
|
userId: ["alice@localhost", "bob@localhost"],
|
|
powerLevel: 100,
|
|
expectation: {
|
|
"alice@localhost": 100,
|
|
"bob@localhost": 100,
|
|
},
|
|
},
|
|
{
|
|
userId: "alice@localhost",
|
|
powerLevel: undefined,
|
|
expectation: {},
|
|
},
|
|
])("should modify power levels of $userId correctly", async ({ userId, powerLevel, expectation }) => {
|
|
httpBackend.when("GET", "/state/m.room.power_levels/").respond(200, {
|
|
users: {
|
|
"alice@localhost": 50,
|
|
},
|
|
});
|
|
|
|
httpBackend
|
|
.when("PUT", "/state/m.room.power_levels")
|
|
.check((req) => {
|
|
expect(req.data.users).toStrictEqual(expectation);
|
|
})
|
|
.respond(200, {});
|
|
|
|
const prom = client.setPowerLevel("!room_id:server", userId, powerLevel);
|
|
await httpBackend.flushAllExpected();
|
|
await prom;
|
|
});
|
|
|
|
it("should use power level from room state if available", async () => {
|
|
client.clientRunning = true;
|
|
client.isInitialSyncComplete = () => true;
|
|
const room = new Room("!room_id:server", client, client.getUserId()!);
|
|
room.currentState.events.set("m.room.power_levels", new Map());
|
|
room.currentState.events.get("m.room.power_levels")!.set(
|
|
"",
|
|
new MatrixEvent({
|
|
type: "m.room.power_levels",
|
|
state_key: "",
|
|
content: {
|
|
users: {
|
|
"@bob:localhost": 50,
|
|
},
|
|
},
|
|
}),
|
|
);
|
|
client.getRoom = () => room;
|
|
|
|
httpBackend
|
|
.when("PUT", "/state/m.room.power_levels")
|
|
.check((req) => {
|
|
expect(req.data).toStrictEqual({
|
|
users: {
|
|
"@bob:localhost": 50,
|
|
[userId]: 42,
|
|
},
|
|
});
|
|
})
|
|
.respond(200, {});
|
|
|
|
const prom = client.setPowerLevel("!room_id:server", userId, 42);
|
|
await httpBackend.flushAllExpected();
|
|
await prom;
|
|
});
|
|
|
|
it("should throw error if state API errors", async () => {
|
|
httpBackend.when("GET", "/state/m.room.power_levels/").respond(500, {
|
|
errcode: "ERR_DERP",
|
|
});
|
|
|
|
const prom = client.setPowerLevel("!room_id:server", userId, 42);
|
|
await Promise.all([
|
|
expect(prom).rejects.toMatchInlineSnapshot(`[ERR_DERP: MatrixError: [500] Unknown message]`),
|
|
httpBackend.flushAllExpected(),
|
|
]);
|
|
});
|
|
|
|
it("should not throw error if /state/ API returns M_NOT_FOUND", async () => {
|
|
httpBackend.when("GET", "/state/m.room.power_levels/").respond(404, {
|
|
errcode: "M_NOT_FOUND",
|
|
});
|
|
|
|
httpBackend
|
|
.when("PUT", "/state/m.room.power_levels")
|
|
.check((req) => {
|
|
expect(req.data).toStrictEqual({
|
|
users: {
|
|
[userId]: 42,
|
|
},
|
|
});
|
|
})
|
|
.respond(200, {});
|
|
|
|
const prom = client.setPowerLevel("!room_id:server", userId, 42);
|
|
await httpBackend.flushAllExpected();
|
|
await prom;
|
|
});
|
|
});
|
|
|
|
describe("setSyncPresence", () => {
|
|
it("should pass calls through to the underlying sync api", () => {
|
|
const setPresence = jest.fn();
|
|
// @ts-ignore
|
|
client.syncApi = { setPresence };
|
|
client.setSyncPresence(SetPresence.Unavailable);
|
|
expect(setPresence).toHaveBeenCalledWith(SetPresence.Unavailable);
|
|
});
|
|
});
|
|
|
|
describe("sendTyping", () => {
|
|
it("should bail early for guests", async () => {
|
|
client.setGuest(true);
|
|
await client.sendTyping("!room:server", true, 100);
|
|
});
|
|
|
|
it("should call /typing API", async () => {
|
|
client.setGuest(false);
|
|
httpBackend
|
|
.when("PUT", "/rooms/!room%3Aserver/typing/" + encodeURIComponent(client.getSafeUserId()))
|
|
.check((req) => {
|
|
expect(req.data).toEqual({
|
|
typing: true,
|
|
timeout: 100,
|
|
});
|
|
})
|
|
.respond(200, {});
|
|
await Promise.all([client.sendTyping("!room:server", true, 100), httpBackend.flushAllExpected()]);
|
|
});
|
|
});
|
|
|
|
describe("setGuestAccess", () => {
|
|
it("should set both guest access and history visibility if opts.allowRead=true", async () => {
|
|
httpBackend
|
|
.when("PUT", "/rooms/!room%3Aserver/state/m.room.guest_access/")
|
|
.check((req) => {
|
|
expect(req.data).toEqual({
|
|
guest_access: "forbidden",
|
|
});
|
|
})
|
|
.respond(200, { event_id: "$event1" });
|
|
httpBackend
|
|
.when("PUT", "/rooms/!room%3Aserver/state/m.room.history_visibility/")
|
|
.check((req) => {
|
|
expect(req.data).toEqual({
|
|
history_visibility: "world_readable",
|
|
});
|
|
})
|
|
.respond(200, { event_id: "$event2" });
|
|
await Promise.all([
|
|
client.setGuestAccess("!room:server", { allowRead: true, allowJoin: false }),
|
|
httpBackend.flushAllExpected(),
|
|
]);
|
|
});
|
|
});
|
|
|
|
describe("searchUserDirectory", () => {
|
|
it("should call /user_directory/search API", async () => {
|
|
httpBackend
|
|
.when("POST", "/user_directory/search")
|
|
.check((req) => {
|
|
expect(req.data).toEqual({
|
|
search_term: "This is my query",
|
|
});
|
|
})
|
|
.respond(200, {});
|
|
await Promise.all([
|
|
client.searchUserDirectory({ term: "This is my query" }),
|
|
httpBackend.flushAllExpected(),
|
|
]);
|
|
});
|
|
});
|
|
|
|
describe("getFallbackAuthUrl", () => {
|
|
it("should return fallback url", () => {
|
|
expect(client.getFallbackAuthUrl("loginType", "authSessionId")).toMatchInlineSnapshot(
|
|
`"http://alice.localhost.test.server/_matrix/client/v3/auth/loginType/fallback/web?session=authSessionId"`,
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("addThreePidOnly", () => {
|
|
it("should make expected POST request", async () => {
|
|
httpBackend
|
|
.when("POST", "/_matrix/client/v3/account/3pid/add")
|
|
.check(function (req) {
|
|
expect(req.data).toEqual({
|
|
client_secret: "secret",
|
|
sid: "sid",
|
|
});
|
|
expect(req.headers["Authorization"]).toBe("Bearer " + accessToken);
|
|
})
|
|
.respond(200, {});
|
|
|
|
await Promise.all([
|
|
client.addThreePidOnly({
|
|
client_secret: "secret",
|
|
sid: "sid",
|
|
}),
|
|
httpBackend.flushAllExpected(),
|
|
]);
|
|
});
|
|
});
|
|
|
|
describe("bindThreePid", () => {
|
|
it("should make expected POST request", async () => {
|
|
httpBackend
|
|
.when("POST", "/_matrix/client/v3/account/3pid/bind")
|
|
.check(function (req) {
|
|
expect(req.data).toEqual({
|
|
client_secret: "secret",
|
|
id_server: "server",
|
|
id_access_token: "token",
|
|
sid: "sid",
|
|
});
|
|
expect(req.headers["Authorization"]).toBe("Bearer " + accessToken);
|
|
})
|
|
.respond(200, {});
|
|
|
|
await Promise.all([
|
|
client.bindThreePid({
|
|
client_secret: "secret",
|
|
id_server: "server",
|
|
id_access_token: "token",
|
|
sid: "sid",
|
|
}),
|
|
httpBackend.flushAllExpected(),
|
|
]);
|
|
});
|
|
});
|
|
|
|
describe("unbindThreePid", () => {
|
|
it("should make expected POST request", async () => {
|
|
httpBackend
|
|
.when("POST", "/_matrix/client/v3/account/3pid/unbind")
|
|
.check(function (req) {
|
|
expect(req.data).toEqual({
|
|
medium: "email",
|
|
address: "alice@server.com",
|
|
id_server: "identity.localhost",
|
|
});
|
|
expect(req.headers["Authorization"]).toBe("Bearer " + accessToken);
|
|
})
|
|
.respond(200, {});
|
|
|
|
await Promise.all([client.unbindThreePid("email", "alice@server.com"), httpBackend.flushAllExpected()]);
|
|
});
|
|
});
|
|
|
|
describe("getRoomSummary", () => {
|
|
const roomId = "!foo:bar";
|
|
const encodedRoomId = encodeURIComponent(roomId);
|
|
|
|
const roomSummary: RoomSummary = {
|
|
"room_id": roomId,
|
|
"name": "My Room",
|
|
"avatar_url": "",
|
|
"topic": "My room topic",
|
|
"world_readable": false,
|
|
"guest_can_join": false,
|
|
"num_joined_members": 1,
|
|
"room_type": "",
|
|
"join_rule": JoinRule.Public,
|
|
"membership": "leave",
|
|
"im.nheko.summary.room_version": "6",
|
|
"im.nheko.summary.encryption": "algo",
|
|
};
|
|
|
|
const prefix = "/_matrix/client/unstable/im.nheko.summary/";
|
|
const suffix = `summary/${encodedRoomId}`;
|
|
const deprecatedSuffix = `rooms/${encodedRoomId}/summary`;
|
|
|
|
const errorUnrecogStatus = 404;
|
|
const errorUnrecogBody = {
|
|
errcode: "M_UNRECOGNIZED",
|
|
error: "Unsupported endpoint",
|
|
};
|
|
|
|
const errorBadreqStatus = 400;
|
|
const errorBadreqBody = {
|
|
errcode: "M_UNKNOWN",
|
|
error: "Invalid request",
|
|
};
|
|
|
|
it("should respond with a valid room summary object", () => {
|
|
httpBackend.when("GET", prefix + suffix).respond(200, roomSummary);
|
|
|
|
const prom = client.getRoomSummary(roomId).then((response) => {
|
|
expect(response).toEqual(roomSummary);
|
|
});
|
|
|
|
httpBackend.flush("");
|
|
return prom;
|
|
});
|
|
|
|
it("should allow fallback to the deprecated endpoint", () => {
|
|
httpBackend.when("GET", prefix + suffix).respond(errorUnrecogStatus, errorUnrecogBody);
|
|
httpBackend.when("GET", prefix + deprecatedSuffix).respond(200, roomSummary);
|
|
|
|
const prom = client.getRoomSummary(roomId).then((response) => {
|
|
expect(response).toEqual(roomSummary);
|
|
});
|
|
|
|
httpBackend.flush("");
|
|
return prom;
|
|
});
|
|
|
|
it("should respond to unsupported path with error", () => {
|
|
httpBackend.when("GET", prefix + suffix).respond(errorUnrecogStatus, errorUnrecogBody);
|
|
httpBackend.when("GET", prefix + deprecatedSuffix).respond(errorUnrecogStatus, errorUnrecogBody);
|
|
|
|
const prom = client.getRoomSummary(roomId).then(
|
|
function (response) {
|
|
throw Error("request not failed");
|
|
},
|
|
function (error) {
|
|
expect(error.httpStatus).toEqual(errorUnrecogStatus);
|
|
expect(error.errcode).toEqual(errorUnrecogBody.errcode);
|
|
expect(error.message).toEqual(`MatrixError: [${errorUnrecogStatus}] ${errorUnrecogBody.error}`);
|
|
},
|
|
);
|
|
|
|
httpBackend.flush("");
|
|
return prom;
|
|
});
|
|
|
|
it("should respond to invalid path arguments with error", () => {
|
|
httpBackend.when("GET", prefix).respond(errorBadreqStatus, errorBadreqBody);
|
|
|
|
const prom = client.getRoomSummary("notAroom").then(
|
|
function (response) {
|
|
throw Error("request not failed");
|
|
},
|
|
function (error) {
|
|
expect(error.httpStatus).toEqual(errorBadreqStatus);
|
|
expect(error.errcode).toEqual(errorBadreqBody.errcode);
|
|
expect(error.message).toEqual(`MatrixError: [${errorBadreqStatus}] ${errorBadreqBody.error}`);
|
|
},
|
|
);
|
|
|
|
httpBackend.flush("");
|
|
return prom;
|
|
});
|
|
});
|
|
|
|
describe("getDomain", () => {
|
|
it("should return null if no userId is set", () => {
|
|
const client = new MatrixClient({ baseUrl: "http://localhost" });
|
|
expect(client.getDomain()).toBeNull();
|
|
});
|
|
|
|
it("should return the domain of the userId", () => {
|
|
expect(client.getDomain()).toBe("localhost");
|
|
});
|
|
});
|
|
|
|
describe("getUserIdLocalpart", () => {
|
|
it("should return null if no userId is set", () => {
|
|
const client = new MatrixClient({ baseUrl: "http://localhost" });
|
|
expect(client.getUserIdLocalpart()).toBeNull();
|
|
});
|
|
|
|
it("should return the localpart of the userId", () => {
|
|
expect(client.getUserIdLocalpart()).toBe("alice");
|
|
});
|
|
});
|
|
});
|
|
|
|
function withThreadId(event: MatrixEvent, newThreadId: string): MatrixEvent {
|
|
const ret = event.toSnapshot();
|
|
ret.setThreadId(newThreadId);
|
|
return ret;
|
|
}
|
|
|
|
const buildEventMessageInThread = (root: MatrixEvent) =>
|
|
new MatrixEvent({
|
|
content: {
|
|
"algorithm": "m.megolm.v1.aes-sha2",
|
|
"ciphertext": "ENCRYPTEDSTUFF",
|
|
"device_id": "XISFUZSKHH",
|
|
"m.relates_to": {
|
|
"event_id": root.getId(),
|
|
"m.in_reply_to": {
|
|
event_id: root.getId()!,
|
|
},
|
|
"rel_type": "m.thread",
|
|
},
|
|
"sender_key": "i3N3CtG/CD2bGB8rA9fW6adLYSDvlUhf2iuU73L65Vg",
|
|
"session_id": "Ja11R/KG6ua0wdk8zAzognrxjio1Gm/RK2Gn6lFL804",
|
|
},
|
|
event_id: "$W4chKIGYowtBblVLkRimeIg8TcdjETnxhDPGfi6NpDg",
|
|
origin_server_ts: 1643815466378,
|
|
room_id: "!STrMRsukXHtqQdSeHa:matrix.org",
|
|
sender: "@andybalaam-test1:matrix.org",
|
|
type: "m.room.encrypted",
|
|
unsigned: { age: 80098509 },
|
|
});
|
|
|
|
const buildEventPollResponseReference = () =>
|
|
new MatrixEvent({
|
|
content: {
|
|
"algorithm": "m.megolm.v1.aes-sha2",
|
|
"ciphertext": "ENCRYPTEDSTUFF",
|
|
"device_id": "XISFUZSKHH",
|
|
"m.relates_to": {
|
|
event_id: "$VLS2ojbPmxb6x8ECetn45hmND6cRDcjgv-j-to9m7Vo",
|
|
rel_type: "m.reference",
|
|
},
|
|
"sender_key": "i3N3CtG/CD2bGB8rA9fW6adLYSDvlUhf2iuU73L65Vg",
|
|
"session_id": "Ja11R/KG6ua0wdk8zAzognrxjio1Gm/RK2Gn6lFL804",
|
|
},
|
|
event_id: "$91JvpezvsF0cKgav3g8W-uEVS4WkDHgxbJZvL3uMR1g",
|
|
origin_server_ts: 1643815458650,
|
|
room_id: "!STrMRsukXHtqQdSeHa:matrix.org",
|
|
sender: "@andybalaam-test1:matrix.org",
|
|
type: "m.room.encrypted",
|
|
unsigned: { age: 80106237 },
|
|
});
|
|
|
|
const buildEventReaction = (event: MatrixEvent) =>
|
|
new MatrixEvent({
|
|
content: {
|
|
"m.relates_to": {
|
|
event_id: event.getId(),
|
|
key: "🤗",
|
|
rel_type: "m.annotation",
|
|
},
|
|
},
|
|
origin_server_ts: 1643977249238,
|
|
sender: "@andybalaam-test1:matrix.org",
|
|
type: "m.reaction",
|
|
unsigned: {
|
|
age: 22598,
|
|
transaction_id: "m1643977249073.16",
|
|
},
|
|
event_id: "$86B2b-x3LgE4DlV4y24b7UHnt72LIA3rzjvMysTtAfA",
|
|
room_id: "!STrMRsukXHtqQdSeHa:matrix.org",
|
|
});
|
|
|
|
const buildEventRedaction = (event: MatrixEvent) =>
|
|
new MatrixEvent({
|
|
content: {},
|
|
origin_server_ts: 1643977249239,
|
|
sender: "@andybalaam-test1:matrix.org",
|
|
redacts: event.getId(),
|
|
type: "m.room.redaction",
|
|
unsigned: {
|
|
age: 22597,
|
|
transaction_id: "m1643977249073.17",
|
|
},
|
|
event_id: "$86B2b-x3LgE4DlV4y24b7UHnt72LIA3rzjvMysTtAfB",
|
|
room_id: "!STrMRsukXHtqQdSeHa:matrix.org",
|
|
});
|
|
|
|
const buildEventPollStartThreadRoot = () =>
|
|
new MatrixEvent({
|
|
content: {
|
|
algorithm: "m.megolm.v1.aes-sha2",
|
|
ciphertext: "ENCRYPTEDSTUFF",
|
|
device_id: "XISFUZSKHH",
|
|
sender_key: "i3N3CtG/CD2bGB8rA9fW6adLYSDvlUhf2iuU73L65Vg",
|
|
session_id: "Ja11R/KG6ua0wdk8zAzognrxjio1Gm/RK2Gn6lFL804",
|
|
},
|
|
event_id: "$VLS2ojbPmxb6x8ECetn45hmND6cRDcjgv-j-to9m7Vo",
|
|
origin_server_ts: 1643815456240,
|
|
room_id: "!STrMRsukXHtqQdSeHa:matrix.org",
|
|
sender: "@andybalaam-test1:matrix.org",
|
|
type: "m.room.encrypted",
|
|
unsigned: { age: 80108647 },
|
|
});
|
|
|
|
const buildEventReply = (target: MatrixEvent) =>
|
|
new MatrixEvent({
|
|
content: {
|
|
"algorithm": "m.megolm.v1.aes-sha2",
|
|
"ciphertext": "ENCRYPTEDSTUFF",
|
|
"device_id": "XISFUZSKHH",
|
|
"m.relates_to": {
|
|
"m.in_reply_to": {
|
|
event_id: target.getId()!,
|
|
},
|
|
},
|
|
"sender_key": "i3N3CtG/CD2bGB8rA9fW6adLYSDvlUhf2iuU73L65Vg",
|
|
"session_id": "Ja11R/KG6ua0wdk8zAzognrxjio1Gm/RK2Gn6lFL804",
|
|
},
|
|
event_id: target.getId()! + Math.random(),
|
|
origin_server_ts: 1643815466378,
|
|
room_id: "!STrMRsukXHtqQdSeHa:matrix.org",
|
|
sender: "@andybalaam-test1:matrix.org",
|
|
type: "m.room.encrypted",
|
|
unsigned: { age: 80098509 },
|
|
});
|
|
|
|
const buildEventRoomName = () =>
|
|
new MatrixEvent({
|
|
content: {
|
|
name: "1 poll, 1 vote, 1 thread",
|
|
},
|
|
event_id: "$QAdyNJtKnl1j7or2yMycbOCvb6bCgvHs5lg3ZMd5xWk",
|
|
origin_server_ts: 1643815441638,
|
|
room_id: "!STrMRsukXHtqQdSeHa:matrix.org",
|
|
sender: "@andybalaam-test1:matrix.org",
|
|
state_key: "",
|
|
type: "m.room.name",
|
|
unsigned: { age: 80123249 },
|
|
});
|
|
|
|
const buildEventEncryption = () =>
|
|
new MatrixEvent({
|
|
content: {
|
|
algorithm: "m.megolm.v1.aes-sha2",
|
|
},
|
|
event_id: "$1hGykogKQkXbHw8bVuyE3BjHnFBEJBcUWnakd0ck2K0",
|
|
origin_server_ts: 1643815441504,
|
|
room_id: "!STrMRsukXHtqQdSeHa:matrix.org",
|
|
sender: "@andybalaam-test1:matrix.org",
|
|
state_key: "",
|
|
type: "m.room.encryption",
|
|
unsigned: { age: 80123383 },
|
|
});
|
|
|
|
const buildEventGuestAccess = () =>
|
|
new MatrixEvent({
|
|
content: {
|
|
guest_access: "can_join",
|
|
},
|
|
event_id: "$4_2n-H6K9-0nPbnjjtIue2SU44tGJsnuTmi6UuSrh-U",
|
|
origin_server_ts: 1643815441414,
|
|
room_id: "!STrMRsukXHtqQdSeHa:matrix.org",
|
|
sender: "@andybalaam-test1:matrix.org",
|
|
state_key: "",
|
|
type: "m.room.guest_access",
|
|
unsigned: { age: 80123473 },
|
|
});
|
|
|
|
const buildEventHistoryVisibility = () =>
|
|
new MatrixEvent({
|
|
content: {
|
|
history_visibility: "shared",
|
|
},
|
|
event_id: "$W6kp44CTnvciOiHSPyhp8dh4n2v1_9kclUPddeaQj0E",
|
|
origin_server_ts: 1643815441331,
|
|
room_id: "!STrMRsukXHtqQdSeHa:matrix.org",
|
|
sender: "@andybalaam-test1:matrix.org",
|
|
state_key: "",
|
|
type: "m.room.history_visibility",
|
|
unsigned: { age: 80123556 },
|
|
});
|
|
|
|
const buildEventJoinRules = () =>
|
|
new MatrixEvent({
|
|
content: {
|
|
join_rule: KnownMembership.Invite,
|
|
},
|
|
event_id: "$6JDDeDp7fEc0F6YnTWMruNcKWFltR3e9wk7wWDDJrAU",
|
|
origin_server_ts: 1643815441191,
|
|
room_id: "!STrMRsukXHtqQdSeHa:matrix.org",
|
|
sender: "@andybalaam-test1:matrix.org",
|
|
state_key: "",
|
|
type: "m.room.join_rules",
|
|
unsigned: { age: 80123696 },
|
|
});
|
|
|
|
const buildEventPowerLevels = () =>
|
|
new MatrixEvent({
|
|
content: {
|
|
ban: 50,
|
|
events: {
|
|
"m.room.avatar": 50,
|
|
"m.room.canonical_alias": 50,
|
|
"m.room.encryption": 100,
|
|
"m.room.history_visibility": 100,
|
|
"m.room.name": 50,
|
|
"m.room.power_levels": 100,
|
|
"m.room.server_acl": 100,
|
|
"m.room.tombstone": 100,
|
|
},
|
|
events_default: 0,
|
|
historical: 100,
|
|
invite: 0,
|
|
kick: 50,
|
|
redact: 50,
|
|
state_default: 50,
|
|
users: {
|
|
"@andybalaam-test1:matrix.org": 100,
|
|
},
|
|
users_default: 0,
|
|
},
|
|
event_id: "$XZY2YgQhXskpc7gmJJG3S0VmS9_QjjCUVeeFTfgfC2E",
|
|
origin_server_ts: 1643815440782,
|
|
room_id: "!STrMRsukXHtqQdSeHa:matrix.org",
|
|
sender: "@andybalaam-test1:matrix.org",
|
|
state_key: "",
|
|
type: "m.room.power_levels",
|
|
unsigned: { age: 80124105 },
|
|
});
|
|
|
|
const buildEventMember = () =>
|
|
new MatrixEvent({
|
|
content: {
|
|
avatar_url: "mxc://matrix.org/aNtbVcFfwotudypZcHsIcPOc",
|
|
displayname: "andybalaam-test1",
|
|
membership: KnownMembership.Join,
|
|
},
|
|
event_id: "$Ex5eVmMs_ti784mo8bgddynbwLvy6231lCycJr7Cl9M",
|
|
origin_server_ts: 1643815439608,
|
|
room_id: "!STrMRsukXHtqQdSeHa:matrix.org",
|
|
sender: "@andybalaam-test1:matrix.org",
|
|
state_key: "@andybalaam-test1:matrix.org",
|
|
type: "m.room.member",
|
|
unsigned: { age: 80125279 },
|
|
});
|
|
|
|
const buildEventCreate = () =>
|
|
new MatrixEvent({
|
|
content: {
|
|
room_version: "6",
|
|
},
|
|
event_id: "$e7j2Gt37k5NPwB6lz2N3V9lO5pUdNK8Ai7i2FPEK-oI",
|
|
origin_server_ts: 1643815438782,
|
|
room_id: "!STrMRsukXHtqQdSeHa:matrix.org",
|
|
sender: "@andybalaam-test1:matrix.org",
|
|
state_key: "",
|
|
type: "m.room.create",
|
|
unsigned: { age: 80126105 },
|
|
});
|