


























@@ -60,6 +60,20 @@ async function pollQaBus(params: {
6060return (await response.json()) as QaBusPollResult;
6161}
626263+async function postQaBusJson(baseUrl: string, path: string, body: unknown) {
64+return await postQaBusRawJson(baseUrl, path, JSON.stringify(body));
65+}
66+67+async function postQaBusRawJson(baseUrl: string, path: string, body: string) {
68+return await fetch(`${baseUrl}${path}`, {
69+method: "POST",
70+headers: {
71+"content-type": "application/json",
72+},
73+ body,
74+});
75+}
76+6377describe("closeQaHttpServer", () => {
6478it("closes idle keep-alive sockets so suite processes can exit", async () => {
6579const server = createServer((_req, res) => {
@@ -122,6 +136,85 @@ describe("qa-bus server", () => {
122136kind: "inbound-message",
123137});
124138});
139+140+it("rejects malformed poll numeric fields before long-polling", async () => {
141+const state = createQaBusState();
142+const bus = await startQaBusServer({ state });
143+stops.push(bus["stop"]);
144+145+const startedAt = Date.now();
146+const response = await postQaBusJson(bus.baseUrl, "/v1/poll", {
147+accountId: "acct-a",
148+cursor: "999",
149+timeoutMs: 500,
150+});
151+152+expect(Date.now() - startedAt).toBeLessThan(300);
153+expect(response.status).toBe(400);
154+await expect(response.json()).resolves.toEqual({
155+error: "poll cursor must be an integer at least 0.",
156+});
157+});
158+159+it("rejects malformed search limits before querying state", async () => {
160+const state = createQaBusState();
161+const bus = await startQaBusServer({ state });
162+stops.push(bus["stop"]);
163+164+const response = await postQaBusJson(bus.baseUrl, "/v1/actions/search", {
165+limit: "all",
166+query: "anything",
167+});
168+169+expect(response.status).toBe(400);
170+await expect(response.json()).resolves.toEqual({
171+error: "search limit must be an integer at least 1.",
172+});
173+});
174+175+it("keeps oversized numeric poll and search fields bounded", async () => {
176+const state = createQaBusState();
177+const bus = await startQaBusServer({ state });
178+stops.push(bus["stop"]);
179+180+const message = state.addInboundMessage({
181+accountId: "acct-a",
182+conversation: { id: "target", kind: "direct" },
183+senderId: "acct-a-user",
184+text: "bounded numeric fields",
185+});
186+187+const pollResponse = await postQaBusJson(bus.baseUrl, "/v1/poll", {
188+accountId: "acct-a",
189+cursor: 0,
190+limit: 10_000,
191+timeoutMs: 60_000,
192+});
193+expect(pollResponse.status).toBe(200);
194+await expect(pollResponse.json()).resolves.toMatchObject({
195+events: [{ message: { id: message.id } }],
196+});
197+198+const searchResponse = await postQaBusJson(bus.baseUrl, "/v1/actions/search", {
199+accountId: "acct-a",
200+limit: 10_000,
201+query: "bounded",
202+});
203+expect(searchResponse.status).toBe(200);
204+await expect(searchResponse.json()).resolves.toMatchObject({
205+messages: [{ id: message.id }],
206+});
207+208+const extremeSearchResponse = await postQaBusRawJson(
209+bus.baseUrl,
210+"/v1/actions/search",
211+`{"accountId":"acct-a","limit":1e309,"query":"bounded"}`,
212+);
213+expect(extremeSearchResponse.status).toBe(200);
214+await expect(extremeSearchResponse.json()).resolves.toMatchObject({
215+messages: [{ id: message.id }],
216+});
217+});
125218});
126219127220describe("handleQaBusRequest", () => {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。