Files
claude-status-webhook/test/queue-consumer.test.js
tiennm99 8c993df72b fix: harden webhook reliability, fix bugs, add test suite
- Statuspage webhook always returns 200 to prevent subscriber removal
- Fix parseKvKey returning string chatId instead of number
- Queue consumer retries on Telegram 5xx instead of acking (prevents message loss)
- Fix observability top-level enabled flag (false → true)
- Add defensive null checks for webhook payload body
- Cache Bot instance per isolate to avoid middleware rebuild per request
- Add vitest + @cloudflare/vitest-pool-workers with 31 tests
- Document DLQ and KV sharding as declined features
2026-04-09 10:29:30 +07:00

80 lines
2.4 KiB
JavaScript

import { describe, it, expect, vi, beforeEach } from "vitest";
import { handleQueue } from "../src/queue-consumer.js";
/**
* Create a mock queue message with ack/retry tracking
*/
function mockMessage(body) {
return {
body,
ack: vi.fn(),
retry: vi.fn(),
};
}
describe("handleQueue", () => {
let env;
beforeEach(() => {
env = {
BOT_TOKEN: "test-token",
claude_status: {
delete: vi.fn(),
},
};
vi.restoreAllMocks();
});
it("acks on successful send", async () => {
vi.stubGlobal("fetch", vi.fn().mockResolvedValue({ ok: true, status: 200 }));
const msg = mockMessage({ chatId: 123, html: "<b>test</b>" });
await handleQueue({ messages: [msg] }, env);
expect(msg.ack).toHaveBeenCalled();
expect(msg.retry).not.toHaveBeenCalled();
});
it("removes subscriber and acks on 403", async () => {
vi.stubGlobal("fetch", vi.fn().mockResolvedValue({ ok: false, status: 403 }));
const msg = mockMessage({ chatId: 123, threadId: null, html: "<b>test</b>" });
await handleQueue({ messages: [msg] }, env);
expect(msg.ack).toHaveBeenCalled();
expect(env.claude_status.delete).toHaveBeenCalled();
});
it("retries on 429 rate limit", async () => {
vi.stubGlobal(
"fetch",
vi.fn().mockResolvedValue({
ok: false,
status: 429,
headers: new Headers({ "Retry-After": "5" }),
})
);
const msg = mockMessage({ chatId: 123, html: "<b>test</b>" });
await handleQueue({ messages: [msg] }, env);
expect(msg.retry).toHaveBeenCalled();
expect(msg.ack).not.toHaveBeenCalled();
});
it("retries on 5xx server error", async () => {
vi.stubGlobal("fetch", vi.fn().mockResolvedValue({ ok: false, status: 502 }));
const msg = mockMessage({ chatId: 123, html: "<b>test</b>" });
await handleQueue({ messages: [msg] }, env);
expect(msg.retry).toHaveBeenCalled();
expect(msg.ack).not.toHaveBeenCalled();
});
it("retries on network error", async () => {
vi.stubGlobal("fetch", vi.fn().mockRejectedValue(new Error("network fail")));
const msg = mockMessage({ chatId: 123, html: "<b>test</b>" });
await handleQueue({ messages: [msg] }, env);
expect(msg.retry).toHaveBeenCalled();
});
it("skips malformed messages", async () => {
const msg = mockMessage({ chatId: null, html: null });
await handleQueue({ messages: [msg] }, env);
expect(msg.ack).toHaveBeenCalled();
});
});