mirror of https://github.com/openclaw/openclaw.git
test(googlechat): cover webhook auth branches
This commit is contained in:
parent
d38cda5aab
commit
bb8e2fceff
|
|
@ -0,0 +1,167 @@
|
|||
import type { IncomingMessage, ServerResponse } from "node:http";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
const readJsonWebhookBodyOrReject = vi.hoisted(() => vi.fn());
|
||||
const resolveWebhookTargetWithAuthOrReject = vi.hoisted(() => vi.fn());
|
||||
const withResolvedWebhookRequestPipeline = vi.hoisted(() => vi.fn());
|
||||
const verifyGoogleChatRequest = vi.hoisted(() => vi.fn());
|
||||
|
||||
vi.mock("../runtime-api.js", () => ({
|
||||
readJsonWebhookBodyOrReject,
|
||||
resolveWebhookTargetWithAuthOrReject,
|
||||
withResolvedWebhookRequestPipeline,
|
||||
}));
|
||||
|
||||
vi.mock("./auth.js", () => ({
|
||||
verifyGoogleChatRequest,
|
||||
}));
|
||||
|
||||
function createRequest(authorization?: string): IncomingMessage {
|
||||
return {
|
||||
method: "POST",
|
||||
url: "/googlechat",
|
||||
headers: {
|
||||
authorization: authorization ?? "",
|
||||
"content-type": "application/json",
|
||||
},
|
||||
} as IncomingMessage;
|
||||
}
|
||||
|
||||
function createResponse() {
|
||||
const res = {
|
||||
statusCode: 0,
|
||||
headers: {} as Record<string, string>,
|
||||
body: "",
|
||||
setHeader(name: string, value: string) {
|
||||
this.headers[name] = value;
|
||||
},
|
||||
end(payload?: string) {
|
||||
this.body = payload ?? "";
|
||||
return this;
|
||||
},
|
||||
} as unknown as ServerResponse & {
|
||||
headers: Record<string, string>;
|
||||
body: string;
|
||||
};
|
||||
return res;
|
||||
}
|
||||
|
||||
function installSimplePipeline(targets: unknown[]) {
|
||||
withResolvedWebhookRequestPipeline.mockImplementation(
|
||||
async ({ handle, req, res }: Record<string, unknown>) =>
|
||||
await handle({
|
||||
targets,
|
||||
req,
|
||||
res,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
describe("googlechat monitor webhook", () => {
|
||||
it("accepts add-on payloads that carry systemIdToken in the body", async () => {
|
||||
installSimplePipeline([
|
||||
{
|
||||
account: {
|
||||
accountId: "default",
|
||||
config: { appPrincipal: "chat-app" },
|
||||
},
|
||||
runtime: { error: vi.fn() },
|
||||
statusSink: vi.fn(),
|
||||
audienceType: "app-url",
|
||||
audience: "https://example.com/googlechat",
|
||||
},
|
||||
]);
|
||||
readJsonWebhookBodyOrReject.mockResolvedValue({
|
||||
ok: true,
|
||||
value: {
|
||||
commonEventObject: { hostApp: "CHAT" },
|
||||
authorizationEventObject: { systemIdToken: "addon-token" },
|
||||
chat: {
|
||||
eventTime: "2026-03-22T00:00:00.000Z",
|
||||
user: { name: "users/123" },
|
||||
messagePayload: {
|
||||
space: { name: "spaces/AAA" },
|
||||
message: { name: "spaces/AAA/messages/1", text: "hello" },
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
resolveWebhookTargetWithAuthOrReject.mockImplementation(async ({ isMatch, targets }) => {
|
||||
for (const target of targets) {
|
||||
if (await isMatch(target)) {
|
||||
return target;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
verifyGoogleChatRequest.mockResolvedValue({ ok: true });
|
||||
const processEvent = vi.fn(async () => {});
|
||||
|
||||
const { createGoogleChatWebhookRequestHandler } = await import("./monitor-webhook.js");
|
||||
const handler = createGoogleChatWebhookRequestHandler({
|
||||
webhookTargets: new Map(),
|
||||
webhookInFlightLimiter: {} as never,
|
||||
processEvent,
|
||||
});
|
||||
|
||||
const req = createRequest();
|
||||
const res = createResponse();
|
||||
await expect(handler(req, res)).resolves.toBe(true);
|
||||
|
||||
expect(verifyGoogleChatRequest).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
bearer: "addon-token",
|
||||
expectedAddOnPrincipal: "chat-app",
|
||||
}),
|
||||
);
|
||||
expect(processEvent).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
type: "MESSAGE",
|
||||
space: { name: "spaces/AAA" },
|
||||
}),
|
||||
expect.anything(),
|
||||
);
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.headers["Content-Type"]).toBe("application/json");
|
||||
});
|
||||
|
||||
it("rejects missing add-on bearer tokens before dispatch", async () => {
|
||||
installSimplePipeline([
|
||||
{
|
||||
account: {
|
||||
accountId: "default",
|
||||
config: { appPrincipal: "chat-app" },
|
||||
},
|
||||
runtime: { error: vi.fn() },
|
||||
},
|
||||
]);
|
||||
readJsonWebhookBodyOrReject.mockResolvedValue({
|
||||
ok: true,
|
||||
value: {
|
||||
commonEventObject: { hostApp: "CHAT" },
|
||||
chat: {
|
||||
messagePayload: {
|
||||
space: { name: "spaces/AAA" },
|
||||
message: { name: "spaces/AAA/messages/1", text: "hello" },
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const processEvent = vi.fn(async () => {});
|
||||
|
||||
const { createGoogleChatWebhookRequestHandler } = await import("./monitor-webhook.js");
|
||||
const handler = createGoogleChatWebhookRequestHandler({
|
||||
webhookTargets: new Map(),
|
||||
webhookInFlightLimiter: {} as never,
|
||||
processEvent,
|
||||
});
|
||||
|
||||
const req = createRequest();
|
||||
const res = createResponse();
|
||||
await expect(handler(req, res)).resolves.toBe(true);
|
||||
|
||||
expect(processEvent).not.toHaveBeenCalled();
|
||||
expect(res.statusCode).toBe(401);
|
||||
expect(res.body).toBe("unauthorized");
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue