test: share googlechat webhook routing helpers

This commit is contained in:
Peter Steinberger
2026-03-13 20:50:09 +00:00
parent d4d0091760
commit 12432ca138

View File

@@ -117,6 +117,34 @@ function registerTwoTargets() {
};
}
async function dispatchWebhookRequest(req: IncomingMessage) {
const res = createMockServerResponse();
const handled = await handleGoogleChatWebhookRequest(req, res);
expect(handled).toBe(true);
return res;
}
async function expectVerifiedRoute(params: {
request: IncomingMessage;
expectedStatus: number;
sinkA: ReturnType<typeof vi.fn>;
sinkB: ReturnType<typeof vi.fn>;
expectedSink: "none" | "A" | "B";
}) {
const res = await dispatchWebhookRequest(params.request);
expect(res.statusCode).toBe(params.expectedStatus);
const expectedCounts =
params.expectedSink === "A" ? [1, 0] : params.expectedSink === "B" ? [0, 1] : [0, 0];
expect(params.sinkA).toHaveBeenCalledTimes(expectedCounts[0]);
expect(params.sinkB).toHaveBeenCalledTimes(expectedCounts[1]);
}
function mockSecondVerifierSuccess() {
vi.mocked(verifyGoogleChatRequest)
.mockResolvedValueOnce({ ok: false, reason: "invalid" })
.mockResolvedValueOnce({ ok: true });
}
describe("Google Chat webhook routing", () => {
afterEach(() => {
setActivePluginRegistry(createEmptyPluginRegistry());
@@ -165,45 +193,37 @@ describe("Google Chat webhook routing", () => {
const { sinkA, sinkB, unregister } = registerTwoTargets();
try {
const res = createMockServerResponse();
const handled = await handleGoogleChatWebhookRequest(
createWebhookRequest({
await expectVerifiedRoute({
request: createWebhookRequest({
authorization: "Bearer test-token",
payload: { type: "ADDED_TO_SPACE", space: { name: "spaces/AAA" } },
}),
res,
);
expect(handled).toBe(true);
expect(res.statusCode).toBe(401);
expect(sinkA).not.toHaveBeenCalled();
expect(sinkB).not.toHaveBeenCalled();
expectedStatus: 401,
sinkA,
sinkB,
expectedSink: "none",
});
} finally {
unregister();
}
});
it("routes to the single verified target when earlier targets fail verification", async () => {
vi.mocked(verifyGoogleChatRequest)
.mockResolvedValueOnce({ ok: false, reason: "invalid" })
.mockResolvedValueOnce({ ok: true });
mockSecondVerifierSuccess();
const { sinkA, sinkB, unregister } = registerTwoTargets();
try {
const res = createMockServerResponse();
const handled = await handleGoogleChatWebhookRequest(
createWebhookRequest({
await expectVerifiedRoute({
request: createWebhookRequest({
authorization: "Bearer test-token",
payload: { type: "ADDED_TO_SPACE", space: { name: "spaces/BBB" } },
}),
res,
);
expect(handled).toBe(true);
expect(res.statusCode).toBe(200);
expect(sinkA).not.toHaveBeenCalled();
expect(sinkB).toHaveBeenCalledTimes(1);
expectedStatus: 200,
sinkA,
sinkB,
expectedSink: "B",
});
} finally {
unregister();
}
@@ -218,10 +238,7 @@ describe("Google Chat webhook routing", () => {
authorization: "Bearer invalid-token",
});
const onSpy = vi.spyOn(req, "on");
const res = createMockServerResponse();
const handled = await handleGoogleChatWebhookRequest(req, res);
expect(handled).toBe(true);
const res = await dispatchWebhookRequest(req);
expect(res.statusCode).toBe(401);
expect(onSpy).not.toHaveBeenCalledWith("data", expect.any(Function));
} finally {
@@ -230,15 +247,12 @@ describe("Google Chat webhook routing", () => {
});
it("supports add-on requests that provide systemIdToken in the body", async () => {
vi.mocked(verifyGoogleChatRequest)
.mockResolvedValueOnce({ ok: false, reason: "invalid" })
.mockResolvedValueOnce({ ok: true });
mockSecondVerifierSuccess();
const { sinkA, sinkB, unregister } = registerTwoTargets();
try {
const res = createMockServerResponse();
const handled = await handleGoogleChatWebhookRequest(
createWebhookRequest({
await expectVerifiedRoute({
request: createWebhookRequest({
payload: {
commonEventObject: { hostApp: "CHAT" },
authorizationEventObject: { systemIdToken: "addon-token" },
@@ -252,13 +266,11 @@ describe("Google Chat webhook routing", () => {
},
},
}),
res,
);
expect(handled).toBe(true);
expect(res.statusCode).toBe(200);
expect(sinkA).not.toHaveBeenCalled();
expect(sinkB).toHaveBeenCalledTimes(1);
expectedStatus: 200,
sinkA,
sinkB,
expectedSink: "B",
});
} finally {
unregister();
}