Files
GMW/tests/moderation/autoDeleteManager.test.ts
T

149 lines
4.2 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from "vitest";
import { attemptAutoDeleteFlaggedMessage } from "../../src/moderation/autoDeleteManager";
import type { MessageRecord } from "../../src/moderation/types";
vi.mock("../../src/config", () => ({
config: {
AUTO_DELETE_FLAGGED_ENABLED: true,
AUTO_DELETE_FLAGGED_DRY_RUN: false,
AUTO_DELETE_FLAGGED_DELAY_MS: 0,
AUTO_DELETE_MIN_CONFIDENCE: 0,
AUTO_DELETE_ALLOWED_SEVERITIES: "",
AUTO_DELETE_ALLOWED_CATEGORIES: "",
AUTO_DELETE_EXCLUDED_CHANNEL_IDS: "",
AUTO_DELETE_EXCLUDED_USER_IDS: "",
},
}));
vi.mock("../../src/logger", () => ({
createChildLogger: () => ({
info: vi.fn(),
warn: vi.fn(),
error: vi.fn(),
debug: vi.fn(),
}),
}));
function createMessage(overrides: Partial<MessageRecord> = {}): MessageRecord {
return {
id: "m1",
guild_id: "g1",
channel_id: "c1",
thread_id: null,
user_id: "u1",
username: "user",
avatar_url: null,
content: "bad",
edited_content: null,
created_at: Date.now(),
edited_at: null,
deleted_at: null,
type: "text",
metadata: null,
ai_status: "flagged",
ai_recommended_action: "delete",
...overrides,
};
}
function createClient(options: {
canManageMessages?: boolean;
fetchError?: unknown;
deleteError?: unknown;
} = {}) {
const deleteMock = vi.fn(async () => {
if (options.deleteError) throw options.deleteError;
});
const fetchMessageMock = vi.fn(async () => {
if (options.fetchError) throw options.fetchError;
return { delete: deleteMock };
});
const permissionsForMock = vi.fn(() => ({
has: vi.fn(() => options.canManageMessages ?? true),
}));
const channel = {
permissionsFor: permissionsForMock,
messages: { fetch: fetchMessageMock },
};
const guild = {
channels: { cache: new Map([["c1", channel]]) },
members: { fetch: vi.fn(async () => ({ id: "self" })) },
};
const client = {
user: { id: "self" },
guilds: { cache: new Map([["g1", guild]]) },
};
return { client, guild, channel, fetchMessageMock, deleteMock };
}
describe("attemptAutoDeleteFlaggedMessage", () => {
beforeEach(() => {
vi.clearAllMocks();
});
it("skips non-flagged messages", async () => {
const { client, deleteMock } = createClient();
const result = await attemptAutoDeleteFlaggedMessage(
client as any,
createMessage({ ai_status: "clean" }),
);
expect(result.reason).toBe("not_flagged_or_warn");
expect(deleteMock).not.toHaveBeenCalled();
});
it("skips when current user lacks Manage Messages", async () => {
const { client, deleteMock } = createClient({ canManageMessages: false });
const result = await attemptAutoDeleteFlaggedMessage(
client as any,
createMessage(),
);
expect(result.reason).toBe("missing_manage_messages");
expect(deleteMock).not.toHaveBeenCalled();
});
it("deletes when message is flagged and permission exists", async () => {
const { client, fetchMessageMock, deleteMock } = createClient({
canManageMessages: true,
});
const result = await attemptAutoDeleteFlaggedMessage(
client as any,
createMessage(), // defaults to flagged
);
expect(result).toEqual({ deleted: true, skipped: false, reason: "deleted" });
expect(fetchMessageMock).toHaveBeenCalledWith("m1");
expect(deleteMock).toHaveBeenCalledTimes(1);
});
it("deletes when message is warn and permission exists", async () => {
const { client, fetchMessageMock, deleteMock } = createClient({
canManageMessages: true,
});
const result = await attemptAutoDeleteFlaggedMessage(
client as any,
createMessage({ ai_status: "warn" }),
);
expect(result).toEqual({ deleted: true, skipped: false, reason: "deleted" });
expect(fetchMessageMock).toHaveBeenCalledWith("m1");
expect(deleteMock).toHaveBeenCalledTimes(1);
});
it("treats unknown message as already deleted", async () => {
const { client } = createClient({ fetchError: { code: 10008 } });
const result = await attemptAutoDeleteFlaggedMessage(
client as any,
createMessage(),
);
expect(result).toEqual({
deleted: true,
skipped: false,
reason: "already_deleted",
});
});
});