




















1+/**
2+ * Regression tests for plugin-registered gateway RPC dispatch (#94127).
3+ */
4+import { afterEach, describe, expect, it, vi } from "vitest";
5+import { createEmptyPluginRegistry } from "../plugins/registry-empty.js";
6+import { resetPluginRuntimeStateForTest, setActivePluginRegistry } from "../plugins/runtime.js";
7+import {
8+createGatewayMethodRegistry,
9+createPluginGatewayMethodDescriptor,
10+} from "./methods/registry.js";
11+import { WRITE_SCOPE } from "./operator-scopes.js";
12+import { handleGatewayRequest } from "./server-methods.js";
13+import type { GatewayRequestHandler } from "./server-methods/types.js";
14+15+describe("handleGatewayRequest plugin gateway dispatch", () => {
16+afterEach(() => {
17+resetPluginRuntimeStateForTest();
18+});
19+20+it("dispatches plugin methods registered after the startup method registry snapshot", async () => {
21+const handler = vi.fn<GatewayRequestHandler>(({ respond }) => {
22+respond(true, { ok: true, ts: 42 });
23+});
24+const activeRegistry = createEmptyPluginRegistry();
25+activeRegistry.gatewayHandlers["demo.ping"] = handler;
26+activeRegistry.gatewayMethodDescriptors.push(
27+createPluginGatewayMethodDescriptor({
28+pluginId: "demo",
29+name: "demo.ping",
30+ handler,
31+scope: WRITE_SCOPE,
32+}),
33+);
34+setActivePluginRegistry(activeRegistry);
35+36+const staleStartupRegistry = createGatewayMethodRegistry([]);
37+const respond = vi.fn();
38+await handleGatewayRequest({
39+req: {
40+type: "req",
41+id: "proof-94127",
42+method: "demo.ping",
43+params: { hello: "world" },
44+},
45+ respond,
46+client: {
47+connId: "conn-proof",
48+connect: {
49+role: "operator",
50+scopes: [WRITE_SCOPE],
51+client: {
52+id: "cli",
53+version: "test",
54+platform: "linux",
55+mode: "cli",
56+},
57+minProtocol: 1,
58+maxProtocol: 1,
59+},
60+},
61+isWebchatConnect: () => false,
62+context: {
63+logGateway: { warn: vi.fn() },
64+} as unknown as Parameters<typeof handleGatewayRequest>[0]["context"],
65+methodRegistry: staleStartupRegistry,
66+});
67+68+expect(handler).toHaveBeenCalledTimes(1);
69+expect(respond).toHaveBeenCalledWith(true, { ok: true, ts: 42 });
70+});
71+72+it("dispatches a method owned by the caller-attached registry even when global state lacks it (#94343)", async () => {
73+const handler = vi.fn<GatewayRequestHandler>(({ respond }) => {
74+respond(true, { ok: true, source: "attached" });
75+});
76+// Active plugin registry does NOT carry the method; only the caller-attached
77+// snapshot owns it, so dispatch must prefer the attached registry.
78+setActivePluginRegistry(createEmptyPluginRegistry());
79+const attachedRegistry = createGatewayMethodRegistry([
80+createPluginGatewayMethodDescriptor({
81+pluginId: "demo",
82+name: "demo.attached",
83+ handler,
84+scope: WRITE_SCOPE,
85+}),
86+]);
87+const respond = vi.fn();
88+await handleGatewayRequest({
89+req: { type: "req", id: "proof-94343", method: "demo.attached", params: {} },
90+ respond,
91+client: {
92+connId: "conn-proof",
93+connect: {
94+role: "operator",
95+scopes: [WRITE_SCOPE],
96+client: { id: "cli", version: "test", platform: "linux", mode: "cli" },
97+minProtocol: 1,
98+maxProtocol: 1,
99+},
100+},
101+isWebchatConnect: () => false,
102+context: {
103+logGateway: { warn: vi.fn() },
104+} as unknown as Parameters<typeof handleGatewayRequest>[0]["context"],
105+methodRegistry: attachedRegistry,
106+});
107+108+expect(handler).toHaveBeenCalledTimes(1);
109+expect(respond).toHaveBeenCalledWith(true, { ok: true, source: "attached" });
110+});
111+112+it("fails closed when neither the attached snapshot nor the live registry owns the method", async () => {
113+const handler = vi.fn<GatewayRequestHandler>();
114+setActivePluginRegistry(createEmptyPluginRegistry());
115+const respond = vi.fn();
116+await handleGatewayRequest({
117+req: { type: "req", id: "proof-unknown", method: "demo.does-not-exist", params: {} },
118+ respond,
119+client: {
120+connId: "conn-proof",
121+connect: {
122+role: "operator",
123+scopes: [WRITE_SCOPE],
124+client: { id: "cli", version: "test", platform: "linux", mode: "cli" },
125+minProtocol: 1,
126+maxProtocol: 1,
127+},
128+},
129+isWebchatConnect: () => false,
130+context: {
131+logGateway: { warn: vi.fn() },
132+} as unknown as Parameters<typeof handleGatewayRequest>[0]["context"],
133+methodRegistry: createGatewayMethodRegistry([]),
134+});
135+136+expect(handler).not.toHaveBeenCalled();
137+const [ok] = respond.mock.calls.at(-1) ?? [];
138+expect(ok).toBe(false);
139+});
140+});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。