mirror of
https://github.com/the-draupnir-project/Draupnir.git
synced 2026-07-31 07:49:20 +00:00
Unit tests for the ban and unban commands. (#542)
* Unit test Ban and UnbanCommands. Fixes https://github.com/the-draupnir-project/Draupnir/issues/441 will follow up with https://github.com/the-draupnir-project/interface-manager/issues/7. Part of https://github.com/the-draupnir-project/planning/issues/22. * Update to @the-draupnir-project/interface-manager@2.1.0. * Rest parameters can only give one argument from prompts. * Update to @the-draupnir-project/interface-manager@2.2.0.
This commit is contained in:
+1
-1
@@ -56,7 +56,7 @@
|
||||
"@sentry/node": "^7.17.2",
|
||||
"@sentry/tracing": "^7.17.2",
|
||||
"@sinclair/typebox": "0.32.34",
|
||||
"@the-draupnir-project/interface-manager": "2.0.0",
|
||||
"@the-draupnir-project/interface-manager": "2.2.0",
|
||||
"@the-draupnir-project/matrix-basic-types": "^0.1.1",
|
||||
"await-lock": "^2.2.2",
|
||||
"better-sqlite3": "^9.4.3",
|
||||
|
||||
+57
-25
@@ -8,19 +8,21 @@
|
||||
// https://github.com/matrix-org/mjolnir
|
||||
// </text>
|
||||
|
||||
import { Draupnir } from "../Draupnir";
|
||||
import {
|
||||
ActionResult,
|
||||
PolicyRoomEditor,
|
||||
PolicyRuleType,
|
||||
isError,
|
||||
Ok,
|
||||
PolicyRoomManager,
|
||||
RoomResolver,
|
||||
PolicyListConfig,
|
||||
} from "matrix-protection-suite";
|
||||
import { resolveRoomReferenceSafe } from "matrix-protection-suite-for-matrix-bot-sdk";
|
||||
import { findPolicyRoomIDFromShortcode } from "./CreateBanListCommand";
|
||||
import {
|
||||
MatrixRoomReference,
|
||||
MatrixUserID,
|
||||
StringUserID,
|
||||
} from "@the-draupnir-project/matrix-basic-types";
|
||||
import {
|
||||
BasicInvocationInformation,
|
||||
@@ -32,22 +34,31 @@ import {
|
||||
tuple,
|
||||
union,
|
||||
} from "@the-draupnir-project/interface-manager";
|
||||
import { DraupnirInterfaceAdaptor } from "./DraupnirCommandPrerequisites";
|
||||
import {
|
||||
DraupnirContextToCommandContextTranslator,
|
||||
DraupnirInterfaceAdaptor,
|
||||
} from "./DraupnirCommandPrerequisites";
|
||||
|
||||
export async function findPolicyRoomEditorFromRoomReference(
|
||||
draupnir: Draupnir,
|
||||
roomResolver: RoomResolver,
|
||||
policyRoomManager: PolicyRoomManager,
|
||||
policyRoomReference: MatrixRoomReference
|
||||
): Promise<ActionResult<PolicyRoomEditor>> {
|
||||
const policyRoomID = await resolveRoomReferenceSafe(
|
||||
draupnir.client,
|
||||
policyRoomReference
|
||||
);
|
||||
const policyRoomID = await roomResolver.resolveRoom(policyRoomReference);
|
||||
if (isError(policyRoomID)) {
|
||||
return policyRoomID;
|
||||
}
|
||||
return await draupnir.policyRoomManager.getPolicyRoomEditor(policyRoomID.ok);
|
||||
return await policyRoomManager.getPolicyRoomEditor(policyRoomID.ok);
|
||||
}
|
||||
|
||||
export type DraupnirBanCommandContext = {
|
||||
policyRoomManager: PolicyRoomManager;
|
||||
issuerManager: PolicyListConfig;
|
||||
defaultReasons: string[];
|
||||
roomResolver: RoomResolver;
|
||||
clientUserID: StringUserID;
|
||||
};
|
||||
|
||||
export const DraupnirBanCommand = describeCommand({
|
||||
summary: "Bans an entity from the policy list.",
|
||||
parameters: tuple(
|
||||
@@ -67,13 +78,13 @@ export const DraupnirBanCommand = describeCommand({
|
||||
MatrixRoomReferencePresentationSchema,
|
||||
StringPresentationType
|
||||
),
|
||||
prompt: async function (draupnir: Draupnir) {
|
||||
prompt: async function ({
|
||||
policyRoomManager,
|
||||
clientUserID,
|
||||
}: DraupnirBanCommandContext) {
|
||||
return Ok({
|
||||
suggestions: draupnir.policyRoomManager
|
||||
.getEditablePolicyRoomIDs(
|
||||
draupnir.clientUserID,
|
||||
PolicyRuleType.User
|
||||
)
|
||||
suggestions: policyRoomManager
|
||||
.getEditablePolicyRoomIDs(clientUserID, PolicyRuleType.User)
|
||||
.map((room) => MatrixRoomIDPresentationType.wrap(room)),
|
||||
});
|
||||
},
|
||||
@@ -83,16 +94,21 @@ export const DraupnirBanCommand = describeCommand({
|
||||
name: "reason",
|
||||
description: "The reason for the ban.",
|
||||
acceptor: StringPresentationType,
|
||||
prompt: async function (draupnir: Draupnir) {
|
||||
prompt: async function ({ defaultReasons }: DraupnirBanCommandContext) {
|
||||
return Ok({
|
||||
suggestions: draupnir.config.commands.ban.defaultReasons.map(
|
||||
(reason) => [StringPresentationType.wrap(reason)]
|
||||
suggestions: defaultReasons.map((reason) =>
|
||||
StringPresentationType.wrap(reason)
|
||||
),
|
||||
});
|
||||
},
|
||||
},
|
||||
async executor(
|
||||
draupnir: Draupnir,
|
||||
{
|
||||
issuerManager,
|
||||
policyRoomManager,
|
||||
roomResolver,
|
||||
clientUserID,
|
||||
}: DraupnirBanCommandContext,
|
||||
_info: BasicInvocationInformation,
|
||||
_keywords,
|
||||
reasonParts,
|
||||
@@ -101,13 +117,19 @@ export const DraupnirBanCommand = describeCommand({
|
||||
): Promise<ActionResult<string>> {
|
||||
const policyRoomReference =
|
||||
typeof policyRoomDesignator === "string"
|
||||
? await findPolicyRoomIDFromShortcode(draupnir, policyRoomDesignator)
|
||||
? await findPolicyRoomIDFromShortcode(
|
||||
issuerManager,
|
||||
policyRoomManager,
|
||||
clientUserID,
|
||||
policyRoomDesignator
|
||||
)
|
||||
: Ok(policyRoomDesignator);
|
||||
if (isError(policyRoomReference)) {
|
||||
return policyRoomReference;
|
||||
}
|
||||
const policyListEditorResult = await findPolicyRoomEditorFromRoomReference(
|
||||
draupnir,
|
||||
roomResolver,
|
||||
policyRoomManager,
|
||||
policyRoomReference.ok
|
||||
);
|
||||
if (isError(policyListEditorResult)) {
|
||||
@@ -128,10 +150,7 @@ export const DraupnirBanCommand = describeCommand({
|
||||
reason
|
||||
);
|
||||
} else {
|
||||
const resolvedRoomReference = await resolveRoomReferenceSafe(
|
||||
draupnir.client,
|
||||
entity
|
||||
);
|
||||
const resolvedRoomReference = await roomResolver.resolveRoom(entity);
|
||||
if (isError(resolvedRoomReference)) {
|
||||
return resolvedRoomReference;
|
||||
}
|
||||
@@ -144,6 +163,19 @@ export const DraupnirBanCommand = describeCommand({
|
||||
},
|
||||
});
|
||||
|
||||
DraupnirContextToCommandContextTranslator.registerTranslation(
|
||||
DraupnirBanCommand,
|
||||
function (draupnir) {
|
||||
return {
|
||||
policyRoomManager: draupnir.policyRoomManager,
|
||||
issuerManager: draupnir.protectedRoomsSet.issuerManager,
|
||||
defaultReasons: draupnir.config.commands.ban.defaultReasons,
|
||||
roomResolver: draupnir.clientPlatform.toRoomResolver(),
|
||||
clientUserID: draupnir.clientUserID,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
DraupnirInterfaceAdaptor.describeRenderer(DraupnirBanCommand, {
|
||||
isAlwaysSupposedToUseDefaultRenderer: true,
|
||||
});
|
||||
|
||||
@@ -12,13 +12,18 @@ import {
|
||||
ActionError,
|
||||
ActionResult,
|
||||
Ok,
|
||||
PolicyListConfig,
|
||||
PolicyRoomManager,
|
||||
PolicyRuleType,
|
||||
PropagationType,
|
||||
isError,
|
||||
} from "matrix-protection-suite";
|
||||
import { listInfo } from "./StatusCommand";
|
||||
import { Draupnir } from "../Draupnir";
|
||||
import { MatrixRoomID } from "@the-draupnir-project/matrix-basic-types";
|
||||
import {
|
||||
MatrixRoomID,
|
||||
StringUserID,
|
||||
} from "@the-draupnir-project/matrix-basic-types";
|
||||
import {
|
||||
BasicInvocationInformation,
|
||||
ParsedKeywords,
|
||||
@@ -84,10 +89,12 @@ DraupnirInterfaceAdaptor.describeRenderer(DraupnirListCreateCommand, {
|
||||
});
|
||||
|
||||
export async function findPolicyRoomIDFromShortcode(
|
||||
draupnir: Draupnir,
|
||||
issuerManager: PolicyListConfig,
|
||||
policyRoomManager: PolicyRoomManager,
|
||||
editingClientUserID: StringUserID,
|
||||
shortcode: string
|
||||
): Promise<ActionResult<MatrixRoomID>> {
|
||||
const info = await listInfo(draupnir);
|
||||
const info = await listInfo(issuerManager, policyRoomManager);
|
||||
const matchingRevisions = info.filter(
|
||||
(list) => list.revision.shortcode === shortcode
|
||||
);
|
||||
@@ -99,7 +106,7 @@ export async function findPolicyRoomIDFromShortcode(
|
||||
return Ok(matchingRevisions[0].revision.room);
|
||||
} else {
|
||||
const remainingRevisions = matchingRevisions.filter((revision) =>
|
||||
revision.revision.isAbleToEdit(draupnir.clientUserID, PolicyRuleType.User)
|
||||
revision.revision.isAbleToEdit(editingClientUserID, PolicyRuleType.User)
|
||||
);
|
||||
if (
|
||||
remainingRevisions.length !== 1 ||
|
||||
|
||||
@@ -90,7 +90,10 @@ export const DraupnirListRulesCommand = describeCommand({
|
||||
summary: "Lists the rules currently in use by Draupnir.",
|
||||
parameters: [],
|
||||
async executor(draupnir: Draupnir): Promise<Result<ListMatches[]>> {
|
||||
const infoResult = await listInfo(draupnir);
|
||||
const infoResult = await listInfo(
|
||||
draupnir.protectedRoomsSet.issuerManager,
|
||||
draupnir.policyRoomManager
|
||||
);
|
||||
return Ok(
|
||||
infoResult.map((policyRoom) => ({
|
||||
room: policyRoom.revision.room,
|
||||
@@ -124,7 +127,10 @@ export const DraupnirRulesMatchingCommand = describeCommand({
|
||||
_rest,
|
||||
entity
|
||||
): Promise<Result<ListMatches[]>> {
|
||||
const policyRooms = await listInfo(draupnir);
|
||||
const policyRooms = await listInfo(
|
||||
draupnir.protectedRoomsSet.issuerManager,
|
||||
draupnir.policyRoomManager
|
||||
);
|
||||
return Ok(
|
||||
policyRooms.map((policyRoom) => {
|
||||
return {
|
||||
|
||||
@@ -12,6 +12,8 @@ import { DOCUMENTATION_URL, PACKAGE_JSON, SOFTWARE_VERSION } from "../config";
|
||||
import {
|
||||
ActionResult,
|
||||
Ok,
|
||||
PolicyListConfig,
|
||||
PolicyRoomManager,
|
||||
PolicyRoomRevision,
|
||||
PolicyRoomWatchProfile,
|
||||
PolicyRuleType,
|
||||
@@ -47,12 +49,14 @@ export interface StatusInfo {
|
||||
documentationURL: string;
|
||||
}
|
||||
|
||||
export async function listInfo(draupnir: Draupnir): Promise<ListInfo[]> {
|
||||
const watchedListProfiles =
|
||||
draupnir.protectedRoomsSet.issuerManager.allWatchedLists;
|
||||
export async function listInfo(
|
||||
issuerManager: PolicyListConfig,
|
||||
policyRoomManager: PolicyRoomManager
|
||||
): Promise<ListInfo[]> {
|
||||
const watchedListProfiles = issuerManager.allWatchedLists;
|
||||
const issuerResults = await Promise.all(
|
||||
watchedListProfiles.map((profile) =>
|
||||
draupnir.policyRoomManager.getPolicyRoomRevisionIssuer(profile.room)
|
||||
policyRoomManager.getPolicyRoomRevisionIssuer(profile.room)
|
||||
)
|
||||
);
|
||||
return issuerResults.map((result) => {
|
||||
@@ -89,7 +93,10 @@ DraupnirInterfaceAdaptor.describeRenderer(DraupnirStatusCommand, {
|
||||
export async function draupnirStatusInfo(
|
||||
draupnir: Draupnir
|
||||
): Promise<StatusInfo> {
|
||||
const watchedListInfo = await listInfo(draupnir);
|
||||
const watchedListInfo = await listInfo(
|
||||
draupnir.protectedRoomsSet.issuerManager,
|
||||
draupnir.policyRoomManager
|
||||
);
|
||||
const protectedWatchedLists = watchedListInfo.filter((info) =>
|
||||
draupnir.protectedRoomsSet.isProtectedRoom(
|
||||
info.revision.room.toRoomIDOrAlias()
|
||||
|
||||
+91
-26
@@ -8,14 +8,23 @@
|
||||
// https://github.com/matrix-org/mjolnir
|
||||
// </text>
|
||||
|
||||
import { Draupnir } from "../Draupnir";
|
||||
import { isError, Ok, PolicyRuleType } from "matrix-protection-suite";
|
||||
import {
|
||||
isError,
|
||||
Ok,
|
||||
PolicyListConfig,
|
||||
PolicyRoomManager,
|
||||
PolicyRuleType,
|
||||
RoomResolver,
|
||||
RoomUnbanner,
|
||||
SetMembership,
|
||||
} from "matrix-protection-suite";
|
||||
import { LogLevel } from "matrix-bot-sdk";
|
||||
import { findPolicyRoomIDFromShortcode } from "./CreateBanListCommand";
|
||||
import {
|
||||
isStringUserID,
|
||||
MatrixGlob,
|
||||
MatrixUserID,
|
||||
StringUserID,
|
||||
} from "@the-draupnir-project/matrix-basic-types";
|
||||
import {
|
||||
describeCommand,
|
||||
@@ -27,33 +36,47 @@ import {
|
||||
union,
|
||||
} from "@the-draupnir-project/interface-manager";
|
||||
import { Result } from "@gnuxie/typescript-result";
|
||||
import { DraupnirInterfaceAdaptor } from "./DraupnirCommandPrerequisites";
|
||||
import {
|
||||
DraupnirContextToCommandContextTranslator,
|
||||
DraupnirInterfaceAdaptor,
|
||||
} from "./DraupnirCommandPrerequisites";
|
||||
import ManagementRoomOutput from "../ManagementRoomOutput";
|
||||
import { DraupnirBanCommandContext } from "./Ban";
|
||||
import { UnlistedUserRedactionQueue } from "../queues/UnlistedUserRedactionQueue";
|
||||
|
||||
async function unbanUserFromRooms(draupnir: Draupnir, rule: MatrixGlob) {
|
||||
await draupnir.managementRoomOutput.logMessage(
|
||||
async function unbanUserFromRooms(
|
||||
{
|
||||
managementRoomOutput,
|
||||
setMembership,
|
||||
roomUnbanner,
|
||||
noop,
|
||||
}: DraupnirUnbanCommandContext,
|
||||
rule: MatrixGlob
|
||||
) {
|
||||
await managementRoomOutput.logMessage(
|
||||
LogLevel.INFO,
|
||||
"Unban",
|
||||
`Unbanning users that match glob: ${rule.regex}`
|
||||
);
|
||||
for (const revision of draupnir.protectedRoomsSet.setMembership.allRooms) {
|
||||
for (const revision of setMembership.allRooms) {
|
||||
for (const member of revision.members()) {
|
||||
if (member.membership !== "ban") {
|
||||
continue;
|
||||
}
|
||||
if (rule.test(member.userID)) {
|
||||
await draupnir.managementRoomOutput.logMessage(
|
||||
await managementRoomOutput.logMessage(
|
||||
LogLevel.DEBUG,
|
||||
"Unban",
|
||||
`Unbanning ${member.userID} in ${revision.room.toRoomIDOrAlias()}`,
|
||||
revision.room.toRoomIDOrAlias()
|
||||
);
|
||||
if (!draupnir.config.noop) {
|
||||
await draupnir.client.unbanUser(
|
||||
member.userID,
|
||||
revision.room.toRoomIDOrAlias()
|
||||
if (!noop) {
|
||||
await roomUnbanner.unbanUser(
|
||||
revision.room.toRoomIDOrAlias(),
|
||||
member.userID
|
||||
);
|
||||
} else {
|
||||
await draupnir.managementRoomOutput.logMessage(
|
||||
await managementRoomOutput.logMessage(
|
||||
LogLevel.WARN,
|
||||
"Unban",
|
||||
`Attempted to unban ${member.userID} in ${revision.room.toRoomIDOrAlias()} but Mjolnir is running in no-op mode`,
|
||||
@@ -65,6 +88,18 @@ async function unbanUserFromRooms(draupnir: Draupnir, rule: MatrixGlob) {
|
||||
}
|
||||
}
|
||||
|
||||
export type DraupnirUnbanCommandContext = {
|
||||
policyRoomManager: PolicyRoomManager;
|
||||
issuerManager: PolicyListConfig;
|
||||
roomResolver: RoomResolver;
|
||||
clientUserID: StringUserID;
|
||||
setMembership: SetMembership;
|
||||
managementRoomOutput: ManagementRoomOutput;
|
||||
noop: boolean;
|
||||
roomUnbanner: RoomUnbanner;
|
||||
unlistedUserRedactionQueue: UnlistedUserRedactionQueue;
|
||||
};
|
||||
|
||||
export const DraupnirUnbanCommand = describeCommand({
|
||||
summary:
|
||||
"Removes an entity from a policy list. If the entity is a glob, then the flag --true must be provided to unban users matching the glob from all protected rooms.",
|
||||
@@ -85,13 +120,13 @@ export const DraupnirUnbanCommand = describeCommand({
|
||||
MatrixRoomReferencePresentationSchema,
|
||||
StringPresentationType
|
||||
),
|
||||
prompt: async function (draupnir: Draupnir) {
|
||||
prompt: async function ({
|
||||
policyRoomManager,
|
||||
clientUserID,
|
||||
}: DraupnirBanCommandContext) {
|
||||
return Ok({
|
||||
suggestions: draupnir.policyRoomManager
|
||||
.getEditablePolicyRoomIDs(
|
||||
draupnir.clientUserID,
|
||||
PolicyRuleType.User
|
||||
)
|
||||
suggestions: policyRoomManager
|
||||
.getEditablePolicyRoomIDs(clientUserID, PolicyRuleType.User)
|
||||
.map((room) => MatrixRoomIDPresentationType.wrap(room)),
|
||||
});
|
||||
},
|
||||
@@ -105,17 +140,29 @@ export const DraupnirUnbanCommand = describeCommand({
|
||||
},
|
||||
},
|
||||
async executor(
|
||||
draupnir: Draupnir,
|
||||
context: DraupnirUnbanCommandContext,
|
||||
_info,
|
||||
keywords,
|
||||
_rest,
|
||||
entity,
|
||||
policyRoomDesignator
|
||||
): Promise<Result<void>> {
|
||||
const roomResolver = draupnir.clientPlatform.toRoomResolver();
|
||||
const {
|
||||
roomResolver,
|
||||
policyRoomManager,
|
||||
issuerManager,
|
||||
clientUserID,
|
||||
unlistedUserRedactionQueue,
|
||||
managementRoomOutput,
|
||||
} = context;
|
||||
const policyRoomReference =
|
||||
typeof policyRoomDesignator === "string"
|
||||
? await findPolicyRoomIDFromShortcode(draupnir, policyRoomDesignator)
|
||||
? await findPolicyRoomIDFromShortcode(
|
||||
issuerManager,
|
||||
policyRoomManager,
|
||||
clientUserID,
|
||||
policyRoomDesignator
|
||||
)
|
||||
: Ok(policyRoomDesignator);
|
||||
if (isError(policyRoomReference)) {
|
||||
return policyRoomReference;
|
||||
@@ -124,8 +171,9 @@ export const DraupnirUnbanCommand = describeCommand({
|
||||
if (isError(policyRoom)) {
|
||||
return policyRoom;
|
||||
}
|
||||
const policyRoomEditor =
|
||||
await draupnir.policyRoomManager.getPolicyRoomEditor(policyRoom.ok);
|
||||
const policyRoomEditor = await policyRoomManager.getPolicyRoomEditor(
|
||||
policyRoom.ok
|
||||
);
|
||||
if (isError(policyRoomEditor)) {
|
||||
return policyRoomEditor;
|
||||
}
|
||||
@@ -156,15 +204,15 @@ export const DraupnirUnbanCommand = describeCommand({
|
||||
string.includes("*") ? true : string.includes("?");
|
||||
const rule = new MatrixGlob(entity.toString());
|
||||
if (isStringUserID(rawEnttiy)) {
|
||||
draupnir.unlistedUserRedactionQueue.removeUser(rawEnttiy);
|
||||
unlistedUserRedactionQueue.removeUser(rawEnttiy);
|
||||
}
|
||||
if (
|
||||
!isGlob(rawEnttiy) ||
|
||||
keywords.getKeywordValue<boolean>("true", false)
|
||||
) {
|
||||
await unbanUserFromRooms(draupnir, rule);
|
||||
await unbanUserFromRooms(context, rule);
|
||||
} else {
|
||||
await draupnir.managementRoomOutput.logMessage(
|
||||
await managementRoomOutput.logMessage(
|
||||
LogLevel.WARN,
|
||||
"Unban",
|
||||
"Running unban without `unban <list> <user> true` will not override existing room level bans"
|
||||
@@ -175,6 +223,23 @@ export const DraupnirUnbanCommand = describeCommand({
|
||||
},
|
||||
});
|
||||
|
||||
DraupnirContextToCommandContextTranslator.registerTranslation(
|
||||
DraupnirUnbanCommand,
|
||||
function (draupnir) {
|
||||
return {
|
||||
policyRoomManager: draupnir.policyRoomManager,
|
||||
issuerManager: draupnir.protectedRoomsSet.issuerManager,
|
||||
roomResolver: draupnir.clientPlatform.toRoomResolver(),
|
||||
clientUserID: draupnir.clientUserID,
|
||||
setMembership: draupnir.protectedRoomsSet.setMembership,
|
||||
managementRoomOutput: draupnir.managementRoomOutput,
|
||||
noop: draupnir.config.noop,
|
||||
roomUnbanner: draupnir.clientPlatform.toRoomUnbanner(),
|
||||
unlistedUserRedactionQueue: draupnir.unlistedUserRedactionQueue,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
DraupnirInterfaceAdaptor.describeRenderer(DraupnirUnbanCommand, {
|
||||
isAlwaysSupposedToUseDefaultRenderer: true,
|
||||
});
|
||||
|
||||
@@ -298,7 +298,10 @@ export class BanPropagationProtection
|
||||
PolicyRuleType.User,
|
||||
Recommendation.Ban
|
||||
);
|
||||
const policyRoomInfo = await listInfo(draupnir);
|
||||
const policyRoomInfo = await listInfo(
|
||||
draupnir.protectedRoomsSet.issuerManager,
|
||||
draupnir.policyRoomManager
|
||||
);
|
||||
if (rulesMatchingUser.length === 0) {
|
||||
return; // user is already unbanned.
|
||||
}
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
// SPDX-FileCopyrightText: 2024 Gnuxie <Gnuxie@protonmail.com>
|
||||
//
|
||||
// SPDX-License-Identifier: AFL-3.0
|
||||
|
||||
import {
|
||||
CommandExecutorHelper,
|
||||
MatrixRoomIDPresentationType,
|
||||
MatrixUserIDPresentationType,
|
||||
PromptRequiredError,
|
||||
} from "@the-draupnir-project/interface-manager";
|
||||
import {
|
||||
MatrixRoomID,
|
||||
MatrixUserID,
|
||||
StringUserID,
|
||||
} from "@the-draupnir-project/matrix-basic-types";
|
||||
import {
|
||||
Ok,
|
||||
PolicyRoomEditor,
|
||||
PolicyRoomManager,
|
||||
PolicyRuleType,
|
||||
PowerLevelsEventContent,
|
||||
Recommendation,
|
||||
RoomResolver,
|
||||
describeProtectedRoomsSet,
|
||||
isOk,
|
||||
randomEventID,
|
||||
} from "matrix-protection-suite";
|
||||
import { createMock } from "ts-auto-mock";
|
||||
import expect from "expect";
|
||||
import { DraupnirBanCommand } from "../../../src/commands/Ban";
|
||||
|
||||
const DraupnirUserID = `@draupnir:ourserver.example.com` as StringUserID;
|
||||
|
||||
async function createProtectedRooms() {
|
||||
return await describeProtectedRoomsSet({
|
||||
rooms: [
|
||||
{
|
||||
policyDescriptions: [
|
||||
{
|
||||
entity: "@existing-spam:spam.example.com",
|
||||
recommendation: Recommendation.Ban,
|
||||
type: PolicyRuleType.User,
|
||||
},
|
||||
],
|
||||
stateDescriptions: [
|
||||
{
|
||||
sender: DraupnirUserID,
|
||||
type: "m.room.power_levels",
|
||||
state_key: "",
|
||||
content: {
|
||||
users: {
|
||||
[DraupnirUserID]: 100,
|
||||
},
|
||||
} satisfies PowerLevelsEventContent,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
describe("Test the DraupnirBanCommand", function () {
|
||||
const roomResolver = createMock<RoomResolver>({
|
||||
async resolveRoom(roomReference) {
|
||||
if (roomReference instanceof MatrixRoomID) {
|
||||
return Ok(roomReference);
|
||||
}
|
||||
throw new TypeError(`We don't really expect to resolve anything`);
|
||||
},
|
||||
});
|
||||
it("Will prompt for the policy list when it is missing", async function () {
|
||||
const { protectedRoomsSet, policyRoomManager } =
|
||||
await createProtectedRooms();
|
||||
const banResult = await CommandExecutorHelper.parseAndInvoke(
|
||||
DraupnirBanCommand,
|
||||
{
|
||||
policyRoomManager,
|
||||
roomResolver,
|
||||
issuerManager: protectedRoomsSet.issuerManager,
|
||||
defaultReasons: ["spam"],
|
||||
clientUserID: `@draupnir:ourserver.example.com` as StringUserID,
|
||||
},
|
||||
{},
|
||||
MatrixUserIDPresentationType.wrap(
|
||||
MatrixUserID.fromUserID("@spam:spam.example.com" as StringUserID)
|
||||
),
|
||||
undefined // no policy room provided, we expect a prompt.
|
||||
);
|
||||
if (isOk(banResult)) {
|
||||
throw new TypeError(`We expect a prompt error to be returned`);
|
||||
}
|
||||
if (!(banResult.error instanceof PromptRequiredError)) {
|
||||
throw new TypeError(`Expected to have a prompt required error`);
|
||||
}
|
||||
expect(banResult.error.parameterRequiringPrompt.name).toBe("list");
|
||||
});
|
||||
it("Will prompt for the reason when it is missing", async function () {
|
||||
const { protectedRoomsSet, policyRoomManager } =
|
||||
await createProtectedRooms();
|
||||
const policyRoom = protectedRoomsSet.allProtectedRooms[0];
|
||||
if (policyRoom === undefined) {
|
||||
throw new TypeError(
|
||||
`There should be a policy room available from the setup`
|
||||
);
|
||||
}
|
||||
const banResult = await CommandExecutorHelper.parseAndInvoke(
|
||||
DraupnirBanCommand,
|
||||
{
|
||||
policyRoomManager,
|
||||
roomResolver,
|
||||
issuerManager: protectedRoomsSet.issuerManager,
|
||||
defaultReasons: ["spam"],
|
||||
clientUserID: `@draupnir:ourserver.example.com` as StringUserID,
|
||||
},
|
||||
{},
|
||||
MatrixUserIDPresentationType.wrap(
|
||||
MatrixUserID.fromUserID("@spam:spam.example.com" as StringUserID)
|
||||
),
|
||||
MatrixRoomIDPresentationType.wrap(policyRoom)
|
||||
);
|
||||
if (isOk(banResult)) {
|
||||
throw new TypeError(`We expect a prompt error to be returned`);
|
||||
}
|
||||
if (!(banResult.error instanceof PromptRequiredError)) {
|
||||
throw new TypeError(`Expected to have a prompt required error`);
|
||||
}
|
||||
expect(banResult.error.parameterRequiringPrompt.name).toBe("reason");
|
||||
});
|
||||
it("Will add a user to the policy list when they are banned", async function () {
|
||||
const { protectedRoomsSet } = await createProtectedRooms();
|
||||
const policyRoom = protectedRoomsSet.allProtectedRooms[0];
|
||||
if (policyRoom === undefined) {
|
||||
throw new TypeError(
|
||||
`There should be a policy room available from the setup`
|
||||
);
|
||||
}
|
||||
const policyRoomManager = createMock<PolicyRoomManager>({
|
||||
async getPolicyRoomEditor(room) {
|
||||
expect(room).toBe(policyRoom);
|
||||
return Ok(
|
||||
createMock<PolicyRoomEditor>({
|
||||
async createPolicy(
|
||||
entityType,
|
||||
recommendation,
|
||||
entity,
|
||||
reason,
|
||||
_additionalProperties
|
||||
) {
|
||||
expect(entityType).toBe(PolicyRuleType.User);
|
||||
expect(recommendation).toBe(Recommendation.Ban);
|
||||
expect(entity).toBe("@spam:spam.example.com");
|
||||
expect(reason).toBe("spam");
|
||||
return Ok(randomEventID());
|
||||
},
|
||||
})
|
||||
);
|
||||
},
|
||||
});
|
||||
const banResult = await CommandExecutorHelper.execute(
|
||||
DraupnirBanCommand,
|
||||
{
|
||||
policyRoomManager,
|
||||
roomResolver,
|
||||
issuerManager: protectedRoomsSet.issuerManager,
|
||||
defaultReasons: ["spam"],
|
||||
clientUserID: `@draupnir:ourserver.example.com` as StringUserID,
|
||||
},
|
||||
{
|
||||
rest: ["spam"],
|
||||
},
|
||||
MatrixUserID.fromUserID("@spam:spam.example.com" as StringUserID),
|
||||
policyRoom
|
||||
);
|
||||
expect(banResult.isOkay).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,132 @@
|
||||
// SPDX-FileCopyrightText: 2024 Gnuxie <Gnuxie@protonmail.com>
|
||||
//
|
||||
// SPDX-License-Identifier: AFL-3.0
|
||||
|
||||
import { CommandExecutorHelper } from "@the-draupnir-project/interface-manager";
|
||||
import {
|
||||
MatrixRoomID,
|
||||
MatrixUserID,
|
||||
StringUserID,
|
||||
} from "@the-draupnir-project/matrix-basic-types";
|
||||
import {
|
||||
Membership,
|
||||
Ok,
|
||||
PolicyRoomEditor,
|
||||
PolicyRoomManager,
|
||||
PolicyRuleType,
|
||||
PowerLevelsEventContent,
|
||||
Recommendation,
|
||||
RoomResolver,
|
||||
RoomUnbanner,
|
||||
describeProtectedRoomsSet,
|
||||
isError,
|
||||
} from "matrix-protection-suite";
|
||||
import { createMock } from "ts-auto-mock";
|
||||
import expect from "expect";
|
||||
import { DraupnirUnbanCommand } from "../../../src/commands/Unban";
|
||||
import ManagementRoomOutput from "../../../src/ManagementRoomOutput";
|
||||
import { UnlistedUserRedactionQueue } from "../../../src/queues/UnlistedUserRedactionQueue";
|
||||
|
||||
const DraupnirUserID = `@draupnir:ourserver.example.com` as StringUserID;
|
||||
const ExistingBanUserID = "@existing-spam:spam.example.com" as StringUserID;
|
||||
|
||||
async function createProtectedRooms() {
|
||||
return await describeProtectedRoomsSet({
|
||||
rooms: [
|
||||
{
|
||||
membershipDescriptions: [
|
||||
{
|
||||
sender: DraupnirUserID,
|
||||
target: ExistingBanUserID,
|
||||
membership: Membership.Ban,
|
||||
},
|
||||
],
|
||||
policyDescriptions: [
|
||||
{
|
||||
entity: ExistingBanUserID,
|
||||
recommendation: Recommendation.Ban,
|
||||
type: PolicyRuleType.User,
|
||||
},
|
||||
],
|
||||
stateDescriptions: [
|
||||
{
|
||||
sender: DraupnirUserID,
|
||||
type: "m.room.power_levels",
|
||||
state_key: "",
|
||||
content: {
|
||||
users: {
|
||||
[DraupnirUserID]: 100,
|
||||
},
|
||||
} satisfies PowerLevelsEventContent,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
describe("Test the DraupnirUnbanCommand", function () {
|
||||
const roomResolver = createMock<RoomResolver>({
|
||||
async resolveRoom(roomReference) {
|
||||
if (roomReference instanceof MatrixRoomID) {
|
||||
return Ok(roomReference);
|
||||
}
|
||||
throw new TypeError(`We don't really expect to resolve anything`);
|
||||
},
|
||||
});
|
||||
it("Will add a user to the policy list when they are banned", async function () {
|
||||
const roomUnbanner = createMock<RoomUnbanner>({
|
||||
async unbanUser(_room, userID, _reason) {
|
||||
expect(userID).toBe(ExistingBanUserID);
|
||||
return Ok(undefined);
|
||||
},
|
||||
});
|
||||
const { protectedRoomsSet, policyRoomManager } =
|
||||
await createProtectedRooms();
|
||||
const policyRoom = protectedRoomsSet.allProtectedRooms[0];
|
||||
if (policyRoom === undefined) {
|
||||
throw new TypeError(
|
||||
`There should be a policy room available from the setup`
|
||||
);
|
||||
}
|
||||
const revisionIssuer =
|
||||
await policyRoomManager.getPolicyRoomRevisionIssuer(policyRoom);
|
||||
if (isError(revisionIssuer)) {
|
||||
throw new TypeError(`Test is wrong`);
|
||||
}
|
||||
const mockPolicyRoomManager = createMock<PolicyRoomManager>({
|
||||
async getPolicyRoomEditor(room) {
|
||||
expect(room).toBe(policyRoom);
|
||||
return Ok(
|
||||
createMock<PolicyRoomEditor>({
|
||||
async unbanEntity(ruleType, entity) {
|
||||
expect(ruleType).toBe(PolicyRuleType.User);
|
||||
expect(entity).toBe(ExistingBanUserID);
|
||||
return Ok(revisionIssuer.ok.currentRevision.allRules());
|
||||
},
|
||||
})
|
||||
);
|
||||
},
|
||||
});
|
||||
const banResult = await CommandExecutorHelper.execute(
|
||||
DraupnirUnbanCommand,
|
||||
{
|
||||
policyRoomManager: mockPolicyRoomManager,
|
||||
setMembership: protectedRoomsSet.setMembership,
|
||||
managementRoomOutput: createMock<ManagementRoomOutput>(),
|
||||
roomResolver,
|
||||
issuerManager: protectedRoomsSet.issuerManager,
|
||||
clientUserID: `@draupnir:ourserver.example.com` as StringUserID,
|
||||
noop: false,
|
||||
roomUnbanner,
|
||||
unlistedUserRedactionQueue: createMock<UnlistedUserRedactionQueue>(),
|
||||
},
|
||||
{
|
||||
rest: ["spam"],
|
||||
},
|
||||
MatrixUserID.fromUserID(ExistingBanUserID),
|
||||
policyRoom
|
||||
);
|
||||
expect(banResult.isOkay).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -256,14 +256,13 @@
|
||||
resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e"
|
||||
integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==
|
||||
|
||||
"@the-draupnir-project/interface-manager@2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@the-draupnir-project/interface-manager/-/interface-manager-2.0.0.tgz#1386bb7d59257d867f963adf600cc506f3ec591d"
|
||||
integrity sha512-DD3D3cOx0LqPJ4oET1+ttpUlVZf+4u8RTqFwxL0Qh8KOWLS9oxBwiw+U0IPDbTa+07hbhzY/hRMRBiqheXS/LQ==
|
||||
"@the-draupnir-project/interface-manager@2.2.0":
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@the-draupnir-project/interface-manager/-/interface-manager-2.2.0.tgz#2326aca4ccbf0b0ed743efb3a20222a5d78ab23e"
|
||||
integrity sha512-ycNBKT78RsRhU+HfHe3gjgtQY2pCn7Umw8uAJBHGzLFpXwk2E9kmv6dGbtGubINjsFdkacr7lVxZwm69K4bSUg==
|
||||
dependencies:
|
||||
"@gnuxie/super-cool-stream" "^0.2.1"
|
||||
"@gnuxie/typescript-result" "^0.2.0"
|
||||
"@the-draupnir-project/matrix-basic-types" "^0.1.1"
|
||||
|
||||
"@the-draupnir-project/matrix-basic-types@^0.1.1":
|
||||
version "0.1.1"
|
||||
|
||||
Reference in New Issue
Block a user