|
| 1 | +import { EventEmitter } from "node:events"; |
1 | 2 | import { H3Event } from "vinxi/http"; |
2 | 3 | import { beforeEach, describe, expect, it, vi } from "vitest"; |
3 | 4 | import { cloneEvent, createFetchEvent, getFetchEvent, mergeResponseHeaders } from "./fetchEvent"; |
@@ -319,3 +320,75 @@ describe("fetchEvent", () => { |
319 | 320 | }); |
320 | 321 | }); |
321 | 322 | }); |
| 323 | + |
| 324 | +describe("request abort signal", () => { |
| 325 | + const createNodeEvent = (request: Request) => { |
| 326 | + const res = Object.assign(new EventEmitter(), { writableEnded: false }); |
| 327 | + const req = { errored: null as Error | null }; |
| 328 | + const event = { |
| 329 | + node: { req, res }, |
| 330 | + context: {}, |
| 331 | + web: { request } |
| 332 | + } as unknown as H3Event; |
| 333 | + mockedVinxiHttp.getWebRequest.mockReturnValue(request); |
| 334 | + return { event, req, res }; |
| 335 | + }; |
| 336 | + |
| 337 | + beforeEach(() => { |
| 338 | + vi.clearAllMocks(); |
| 339 | + }); |
| 340 | + |
| 341 | + it("aborts when the response closes before it was sent", () => { |
| 342 | + const { event, res } = createNodeEvent(new Request("http://localhost/api")); |
| 343 | + const { request } = createFetchEvent(event); |
| 344 | + expect(request.signal.aborted).toBe(false); |
| 345 | + |
| 346 | + res.emit("close"); |
| 347 | + |
| 348 | + expect(request.signal.aborted).toBe(true); |
| 349 | + }); |
| 350 | + |
| 351 | + it("does not abort when the response closes after it was sent", () => { |
| 352 | + const { event, res } = createNodeEvent(new Request("http://localhost/api")); |
| 353 | + const { request } = createFetchEvent(event); |
| 354 | + |
| 355 | + res.writableEnded = true; |
| 356 | + res.emit("close"); |
| 357 | + |
| 358 | + expect(request.signal.aborted).toBe(false); |
| 359 | + }); |
| 360 | + |
| 361 | + it("aborts with the request error when the request failed", () => { |
| 362 | + const { event, req, res } = createNodeEvent(new Request("http://localhost/api")); |
| 363 | + const { request } = createFetchEvent(event); |
| 364 | + const error = new Error("socket hang up"); |
| 365 | + |
| 366 | + req.errored = error; |
| 367 | + res.emit("close"); |
| 368 | + |
| 369 | + expect(request.signal.aborted).toBe(true); |
| 370 | + expect(request.signal.reason).toBe(error); |
| 371 | + }); |
| 372 | + |
| 373 | + it("keeps the abort signal of the platform request", () => { |
| 374 | + const controller = new AbortController(); |
| 375 | + const { event } = createNodeEvent( |
| 376 | + new Request("http://localhost/api", { signal: controller.signal }) |
| 377 | + ); |
| 378 | + const { request } = createFetchEvent(event); |
| 379 | + |
| 380 | + controller.abort(); |
| 381 | + |
| 382 | + expect(request.signal.aborted).toBe(true); |
| 383 | + }); |
| 384 | + |
| 385 | + it("gives later readers the same request, with its body", async () => { |
| 386 | + const { event } = createNodeEvent( |
| 387 | + new Request("http://localhost/api", { method: "POST", body: "hello" }) |
| 388 | + ); |
| 389 | + const { request } = createFetchEvent(event); |
| 390 | + |
| 391 | + expect(event.web!.request).toBe(request); |
| 392 | + expect(await request.text()).toBe("hello"); |
| 393 | + }); |
| 394 | +}); |
0 commit comments