feat: enhance moderation functionality with type improvements and global broadcaster integration

This commit is contained in:
MythEclipse
2026-05-15 07:13:37 +07:00
parent 930c399484
commit 235c1120c2
12 changed files with 287 additions and 133 deletions

View File

@@ -1,17 +1,70 @@
import type { Mock } from "vitest";
import { describe, expect, it, vi } from "vitest";
import { createBroadcaster } from "../../src/moderation/broadcaster";
import {
type BroadcasterClient,
createBroadcaster,
} from "../../src/moderation/broadcaster";
import type {
AttachmentRecord,
MessageRecord,
} from "../../src/moderation/types";
function client() {
type TestClient = BroadcasterClient & { send: Mock };
function client(): TestClient {
return { readyState: 1, send: vi.fn() };
}
function messageRecord(overrides: Partial<MessageRecord> = {}): MessageRecord {
return {
id: "m1",
guild_id: "guild-1",
channel_id: "channel-1",
thread_id: null,
user_id: "user-1",
username: "alice",
avatar_url: null,
content: "test",
edited_content: null,
created_at: 1,
edited_at: null,
deleted_at: null,
type: "text",
metadata: null,
...overrides,
};
}
function attachmentRecord(
overrides: Partial<AttachmentRecord> = {},
): AttachmentRecord {
return {
id: "a1",
message_id: "m1",
guild_id: "guild-1",
channel_id: "channel-1",
thread_id: null,
user_id: "user-1",
filename: "image.png",
size: 1,
type: "image/png",
discord_url: "https://example.com/image.png",
uploaded_url: null,
upload_status: "pending",
upload_error: null,
created_at: 1,
uploaded_at: null,
...overrides,
};
}
describe("createBroadcaster", () => {
it("sends JSON events to open clients", () => {
const ws = client();
const broadcaster = createBroadcaster();
broadcaster.addClient(ws as any);
broadcaster.messageAnalyzed({ id: "m1", ai_status: "clean" } as any);
broadcaster.addClient(ws);
broadcaster.messageAnalyzed(messageRecord({ ai_status: "clean" }));
expect(ws.send).toHaveBeenCalledTimes(1);
expect(JSON.parse(ws.send.mock.calls[0][0])).toMatchObject({
@@ -21,10 +74,10 @@ describe("createBroadcaster", () => {
});
it("skips closed clients", () => {
const ws = { readyState: 3, send: vi.fn() };
const ws: TestClient = { readyState: 3, send: vi.fn() };
const broadcaster = createBroadcaster();
broadcaster.addClient(ws as any);
broadcaster.addClient(ws);
broadcaster.messageDeleted({ id: "m1", deleted_at: 123 });
expect(ws.send).not.toHaveBeenCalled();
@@ -36,14 +89,11 @@ describe("createBroadcaster", () => {
const ws3 = client();
const broadcaster = createBroadcaster();
broadcaster.addClient(ws1 as any);
broadcaster.addClient(ws2 as any);
broadcaster.addClient(ws3 as any);
broadcaster.addClient(ws1);
broadcaster.addClient(ws2);
broadcaster.addClient(ws3);
broadcaster.messageCreated({
id: "m1",
content: "test",
} as any);
broadcaster.messageCreated(messageRecord());
expect(ws1.send).toHaveBeenCalledTimes(1);
expect(ws2.send).toHaveBeenCalledTimes(1);
@@ -61,14 +111,14 @@ describe("createBroadcaster", () => {
throw new Error("Send failed");
});
broadcaster.addClient(ws1 as any);
broadcaster.addClient(ws2 as any);
broadcaster.addClient(ws3 as any);
broadcaster.addClient(ws1);
broadcaster.addClient(ws2);
broadcaster.addClient(ws3);
broadcaster.messageUpdated({
id: "m1",
content: "updated",
} as any);
});
// ws1 attempted send (threw)
expect(ws1.send).toHaveBeenCalledTimes(1);
@@ -84,16 +134,16 @@ describe("createBroadcaster", () => {
expect(broadcaster.clientCount()).toBe(0);
broadcaster.addClient(ws1 as any);
broadcaster.addClient(ws1);
expect(broadcaster.clientCount()).toBe(1);
broadcaster.addClient(ws2 as any);
broadcaster.addClient(ws2);
expect(broadcaster.clientCount()).toBe(2);
broadcaster.removeClient(ws1 as any);
broadcaster.removeClient(ws1);
expect(broadcaster.clientCount()).toBe(1);
broadcaster.removeClient(ws2 as any);
broadcaster.removeClient(ws2);
expect(broadcaster.clientCount()).toBe(0);
});
@@ -101,11 +151,8 @@ describe("createBroadcaster", () => {
const ws = client();
const broadcaster = createBroadcaster();
broadcaster.addClient(ws as any);
broadcaster.attachmentCreated({
id: "a1",
message_id: "m1",
} as any);
broadcaster.addClient(ws);
broadcaster.attachmentCreated(attachmentRecord());
expect(ws.send).toHaveBeenCalledTimes(1);
const payload = JSON.parse(ws.send.mock.calls[0][0]);