mswjs / interceptors

Low-level network interception library.
https://npm.im/@mswjs/interceptors
MIT License
539 stars 123 forks source link

Issue with native fetch introduce after 0.25.1? #574

Closed lox closed 3 months ago

lox commented 4 months ago

I have been trying to upgrade to the latest 0.29.1 from 0.25.1 and running into a variety of issues that I am trying to isolate down top the smallest repros that I can. This is the first oddity I have encountered.

Running:

@mswjs/interceptors 0.25.1
node v20.5.1
import { FetchInterceptor } from '@mswjs/interceptors/fetch'
import http from 'http'

const interceptor = new FetchInterceptor()
interceptor.apply()

interceptor.on('request', ({ request, requestId }) => {
  request.respondWith(
    new Response('Intercepted', {
      status: 200,
      statusText: 'OK',
    })
  )
})

const server = http.createServer((req, res) => {
  res.statusCode = 200
  res.setHeader('Content-Type', 'text/plain')
  res.end('hello world')
})

server.listen(3000, '127.0.0.1', () => {
  console.log('Server running at http://127.0.0.1:3000/')
})

const response = await fetch('http://localhost:3000/', {
  method: 'GET',
})

console.log(await response.text())

interceptor.dispose()
server.close()

On 0.25.1 this returned:

❯ node scripts/testHttpIntercept.js
Server running at http://127.0.0.1:3000/
Intercepted

On 0.29.1 this runs, but hangs on:

❯ node scripts/testHttpIntercept.js
Intercepted
Server running at http://127.0.0.1:3000/

Am I missing something very basic here?

lox commented 4 months ago

I've tested and this works as expected up to v0.28.4, it breaks in https://github.com/mswjs/interceptors/releases/tag/v0.29.0

kettanaito commented 4 months ago

Please also make sure to update Undici to the latest version. There were bugs that broke your code in unapparent ways.

I will take a look at this once I have a minute.

kettanaito commented 3 months ago

I've just tried this on the latest version of Interceptors and the test passes without issues:

import http from 'node:http'
import { beforeAll, afterEach, afterAll, it, expect } from 'vitest'
import { FetchInterceptor } from '../../../../src/interceptors/fetch'

const httpServer = http.createServer((req, res) => {
  res.statusCode = 200
  res.setHeader('Content-Type', 'text/plain')
  res.end('hello world')
})

const interceptor = new FetchInterceptor()

beforeAll(async () => {
  interceptor.apply()

  await new Promise<void>((resolve) => {
    httpServer.listen(3000, 'localhost', resolve)
  })
})

afterEach(() => {
  interceptor.removeAllListeners()
})

afterAll(async () => {
  interceptor.dispose()
  await new Promise<void>((resolve, reject) => {
    httpServer.close((error) => {
      if (error) reject(error)
      resolve()
    })
  })
})

it('responds to a fetch request', async () => {
  interceptor.on('request', ({ request }) => {
    request.respondWith(new Response('mock'))
  })

  const response = await fetch('http://localhost:3000/')

  expect(response.status).toBe(200)
  await expect(response.text()).resolves.toBe('mock')
})

This is the exact test case from above + a proper test setup to spawn the server/apply the interceptor properly in test (using the hooks).

Update to the latest version of the library, the issue seems to have been fixed. If not, please provide a reproduction repository I can clone and run. Thanks!