mirror of
https://github.com/matrix-org/mjolnir.git
synced 2024-10-01 01:36:06 -04:00
update tests to remove use of panataliamon
This commit is contained in:
parent
c9a757f6e2
commit
e640fc968b
@ -16,7 +16,7 @@ rawHomeserverUrl: "http://localhost:8081"
|
||||
pantalaimon:
|
||||
# If true, accessToken above is ignored and the username/password below will be
|
||||
# used instead. The access token of the bot will be stored in the dataPath.
|
||||
use: true
|
||||
use: false
|
||||
|
||||
# The username to login with.
|
||||
username: mjolnir
|
||||
@ -25,6 +25,11 @@ pantalaimon:
|
||||
# stored the access token.
|
||||
password: mjolnir
|
||||
|
||||
encryption:
|
||||
use: true
|
||||
username: test
|
||||
password: testPassword
|
||||
|
||||
# The directory the bot should store various bits of information in
|
||||
dataPath: "./test/harness/mjolnir-data/"
|
||||
|
||||
|
@ -44,6 +44,7 @@
|
||||
"typescript-formatter": "^7.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.7.6",
|
||||
"@sentry/node": "^7.17.2",
|
||||
"@sentry/tracing": "^7.17.2",
|
||||
"@tensorflow/tfjs-node": "^4.21.0",
|
||||
|
@ -1,8 +1,7 @@
|
||||
import { strict as assert } from "assert";
|
||||
|
||||
import { matrixClient } from "./mjolnirSetupUtils";
|
||||
import { newTestUser } from "./clientHelper";
|
||||
import { ReportManager, ABUSE_ACTION_CONFIRMATION_KEY, ABUSE_REPORT_KEY } from "../../src/report/ReportManager";
|
||||
import { ABUSE_REPORT_KEY } from "../../src/report/ReportManager";
|
||||
|
||||
/**
|
||||
* Test the ability to turn abuse reports into room messages.
|
||||
@ -30,8 +29,9 @@ describe("Test: Reporting abuse", async () => {
|
||||
this.timeout(90000);
|
||||
|
||||
// Listen for any notices that show up.
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
let notices: any[] = [];
|
||||
this.mjolnir.client.on("room.event", (roomId, event) => {
|
||||
this.mjolnir.client.on("room.event", (roomId: string, event: any) => {
|
||||
if (roomId = this.mjolnir.managementRoomId) {
|
||||
notices.push(event);
|
||||
}
|
||||
@ -225,8 +225,9 @@ describe("Test: Reporting abuse", async () => {
|
||||
this.timeout(60000);
|
||||
|
||||
// Listen for any notices that show up.
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
let notices: any[] = [];
|
||||
this.mjolnir.client.on("room.event", (roomId, event) => {
|
||||
this.mjolnir.client.on("room.event", (roomId: string, event: any) => {
|
||||
if (roomId = this.mjolnir.managementRoomId) {
|
||||
notices.push(event);
|
||||
}
|
||||
@ -244,8 +245,8 @@ describe("Test: Reporting abuse", async () => {
|
||||
let badUserId = await badUser.getUserId();
|
||||
|
||||
let roomId = await moderatorUser.createRoom({ invite: [await badUser.getUserId()] });
|
||||
await moderatorUser.inviteUser(await goodUser.getUserId(), roomId);
|
||||
await moderatorUser.inviteUser(await badUser.getUserId(), roomId);
|
||||
await moderatorUser.inviteUser(goodUserId, roomId);
|
||||
await moderatorUser.inviteUser(badUserId, roomId);
|
||||
await badUser.joinRoom(roomId);
|
||||
await goodUser.joinRoom(roomId);
|
||||
|
||||
@ -257,9 +258,9 @@ describe("Test: Reporting abuse", async () => {
|
||||
// Exchange a few messages.
|
||||
let goodText = `GOOD: ${Math.random()}`; // Will NOT be reported.
|
||||
let badText = `BAD: ${Math.random()}`; // Will be reported as abuse.
|
||||
let goodEventId = await goodUser.sendText(roomId, goodText);
|
||||
await goodUser.sendText(roomId, goodText);
|
||||
let badEventId = await badUser.sendText(roomId, badText);
|
||||
let goodEventId2 = await goodUser.sendText(roomId, goodText);
|
||||
await goodUser.sendText(roomId, goodText);
|
||||
|
||||
console.log("Test: Reporting abuse - send reports");
|
||||
|
||||
|
@ -1,55 +1,80 @@
|
||||
import { HmacSHA1 } from "crypto-js";
|
||||
import { getRequestFn, LogService, MatrixClient, MemoryStorageProvider, PantalaimonClient } from "@vector-im/matrix-bot-sdk";
|
||||
import {
|
||||
MatrixClient,
|
||||
MemoryStorageProvider,
|
||||
RustSdkCryptoStorageProvider
|
||||
} from "@vector-im/matrix-bot-sdk";
|
||||
import { PathLike, promises as fs} from "fs";
|
||||
import axios from "axios";
|
||||
|
||||
const REGISTRATION_ATTEMPTS = 10;
|
||||
const REGISTRATION_RETRY_BASE_DELAY_MS = 100;
|
||||
let CryptoStorePaths: any = [];
|
||||
|
||||
/**
|
||||
* Register a user using the synapse admin api that requires the use of a registration secret rather than an admin user.
|
||||
* This should only be used by test code and should not be included from any file in the source directory
|
||||
* either by explicit imports or copy pasting.
|
||||
*
|
||||
* @param homeserver the homeserver url
|
||||
* @param username The username to give the user.
|
||||
* @param displayname The displayname to give the user.
|
||||
* @param password The password to use.
|
||||
* @param admin True to make the user an admin, false otherwise.
|
||||
* @returns The response from synapse.
|
||||
* @returns The access token from logging in.
|
||||
*/
|
||||
export async function registerUser(homeserver: string, username: string, displayname: string, password: string, admin: boolean): Promise<void> {
|
||||
export async function registerUser(homeserver: string, username: string, displayname: string, password: string, admin: boolean): Promise<string> {
|
||||
let registerUrl = `${homeserver}/_synapse/admin/v1/register`
|
||||
const data: {nonce: string} = await new Promise((resolve, reject) => {
|
||||
getRequestFn()({uri: registerUrl, method: "GET", timeout: 60000}, (error: any, response: any, resBody: any) => {
|
||||
error ? reject(error) : resolve(JSON.parse(resBody))
|
||||
});
|
||||
});
|
||||
const nonce = data.nonce!;
|
||||
const response = await axios({method: 'get', url: registerUrl, timeout: 60000})
|
||||
const nonce = response.data.nonce
|
||||
let mac = HmacSHA1(`${nonce}\0${username}\0${password}\0${admin ? 'admin' : 'notadmin'}`, 'REGISTRATION_SHARED_SECRET');
|
||||
|
||||
for (let i = 1; i <= REGISTRATION_ATTEMPTS; ++i) {
|
||||
const registerConfig = {
|
||||
url: registerUrl,
|
||||
method: "post",
|
||||
headers: {"Content-Type": "application/json"},
|
||||
data: JSON.stringify({
|
||||
nonce,
|
||||
username,
|
||||
displayname,
|
||||
password,
|
||||
admin,
|
||||
mac: mac.toString()
|
||||
}),
|
||||
timeout: 60000
|
||||
}
|
||||
try {
|
||||
const params = {
|
||||
uri: registerUrl,
|
||||
method: "POST",
|
||||
headers: {"Content-Type": "application/json"},
|
||||
body: JSON.stringify({
|
||||
nonce,
|
||||
username,
|
||||
displayname,
|
||||
password,
|
||||
admin,
|
||||
mac: mac.toString()
|
||||
}),
|
||||
timeout: 60000
|
||||
}
|
||||
return await new Promise((resolve, reject) => {
|
||||
getRequestFn()(params, (error: any) => error ? reject(error) : resolve());
|
||||
});
|
||||
let resp = await axios(registerConfig)
|
||||
return resp.data?.access_token
|
||||
} catch (ex) {
|
||||
const code = ex.response.data.errcode
|
||||
|
||||
// In case of timeout or throttling, backoff and retry.
|
||||
if (ex?.code === 'ESOCKETTIMEDOUT' || ex?.code === 'ETIMEDOUT'
|
||||
|| ex?.body?.errcode === 'M_LIMIT_EXCEEDED') {
|
||||
if (code === 'ESOCKETTIMEDOUT' || code === 'ETIMEDOUT'
|
||||
|| code === 'M_LIMIT_EXCEEDED') {
|
||||
await new Promise(resolve => setTimeout(resolve, REGISTRATION_RETRY_BASE_DELAY_MS * i * i));
|
||||
continue;
|
||||
}
|
||||
if (code === 'M_USER_IN_USE') {
|
||||
const loginUrl = `${homeserver}/_matrix/client/r0/login`
|
||||
const loginConfig = {
|
||||
url: loginUrl,
|
||||
method: "post",
|
||||
headers: {"Content-Type": "application/json"},
|
||||
data: JSON.stringify({
|
||||
"type": "m.login.password",
|
||||
"identifier": {
|
||||
"type": "m.id.user",
|
||||
"user": username
|
||||
},
|
||||
"password": password
|
||||
}),
|
||||
timeout: 60000
|
||||
}
|
||||
let resp2 = await axios(loginConfig)
|
||||
return resp2.data?.access_token
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
@ -78,31 +103,24 @@ export type RegistrationOptions = {
|
||||
/**
|
||||
* Register a new test user.
|
||||
*
|
||||
* @returns A string that is both the username and password of a new user.
|
||||
* @returns an access token for a new test account
|
||||
*/
|
||||
async function registerNewTestUser(homeserver: string, options: RegistrationOptions) {
|
||||
async function registerNewTestUser(homeserver: string, options: RegistrationOptions): Promise<string> {
|
||||
do {
|
||||
let username;
|
||||
let accessToken: string;
|
||||
if ("exact" in options.name) {
|
||||
username = options.name.exact;
|
||||
} else {
|
||||
username = `mjolnir-test-user-${options.name.contains}${Math.floor(Math.random() * 100000)}`
|
||||
}
|
||||
try {
|
||||
await registerUser(homeserver, username, username, username, Boolean(options.isAdmin));
|
||||
return username;
|
||||
accessToken = await registerUser(homeserver, username, username, username, Boolean(options.isAdmin));
|
||||
return accessToken;
|
||||
} catch (e) {
|
||||
if (e?.body?.errcode === 'M_USER_IN_USE') {
|
||||
if ("exact" in options.name) {
|
||||
LogService.debug("test/clientHelper", `${username} already registered, reusing`);
|
||||
return username;
|
||||
} else {
|
||||
LogService.debug("test/clientHelper", `${username} already registered, trying another`);
|
||||
}
|
||||
} else {
|
||||
console.error(`failed to register user ${e}`);
|
||||
throw e;
|
||||
}
|
||||
console.error(`failed to register user ${e}`);
|
||||
throw e;
|
||||
|
||||
}
|
||||
} while (true);
|
||||
}
|
||||
@ -112,10 +130,17 @@ async function registerNewTestUser(homeserver: string, options: RegistrationOpti
|
||||
*
|
||||
* @returns A new `MatrixClient` session for a unique test user.
|
||||
*/
|
||||
export async function newTestUser(homeserver: string, options: RegistrationOptions): Promise<MatrixClient> {
|
||||
const username = await registerNewTestUser(homeserver, options);
|
||||
const pantalaimon = new PantalaimonClient(homeserver, new MemoryStorageProvider());
|
||||
const client = await pantalaimon.createClientWithCredentials(username, username);
|
||||
export async function newTestUser(homeserver: string, options: RegistrationOptions, encrypted: boolean = false): Promise<MatrixClient> {
|
||||
const accessToken = await registerNewTestUser(homeserver, options);
|
||||
let client;
|
||||
if (encrypted) {
|
||||
const cStore = await getTempCryptoStore();
|
||||
client = new MatrixClient(homeserver, accessToken, new MemoryStorageProvider(), cStore);
|
||||
await client.crypto.prepare()
|
||||
} else {
|
||||
client = new MatrixClient(homeserver, accessToken, new MemoryStorageProvider())
|
||||
}
|
||||
|
||||
if (!options.isThrottled) {
|
||||
let userId = await client.getUserId();
|
||||
await overrideRatelimitForUser(homeserver, userId);
|
||||
@ -132,17 +157,11 @@ let _globalAdminUser: MatrixClient;
|
||||
async function getGlobalAdminUser(homeserver: string): Promise<MatrixClient> {
|
||||
// Initialize global admin user if needed.
|
||||
if (!_globalAdminUser) {
|
||||
let accessToken: string;
|
||||
const USERNAME = "mjolnir-test-internal-admin-user";
|
||||
try {
|
||||
await registerUser(homeserver, USERNAME, USERNAME, USERNAME, true);
|
||||
} catch (e) {
|
||||
if (e.isAxiosError && e?.response?.data?.errcode === 'M_USER_IN_USE') {
|
||||
// Then we've already registered the user in a previous run and that is ok.
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
_globalAdminUser = await new PantalaimonClient(homeserver, new MemoryStorageProvider()).createClientWithCredentials(USERNAME, USERNAME);
|
||||
accessToken = await registerUser(homeserver, USERNAME, USERNAME, USERNAME, true);
|
||||
_globalAdminUser = await new MatrixClient(homeserver, accessToken, new MemoryStorageProvider())
|
||||
await _globalAdminUser
|
||||
}
|
||||
return _globalAdminUser;
|
||||
}
|
||||
@ -177,6 +196,23 @@ export function noticeListener(targetRoomdId: string, cb: (event: any) => void)
|
||||
return (roomId: string, event: any) => {
|
||||
if (roomId !== targetRoomdId) return;
|
||||
if (event?.content?.msgtype !== "m.notice") return;
|
||||
cb(event);
|
||||
cb(event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tears down temporary crypto stores created for testing
|
||||
*/
|
||||
export async function teardownCryptoStores() {
|
||||
await Promise.all(CryptoStorePaths.map((p: PathLike) => fs.rm(p, { force: true, recursive: true})));
|
||||
CryptoStorePaths = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to create temp crypto store for testing
|
||||
*/
|
||||
export async function getTempCryptoStore() {
|
||||
const cryptoDir = await fs.mkdtemp('mjolnir-integration-test');
|
||||
CryptoStorePaths.push(cryptoDir);
|
||||
return new RustSdkCryptoStorageProvider(cryptoDir, 0);
|
||||
}
|
||||
|
@ -33,6 +33,8 @@ describe("Test: The make admin command", function () {
|
||||
LogService.debug("makeadminTest", `moderator creating targetRoom: ${targetRoom}; and inviting ${mjolnirUserId}`);
|
||||
await moderator.sendMessage(this.mjolnir.managementRoomId, { msgtype: 'm.text.', body: `!mjolnir rooms add ${targetRoom}` });
|
||||
LogService.debug("makeadminTest", `Adding targetRoom: ${targetRoom}`);
|
||||
// allow bot time to join room
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
try {
|
||||
await moderator.start();
|
||||
await userA.start();
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { read as configRead } from "../../src/config";
|
||||
import { makeMjolnir, teardownManagementRoom } from "./mjolnirSetupUtils";
|
||||
import dns from 'node:dns';
|
||||
import {teardownCryptoStores} from "./clientHelper";
|
||||
|
||||
// Necessary for CI: Node 17+ defaults to using ipv6 first, but Github Actions does not support ipv6
|
||||
dns.setDefaultResultOrder('ipv4first');
|
||||
@ -39,7 +40,8 @@ export const mochaHooks = {
|
||||
]);
|
||||
// remove alias from management room and leave it.
|
||||
await teardownManagementRoom(this.mjolnir.client, this.mjolnir.managementRoomId, this.managementRoomAlias);
|
||||
await teardownCryptoStores()
|
||||
console.error("---- completed test", JSON.stringify(this.currentTest.title), "\n\n"); // Makes MatrixClient error logs a bit easier to parse.
|
||||
}
|
||||
]
|
||||
],
|
||||
};
|
||||
|
@ -15,7 +15,6 @@ limitations under the License.
|
||||
*/
|
||||
import {
|
||||
MatrixClient,
|
||||
PantalaimonClient,
|
||||
MemoryStorageProvider,
|
||||
LogService,
|
||||
LogLevel,
|
||||
@ -23,7 +22,7 @@ import {
|
||||
} from "@vector-im/matrix-bot-sdk";
|
||||
|
||||
import { Mjolnir} from '../../src/Mjolnir';
|
||||
import { overrideRatelimitForUser, registerUser } from "./clientHelper";
|
||||
import {getTempCryptoStore, overrideRatelimitForUser, registerUser} from "./clientHelper";
|
||||
import { initializeGlobalPerformanceMetrics, initializeSentry, patchMatrixClient } from "../../src/utils";
|
||||
import { IConfig } from "../../src/config";
|
||||
|
||||
@ -54,15 +53,9 @@ async function configureMjolnir(config: IConfig) {
|
||||
initializeSentry(config);
|
||||
initializeGlobalPerformanceMetrics(config);
|
||||
|
||||
try {
|
||||
await registerUser(config.homeserverUrl, config.pantalaimon.username, config.pantalaimon.username, config.pantalaimon.password, true)
|
||||
} catch (e) {
|
||||
if (e?.body?.errcode === 'M_USER_IN_USE') {
|
||||
console.log(`${config.pantalaimon.username} already registered, skipping`);
|
||||
return;
|
||||
}
|
||||
throw e;
|
||||
};
|
||||
let accessToken = await registerUser(config.homeserverUrl, config.encryption.username, config.encryption.username, config.encryption.password, true)
|
||||
|
||||
return accessToken
|
||||
}
|
||||
|
||||
export function mjolnir(): Mjolnir | null {
|
||||
@ -78,12 +71,14 @@ let globalMjolnir: Mjolnir | null;
|
||||
* Return a test instance of Mjolnir.
|
||||
*/
|
||||
export async function makeMjolnir(config: IConfig): Promise<Mjolnir> {
|
||||
await configureMjolnir(config);
|
||||
let accessToken = await configureMjolnir(config);
|
||||
let cryptoStore = await getTempCryptoStore()
|
||||
LogService.setLogger(new RichConsoleLogger());
|
||||
LogService.setLevel(LogLevel.fromString(config.logLevel, LogLevel.DEBUG));
|
||||
LogService.info("test/mjolnirSetupUtils", "Starting bot...");
|
||||
const pantalaimon = new PantalaimonClient(config.homeserverUrl, new MemoryStorageProvider());
|
||||
const client = await pantalaimon.createClientWithCredentials(config.pantalaimon.username, config.pantalaimon.password);
|
||||
let client = new MatrixClient(config.homeserverUrl, accessToken, new MemoryStorageProvider(), cryptoStore);
|
||||
await client.crypto.prepare()
|
||||
|
||||
await overrideRatelimitForUser(config.homeserverUrl, await client.getUserId());
|
||||
patchMatrixClient();
|
||||
await ensureAliasedRoomExists(client, config.managementRoom);
|
||||
|
19
yarn.lock
19
yarn.lock
@ -775,6 +775,15 @@ aws4@^1.8.0:
|
||||
resolved "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz"
|
||||
integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==
|
||||
|
||||
axios@^1.7.6:
|
||||
version "1.7.7"
|
||||
resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.7.tgz#2f554296f9892a72ac8d8e4c5b79c14a91d0a47f"
|
||||
integrity sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==
|
||||
dependencies:
|
||||
follow-redirects "^1.15.6"
|
||||
form-data "^4.0.0"
|
||||
proxy-from-env "^1.1.0"
|
||||
|
||||
balanced-match@^1.0.0:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz"
|
||||
@ -1741,6 +1750,11 @@ fn.name@1.x.x:
|
||||
resolved "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz"
|
||||
integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==
|
||||
|
||||
follow-redirects@^1.15.6:
|
||||
version "1.15.9"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.9.tgz#a604fa10e443bf98ca94228d9eebcc2e8a2c8ee1"
|
||||
integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==
|
||||
|
||||
forever-agent@~0.6.1:
|
||||
version "0.6.1"
|
||||
resolved "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz"
|
||||
@ -3237,6 +3251,11 @@ proxy-addr@~2.0.7:
|
||||
forwarded "0.2.0"
|
||||
ipaddr.js "1.9.1"
|
||||
|
||||
proxy-from-env@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
|
||||
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
|
||||
|
||||
pseudomap@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz"
|
||||
|
Loading…
Reference in New Issue
Block a user