



























@@ -5,6 +5,11 @@ vi.mock("../../../commands/message.js", () => ({
55messageCommand: messageCommandMock,
66}));
778+const getChannelPluginMock = vi.fn();
9+vi.mock("../../../channels/plugins/index.js", () => ({
10+getChannelPlugin: getChannelPluginMock,
11+}));
12+813vi.mock("../../../globals.js", () => ({
914danger: (s: string) => s,
1015setVerbose: vi.fn(),
@@ -70,6 +75,14 @@ async function runSendAction(opts: Record<string, unknown> = {}) {
7075await expect(runMessageAction("send", { ...baseSendOptions, ...opts })).rejects.toThrow("exit");
7176}
727778+function mockChannelExecutionModes(modes: Record<string, "gateway" | "local"> = {}) {
79+getChannelPluginMock.mockImplementation((id: string) => ({
80+actions: {
81+resolveExecutionMode: () => modes[id] ?? "local",
82+},
83+}));
84+}
85+7386function expectNoAccountFieldInPassedOptions() {
7487const passedOpts = (
7588messageCommandMock.mock.calls as unknown as Array<[Record<string, unknown>]>
@@ -84,6 +97,8 @@ function expectNoAccountFieldInPassedOptions() {
8497describe("runMessageAction", () => {
8598beforeEach(() => {
8699vi.clearAllMocks();
100+getChannelPluginMock.mockReset();
101+mockChannelExecutionModes({ telegram: "gateway" });
87102messageCommandMock.mockClear().mockResolvedValue(undefined);
88103hasHooksMock.mockClear().mockReturnValue(false);
89104runGatewayStopMock.mockClear().mockResolvedValue(undefined);
@@ -113,12 +128,152 @@ describe("runMessageAction", () => {
113128});
114129115130it("narrows plugin loading from a channel-prefixed target", async () => {
131+await runSendAction({ channel: undefined, target: "discord:channel:12345" });
132+133+expect(ensurePluginRegistryLoaded).toHaveBeenCalledWith({
134+scope: "configured-channels",
135+onlyChannelIds: ["discord"],
136+});
137+});
138+139+it("skips local plugin preload for any gateway-owned scoped channel action", async () => {
140+mockChannelExecutionModes({ discord: "gateway" });
141+142+await runSendAction({ target: "channel:12345" });
143+144+expect(ensurePluginRegistryLoaded).not.toHaveBeenCalled();
145+expect(messageCommandMock).toHaveBeenCalledWith(
146+expect.objectContaining({
147+action: "send",
148+channel: "discord",
149+target: "channel:12345",
150+message: "hi",
151+}),
152+expect.anything(),
153+expect.anything(),
154+);
155+expect(exitMock).toHaveBeenCalledWith(0);
156+});
157+158+it("keeps broadcast on the local preload path for same-channel prefixed targets", async () => {
159+const runMessageAction = createRunMessageAction();
160+161+await expect(
162+runMessageAction("broadcast", {
163+targets: ["telegram:1", "telegram:2"],
164+message: "hi",
165+}),
166+).rejects.toThrow("exit");
167+168+expect(ensurePluginRegistryLoaded).toHaveBeenCalledWith({
169+scope: "configured-channels",
170+onlyChannelIds: ["telegram"],
171+});
172+expect(messageCommandMock).toHaveBeenCalledWith(
173+expect.objectContaining({
174+action: "broadcast",
175+targets: ["telegram:1", "telegram:2"],
176+message: "hi",
177+}),
178+expect.anything(),
179+expect.anything(),
180+);
181+});
182+183+it("keeps unknown actions on the local preload path", async () => {
184+mockChannelExecutionModes({ discord: "gateway" });
185+const runMessageAction = createRunMessageAction();
186+187+await expect(
188+runMessageAction("custom-action", {
189+ ...baseSendOptions,
190+target: "channel:12345",
191+}),
192+).rejects.toThrow("exit");
193+194+expect(ensurePluginRegistryLoaded).toHaveBeenCalledWith({
195+scope: "configured-channels",
196+onlyChannelIds: ["discord"],
197+});
198+expect(messageCommandMock).toHaveBeenCalledWith(
199+expect.objectContaining({
200+action: "custom-action",
201+}),
202+expect.anything(),
203+expect.anything(),
204+);
205+});
206+207+it("preloads when the scoped channel plugin is not cheaply available", async () => {
208+getChannelPluginMock.mockReturnValue(undefined);
209+210+await runSendAction({ target: "channel:12345" });
211+212+expect(ensurePluginRegistryLoaded).toHaveBeenCalledWith({
213+scope: "configured-channels",
214+onlyChannelIds: ["discord"],
215+});
216+});
217+218+it("keeps target-prefixed Telegram sends from local plugin preload", async () => {
116219await runSendAction({ channel: undefined, target: "telegram:12345" });
117220221+expect(ensurePluginRegistryLoaded).not.toHaveBeenCalled();
222+expect(messageCommandMock).toHaveBeenCalledWith(
223+expect.objectContaining({
224+action: "send",
225+target: "telegram:12345",
226+message: "hi",
227+}),
228+expect.anything(),
229+expect.anything(),
230+);
231+expect(exitMock).toHaveBeenCalledWith(0);
232+});
233+234+it("keeps explicit Telegram sends on the normal command path without local plugin preload", async () => {
235+await runSendAction({
236+channel: "telegram",
237+account: "default",
238+target: "@ops",
239+media: "./diagram.png",
240+presentation: '{"blocks":[{"type":"buttons","buttons":[{"label":"OK","value":"ok"}]}]}',
241+delivery: '{"pin":true}',
242+forceDocument: true,
243+});
244+245+expect(ensurePluginRegistryLoaded).not.toHaveBeenCalled();
246+expect(messageCommandMock).toHaveBeenCalledWith(
247+expect.objectContaining({
248+action: "send",
249+channel: "telegram",
250+accountId: "default",
251+target: "@ops",
252+message: "hi",
253+media: "./diagram.png",
254+presentation: '{"blocks":[{"type":"buttons","buttons":[{"label":"OK","value":"ok"}]}]}',
255+delivery: '{"pin":true}',
256+forceDocument: true,
257+}),
258+expect.anything(),
259+expect.anything(),
260+);
261+expectNoAccountFieldInPassedOptions();
262+expect(exitMock).toHaveBeenCalledWith(0);
263+});
264+265+it("keeps Telegram dry-runs on the local preload path for local validation", async () => {
266+await runSendAction({
267+channel: "telegram",
268+target: "@ops",
269+dryRun: true,
270+});
271+118272expect(ensurePluginRegistryLoaded).toHaveBeenCalledWith({
119273scope: "configured-channels",
120274onlyChannelIds: ["telegram"],
121275});
276+expect(messageCommandMock).toHaveBeenCalled();
122277});
123278124279it("loads configured channel plugins for mixed broadcast target prefixes", async () => {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。