Spaces:
Paused
Paused
File size: 3,606 Bytes
fb4d8fe | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | import crypto from "node:crypto";
import { describe, expect, it, vi } from "vitest";
import { createLineWebhookMiddleware } from "./webhook.js";
const sign = (body: string, secret: string) =>
crypto.createHmac("SHA256", secret).update(body).digest("base64");
const createRes = () => {
const res = {
status: vi.fn(),
json: vi.fn(),
headersSent: false,
} as any;
res.status.mockReturnValue(res);
res.json.mockReturnValue(res);
return res;
};
describe("createLineWebhookMiddleware", () => {
it("parses JSON from raw string body", async () => {
const onEvents = vi.fn(async () => {});
const secret = "secret";
const rawBody = JSON.stringify({ events: [{ type: "message" }] });
const middleware = createLineWebhookMiddleware({ channelSecret: secret, onEvents });
const req = {
headers: { "x-line-signature": sign(rawBody, secret) },
body: rawBody,
} as any;
const res = createRes();
await middleware(req, res, {} as any);
expect(res.status).toHaveBeenCalledWith(200);
expect(onEvents).toHaveBeenCalledWith(expect.objectContaining({ events: expect.any(Array) }));
});
it("parses JSON from raw buffer body", async () => {
const onEvents = vi.fn(async () => {});
const secret = "secret";
const rawBody = JSON.stringify({ events: [{ type: "follow" }] });
const middleware = createLineWebhookMiddleware({ channelSecret: secret, onEvents });
const req = {
headers: { "x-line-signature": sign(rawBody, secret) },
body: Buffer.from(rawBody, "utf-8"),
} as any;
const res = createRes();
await middleware(req, res, {} as any);
expect(res.status).toHaveBeenCalledWith(200);
expect(onEvents).toHaveBeenCalledWith(expect.objectContaining({ events: expect.any(Array) }));
});
it("rejects invalid JSON payloads", async () => {
const onEvents = vi.fn(async () => {});
const secret = "secret";
const rawBody = "not json";
const middleware = createLineWebhookMiddleware({ channelSecret: secret, onEvents });
const req = {
headers: { "x-line-signature": sign(rawBody, secret) },
body: rawBody,
} as any;
const res = createRes();
await middleware(req, res, {} as any);
expect(res.status).toHaveBeenCalledWith(400);
expect(onEvents).not.toHaveBeenCalled();
});
it("rejects webhooks with invalid signatures", async () => {
const onEvents = vi.fn(async () => {});
const secret = "secret";
const rawBody = JSON.stringify({ events: [{ type: "message" }] });
const middleware = createLineWebhookMiddleware({ channelSecret: secret, onEvents });
const req = {
headers: { "x-line-signature": "invalid-signature" },
body: rawBody,
} as any;
const res = createRes();
await middleware(req, res, {} as any);
expect(res.status).toHaveBeenCalledWith(401);
expect(onEvents).not.toHaveBeenCalled();
});
it("rejects webhooks with signatures computed using wrong secret", async () => {
const onEvents = vi.fn(async () => {});
const correctSecret = "correct-secret";
const wrongSecret = "wrong-secret";
const rawBody = JSON.stringify({ events: [{ type: "message" }] });
const middleware = createLineWebhookMiddleware({ channelSecret: correctSecret, onEvents });
const req = {
headers: { "x-line-signature": sign(rawBody, wrongSecret) },
body: rawBody,
} as any;
const res = createRes();
await middleware(req, res, {} as any);
expect(res.status).toHaveBeenCalledWith(401);
expect(onEvents).not.toHaveBeenCalled();
});
});
|