http-bridge.spec.ts 1.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647
  1. import { EventEmitter } from 'node:events'
  2. import { Readable } from 'node:stream'
  3. import type { IncomingMessage, ServerResponse } from 'node:http'
  4. import { describe, expect, it } from 'vitest'
  5. import { bridge } from '../src/http-bridge.ts'
  6. describe('HTTP bridge abort', () => {
  7. it('aborts a pending native picker request when the browser disconnects', async () => {
  8. const body = JSON.stringify({
  9. type: 'client-request', rpcId: 'picker-1', method: 'host.pickDirectory', payload: {},
  10. })
  11. const request = Readable.from([Buffer.from(body)]) as unknown as IncomingMessage
  12. Object.assign(request, {
  13. url: '/api/host.pickDirectory',
  14. method: 'POST',
  15. headers: { 'content-type': 'application/json' },
  16. })
  17. const response = Object.assign(new EventEmitter(), {
  18. writableEnded: false,
  19. writeHead() { return this },
  20. write() { return true },
  21. end() { this.writableEnded = true; return this },
  22. }) as unknown as ServerResponse
  23. let resolveStarted!: () => void
  24. const started = new Promise<void>((resolve) => { resolveStarted = resolve })
  25. let carrierSignal: AbortSignal | undefined
  26. const pending = bridge(request, response, {
  27. fetch: async (input) => {
  28. const fetchRequest = input as Request
  29. carrierSignal = fetchRequest.signal
  30. resolveStarted()
  31. if (!fetchRequest.signal.aborted) {
  32. await new Promise<void>((resolve) => {
  33. fetchRequest.signal.addEventListener('abort', () => { resolve() }, { once: true })
  34. })
  35. }
  36. return Response.json({ aborted: fetchRequest.signal.aborted })
  37. },
  38. })
  39. await started
  40. response.emit('close')
  41. await pending
  42. expect(carrierSignal?.aborted).toBe(true)
  43. })
  44. })