mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-01 20:31:19 +00:00
178 lines
5.5 KiB
TypeScript
178 lines
5.5 KiB
TypeScript
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
import {
|
|
dispatchReplyWithBufferedBlockDispatcher,
|
|
finalizeInboundContextMock,
|
|
registerPluginHttpRouteMock,
|
|
resolveAgentRouteMock,
|
|
} from "./channel.test-mocks.js";
|
|
import { makeFormBody, makeReq, makeRes } from "./test-http-utils.js";
|
|
|
|
type RegisteredRoute = {
|
|
path: string;
|
|
accountId: string;
|
|
handler: (req: IncomingMessage, res: ServerResponse) => Promise<void>;
|
|
};
|
|
|
|
const { createSynologyChatPlugin } = await import("./channel.js");
|
|
describe("Synology channel wiring integration", () => {
|
|
beforeEach(() => {
|
|
registerPluginHttpRouteMock.mockClear();
|
|
dispatchReplyWithBufferedBlockDispatcher.mockClear();
|
|
finalizeInboundContextMock.mockClear();
|
|
resolveAgentRouteMock.mockClear();
|
|
});
|
|
|
|
it("registers real webhook handler with resolved account config and enforces allowlist", async () => {
|
|
const plugin = createSynologyChatPlugin();
|
|
const abortController = new AbortController();
|
|
const ctx = {
|
|
cfg: {
|
|
channels: {
|
|
"synology-chat": {
|
|
enabled: true,
|
|
accounts: {
|
|
alerts: {
|
|
enabled: true,
|
|
token: "valid-token",
|
|
incomingUrl: "https://nas.example.com/incoming",
|
|
webhookPath: "/webhook/synology-alerts",
|
|
dmPolicy: "allowlist",
|
|
allowedUserIds: ["456"],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
accountId: "alerts",
|
|
log: { info: vi.fn(), warn: vi.fn(), error: vi.fn() },
|
|
abortSignal: abortController.signal,
|
|
};
|
|
|
|
const started = plugin.gateway.startAccount(ctx);
|
|
expect(registerPluginHttpRouteMock).toHaveBeenCalledTimes(1);
|
|
|
|
const firstCall = registerPluginHttpRouteMock.mock.calls[0];
|
|
expect(firstCall).toBeTruthy();
|
|
if (!firstCall) throw new Error("Expected registerPluginHttpRoute to be called");
|
|
const registered = firstCall[0];
|
|
expect(registered.path).toBe("/webhook/synology-alerts");
|
|
expect(registered.accountId).toBe("alerts");
|
|
|
|
const req = makeReq(
|
|
"POST",
|
|
makeFormBody({
|
|
token: "valid-token",
|
|
user_id: "123",
|
|
username: "unauthorized-user",
|
|
text: "Hello",
|
|
}),
|
|
);
|
|
const res = makeRes();
|
|
await registered.handler(req, res);
|
|
|
|
expect(res._status).toBe(403);
|
|
expect(res._body).toContain("not authorized");
|
|
expect(dispatchReplyWithBufferedBlockDispatcher).not.toHaveBeenCalled();
|
|
abortController.abort();
|
|
await started;
|
|
});
|
|
|
|
it("isolates same user_id across different accounts", async () => {
|
|
const plugin = createSynologyChatPlugin();
|
|
const alphaAbortController = new AbortController();
|
|
const betaAbortController = new AbortController();
|
|
const cfg = {
|
|
channels: {
|
|
"synology-chat": {
|
|
enabled: true,
|
|
accounts: {
|
|
alpha: {
|
|
enabled: true,
|
|
token: "token-alpha",
|
|
incomingUrl: "https://nas.example.com/incoming-alpha",
|
|
webhookPath: "/webhook/synology-alpha",
|
|
dmPolicy: "open",
|
|
},
|
|
beta: {
|
|
enabled: true,
|
|
token: "token-beta",
|
|
incomingUrl: "https://nas.example.com/incoming-beta",
|
|
webhookPath: "/webhook/synology-beta",
|
|
dmPolicy: "open",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
session: {
|
|
dmScope: "main",
|
|
},
|
|
};
|
|
|
|
const alphaStarted = plugin.gateway.startAccount({
|
|
cfg,
|
|
accountId: "alpha",
|
|
log: { info: vi.fn(), warn: vi.fn(), error: vi.fn() },
|
|
abortSignal: alphaAbortController.signal,
|
|
});
|
|
const betaStarted = plugin.gateway.startAccount({
|
|
cfg,
|
|
accountId: "beta",
|
|
log: { info: vi.fn(), warn: vi.fn(), error: vi.fn() },
|
|
abortSignal: betaAbortController.signal,
|
|
});
|
|
|
|
expect(registerPluginHttpRouteMock).toHaveBeenCalledTimes(2);
|
|
const alphaRoute = registerPluginHttpRouteMock.mock.calls[0]?.[0];
|
|
const betaRoute = registerPluginHttpRouteMock.mock.calls[1]?.[0];
|
|
if (!alphaRoute || !betaRoute) {
|
|
throw new Error("Expected both Synology Chat routes to register");
|
|
}
|
|
|
|
const alphaReq = makeReq(
|
|
"POST",
|
|
makeFormBody({
|
|
token: "token-alpha",
|
|
user_id: "123",
|
|
username: "alice",
|
|
text: "alpha secret",
|
|
}),
|
|
);
|
|
const alphaRes = makeRes();
|
|
await alphaRoute.handler(alphaReq, alphaRes);
|
|
|
|
const betaReq = makeReq(
|
|
"POST",
|
|
makeFormBody({
|
|
token: "token-beta",
|
|
user_id: "123",
|
|
username: "bob",
|
|
text: "beta secret",
|
|
}),
|
|
);
|
|
const betaRes = makeRes();
|
|
await betaRoute.handler(betaReq, betaRes);
|
|
|
|
expect(alphaRes._status).toBe(204);
|
|
expect(betaRes._status).toBe(204);
|
|
expect(dispatchReplyWithBufferedBlockDispatcher).toHaveBeenCalledTimes(2);
|
|
expect(finalizeInboundContextMock).toHaveBeenCalledTimes(2);
|
|
|
|
const alphaCtx = finalizeInboundContextMock.mock.calls[0]?.[0];
|
|
const betaCtx = finalizeInboundContextMock.mock.calls[1]?.[0];
|
|
expect(alphaCtx).toMatchObject({
|
|
AccountId: "alpha",
|
|
SessionKey: "agent:agent-alpha:synology-chat:alpha:direct:123",
|
|
});
|
|
expect(betaCtx).toMatchObject({
|
|
AccountId: "beta",
|
|
SessionKey: "agent:agent-beta:synology-chat:beta:direct:123",
|
|
});
|
|
|
|
alphaAbortController.abort();
|
|
betaAbortController.abort();
|
|
await alphaStarted;
|
|
await betaStarted;
|
|
});
|
|
});
|