






















@@ -306,6 +306,116 @@ describe("runMessageAction", () => {
306306expect(exitMock).not.toHaveBeenCalledWith(0);
307307});
308308309+it.each([
310+[
311+"poll duration hours",
312+"poll",
313+{
314+channel: "discord",
315+target: "123",
316+pollQuestion: "ship?",
317+pollOption: ["yes", "no"],
318+pollDurationHours: "1.5",
319+},
320+"--poll-duration-hours",
321+],
322+[
323+"poll duration seconds",
324+"poll",
325+{
326+channel: "telegram",
327+target: "123",
328+pollQuestion: "ship?",
329+pollOption: ["yes", "no"],
330+pollDurationSeconds: "60s",
331+},
332+"--poll-duration-seconds",
333+],
334+[
335+"timeout duration",
336+"timeout",
337+{ guildId: "g", userId: "u", durationMin: "5m" },
338+"--duration-min",
339+],
340+["ban delete days", "ban", { guildId: "g", userId: "u", deleteDays: "7d" }, "--delete-days"],
341+["read limit", "read", { channel: "discord", target: "123", limit: "10x" }, "--limit"],
342+["search limit", "search", { guildId: "g", query: "hello", limit: "10x" }, "--limit"],
343+["pins limit", "list-pins", { channel: "discord", target: "123", limit: "10x" }, "--limit"],
344+[
345+"reactions limit",
346+"reactions",
347+{ channel: "discord", target: "123", messageId: "m", limit: "10x" },
348+"--limit",
349+],
350+[
351+"thread auto archive minutes",
352+"thread-create",
353+{
354+channel: "discord",
355+target: "123",
356+threadName: "ops",
357+autoArchiveMin: "60m",
358+},
359+"--auto-archive-min",
360+],
361+["thread list limit", "thread-list", { guildId: "g", limit: "10x" }, "--limit"],
362+])("rejects malformed numeric CLI option for %s", async (_name, action, opts, flag) => {
363+const runMessageAction = createRunMessageAction();
364+365+await expect(runMessageAction(action, opts)).rejects.toThrow("exit");
366+367+const kind = flag === "--delete-days" ? "non-negative" : "positive";
368+expect(errorMock).toHaveBeenCalledWith(`Error: ${flag} must be a ${kind} integer.`);
369+expect(ensurePluginRegistryLoaded).not.toHaveBeenCalled();
370+expect(messageCommandMock).not.toHaveBeenCalled();
371+expect(exitMock).toHaveBeenCalledWith(1);
372+expect(exitMock).not.toHaveBeenCalledWith(0);
373+});
374+375+it.each([
376+["pollDurationHours", "0", "--poll-duration-hours"],
377+["pollDurationSeconds", "-1", "--poll-duration-seconds"],
378+["durationMin", "", "--duration-min"],
379+["deleteDays", Number.NaN, "--delete-days"],
380+["limit", 1.2, "--limit"],
381+["autoArchiveMin", null, "--auto-archive-min"],
382+])("rejects non-positive or non-integer %s values", async (key, value, flag) => {
383+const runMessageAction = createRunMessageAction();
384+385+await expect(
386+runMessageAction("send", {
387+ ...baseSendOptions,
388+[key]: value,
389+}),
390+).rejects.toThrow("exit");
391+392+const kind = flag === "--delete-days" ? "non-negative" : "positive";
393+expect(errorMock).toHaveBeenCalledWith(`Error: ${flag} must be a ${kind} integer.`);
394+expect(messageCommandMock).not.toHaveBeenCalled();
395+expect(exitMock).toHaveBeenCalledWith(1);
396+});
397+398+it("allows zero delete-days for no-history Discord bans", async () => {
399+const runMessageAction = createRunMessageAction();
400+401+await expect(
402+runMessageAction("ban", {
403+guildId: "g",
404+userId: "u",
405+deleteDays: "0",
406+}),
407+).rejects.toThrow("exit");
408+409+expect(errorMock).not.toHaveBeenCalled();
410+expectMessageCommandOptions({
411+action: "ban",
412+guildId: "g",
413+userId: "u",
414+deleteDays: "0",
415+});
416+expect(exitMock).toHaveBeenCalledWith(0);
417+});
418+309419it("runs gateway_stop hooks before exit when registered", async () => {
310420hasHooksMock.mockReturnValueOnce(true);
311421await runSendAction();
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。