-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.queue.presenter.test.ts
More file actions
161 lines (140 loc) · 5.81 KB
/
Copy pathindex.queue.presenter.test.ts
File metadata and controls
161 lines (140 loc) · 5.81 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
import { beforeEach, describe, expect, it, vi } from "vitest";
async function loadMonitoredMessageHandler(setupAdditionalMocks?: () => Promise<void>) {
let capturedOptions: { onMonitoredMessage?: (message: any) => Promise<void> } | null = null;
await vi.doMock("../src/discord/client.js", async () => {
class MockDiscordBot {
constructor(options: any) {
capturedOptions = options;
}
async start(): Promise<void> {
return;
}
}
return { DiscordBot: MockDiscordBot };
});
const processOnSpy = vi.spyOn(process, "on").mockImplementation(() => process as any);
try {
if (setupAdditionalMocks) await setupAdditionalMocks();
await import("../src/index.js");
} finally {
processOnSpy.mockRestore();
}
if (!capturedOptions) throw new Error("Failed to capture onMonitoredMessage handler");
const handler = (capturedOptions as any).onMonitoredMessage;
if (typeof handler !== "function") throw new Error("Failed to capture onMonitoredMessage handler");
return handler as (message: any) => Promise<void>;
}
describe("QueuePresenter lifecycle (crawl command)", () => {
beforeEach(() => {
vi.clearAllMocks();
vi.resetModules();
delete process.env.OB_CLI_PATH;
});
it("posts a status message, updates it on success, and clears it", async () => {
const runQueueCommandMock = vi.fn(async (url: string, _opts?: any) => ({ success: true, url, id: 1 }));
const onMonitoredMessage = await loadMonitoredMessageHandler(async () => {
await vi.doMock("../src/bot/cli-runner.js", async (importOriginal) => {
const actual: any = await importOriginal();
return {
...actual,
runAddCommand: vi.fn(),
runQueueCommand: runQueueCommandMock,
runSummaryCommand: vi.fn(),
runStatsCommand: vi.fn(async () => ({ totalLinks: 0, processedCount: 0, pendingCount: 0, failedCount: 0 })),
isCliAvailable: vi.fn(async () => true),
CliRunnerError: actual.CliRunnerError,
};
});
});
const replies: string[] = [];
// Simulate a posted status message object with edit/delete
const statusMessage: any = {
id: "status-1",
content: "",
edit: vi.fn(async (text: string) => { statusMessage.content = String(text); }),
delete: vi.fn(async () => { statusMessage.deleted = true; }),
};
let replyCallCount = 0;
const message: any = {
content: "crawl https://crawl.example/start",
author: { id: "author-1" },
id: "message-1",
channelId: "channel-1",
client: { user: { id: "bot-user-1" } },
react: vi.fn(async (_emoji: string) => undefined),
reply: vi.fn(async (text: string) => {
replies.push(String(text));
replyCallCount++;
// First reply (status) returns a message-like object so the presenter can edit it
if (replyCallCount === 1) return statusMessage;
// subsequent replies return a plain value
return undefined;
}),
startThread: vi.fn(),
reactions: { cache: new Map<string, any>() },
};
await onMonitoredMessage(message);
expect(runQueueCommandMock).toHaveBeenCalledWith("https://crawl.example/start", {
channelId: "channel-1",
messageId: "message-1",
authorId: "author-1",
});
// Expect initial status posted and then a queued reply
expect(replies.length).toBeGreaterThanOrEqual(1);
// Verify presenter attempted to edit the initially posted status to reflect queued state
expect(statusMessage.edit).toHaveBeenCalled();
// And the presenter cleared (deleted) the status message
expect(statusMessage.delete).toHaveBeenCalled();
// Reaction updates occurred
expect(message.react).toHaveBeenCalledWith("👀");
expect(message.react).toHaveBeenCalledWith("✅");
});
it("posts a status message, updates it on failure, and clears it", async () => {
const runQueueCommandMock = vi.fn(async (url: string, _opts?: any) => ({ success: false, url, error: "boom" }));
const onMonitoredMessage = await loadMonitoredMessageHandler(async () => {
await vi.doMock("../src/bot/cli-runner.js", async (importOriginal) => {
const actual: any = await importOriginal();
return {
...actual,
runAddCommand: vi.fn(),
runQueueCommand: runQueueCommandMock,
runSummaryCommand: vi.fn(),
runStatsCommand: vi.fn(async () => ({ totalLinks: 0, processedCount: 0, pendingCount: 0, failedCount: 0 })),
isCliAvailable: vi.fn(async () => true),
CliRunnerError: actual.CliRunnerError,
};
});
});
const replies: string[] = [];
const statusMessage: any = {
id: "status-2",
content: "",
edit: vi.fn(async (text: string) => { statusMessage.content = String(text); }),
delete: vi.fn(async () => { statusMessage.deleted = true; }),
};
let replyCallCount = 0;
const message: any = {
content: "crawl https://crawl.example/start",
author: { id: "author-1" },
id: "message-2",
channelId: "channel-1",
client: { user: { id: "bot-user-1" } },
react: vi.fn(async (_emoji: string) => undefined),
reply: vi.fn(async (text: string) => {
replies.push(String(text));
replyCallCount++;
if (replyCallCount === 1) return statusMessage;
return undefined;
}),
startThread: vi.fn(),
reactions: { cache: new Map<string, any>() },
};
await onMonitoredMessage(message);
expect(runQueueCommandMock).toHaveBeenCalled();
// Presenter should have edited the initial status to include failure note
expect(statusMessage.edit).toHaveBeenCalled();
expect(statusMessage.delete).toHaveBeenCalled();
expect(message.react).toHaveBeenCalledWith("👀");
expect(message.react).toHaveBeenCalledWith("⚠️");
});
});