openclaw/extensions/synology-chat/src/channel.integration.test.ts

78 lines
2.5 KiB
TypeScript
Raw Normal View History

import type { IncomingMessage, ServerResponse } from "node:http";
import { beforeEach, describe, expect, it, vi } from "vitest";
2026-03-13 22:56:55 +00:00
import {
dispatchReplyWithBufferedBlockDispatcher,
registerPluginHttpRouteMock,
} 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();
});
it("registers real webhook handler with resolved account config and enforces allowlist", async () => {
const plugin = createSynologyChatPlugin();
fix(synology-chat): prevent restart loop in startAccount (#23074) * fix(synology-chat): prevent restart loop in startAccount startAccount must return a Promise that stays pending while the channel is running. The gateway wraps the return value in Promise.resolve(), and when it resolves, the gateway thinks the channel crashed and auto-restarts with exponential backoff (5s → 10s → 20s..., up to 10 attempts). Replace the synchronous { stop } return with a Promise<void> that resolves only when ctx.abortSignal fires, keeping the channel alive until shutdown. Tested on Synology DS923+ with DSM 7.2 — single startup, no restart loop. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(synology-chat): add type guards for startAccount return value startAccount returns `void | { stop: () => void }` — TypeScript requires a type guard before accessing .stop on the union type. Added proper checks in both integration and unit tests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(synology-chat): use Readable stream in integration test for Windows compat Replace EventEmitter + process.nextTick with Readable stream for request body simulation. The process.nextTick approach caused the test to hang on Windows CI (120s timeout) because events were not reliably delivered to readBody() listeners. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: stabilize synology gateway account lifecycle (#23074) (thanks @druide67) --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-03-02 21:06:16 +01:00
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() },
fix(synology-chat): prevent restart loop in startAccount (#23074) * fix(synology-chat): prevent restart loop in startAccount startAccount must return a Promise that stays pending while the channel is running. The gateway wraps the return value in Promise.resolve(), and when it resolves, the gateway thinks the channel crashed and auto-restarts with exponential backoff (5s → 10s → 20s..., up to 10 attempts). Replace the synchronous { stop } return with a Promise<void> that resolves only when ctx.abortSignal fires, keeping the channel alive until shutdown. Tested on Synology DS923+ with DSM 7.2 — single startup, no restart loop. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(synology-chat): add type guards for startAccount return value startAccount returns `void | { stop: () => void }` — TypeScript requires a type guard before accessing .stop on the union type. Added proper checks in both integration and unit tests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(synology-chat): use Readable stream in integration test for Windows compat Replace EventEmitter + process.nextTick with Readable stream for request body simulation. The process.nextTick approach caused the test to hang on Windows CI (120s timeout) because events were not reliably delivered to readBody() listeners. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: stabilize synology gateway account lifecycle (#23074) (thanks @druide67) --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-03-02 21:06:16 +01:00
abortSignal: abortController.signal,
};
fix(synology-chat): prevent restart loop in startAccount (#23074) * fix(synology-chat): prevent restart loop in startAccount startAccount must return a Promise that stays pending while the channel is running. The gateway wraps the return value in Promise.resolve(), and when it resolves, the gateway thinks the channel crashed and auto-restarts with exponential backoff (5s → 10s → 20s..., up to 10 attempts). Replace the synchronous { stop } return with a Promise<void> that resolves only when ctx.abortSignal fires, keeping the channel alive until shutdown. Tested on Synology DS923+ with DSM 7.2 — single startup, no restart loop. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(synology-chat): add type guards for startAccount return value startAccount returns `void | { stop: () => void }` — TypeScript requires a type guard before accessing .stop on the union type. Added proper checks in both integration and unit tests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(synology-chat): use Readable stream in integration test for Windows compat Replace EventEmitter + process.nextTick with Readable stream for request body simulation. The process.nextTick approach caused the test to hang on Windows CI (120s timeout) because events were not reliably delivered to readBody() listeners. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: stabilize synology gateway account lifecycle (#23074) (thanks @druide67) --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-03-02 21:06:16 +01:00
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");
expect(typeof registered.handler).toBe("function");
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();
fix(synology-chat): prevent restart loop in startAccount (#23074) * fix(synology-chat): prevent restart loop in startAccount startAccount must return a Promise that stays pending while the channel is running. The gateway wraps the return value in Promise.resolve(), and when it resolves, the gateway thinks the channel crashed and auto-restarts with exponential backoff (5s → 10s → 20s..., up to 10 attempts). Replace the synchronous { stop } return with a Promise<void> that resolves only when ctx.abortSignal fires, keeping the channel alive until shutdown. Tested on Synology DS923+ with DSM 7.2 — single startup, no restart loop. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(synology-chat): add type guards for startAccount return value startAccount returns `void | { stop: () => void }` — TypeScript requires a type guard before accessing .stop on the union type. Added proper checks in both integration and unit tests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(synology-chat): use Readable stream in integration test for Windows compat Replace EventEmitter + process.nextTick with Readable stream for request body simulation. The process.nextTick approach caused the test to hang on Windows CI (120s timeout) because events were not reliably delivered to readBody() listeners. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: stabilize synology gateway account lifecycle (#23074) (thanks @druide67) --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-03-02 21:06:16 +01:00
abortController.abort();
await started;
});
});