A tool for parsing traffic on the jetstream and applying a moderation workstream based on regexp based rules
1import { beforeEach, describe, expect, it, vi } from "vitest";
2// --- Imports Second ---
3import { checkAccountLabels } from "../accountModeration.js";
4import { agent } from "../agent.js";
5import { createPostLabel } from "../moderation.js";
6import { tryClaimPostLabel } from "../redis.js";
7
8// --- Mocks First ---
9
10vi.mock("../agent.js", () => ({
11 agent: {
12 tools: {
13 ozone: {
14 moderation: {
15 getRepo: vi.fn(),
16 getRecord: vi.fn(),
17 emitEvent: vi.fn(),
18 },
19 },
20 },
21 },
22 isLoggedIn: Promise.resolve(true),
23}));
24
25vi.mock("../redis.js", () => ({
26 tryClaimPostLabel: vi.fn(),
27 tryClaimAccountLabel: vi.fn(),
28}));
29
30vi.mock("../logger.js", () => ({
31 logger: {
32 info: vi.fn(),
33 debug: vi.fn(),
34 warn: vi.fn(),
35 error: vi.fn(),
36 },
37}));
38
39vi.mock("../config.js", () => ({
40 MOD_DID: "did:plc:moderator123",
41}));
42
43vi.mock("../limits.js", () => ({
44 limit: vi.fn((fn) => fn()),
45}));
46
47describe("Moderation Logic", () => {
48 beforeEach(() => {
49 vi.clearAllMocks();
50 });
51
52 describe("checkAccountLabels", () => {
53 it("should return true if label exists on account", async () => {
54 vi.mocked(agent.tools.ozone.moderation.getRepo).mockResolvedValueOnce({
55 data: {
56 labels: [
57 {
58 val: "spam",
59 src: "did:plc:test",
60 uri: "at://test",
61 cts: "2024-01-01T00:00:00Z",
62 },
63 {
64 val: "window-reply",
65 src: "did:plc:test",
66 uri: "at://test",
67 cts: "2024-01-01T00:00:00Z",
68 },
69 ],
70 },
71 } as any);
72 const result = await checkAccountLabels(
73 "did:plc:test123",
74 "window-reply",
75 );
76 expect(result).toBe(true);
77 });
78 });
79
80 describe("createPostLabel with Caching", () => {
81 const URI = "at://did:plc:test/app.bsky.feed.post/123";
82 const CID = "bafybeig6xv5nwph5j7grrlp3pdeolqptpep5nfljmdkmtcf2l4wisa2mfa";
83 const LABEL = "test-label";
84 const COMMENT = "test comment";
85
86 it("should skip if claim fails (already claimed)", async () => {
87 vi.mocked(tryClaimPostLabel).mockResolvedValue(false);
88
89 await createPostLabel(URI, CID, LABEL, COMMENT, undefined);
90
91 expect(vi.mocked(tryClaimPostLabel)).toHaveBeenCalledWith(URI, LABEL);
92 expect(
93 vi.mocked(agent.tools.ozone.moderation.getRecord),
94 ).not.toHaveBeenCalled();
95 expect(
96 vi.mocked(agent.tools.ozone.moderation.emitEvent),
97 ).not.toHaveBeenCalled();
98 });
99
100 it("should skip event if claimed but already labeled via API", async () => {
101 vi.mocked(tryClaimPostLabel).mockResolvedValue(true);
102 vi.mocked(agent.tools.ozone.moderation.getRecord).mockResolvedValue({
103 data: {
104 labels: [
105 {
106 val: LABEL,
107 src: "did:plc:test",
108 uri: URI,
109 cts: "2024-01-01T00:00:00Z",
110 },
111 ],
112 },
113 } as any);
114
115 await createPostLabel(URI, CID, LABEL, COMMENT, undefined);
116
117 expect(vi.mocked(tryClaimPostLabel)).toHaveBeenCalledWith(URI, LABEL);
118 expect(
119 vi.mocked(agent.tools.ozone.moderation.getRecord),
120 ).toHaveBeenCalledWith({ uri: URI }, expect.any(Object));
121 expect(
122 vi.mocked(agent.tools.ozone.moderation.emitEvent),
123 ).not.toHaveBeenCalled();
124 });
125
126 it("should emit event if claimed and not labeled anywhere", async () => {
127 vi.mocked(tryClaimPostLabel).mockResolvedValue(true);
128 vi.mocked(agent.tools.ozone.moderation.getRecord).mockResolvedValue({
129 data: { labels: [] },
130 } as any);
131 vi.mocked(agent.tools.ozone.moderation.emitEvent).mockResolvedValue({
132 success: true,
133 } as any);
134
135 await createPostLabel(URI, CID, LABEL, COMMENT, undefined);
136
137 expect(vi.mocked(tryClaimPostLabel)).toHaveBeenCalledWith(URI, LABEL);
138 expect(
139 vi.mocked(agent.tools.ozone.moderation.getRecord),
140 ).toHaveBeenCalledWith({ uri: URI }, expect.any(Object));
141 expect(
142 vi.mocked(agent.tools.ozone.moderation.emitEvent),
143 ).toHaveBeenCalled();
144 });
145 });
146});