Tidy up documentation and test utils

This commit is contained in:
gnuxie 2021-09-24 10:12:18 +01:00
parent a86afd3cc8
commit 0a6b122f5c
6 changed files with 58 additions and 40 deletions

View File

@ -41,7 +41,7 @@ if (config.health.healthz.enabled) {
}
(async function () {
const storagePath = path.isAbsolute(config.dataPath) ? config.dataPath : path.join(__dirname, '../', config.dataPath)
const storagePath = path.isAbsolute(config.dataPath) ? config.dataPath : path.join(__dirname, '../', config.dataPath);
const storage = new SimpleFsStorageProvider(path.join(storagePath, "bot.json"));
let client: MatrixClient;

View File

@ -23,14 +23,14 @@ import { Mjolnir } from "./Mjolnir";
/**
* Adds a listener to the client that will automatically accept invitations.
* @param {MatrixClient} client
* @param {MatrixClient} client
* @param options By default accepts invites from anyone.
* @param {string} options.managementRoom The room to report ignored invitations to if `recordIgnoredInvites` is true.
* @param {boolean} options.recordIgnoredInvites Whether to report invites that will be ignored to the `managementRoom`.
* @param {boolean} options.autojoinOnlyIfManager Whether to only accept an invitation by a user present in the `managementRoom`.
* @param {string} options.acceptInvitesFromGroup A group of users to accept invites from, ignores invites form users not in this group.
*/
export function addJoinOnInviteListener(client: MatrixClient, options) {
function addJoinOnInviteListener(client: MatrixClient, options) {
client.on("room.invite", async (roomId: string, inviteEvent: any) => {
const membershipEvent = new MembershipEvent(inviteEvent);
@ -92,4 +92,4 @@ export async function setupMjolnir(client, config): Promise<Mjolnir> {
await logMessage(LogLevel.INFO, "index", "Mjolnir is starting up. Use !mjolnir to query status.");
return new Mjolnir(client, protectedRooms, banLists);
}
}

View File

@ -3,6 +3,16 @@ import { HmacSHA1 } from "crypto-js";
import { MatrixClient, MemoryStorageProvider, PantalaimonClient } from "matrix-bot-sdk";
import config from "../../src/config";
/**
* 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 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.
*/
export async function registerUser(username: string, displayname: string, password: string, admin: boolean) {
let registerUrl = `${config.homeserverUrl}/_synapse/admin/v1/register`
let { data } = await axios.get(registerUrl);
@ -27,9 +37,9 @@ export async function registerNewTestUser(isAdmin: boolean) {
let isUserValid = false;
let username;
do {
username = `test-user-${Math.floor(Math.random() * 100000)}`
username = `mjolnir-test-user-${Math.floor(Math.random() * 100000)}`
await registerUser(username, username, username, isAdmin).then(_ => isUserValid = true).catch(e => {
if (e.isAxiosError && e.response.data.errcode === 'M_USER_IN_USE') {
if (e.isAxiosError && e?.response?.data?.errcode === 'M_USER_IN_USE') {
// FIXME: Replace with the real logging service.
console.log(`${username} already registered, trying another`);
false // continue and try again
@ -42,16 +52,27 @@ export async function registerNewTestUser(isAdmin: boolean) {
return username;
}
export async function newTestUser(isAdmin?: boolean): Promise<MatrixClient> {
/**
* Registers a unique test user and returns a `MatrixClient` logged in and ready to use.
* @param isAdmin Whether to make the user an admin.
* @returns A new `MatrixClient` session for a unique test user.
*/
export async function newTestUser(isAdmin : boolean = false): Promise<MatrixClient> {
const username = await registerNewTestUser(isAdmin);
const pantalaimon = new PantalaimonClient(config.homeserverUrl, new MemoryStorageProvider());
return await pantalaimon.createClientWithCredentials(username, username);
}
/**
* Utility to create an event listener for m.notice msgtype m.room.messages.
* @param targetRoomdId The roomId to listen into.
* @param cb The callback when a m.notice event is found in targetRoomId.
* @returns The callback to pass to `MatrixClient.on('room.message', cb)`
*/
export function noticeListener(targetRoomdId: string, cb) {
return (roomId, event) => {
if (roomId !== targetRoomdId) return;
if (event?.content?.msgtype !== "m.notice") return;
cb(event);
}
}
}

View File

@ -1,9 +1,11 @@
import config from "../../src/config";
import { Mjolnir } from "../../src/Mjolnir";
import { makeMjolnir, teardownManagementRoom } from "./mjolnirSetupUtils";
// when mjolnir starts it clobbers the config, which is cached between runs,
// by resolving the alias and setting it to a roomid.
// When Mjolnir starts (src/index.ts) it clobbers the config by resolving the management room
// alias specified in the config (config.managementRoom) and overwriting that with the room ID.
// Unfortunately every piece of code importing that config imports the same instance, including
// testing code, which is problematic when we want to create a fresh management room for each test.
// So there is some code in here to "undo" the mutation after we stop Mjolnir syncing.
export const mochaHooks = {
beforeEach: [
async function() {
@ -16,12 +18,12 @@ export const mochaHooks = {
async function() {
console.log("stopping mjolnir");
await this.mjolnir.stop();
// unclobber mjolnir's dirty work, i thought the config was being cached
// and was global, but that might have just been supersitiion, needs confirming.
// Mjolnir resolves config.managementRoom and overwrites it, so we undo this here
// after stopping Mjolnir for the next time we setup a Mjolnir and a management room.
let managementRoomId = config.managementRoom;
config.managementRoom = this.managementRoomAlias;
// remove alias from management room and leave it.
await teardownManagementRoom(this.mjolnir.client, managementRoomId, this.managementRoomAlias);
}
]
};
};

View File

@ -3,7 +3,6 @@ import { assert } from "console";
import config from "../../src/config";
import { newTestUser, noticeListener } from "./clientHelper"
// need the start and newTestUser and then the stop call to be in setup and tear down.
describe("help command", () => {
let client;
before(async function () {

View File

@ -27,27 +27,20 @@ import * as fs from 'fs/promises';
import { setupMjolnir } from '../../src/setup';
import { registerUser } from "./clientHelper";
export async function createManagementRoom(client: MatrixClient) {
let roomId = await client.createRoom();
return await client.createRoomAlias(config.managementRoom, roomId);
}
export async function ensureManagementRoomExists(client: MatrixClient): Promise<string> {
return await client.resolveRoom(config.managementRoom).catch(async e => {
if (e?.body?.errcode === 'M_NOT_FOUND') {
console.info("moderation room hasn't been created yet, so we're making it now.")
return await createManagementRoom(client);
}
throw e;
});
}
export async function ensureLobbyRoomExists(client: MatrixClient): Promise<string> {
const alias = Permalinks.parseUrl(config.protectedRooms[0]).roomIdOrAlias;
return await client.resolveRoom(alias).catch(async e => {
/**
* Ensures that a room exists with the alias, if it does not exist we create it.
* @param client The MatrixClient to use to resolve or create the aliased room.
* @param alias The alias of the room.
* @returns The room ID of the aliased room.
*/
export async function ensureAliasedRoomExists(client: MatrixClient, alias: string): Promise<string> {
return await client.resolveRoom(alias)
.catch(async e => {
if (e?.body?.errcode === 'M_NOT_FOUND') {
console.info(`${alias} hasn't been created yet, so we're making it now.`)
return await client.createRoomAlias(alias, await client.createRoom());
let roomId = await client.createRoom();
await client.createRoomAlias(config.managementRoom, roomId);
return roomId
}
throw e;
});
@ -64,20 +57,23 @@ async function configureMjolnir() {
});
}
// it actually might make sense to give mjolnir a clean plate each time we setup and teardown a test.
// the only issues with this might be e.g. if we need to delete a community or something
// that mjolnir sets up each time, but tbh we should probably just avoid setting things like that and tearing it down.
// One thing that probably should not be persisted between tests is the management room, subscribed lists and protected rooms.
export async function makeMjolnir() {
await configureMjolnir();
console.info('starting mjolnir');
const pantalaimon = new PantalaimonClient(config.homeserverUrl, new MemoryStorageProvider());
const client = await pantalaimon.createClientWithCredentials(config.pantalaimon.username, config.pantalaimon.password);
await ensureManagementRoomExists(client);
await ensureAliasedRoomExists(client, config.managementRoom);
return await setupMjolnir(client, config);
}
/**
* Remove the alias and leave the room, can't be implicitly provided from the config because Mjolnir currently mutates it.
* @param client The client to use to leave the room.
* @param roomId The roomId of the room to leave.
* @param alias The alias to remove from the room.
*/
export async function teardownManagementRoom(client: MatrixClient, roomId: string, alias: string) {
await client.deleteRoomAlias(alias);
await client.leaveRoom(roomId);
}
}