




















@@ -1,5 +1,6 @@
11import crypto from "node:crypto";
22import type { IncomingMessage, ServerResponse } from "node:http";
3+import type { RuntimeEnv } from "openclaw/plugin-sdk/runtime-env";
34import { createMockIncomingRequest } from "openclaw/plugin-sdk/test-env";
45import { describe, expect, it, vi } from "vitest";
56import { createLineNodeWebhookHandler, readLineWebhookRequestBody } from "./webhook-node.js";
@@ -29,6 +30,20 @@ function createRes() {
29303031const SECRET = "secret";
313233+type RuntimeEnvMock = RuntimeEnv & {
34+error: ReturnType<typeof vi.fn<(...args: unknown[]) => void>>;
35+exit: ReturnType<typeof vi.fn<(code: number) => void>>;
36+log: ReturnType<typeof vi.fn<(...args: unknown[]) => void>>;
37+};
38+39+function createRuntimeMock(): RuntimeEnvMock {
40+return {
41+error: vi.fn<(...args: unknown[]) => void>(),
42+exit: vi.fn<(code: number) => void>(),
43+log: vi.fn<(...args: unknown[]) => void>(),
44+};
45+}
46+3247function createMiddlewareRes() {
3348const res = {
3449status: vi.fn(),
@@ -42,7 +57,7 @@ function createMiddlewareRes() {
42574358function createPostWebhookTestHarness(rawBody: string, secret = "secret") {
4459const bot = { handleWebhook: vi.fn(async () => {}) };
45-const runtime = { log: vi.fn(), error: vi.fn(), exit: vi.fn() };
60+const runtime = createRuntimeMock();
4661const handler = createLineNodeWebhookHandler({
4762channelSecret: secret,
4863 bot,
@@ -71,11 +86,7 @@ async function invokeWebhook(params: {
7186headers?: Record<string, string>;
7287onEvents?: ReturnType<typeof vi.fn>;
7388autoSign?: boolean;
74-runtime?: {
75-log: ReturnType<typeof vi.fn>;
76-error: ReturnType<typeof vi.fn>;
77-exit: ReturnType<typeof vi.fn>;
78-};
89+runtime?: RuntimeEnv;
7990}) {
8091const onEventsMock = params.onEvents ?? vi.fn(async () => {});
8192const middleware = createLineWebhookMiddleware({
@@ -138,7 +149,7 @@ async function invokeNodePostContract(params: {
138149throw params.failWith;
139150}
140151});
141-const runtime = { log: vi.fn(), error: vi.fn(), exit: vi.fn() };
152+const runtime = createRuntimeMock();
142153const handler = createLineNodeWebhookHandler({
143154channelSecret: SECRET,
144155bot: { handleWebhook: dispatched },
@@ -167,7 +178,7 @@ async function invokeMiddlewarePostContract(params: {
167178rawBody: string;
168179signed: boolean;
169180}) {
170-const runtime = { log: vi.fn(), error: vi.fn(), exit: vi.fn() };
181+const runtime = createRuntimeMock();
171182const onEvents = vi.fn(async () => {
172183if (params.failWith) {
173184throw params.failWith;
@@ -182,6 +193,7 @@ async function invokeMiddlewarePostContract(params: {
182193});
183194return {
184195body: res.json.mock.calls.at(-1)?.[0],
196+contentType: undefined,
185197 dispatched,
186198runtimeError: runtime.error,
187199status: res.status.mock.calls.at(-1)?.[0],
@@ -288,7 +300,7 @@ describe("LINE webhook shared POST contract", () => {
288300describe("createLineNodeWebhookHandler", () => {
289301it("returns 200 for GET", async () => {
290302const bot = { handleWebhook: vi.fn(async () => {}) };
291-const runtime = { log: vi.fn(), error: vi.fn(), exit: vi.fn() };
303+const runtime = createRuntimeMock();
292304const handler = createLineNodeWebhookHandler({
293305channelSecret: "secret",
294306 bot,
@@ -305,7 +317,7 @@ describe("createLineNodeWebhookHandler", () => {
305317306318it("returns 204 for HEAD", async () => {
307319const bot = { handleWebhook: vi.fn(async () => {}) };
308-const runtime = { log: vi.fn(), error: vi.fn(), exit: vi.fn() };
320+const runtime = createRuntimeMock();
309321const handler = createLineNodeWebhookHandler({
310322channelSecret: "secret",
311323 bot,
@@ -333,7 +345,7 @@ describe("createLineNodeWebhookHandler", () => {
333345334346it("rejects unsigned POST requests before reading the body", async () => {
335347const bot = { handleWebhook: vi.fn(async () => {}) };
336-const runtime = { log: vi.fn(), error: vi.fn(), exit: vi.fn() };
348+const runtime = createRuntimeMock();
337349const readBody = vi.fn(async () => JSON.stringify({ events: [{ type: "message" }] }));
338350const handler = createLineNodeWebhookHandler({
339351channelSecret: "secret",
@@ -353,7 +365,7 @@ describe("createLineNodeWebhookHandler", () => {
353365it("uses strict pre-auth limits for signed POST requests", async () => {
354366const rawBody = JSON.stringify({ events: [{ type: "message" }] });
355367const bot = { handleWebhook: vi.fn(async () => {}) };
356-const runtime = { log: vi.fn(), error: vi.fn(), exit: vi.fn() };
368+const runtime = createRuntimeMock();
357369const readBody = vi.fn(async (_req: IncomingMessage, maxBytes: number, timeoutMs?: number) => {
358370expect(maxBytes).toBe(64 * 1024);
359371expect(timeoutMs).toBe(5_000);
@@ -414,7 +426,7 @@ describe("createLineNodeWebhookHandler", () => {
414426),
415427};
416428const onRequestAuthenticated = vi.fn();
417-const runtime = { log: vi.fn(), error: vi.fn(), exit: vi.fn() };
429+const runtime = createRuntimeMock();
418430const handler = createLineNodeWebhookHandler({
419431channelSecret: SECRET,
420432 bot,
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。