From cf786e54fb4d6db8c534e2bc5537e7bc7d92970e Mon Sep 17 00:00:00 2001
From: Anurag Roy <anurag@2pirad.com>
Date: Sun, 31 Dec 2023 21:00:39 +0530
Subject: [PATCH 1/3] Port first half of tests

---
 package.json                              |   2 +-
 test/fetch/407-statuscode-window-null.js  |   7 +-
 test/fetch/abort.js                       |  31 +-
 test/fetch/abort2.js                      |  15 +-
 test/fetch/about-uri.js                   |  15 +-
 test/fetch/blob-uri.js                    |  57 +--
 test/fetch/bundle.js                      |  28 +-
 test/fetch/client-error-stack-trace.js    |   7 +-
 test/fetch/client-fetch.js                | 305 +++++++------
 test/fetch/client-node-max-header-size.js |  11 +-
 test/fetch/content-length.js              |   9 +-
 test/fetch/cookies.js                     |  24 +-
 test/fetch/data-uri.js                    | 122 +++---
 test/fetch/encoding.js                    |  13 +-
 test/fetch/fetch-leak.js                  |  24 +-
 test/fetch/fetch-timeouts.js              |  20 +-
 test/fetch/file.js                        | 111 +++--
 test/fetch/formdata.js                    | 260 +++++------
 test/fetch/general.js                     |   9 +-
 test/fetch/headers.js                     | 511 ++++++++++------------
 test/fetch/http2.js                       | 181 ++++----
 21 files changed, 827 insertions(+), 935 deletions(-)

diff --git a/package.json b/package.json
index 56d6207f9c0..e94e7371ab5 100644
--- a/package.json
+++ b/package.json
@@ -78,7 +78,7 @@
     "test": "node scripts/generate-pem && npm run test:tap && npm run test:node-fetch && npm run test:fetch && npm run test:cookies && npm run test:wpt && npm run test:websocket && npm run test:jest && npm run test:typescript",
     "test:cookies": "borp --coverage -p \"test/cookie/*.js\"",
     "test:node-fetch": "mocha --exit test/node-fetch",
-    "test:fetch": "npm run build:node && tap --expose-gc test/fetch/*.js && borp --coverage -p \"test/webidl/*.js\"",
+    "test:fetch": "npm run build:node && node --expose-gc --test test/fetch/*.js && borp --coverage -p \"test/webidl/*.js\"",
     "test:jest": "jest",
     "test:tap": "tap test/*.js test/diagnostics-channel/*.js",
     "test:tdd": "tap test/*.js test/diagnostics-channel/*.js -w",
diff --git a/test/fetch/407-statuscode-window-null.js b/test/fetch/407-statuscode-window-null.js
index e22554fac15..4311b0ed96b 100644
--- a/test/fetch/407-statuscode-window-null.js
+++ b/test/fetch/407-statuscode-window-null.js
@@ -3,7 +3,8 @@
 const { fetch } = require('../..')
 const { createServer } = require('http')
 const { once } = require('events')
-const { test } = require('tap')
+const { test } = require('node:test')
+const assert = require('node:assert')
 
 test('Receiving a 407 status code w/ a window option present should reject', async (t) => {
   const server = createServer((req, res) => {
@@ -11,10 +12,10 @@ test('Receiving a 407 status code w/ a window option present should reject', asy
     res.end()
   }).listen(0)
 
-  t.teardown(server.close.bind(server))
+  t.after(server.close.bind(server))
   await once(server, 'listening')
 
   // if init.window exists, the spec tells us to set request.window to 'no-window',
   // which later causes the request to be rejected if the status code is 407
-  await t.rejects(fetch(`http://localhost:${server.address().port}`, { window: null }))
+  await assert.rejects(fetch(`http://localhost:${server.address().port}`, { window: null }))
 })
diff --git a/test/fetch/abort.js b/test/fetch/abort.js
index 367201f3a16..1f819dae6be 100644
--- a/test/fetch/abort.js
+++ b/test/fetch/abort.js
@@ -1,6 +1,8 @@
 'use strict'
 
-const { test } = require('tap')
+const { test } = require('node:test')
+const assert = require('node:assert')
+const { tspl } = require('@matteo.collina/tspl')
 const { fetch } = require('../..')
 const { createServer } = require('http')
 const { once } = require('events')
@@ -17,7 +19,7 @@ test('Allow the usage of custom implementation of AbortController', async (t) =>
     res.end(JSON.stringify(body))
   })
 
-  t.teardown(server.close.bind(server))
+  t.after(server.close.bind(server))
 
   server.listen(0)
   await once(server, 'listening')
@@ -31,43 +33,42 @@ test('Allow the usage of custom implementation of AbortController', async (t) =>
       signal
     })
   } catch (e) {
-    t.equal(e.code, DOMException.ABORT_ERR)
+    assert.strictEqual(e.code, DOMException.ABORT_ERR)
   }
 })
 
 test('allows aborting with custom errors', async (t) => {
   const server = createServer().listen(0)
 
-  t.teardown(server.close.bind(server))
+  t.after(server.close.bind(server))
   await once(server, 'listening')
 
-  t.test('Using AbortSignal.timeout with cause', async (t) => {
-    t.plan(2)
-
+  await t.test('Using AbortSignal.timeout with cause', async () => {
+    const { strictEqual } = tspl(t, { plan: 2 })
     try {
       await fetch(`http://localhost:${server.address().port}`, {
         signal: AbortSignal.timeout(50)
       })
-      t.fail('should throw')
+      assert.fail('should throw')
     } catch (err) {
       if (err.name === 'TypeError') {
         const cause = err.cause
-        t.equal(cause.name, 'HeadersTimeoutError')
-        t.equal(cause.code, 'UND_ERR_HEADERS_TIMEOUT')
+        strictEqual(cause.name, 'HeadersTimeoutError')
+        strictEqual(cause.code, 'UND_ERR_HEADERS_TIMEOUT')
       } else if (err.name === 'TimeoutError') {
-        t.equal(err.code, DOMException.TIMEOUT_ERR)
-        t.equal(err.cause, undefined)
+        strictEqual(err.code, DOMException.TIMEOUT_ERR)
+        strictEqual(err.cause, undefined)
       } else {
-        t.error(err)
+        throw err
       }
     }
   })
 
-  t.test('Error defaults to an AbortError DOMException', async (t) => {
+  t.test('Error defaults to an AbortError DOMException', async () => {
     const ac = new AbortController()
     ac.abort() // no reason
 
-    await t.rejects(
+    await assert.rejects(
       fetch(`http://localhost:${server.address().port}`, {
         signal: ac.signal
       }),
diff --git a/test/fetch/abort2.js b/test/fetch/abort2.js
index b07482d9706..03a58a78baa 100644
--- a/test/fetch/abort2.js
+++ b/test/fetch/abort2.js
@@ -1,6 +1,7 @@
 'use strict'
 
-const { test } = require('tap')
+const { test } = require('node:test')
+const assert = require('node:assert')
 const { fetch } = require('../..')
 const { createServer } = require('http')
 const { once } = require('events')
@@ -18,7 +19,7 @@ test('parallel fetch with the same AbortController works as expected', async (t)
     res.end(JSON.stringify(body))
   })
 
-  t.teardown(server.close.bind(server))
+  t.after(server.close.bind(server))
 
   const abortController = new AbortController()
 
@@ -49,11 +50,9 @@ test('parallel fetch with the same AbortController works as expected', async (t)
     return a
   }, { resolved: [], rejected: [] })
 
-  t.equal(rejected.length, 9) // out of 10 requests, only 1 should succeed
-  t.equal(resolved.length, 1)
+  assert.strictEqual(rejected.length, 9) // out of 10 requests, only 1 should succeed
+  assert.strictEqual(resolved.length, 1)
 
-  t.ok(rejected.every(rej => rej.reason?.code === DOMException.ABORT_ERR))
-  t.same(resolved[0].value, body)
-
-  t.end()
+  assert.ok(rejected.every(rej => rej.reason?.code === DOMException.ABORT_ERR))
+  assert.deepStrictEqual(resolved[0].value, body)
 })
diff --git a/test/fetch/about-uri.js b/test/fetch/about-uri.js
index ac9cbf2be90..fc8ef5f3455 100644
--- a/test/fetch/about-uri.js
+++ b/test/fetch/about-uri.js
@@ -1,21 +1,20 @@
 'use strict'
 
-const { test } = require('tap')
+const { test } = require('node:test')
+const assert = require('node:assert')
 const { fetch } = require('../..')
 
 test('fetching about: uris', async (t) => {
-  t.test('about:blank', async (t) => {
-    await t.rejects(fetch('about:blank'))
+  await t.test('about:blank', async () => {
+    await assert.rejects(fetch('about:blank'))
   })
 
-  t.test('All other about: urls should return an error', async (t) => {
+  await t.test('All other about: urls should return an error', async () => {
     try {
       await fetch('about:config')
-      t.fail('fetching about:config should fail')
+      assert.fail('fetching about:config should fail')
     } catch (e) {
-      t.ok(e, 'this error was expected')
-    } finally {
-      t.end()
+      assert.ok(e, 'this error was expected')
     }
   })
 })
diff --git a/test/fetch/blob-uri.js b/test/fetch/blob-uri.js
index f9db96c36b1..9ba2c5f30ca 100644
--- a/test/fetch/blob-uri.js
+++ b/test/fetch/blob-uri.js
@@ -1,6 +1,7 @@
 'use strict'
 
-const { test } = require('tap')
+const { test } = require('node:test')
+const assert = require('node:assert')
 const { fetch } = require('../..')
 const { Blob } = require('buffer')
 
@@ -16,85 +17,73 @@ test('fetching blob: uris', async (t) => {
     objectURL = URL.createObjectURL(blob)
   })
 
-  t.test('a normal fetch request works', async (t) => {
+  await t.test('a normal fetch request works', async () => {
     const res = await fetch(objectURL)
 
-    t.equal(blobContents, await res.text())
-    t.equal(blob.type, res.headers.get('Content-Type'))
-    t.equal(`${blob.size}`, res.headers.get('Content-Length'))
-    t.end()
+    assert.strictEqual(blobContents, await res.text())
+    assert.strictEqual(blob.type, res.headers.get('Content-Type'))
+    assert.strictEqual(`${blob.size}`, res.headers.get('Content-Length'))
   })
 
-  t.test('non-GET method to blob: fails', async (t) => {
+  await t.test('non-GET method to blob: fails', async () => {
     try {
       await fetch(objectURL, {
         method: 'POST'
       })
-      t.fail('expected POST to blob: uri to fail')
+      assert.fail('expected POST to blob: uri to fail')
     } catch (e) {
-      t.ok(e, 'Got the expected error')
-    } finally {
-      t.end()
+      assert.ok(e, 'Got the expected error')
     }
   })
 
   // https://github.com/web-platform-tests/wpt/blob/7b0ebaccc62b566a1965396e5be7bb2bc06f841f/FileAPI/url/resources/fetch-tests.js#L36-L41
-  t.test('fetching revoked URL should fail', async (t) => {
+  await t.test('fetching revoked URL should fail', async () => {
     URL.revokeObjectURL(objectURL)
 
     try {
       await fetch(objectURL)
-      t.fail('expected revoked blob: url to fail')
+      assert.fail('expected revoked blob: url to fail')
     } catch (e) {
-      t.ok(e, 'Got the expected error')
-    } finally {
-      t.end()
+      assert.ok(e, 'Got the expected error')
     }
   })
 
   // https://github.com/web-platform-tests/wpt/blob/7b0ebaccc62b566a1965396e5be7bb2bc06f841f/FileAPI/url/resources/fetch-tests.js#L28-L34
-  t.test('works with a fragment', async (t) => {
+  await t.test('works with a fragment', async () => {
     const res = await fetch(objectURL + '#fragment')
 
-    t.equal(blobContents, await res.text())
-    t.end()
+    assert.strictEqual(blobContents, await res.text())
   })
 
   // https://github.com/web-platform-tests/wpt/blob/7b0ebaccc62b566a1965396e5be7bb2bc06f841f/FileAPI/url/resources/fetch-tests.js#L52-L56
-  t.test('Appending a query string to blob: url should cause fetch to fail', async (t) => {
+  await t.test('Appending a query string to blob: url should cause fetch to fail', async () => {
     try {
       await fetch(objectURL + '?querystring')
-      t.fail('expected ?querystring blob: url to fail')
+      assert.fail('expected ?querystring blob: url to fail')
     } catch (e) {
-      t.ok(e, 'Got the expected error')
-    } finally {
-      t.end()
+      assert.ok(e, 'Got the expected error')
     }
   })
 
   // https://github.com/web-platform-tests/wpt/blob/7b0ebaccc62b566a1965396e5be7bb2bc06f841f/FileAPI/url/resources/fetch-tests.js#L58-L62
-  t.test('Appending a path should cause fetch to fail', async (t) => {
+  await t.test('Appending a path should cause fetch to fail', async () => {
     try {
       await fetch(objectURL + '/path')
-      t.fail('expected /path blob: url to fail')
+      assert.fail('expected /path blob: url to fail')
     } catch (e) {
-      t.ok(e, 'Got the expected error')
-    } finally {
-      t.end()
+      assert.ok(e, 'Got the expected error')
     }
   })
 
   // https://github.com/web-platform-tests/wpt/blob/7b0ebaccc62b566a1965396e5be7bb2bc06f841f/FileAPI/url/resources/fetch-tests.js#L64-L70
-  t.test('these http methods should fail', async (t) => {
+  await t.test('these http methods should fail', async () => {
     for (const method of ['HEAD', 'POST', 'DELETE', 'OPTIONS', 'PUT', 'CUSTOM']) {
       try {
         await fetch(objectURL, { method })
-        t.fail(`${method} fetch should have failed`)
+        assert.fail(`${method} fetch should have failed`)
       } catch (e) {
-        t.ok(e, `${method} blob url - test succeeded`)
+        assert.ok(e, `${method} blob url - test succeeded`)
       }
     }
-
-    t.end()
   })
 })
diff --git a/test/fetch/bundle.js b/test/fetch/bundle.js
index 93e605bf357..f38b81d583f 100644
--- a/test/fetch/bundle.js
+++ b/test/fetch/bundle.js
@@ -1,26 +1,25 @@
 'use strict'
 
-const { test } = require('tap')
+const { test } = require('node:test')
+const assert = require('node:assert')
 
 const { Response, Request, FormData, Headers } = require('../../undici-fetch')
 
-test('bundle sets constructor.name and .name properly', (t) => {
-  t.equal(new Response().constructor.name, 'Response')
-  t.equal(Response.name, 'Response')
+test('bundle sets constructor.name and .name properly', () => {
+  assert.strictEqual(new Response().constructor.name, 'Response')
+  assert.strictEqual(Response.name, 'Response')
 
-  t.equal(new Request('http://a').constructor.name, 'Request')
-  t.equal(Request.name, 'Request')
+  assert.strictEqual(new Request('http://a').constructor.name, 'Request')
+  assert.strictEqual(Request.name, 'Request')
 
-  t.equal(new Headers().constructor.name, 'Headers')
-  t.equal(Headers.name, 'Headers')
+  assert.strictEqual(new Headers().constructor.name, 'Headers')
+  assert.strictEqual(Headers.name, 'Headers')
 
-  t.equal(new FormData().constructor.name, 'FormData')
-  t.equal(FormData.name, 'FormData')
-
-  t.end()
+  assert.strictEqual(new FormData().constructor.name, 'FormData')
+  assert.strictEqual(FormData.name, 'FormData')
 })
 
-test('regression test for https://github.com/nodejs/node/issues/50263', (t) => {
+test('regression test for https://github.com/nodejs/node/issues/50263', () => {
   const request = new Request('https://a', {
     headers: {
       test: 'abc'
@@ -30,6 +29,5 @@ test('regression test for https://github.com/nodejs/node/issues/50263', (t) => {
 
   const request1 = new Request(request, { body: 'does not matter' })
 
-  t.equal(request1.headers.get('test'), 'abc')
-  t.end()
+  assert.strictEqual(request1.headers.get('test'), 'abc')
 })
diff --git a/test/fetch/client-error-stack-trace.js b/test/fetch/client-error-stack-trace.js
index 7d94aa8b73c..ddc7f4d25cf 100644
--- a/test/fetch/client-error-stack-trace.js
+++ b/test/fetch/client-error-stack-trace.js
@@ -1,6 +1,7 @@
 'use strict'
 
-const { test } = require('tap')
+const { test } = require('node:test')
+const assert = require('node:assert')
 const { fetch } = require('../..')
 const { fetch: fetchIndex } = require('../../index-fetch')
 
@@ -8,7 +9,7 @@ test('FETCH: request errors and prints trimmed stack trace', async (t) => {
   try {
     await fetch('http://a.com')
   } catch (error) {
-    t.match(error.stack, `at Test.<anonymous> (${__filename}`)
+    assert.ok(error.stack.includes(`at async TestContext.<anonymous> (${__filename}`))
   }
 })
 
@@ -16,6 +17,6 @@ test('FETCH-index: request errors and prints trimmed stack trace', async (t) =>
   try {
     await fetchIndex('http://a.com')
   } catch (error) {
-    t.match(error.stack, `at Test.<anonymous> (${__filename}`)
+    assert.ok(error.stack.includes(`at async TestContext.<anonymous> (${__filename}`))
   }
 })
diff --git a/test/fetch/client-fetch.js b/test/fetch/client-fetch.js
index 4046785f795..70e2f6cfc6c 100644
--- a/test/fetch/client-fetch.js
+++ b/test/fetch/client-fetch.js
@@ -2,12 +2,13 @@
 
 'use strict'
 
-const { test, teardown } = require('tap')
+const { test, after } = require('node:test')
+const assert = require('node:assert')
+const { tspl } = require('@matteo.collina/tspl')
 const { createServer } = require('http')
 const { Blob } = require('buffer')
 const { fetch, Response, Request, FormData, File } = require('../..')
 const { Client, setGlobalDispatcher, Agent } = require('../..')
-const { nodeMajor, nodeMinor } = require('../../lib/core/util')
 const nodeFetch = require('../../index-fetch')
 const { once } = require('events')
 const { gzipSync } = require('zlib')
@@ -20,86 +21,90 @@ setGlobalDispatcher(new Agent({
 }))
 
 test('function signature', (t) => {
-  t.plan(2)
+  const { strictEqual } = tspl(t, { plan: 2 })
 
-  t.equal(fetch.name, 'fetch')
-  t.equal(fetch.length, 1)
+  strictEqual(fetch.name, 'fetch')
+  strictEqual(fetch.length, 1)
 })
 
 test('args validation', async (t) => {
-  t.plan(2)
+  const { rejects } = tspl(t, { plan: 2 })
 
-  await t.rejects(fetch(), TypeError)
-  await t.rejects(fetch('ftp://unsupported'), TypeError)
+  await rejects(fetch(), TypeError)
+  await rejects(fetch('ftp://unsupported'), TypeError)
 })
 
-test('request json', (t) => {
-  t.plan(1)
+test('request json', (t, done) => {
+  const { deepStrictEqual } = tspl(t, { plan: 1 })
 
   const obj = { asd: true }
   const server = createServer((req, res) => {
     res.end(JSON.stringify(obj))
   })
-  t.teardown(server.close.bind(server))
+  t.after(server.close.bind(server))
 
   server.listen(0, async () => {
     const body = await fetch(`http://localhost:${server.address().port}`)
-    t.strictSame(obj, await body.json())
+    deepStrictEqual(obj, await body.json())
+    done()
   })
 })
 
-test('request text', (t) => {
-  t.plan(1)
+test('request text', (t, done) => {
+  const { strictEqual } = tspl(t, { plan: 1 })
 
   const obj = { asd: true }
   const server = createServer((req, res) => {
     res.end(JSON.stringify(obj))
   })
-  t.teardown(server.close.bind(server))
+  t.after(server.close.bind(server))
 
   server.listen(0, async () => {
     const body = await fetch(`http://localhost:${server.address().port}`)
-    t.strictSame(JSON.stringify(obj), await body.text())
+    strictEqual(JSON.stringify(obj), await body.text())
+    done()
   })
 })
 
-test('request arrayBuffer', (t) => {
-  t.plan(1)
+test('request arrayBuffer', (t, done) => {
+  const { deepStrictEqual } = tspl(t, { plan: 1 })
 
   const obj = { asd: true }
   const server = createServer((req, res) => {
     res.end(JSON.stringify(obj))
   })
-  t.teardown(server.close.bind(server))
+  t.after(server.close.bind(server))
 
   server.listen(0, async () => {
     const body = await fetch(`http://localhost:${server.address().port}`)
-    t.strictSame(Buffer.from(JSON.stringify(obj)), Buffer.from(await body.arrayBuffer()))
+    deepStrictEqual(Buffer.from(JSON.stringify(obj)), Buffer.from(await body.arrayBuffer()))
+    done()
   })
 })
 
-test('should set type of blob object to the value of the `Content-Type` header from response', (t) => {
-  t.plan(1)
+test('should set type of blob object to the value of the `Content-Type` header from response', (t, done) => {
+  const { strictEqual } = tspl(t, { plan: 1 })
 
   const obj = { asd: true }
   const server = createServer((req, res) => {
     res.setHeader('Content-Type', 'application/json')
     res.end(JSON.stringify(obj))
   })
-  t.teardown(server.close.bind(server))
+  t.after(server.close.bind(server))
 
   server.listen(0, async () => {
     const response = await fetch(`http://localhost:${server.address().port}`)
-    t.equal('application/json', (await response.blob()).type)
+    strictEqual('application/json', (await response.blob()).type)
+    done()
   })
 })
 
-test('pre aborted with readable request body', (t) => {
-  t.plan(2)
+test('pre aborted with readable request body', (t, done) => {
+  const { strictEqual } = tspl(t, { plan: 2 })
 
   const server = createServer((req, res) => {
   })
-  t.teardown(server.close.bind(server))
+  t.after(server.close.bind(server))
 
   server.listen(0, async () => {
     const ac = new AbortController()
@@ -109,33 +114,33 @@ test('pre aborted with readable request body', (t) => {
       method: 'POST',
       body: new ReadableStream({
         async cancel (reason) {
-          t.equal(reason.name, 'AbortError')
+          strictEqual(reason.name, 'AbortError')
         }
       }),
       duplex: 'half'
     }).catch(err => {
-      t.equal(err.name, 'AbortError')
-    })
+      strictEqual(err.name, 'AbortError')
+    }).finally(done)
   })
 })
 
-test('pre aborted with closed readable request body', (t) => {
-  t.plan(2)
+test('pre aborted with closed readable request body', (t, done) => {
+  const { ok, strictEqual } = tspl(t, { plan: 2 })
 
   const server = createServer((req, res) => {
   })
-  t.teardown(server.close.bind(server))
+  t.after(server.close.bind(server))
 
   server.listen(0, async () => {
     const ac = new AbortController()
     ac.abort()
     const body = new ReadableStream({
       async start (c) {
-        t.pass()
+        ok(true)
         c.close()
       },
       async cancel (reason) {
-        t.fail()
+        assert.fail()
       }
     })
     queueMicrotask(() => {
@@ -145,32 +150,34 @@ test('pre aborted with closed readable request body', (t) => {
         body,
         duplex: 'half'
       }).catch(err => {
-        t.equal(err.name, 'AbortError')
-      })
+        strictEqual(err.name, 'AbortError')
+      }).finally(done)
     })
   })
 })
 
-test('unsupported formData 1', (t) => {
-  t.plan(1)
+test('unsupported formData 1', (t, done) => {
+  const { strictEqual } = tspl(t, { plan: 1 })
 
   const server = createServer((req, res) => {
     res.setHeader('content-type', 'asdasdsad')
     res.end()
   })
-  t.teardown(server.close.bind(server))
+  t.after(server.close.bind(server))
 
   server.listen(0, () => {
     fetch(`http://localhost:${server.address().port}`)
       .then(res => res.formData())
       .catch(err => {
-        t.equal(err.name, 'TypeError')
+        strictEqual(err.name, 'TypeError')
       })
+      .finally(done)
   })
 })
 
 test('multipart formdata not base64', async (t) => {
-  t.plan(2)
+  const { strictEqual } = tspl(t, { plan: 2 })
+
   // Construct example form data, with text and blob fields
   const formData = new FormData()
   formData.append('field1', 'value1')
@@ -186,23 +193,21 @@ test('multipart formdata not base64', async (t) => {
     res.write(formRaw)
     res.end()
   })
-  t.teardown(server.close.bind(server))
+  t.after(server.close.bind(server))
 
   const listen = promisify(server.listen.bind(server))
   await listen(0)
 
   const res = await fetch(`http://localhost:${server.address().port}`)
   const form = await res.formData()
-  t.equal(form.get('field1'), 'value1')
+  strictEqual(form.get('field1'), 'value1')
 
   const text = await form.get('field2').text()
-  t.equal(text, 'example\ntext file')
+  strictEqual(text, 'example\ntext file')
 })
 
-// TODO(@KhafraDev): re-enable this test once the issue is fixed
-// See https://github.com/nodejs/node/issues/47301
-test('multipart formdata base64', { skip: nodeMajor >= 19 && nodeMinor >= 8 }, (t) => {
-  t.plan(1)
+test('multipart formdata base64', (t, done) => {
+  const { strictEqual } = tspl(t, { plan: 1 })
 
   // Example form data with base64 encoding
   const data = randomFillSync(Buffer.alloc(256))
@@ -216,7 +221,7 @@ test('multipart formdata base64', { skip: nodeMajor >= 19 && nodeMinor >= 8 }, (
     }
     res.end()
   })
-  t.teardown(server.close.bind(server))
+  t.after(server.close.bind(server))
 
   server.listen(0, () => {
     fetch(`http://localhost:${server.address().port}`)
@@ -224,13 +229,14 @@ test('multipart formdata base64', { skip: nodeMajor >= 19 && nodeMinor >= 8 }, (
       .then(form => form.get('file').arrayBuffer())
       .then(buffer => createHash('sha256').update(Buffer.from(buffer)).digest('base64'))
       .then(digest => {
-        t.equal(createHash('sha256').update(data).digest('base64'), digest)
+        strictEqual(createHash('sha256').update(data).digest('base64'), digest)
       })
+      .finally(done)
   })
 })
 
 test('multipart fromdata non-ascii filed names', async (t) => {
-  t.plan(1)
+  const { strictEqual } = tspl(t, { plan: 1 })
 
   const request = new Request('http://localhost', {
     method: 'POST',
@@ -246,11 +252,11 @@ test('multipart fromdata non-ascii filed names', async (t) => {
   })
 
   const form = await request.formData()
-  t.equal(form.get('fiŝo'), 'value1')
+  strictEqual(form.get('fiŝo'), 'value1')
 })
 
 test('busboy emit error', async (t) => {
-  t.plan(1)
+  const { rejects } = tspl(t, { plan: 1 })
   const formData = new FormData()
   formData.append('field1', 'value1')
 
@@ -262,121 +268,125 @@ test('busboy emit error', async (t) => {
     res.write(formRaw)
     res.end()
   })
-  t.teardown(server.close.bind(server))
+  t.after(server.close.bind(server))
 
   const listen = promisify(server.listen.bind(server))
   await listen(0)
 
   const res = await fetch(`http://localhost:${server.address().port}`)
-  await t.rejects(res.formData(), 'Unexpected end of multipart data')
+  await rejects(res.formData(), 'Unexpected end of multipart data')
 })
 
 // https://github.com/nodejs/undici/issues/2244
 test('parsing formData preserve full path on files', async (t) => {
-  t.plan(1)
+  const { strictEqual } = tspl(t, { plan: 1 })
   const formData = new FormData()
   formData.append('field1', new File(['foo'], 'a/b/c/foo.txt'))
 
   const tempRes = new Response(formData)
   const form = await tempRes.formData()
 
-  t.equal(form.get('field1').name, 'a/b/c/foo.txt')
+  strictEqual(form.get('field1').name, 'a/b/c/foo.txt')
 })
 
-test('urlencoded formData', (t) => {
-  t.plan(2)
+test('urlencoded formData', (t, done) => {
+  const { strictEqual } = tspl(t, { plan: 2 })
 
   const server = createServer((req, res) => {
     res.setHeader('content-type', 'application/x-www-form-urlencoded')
     res.end('field1=value1&field2=value2')
   })
-  t.teardown(server.close.bind(server))
+  t.after(server.close.bind(server))
 
   server.listen(0, () => {
     fetch(`http://localhost:${server.address().port}`)
       .then(res => res.formData())
       .then(formData => {
-        t.equal(formData.get('field1'), 'value1')
-        t.equal(formData.get('field2'), 'value2')
+        strictEqual(formData.get('field1'), 'value1')
+        strictEqual(formData.get('field2'), 'value2')
       })
+      .finally(done)
   })
 })
 
-test('text with BOM', (t) => {
-  t.plan(1)
+test('text with BOM', (t, done) => {
+  const { strictEqual } = tspl(t, { plan: 1 })
 
   const server = createServer((req, res) => {
     res.setHeader('content-type', 'application/x-www-form-urlencoded')
     res.end('\uFEFFtest=\uFEFF')
   })
-  t.teardown(server.close.bind(server))
+  t.after(server.close.bind(server))
 
   server.listen(0, () => {
     fetch(`http://localhost:${server.address().port}`)
       .then(res => res.text())
       .then(text => {
-        t.equal(text, 'test=\uFEFF')
+        strictEqual(text, 'test=\uFEFF')
       })
+      .finally(done)
   })
 })
 
-test('formData with BOM', (t) => {
-  t.plan(1)
+test('formData with BOM', (t, done) => {
+  const { strictEqual } = tspl(t, { plan: 1 })
 
   const server = createServer((req, res) => {
     res.setHeader('content-type', 'application/x-www-form-urlencoded')
     res.end('\uFEFFtest=\uFEFF')
   })
-  t.teardown(server.close.bind(server))
+  t.after(server.close.bind(server))
 
   server.listen(0, () => {
     fetch(`http://localhost:${server.address().port}`)
       .then(res => res.formData())
       .then(formData => {
-        t.equal(formData.get('\uFEFFtest'), '\uFEFF')
+        strictEqual(formData.get('\uFEFFtest'), '\uFEFF')
       })
+      .finally(done)
   })
 })
 
-test('locked blob body', (t) => {
-  t.plan(1)
+test('locked blob body', (t, done) => {
+  const { strictEqual } = tspl(t, { plan: 1 })
 
   const server = createServer((req, res) => {
     res.end()
   })
-  t.teardown(server.close.bind(server))
+  t.after(server.close.bind(server))
 
   server.listen(0, async () => {
     const res = await fetch(`http://localhost:${server.address().port}`)
     const reader = res.body.getReader()
     res.blob().catch(err => {
-      t.equal(err.message, 'Body is unusable')
+      strictEqual(err.message, 'Body is unusable')
       reader.cancel()
-    })
+    }).finally(done)
   })
 })
 
-test('disturbed blob body', (t) => {
-  t.plan(2)
+test('disturbed blob body', (t, done) => {
+  const { ok, strictEqual } = tspl(t, { plan: 2 })
 
   const server = createServer((req, res) => {
     res.end()
   })
-  t.teardown(server.close.bind(server))
+  t.after(server.close.bind(server))
 
   server.listen(0, async () => {
     const res = await fetch(`http://localhost:${server.address().port}`)
-    res.blob().then(() => {
-      t.pass(2)
+    await res.blob().then(() => {
+      ok(true)
     })
-    res.blob().catch(err => {
-      t.equal(err.message, 'Body is unusable')
+    await res.blob().catch(err => {
+      strictEqual(err.message, 'Body is unusable')
     })
+    done()
   })
 })
 
-test('redirect with body', (t) => {
-  t.plan(3)
+test('redirect with body', (t, done) => {
+  const { strictEqual } = tspl(t, { plan: 3 })
 
   let count = 0
   const server = createServer(async (req, res) => {
@@ -384,7 +394,7 @@ test('redirect with body', (t) => {
     for await (const chunk of req) {
       body += chunk
     }
-    t.equal(body, 'asd')
+    strictEqual(body, 'asd')
     if (count++ === 0) {
       res.setHeader('location', 'asd')
       res.statusCode = 302
@@ -393,19 +403,20 @@ test('redirect with body', (t) => {
       res.end(String(count))
     }
   })
-  t.teardown(server.close.bind(server))
+  t.after(server.close.bind(server))
 
   server.listen(0, async () => {
     const res = await fetch(`http://localhost:${server.address().port}`, {
       method: 'PUT',
       body: 'asd'
     })
-    t.equal(await res.text(), '2')
+    strictEqual(await res.text(), '2')
+    done()
   })
 })
 
-test('redirect with stream', (t) => {
-  t.plan(3)
+test('redirect with stream', (t, done) => {
+  const { strictEqual } = tspl(t, { plan: 3 })
 
   const location = '/asd'
   const body = 'hello!'
@@ -420,20 +431,21 @@ test('redirect with stream', (t) => {
       }
     }, 50)
   })
-  t.teardown(server.close.bind(server))
+  t.after(server.close.bind(server))
 
   server.listen(0, async () => {
     const res = await fetch(`http://localhost:${server.address().port}`, {
       redirect: 'manual'
     })
-    t.equal(res.status, 302)
-    t.equal(res.headers.get('location'), location)
-    t.equal(await res.text(), body)
+    strictEqual(res.status, 302)
+    strictEqual(res.headers.get('location'), location)
+    strictEqual(await res.text(), body)
+    done()
   })
 })
 
 test('fail to extract locked body', (t) => {
-  t.plan(1)
+  const { strictEqual } = tspl(t, { plan: 1 })
 
   const stream = new ReadableStream({})
   const reader = stream.getReader()
@@ -441,13 +453,13 @@ test('fail to extract locked body', (t) => {
     // eslint-disable-next-line
     new Response(stream)
   } catch (err) {
-    t.equal(err.name, 'TypeError')
+    strictEqual(err.name, 'TypeError')
   }
   reader.cancel()
 })
 
 test('fail to extract locked body', (t) => {
-  t.plan(1)
+  const { strictEqual } = tspl(t, { plan: 1 })
 
   const stream = new ReadableStream({})
   const reader = stream.getReader()
@@ -459,13 +471,13 @@ test('fail to extract locked body', (t) => {
       keepalive: true
     })
   } catch (err) {
-    t.equal(err.message, 'keepalive')
+    strictEqual(err.message, 'keepalive')
   }
   reader.cancel()
 })
 
-test('post FormData with Blob', (t) => {
-  t.plan(1)
+test('post FormData with Blob', (t, done) => {
+  const { ok } = tspl(t, { plan: 1 })
 
   const body = new FormData()
   body.append('field1', new Blob(['asd1']))
@@ -473,19 +485,20 @@ test('post FormData with Blob', (t) => {
   const server = createServer((req, res) => {
     req.pipe(res)
   })
-  t.teardown(server.close.bind(server))
+  t.after(server.close.bind(server))
 
   server.listen(0, async () => {
     const res = await fetch(`http://localhost:${server.address().port}`, {
       method: 'PUT',
       body
     })
-    t.ok(/asd1/.test(await res.text()))
+    ok(/asd1/.test(await res.text()))
+    done()
   })
 })
 
-test('post FormData with File', (t) => {
-  t.plan(2)
+test('post FormData with File', (t, done) => {
+  const { ok } = tspl(t, { plan: 2 })
 
   const body = new FormData()
   body.append('field1', new File(['asd1'], 'filename123'))
@@ -493,7 +506,7 @@ test('post FormData with File', (t) => {
   const server = createServer((req, res) => {
     req.pipe(res)
   })
-  t.teardown(server.close.bind(server))
+  t.after(server.close.bind(server))
 
   server.listen(0, async () => {
     const res = await fetch(`http://localhost:${server.address().port}`, {
@@ -501,29 +514,30 @@ test('post FormData with File', (t) => {
       body
     })
     const result = await res.text()
-    t.ok(/asd1/.test(result))
-    t.ok(/filename123/.test(result))
+    ok(/asd1/.test(result))
+    ok(/filename123/.test(result))
+    done()
   })
 })
 
 test('invalid url', async (t) => {
-  t.plan(1)
+  const { match } = tspl(t, { plan: 1 })
 
   try {
     await fetch('http://invalid')
   } catch (e) {
-    t.match(e.cause.message, 'invalid')
+    match(e.cause.message, /invalid/)
   }
 })
 
-test('custom agent', (t) => {
-  t.plan(2)
+test('custom agent', (t, done) => {
+  const { ok, deepStrictEqual } = tspl(t, { plan: 2 })
 
   const obj = { asd: true }
   const server = createServer((req, res) => {
     res.end(JSON.stringify(obj))
   })
-  t.teardown(server.close.bind(server))
+  t.after(server.close.bind(server))
 
   server.listen(0, async () => {
     const dispatcher = new Client('http://localhost:' + server.address().port, {
@@ -532,25 +546,26 @@ test('custom agent', (t) => {
     })
     const oldDispatch = dispatcher.dispatch
     dispatcher.dispatch = function (options, handler) {
-      t.pass('custom dispatcher')
+      ok(true)
       return oldDispatch.call(this, options, handler)
     }
-    t.teardown(server.close.bind(server))
+    t.after(server.close.bind(server))
     const body = await fetch(`http://localhost:${server.address().port}`, {
       dispatcher
     })
-    t.strictSame(obj, await body.json())
+    deepStrictEqual(obj, await body.json())
+    done()
   })
 })
 
-test('custom agent node fetch', (t) => {
-  t.plan(2)
+test('custom agent node fetch', (t, done) => {
+  const { ok, deepStrictEqual } = tspl(t, { plan: 2 })
 
   const obj = { asd: true }
   const server = createServer((req, res) => {
     res.end(JSON.stringify(obj))
   })
-  t.teardown(server.close.bind(server))
+  t.after(server.close.bind(server))
 
   server.listen(0, async () => {
     const dispatcher = new Client('http://localhost:' + server.address().port, {
@@ -559,41 +574,43 @@ test('custom agent node fetch', (t) => {
     })
     const oldDispatch = dispatcher.dispatch
     dispatcher.dispatch = function (options, handler) {
-      t.pass('custom dispatcher')
+      ok(true)
       return oldDispatch.call(this, options, handler)
     }
-    t.teardown(server.close.bind(server))
+    t.after(server.close.bind(server))
     const body = await nodeFetch.fetch(`http://localhost:${server.address().port}`, {
       dispatcher
     })
-    t.strictSame(obj, await body.json())
+    deepStrictEqual(obj, await body.json())
+    done()
   })
 })
 
-test('error on redirect', async (t) => {
+test('error on redirect', (t, done) => {
   const server = createServer((req, res) => {
     res.statusCode = 302
     res.end()
   })
-  t.teardown(server.close.bind(server))
+  t.after(server.close.bind(server))
 
   server.listen(0, async () => {
     const errorCause = await fetch(`http://localhost:${server.address().port}`, {
       redirect: 'error'
     }).catch((e) => e.cause)
 
-    t.equal(errorCause.message, 'unexpected redirect')
+    assert.strictEqual(errorCause.message, 'unexpected redirect')
+    done()
   })
 })
 
 // https://github.com/nodejs/undici/issues/1527
 test('fetching with Request object - issue #1527', async (t) => {
   const server = createServer((req, res) => {
-    t.pass()
+    assert.ok(true)
     res.end()
   }).listen(0)
 
-  t.teardown(server.close.bind(server))
+  t.after(server.close.bind(server))
   await once(server, 'listening')
 
   const body = JSON.stringify({ foo: 'bar' })
@@ -602,17 +619,16 @@ test('fetching with Request object - issue #1527', async (t) => {
     body
   })
 
-  await t.resolves(fetch(request))
-  t.end()
+  await assert.doesNotReject(fetch(request))
 })
 
-test('do not decode redirect body', (t) => {
-  t.plan(3)
+test('do not decode redirect body', (t, done) => {
+  const { ok, strictEqual } = tspl(t, { plan: 3 })
 
   const obj = { asd: true }
   const server = createServer((req, res) => {
     if (req.url === '/resource') {
-      t.pass('we redirect')
+      ok(true)
       res.statusCode = 301
       res.setHeader('location', '/resource/')
       // Some dumb http servers set the content-encoding gzip
@@ -621,34 +637,36 @@ test('do not decode redirect body', (t) => {
       res.end()
       return
     }
-    t.pass('actual response')
+    ok(true)
     res.setHeader('content-encoding', 'gzip')
     res.end(gzipSync(JSON.stringify(obj)))
   })
-  t.teardown(server.close.bind(server))
+  t.after(server.close.bind(server))
 
   server.listen(0, async () => {
     const body = await fetch(`http://localhost:${server.address().port}/resource`)
-    t.strictSame(JSON.stringify(obj), await body.text())
+    strictEqual(JSON.stringify(obj), await body.text())
+    done()
   })
 })
 
-test('decode non-redirect body with location header', (t) => {
-  t.plan(2)
+test('decode non-redirect body with location header', (t, done) => {
+  const { ok, strictEqual } = tspl(t, { plan: 2 })
 
   const obj = { asd: true }
   const server = createServer((req, res) => {
-    t.pass('response')
+    ok(true)
     res.statusCode = 201
     res.setHeader('location', '/resource/')
     res.setHeader('content-encoding', 'gzip')
     res.end(gzipSync(JSON.stringify(obj)))
   })
-  t.teardown(server.close.bind(server))
+  t.after(server.close.bind(server))
 
   server.listen(0, async () => {
     const body = await fetch(`http://localhost:${server.address().port}/resource`)
-    t.strictSame(JSON.stringify(obj), await body.text())
+    strictEqual(JSON.stringify(obj), await body.text())
+    done()
   })
 })
 
@@ -669,7 +687,7 @@ test('Receiving non-Latin1 headers', async (t) => {
     res.end()
   }).listen(0)
 
-  t.teardown(server.close.bind(server))
+  t.after(server.close.bind(server))
   await once(server, 'listening')
 
   const url = `http://localhost:${server.address().port}`
@@ -679,9 +697,8 @@ test('Receiving non-Latin1 headers', async (t) => {
     .map(([, v]) => v)
   const lengths = cdHeaders.map(h => h.length)
 
-  t.same(cdHeaders, ContentDisposition)
-  t.same(lengths, [30, 34, 94, 104, 90])
-  t.end()
+  assert.deepStrictEqual(cdHeaders, ContentDisposition)
+  assert.deepStrictEqual(lengths, [30, 34, 94, 104, 90])
 })
 
-teardown(() => process.exit())
+after(() => process.exit())
diff --git a/test/fetch/client-node-max-header-size.js b/test/fetch/client-node-max-header-size.js
index 432a576b97e..260e11ec378 100644
--- a/test/fetch/client-node-max-header-size.js
+++ b/test/fetch/client-node-max-header-size.js
@@ -1,23 +1,22 @@
 'use strict'
 
 const { execSync } = require('node:child_process')
-const { test } = require('tap')
+const { test } = require('node:test')
+const assert = require('node:assert')
 
 const command = 'node -e "require(\'./undici-fetch.js\').fetch(\'https://httpbin.org/get\')"'
 
-test("respect Node.js' --max-http-header-size", async (t) => {
-  t.throws(
+test("respect Node.js' --max-http-header-size", async () => {
+  assert.throws(
     // TODO: Drop the `--unhandled-rejections=throw` once we drop Node.js 14
     () => execSync(`${command} --max-http-header-size=1 --unhandled-rejections=throw`),
     /UND_ERR_HEADERS_OVERFLOW/,
     'max-http-header-size=1 should throw'
   )
 
-  t.doesNotThrow(
+  assert.doesNotThrow(
     () => execSync(command),
     /UND_ERR_HEADERS_OVERFLOW/,
     'default max-http-header-size should not throw'
   )
-
-  t.end()
 })
diff --git a/test/fetch/content-length.js b/test/fetch/content-length.js
index 9264091069b..f4fb966afbc 100644
--- a/test/fetch/content-length.js
+++ b/test/fetch/content-length.js
@@ -1,6 +1,7 @@
 'use strict'
 
-const { test } = require('tap')
+const { test } = require('node:test')
+const assert = require('node:assert')
 const { createServer } = require('http')
 const { once } = require('events')
 const { Blob } = require('buffer')
@@ -10,13 +11,13 @@ const { fetch, FormData } = require('../..')
 test('Content-Length is set when using a FormData body with fetch', async (t) => {
   const server = createServer((req, res) => {
     // TODO: check the length's value once the boundary has a fixed length
-    t.ok('content-length' in req.headers) // request has content-length header
-    t.ok(!Number.isNaN(Number(req.headers['content-length'])))
+    assert.ok('content-length' in req.headers) // request has content-length header
+    assert.ok(!Number.isNaN(Number(req.headers['content-length'])))
     res.end()
   }).listen(0)
 
   await once(server, 'listening')
-  t.teardown(server.close.bind(server))
+  t.after(server.close.bind(server))
 
   const fd = new FormData()
   fd.set('file', new Blob(['hello world 👋'], { type: 'text/plain' }), 'readme.md')
diff --git a/test/fetch/cookies.js b/test/fetch/cookies.js
index 18b001d8644..225aeed4e92 100644
--- a/test/fetch/cookies.js
+++ b/test/fetch/cookies.js
@@ -2,7 +2,9 @@
 
 const { once } = require('events')
 const { createServer } = require('http')
-const { test } = require('tap')
+const { test } = require('node:test')
+const assert = require('node:assert')
+const { tspl } = require('@matteo.collina/tspl')
 const { fetch, Headers } = require('../..')
 
 test('Can receive set-cookie headers from a server using fetch - issue #1262', async (t) => {
@@ -11,29 +13,27 @@ test('Can receive set-cookie headers from a server using fetch - issue #1262', a
     res.end()
   }).listen(0)
 
-  t.teardown(server.close.bind(server))
+  t.after(server.close.bind(server))
   await once(server, 'listening')
 
   const response = await fetch(`http://localhost:${server.address().port}`)
 
-  t.equal(response.headers.get('set-cookie'), 'name=value; Domain=example.com')
+  assert.strictEqual(response.headers.get('set-cookie'), 'name=value; Domain=example.com')
 
   const response2 = await fetch(`http://localhost:${server.address().port}`, {
     credentials: 'include'
   })
 
-  t.equal(response2.headers.get('set-cookie'), 'name=value; Domain=example.com')
-
-  t.end()
+  assert.strictEqual(response2.headers.get('set-cookie'), 'name=value; Domain=example.com')
 })
 
 test('Can send cookies to a server with fetch - issue #1463', async (t) => {
   const server = createServer((req, res) => {
-    t.equal(req.headers.cookie, 'value')
+    assert.strictEqual(req.headers.cookie, 'value')
     res.end()
   }).listen(0)
 
-  t.teardown(server.close.bind(server))
+  t.after(server.close.bind(server))
   await once(server, 'listening')
 
   const headersInit = [
@@ -45,19 +45,17 @@ test('Can send cookies to a server with fetch - issue #1463', async (t) => {
   for (const headers of headersInit) {
     await fetch(`http://localhost:${server.address().port}`, { headers })
   }
-
-  t.end()
 })
 
 test('Cookie header is delimited with a semicolon rather than a comma - issue #1905', async (t) => {
-  t.plan(1)
+  const { strictEqual } = tspl(t, { plan: 1 })
 
   const server = createServer((req, res) => {
-    t.equal(req.headers.cookie, 'FOO=lorem-ipsum-dolor-sit-amet; BAR=the-quick-brown-fox')
+    strictEqual(req.headers.cookie, 'FOO=lorem-ipsum-dolor-sit-amet; BAR=the-quick-brown-fox')
     res.end()
   }).listen(0)
 
-  t.teardown(server.close.bind(server))
+  t.after(server.close.bind(server))
   await once(server, 'listening')
 
   await fetch(`http://localhost:${server.address().port}`, {
diff --git a/test/fetch/data-uri.js b/test/fetch/data-uri.js
index 6191bfe6aa5..de3c44a6eef 100644
--- a/test/fetch/data-uri.js
+++ b/test/fetch/data-uri.js
@@ -1,6 +1,8 @@
 'use strict'
 
-const { test } = require('tap')
+const { test } = require('node:test')
+const assert = require('node:assert')
+const { tspl } = require('@matteo.collina/tspl')
 const {
   URLSerializer,
   collectASequenceOfCodePoints,
@@ -10,62 +12,52 @@ const {
 } = require('../../lib/fetch/dataURL')
 const { fetch } = require('../..')
 
-test('https://url.spec.whatwg.org/#concept-url-serializer', (t) => {
-  t.test('url scheme gets appended', (t) => {
+test('https://url.spec.whatwg.org/#concept-url-serializer', async (t) => {
+  await t.test('url scheme gets appended', () => {
     const url = new URL('https://www.google.com/')
     const serialized = URLSerializer(url)
 
-    t.ok(serialized.startsWith(url.protocol))
-    t.end()
+    assert.ok(serialized.startsWith(url.protocol))
   })
 
-  t.test('non-null url host with authentication', (t) => {
+  await t.test('non-null url host with authentication', () => {
     const url = new URL('https://username:password@google.com')
     const serialized = URLSerializer(url)
 
-    t.ok(serialized.includes(`//${url.username}:${url.password}`))
-    t.ok(serialized.endsWith('@google.com/'))
-    t.end()
+    assert.ok(serialized.includes(`//${url.username}:${url.password}`))
+    assert.ok(serialized.endsWith('@google.com/'))
   })
 
-  t.test('null url host', (t) => {
+  await t.test('null url host', () => {
     for (const url of ['web+demo:/.//not-a-host/', 'web+demo:/path/..//not-a-host/']) {
-      t.equal(
+      assert.strictEqual(
         URLSerializer(new URL(url)),
         'web+demo:/.//not-a-host/'
       )
     }
-
-    t.end()
   })
 
-  t.test('url with query works', (t) => {
-    t.equal(
+  await t.test('url with query works', () => {
+    assert.strictEqual(
       URLSerializer(new URL('https://www.google.com/?fetch=undici')),
       'https://www.google.com/?fetch=undici'
     )
-
-    t.end()
   })
 
-  t.test('exclude fragment', (t) => {
-    t.equal(
+  await t.test('exclude fragment', () => {
+    assert.strictEqual(
       URLSerializer(new URL('https://www.google.com/#frag')),
       'https://www.google.com/#frag'
     )
 
-    t.equal(
+    assert.strictEqual(
       URLSerializer(new URL('https://www.google.com/#frag'), true),
       'https://www.google.com/'
     )
-
-    t.end()
   })
-
-  t.end()
 })
 
-test('https://infra.spec.whatwg.org/#collect-a-sequence-of-code-points', (t) => {
+test('https://infra.spec.whatwg.org/#collect-a-sequence-of-code-points', () => {
   const input = 'text/plain;base64,'
   const position = { position: 0 }
   const result = collectASequenceOfCodePoints(
@@ -74,96 +66,84 @@ test('https://infra.spec.whatwg.org/#collect-a-sequence-of-code-points', (t) =>
     position
   )
 
-  t.strictSame(result, 'text/plain')
-  t.strictSame(position.position, input.indexOf(';'))
-  t.end()
+  assert.strictEqual(result, 'text/plain')
+  assert.strictEqual(position.position, input.indexOf(';'))
 })
 
-test('https://url.spec.whatwg.org/#string-percent-decode', (t) => {
-  t.test('encodes %{2} in range properly', (t) => {
+test('https://url.spec.whatwg.org/#string-percent-decode', async (t) => {
+  await t.test('encodes %{2} in range properly', () => {
     const input = '%FF'
     const percentDecoded = stringPercentDecode(input)
 
-    t.same(percentDecoded, new Uint8Array([255]))
-    t.end()
+    assert.deepStrictEqual(percentDecoded, new Uint8Array([255]))
   })
 
-  t.test('encodes %{2} not in range properly', (t) => {
+  await t.test('encodes %{2} not in range properly', () => {
     const input = 'Hello %XD World'
     const percentDecoded = stringPercentDecode(input)
     const expected = [...input].map(c => c.charCodeAt(0))
 
-    t.same(percentDecoded, expected)
-    t.end()
+    assert.deepStrictEqual(percentDecoded, new Uint8Array(expected))
   })
 
-  t.test('normal string works', (t) => {
+  await t.test('normal string works', () => {
     const input = 'Hello world'
     const percentDecoded = stringPercentDecode(input)
     const expected = [...input].map(c => c.charCodeAt(0))
 
-    t.same(percentDecoded, Uint8Array.from(expected))
-    t.end()
+    assert.deepStrictEqual(percentDecoded, Uint8Array.from(expected))
   })
-
-  t.end()
 })
 
-test('https://mimesniff.spec.whatwg.org/#parse-a-mime-type', (t) => {
-  t.same(parseMIMEType('text/plain'), {
+test('https://mimesniff.spec.whatwg.org/#parse-a-mime-type', () => {
+  assert.deepStrictEqual(parseMIMEType('text/plain'), {
     type: 'text',
     subtype: 'plain',
     parameters: new Map(),
     essence: 'text/plain'
   })
 
-  t.same(parseMIMEType('text/html;charset="shift_jis"iso-2022-jp'), {
+  assert.deepStrictEqual(parseMIMEType('text/html;charset="shift_jis"iso-2022-jp'), {
     type: 'text',
     subtype: 'html',
     parameters: new Map([['charset', 'shift_jis']]),
     essence: 'text/html'
   })
 
-  t.same(parseMIMEType('application/javascript'), {
+  assert.deepStrictEqual(parseMIMEType('application/javascript'), {
     type: 'application',
     subtype: 'javascript',
     parameters: new Map(),
     essence: 'application/javascript'
   })
-
-  t.end()
 })
 
-test('https://fetch.spec.whatwg.org/#collect-an-http-quoted-string', (t) => {
+test('https://fetch.spec.whatwg.org/#collect-an-http-quoted-string', async (t) => {
   // https://fetch.spec.whatwg.org/#example-http-quoted-string
-  t.test('first', (t) => {
+  await t.test('first', () => {
     const position = { position: 0 }
 
-    t.strictSame(collectAnHTTPQuotedString('"\\', {
+    assert.strictEqual(collectAnHTTPQuotedString('"\\', {
       position: 0
     }), '"\\')
-    t.strictSame(collectAnHTTPQuotedString('"\\', position, true), '\\')
-    t.strictSame(position.position, 2)
-    t.end()
+    assert.strictEqual(collectAnHTTPQuotedString('"\\', position, true), '\\')
+    assert.strictEqual(position.position, 2)
   })
 
-  t.test('second', (t) => {
+  await t.test('second', () => {
     const position = { position: 0 }
     const input = '"Hello" World'
 
-    t.strictSame(collectAnHTTPQuotedString(input, {
+    assert.strictEqual(collectAnHTTPQuotedString(input, {
       position: 0
     }), '"Hello"')
-    t.strictSame(collectAnHTTPQuotedString(input, position, true), 'Hello')
-    t.strictSame(position.position, 7)
-    t.end()
+    assert.strictEqual(collectAnHTTPQuotedString(input, position, true), 'Hello')
+    assert.strictEqual(position.position, 7)
   })
-
-  t.end()
 })
 
 // https://github.com/nodejs/undici/issues/1574
-test('too long base64 url', async (t) => {
+test('too long base64 url', async () => {
   const inputStr = 'a'.repeat(1 << 20)
   const base64 = Buffer.from(inputStr).toString('base64')
   const dataURIPrefix = 'data:application/octet-stream;base64,'
@@ -172,43 +152,43 @@ test('too long base64 url', async (t) => {
     const res = await fetch(dataURL)
     const buf = await res.arrayBuffer()
     const outputStr = Buffer.from(buf).toString('ascii')
-    t.same(outputStr, inputStr)
+    assert.strictEqual(outputStr, inputStr)
   } catch (e) {
-    t.fail(`failed to fetch ${dataURL}`)
+    assert.fail(`failed to fetch ${dataURL}`)
   }
 })
 
 test('https://domain.com/#', (t) => {
-  t.plan(1)
+  const { strictEqual } = tspl(t, { plan: 1 })
   const domain = 'https://domain.com/#a'
   const serialized = URLSerializer(new URL(domain))
-  t.equal(serialized, domain)
+  strictEqual(serialized, domain)
 })
 
 test('https://domain.com/?', (t) => {
-  t.plan(1)
+  const { strictEqual } = tspl(t, { plan: 1 })
   const domain = 'https://domain.com/?a=b'
   const serialized = URLSerializer(new URL(domain))
-  t.equal(serialized, domain)
+  strictEqual(serialized, domain)
 })
 
 // https://github.com/nodejs/undici/issues/2474
 test('hash url', (t) => {
-  t.plan(1)
+  const { strictEqual } = tspl(t, { plan: 1 })
   const domain = 'https://domain.com/#a#b'
   const url = new URL(domain)
   const serialized = URLSerializer(url, true)
-  t.equal(serialized, url.href.substring(0, url.href.length - url.hash.length))
+  strictEqual(serialized, url.href.substring(0, url.href.length - url.hash.length))
 })
 
 // https://github.com/nodejs/undici/issues/2474
 test('data url that includes the hash', async (t) => {
-  t.plan(1)
+  const { strictEqual, fail } = tspl(t, { plan: 1 })
   const dataURL = 'data:,node#js#'
   try {
     const res = await fetch(dataURL)
-    t.equal(await res.text(), 'node')
+    strictEqual(await res.text(), 'node')
   } catch (error) {
-    t.fail(`failed to fetch ${dataURL}`)
+    fail(`failed to fetch ${dataURL}`)
   }
 })
diff --git a/test/fetch/encoding.js b/test/fetch/encoding.js
index 75d8fc37d5b..fe9af0dfc25 100644
--- a/test/fetch/encoding.js
+++ b/test/fetch/encoding.js
@@ -1,6 +1,7 @@
 'use strict'
 
-const { test } = require('tap')
+const { test } = require('node:test')
+const assert = require('node:assert')
 const { createServer } = require('http')
 const { once } = require('events')
 const { fetch } = require('../..')
@@ -23,13 +24,13 @@ test('content-encoding header is case-iNsENsITIve', async (t) => {
     brotli.end()
   }).listen(0)
 
-  t.teardown(server.close.bind(server))
+  t.after(server.close.bind(server))
   await once(server, 'listening')
 
   const response = await fetch(`http://localhost:${server.address().port}`)
 
-  t.equal(await response.text(), text)
-  t.equal(response.headers.get('content-encoding'), contentCodings)
+  assert.strictEqual(await response.text(), text)
+  assert.strictEqual(response.headers.get('content-encoding'), contentCodings)
 })
 
 test('response decompression according to content-encoding should be handled in a correct order', async (t) => {
@@ -49,10 +50,10 @@ test('response decompression according to content-encoding should be handled in
     gzip.end()
   }).listen(0)
 
-  t.teardown(server.close.bind(server))
+  t.after(server.close.bind(server))
   await once(server, 'listening')
 
   const response = await fetch(`http://localhost:${server.address().port}`)
 
-  t.equal(await response.text(), text)
+  assert.strictEqual(await response.text(), text)
 })
diff --git a/test/fetch/fetch-leak.js b/test/fetch/fetch-leak.js
index b8e6b16709f..3ea821aaa0f 100644
--- a/test/fetch/fetch-leak.js
+++ b/test/fetch/fetch-leak.js
@@ -1,21 +1,22 @@
 'use strict'
 
-const { test } = require('tap')
+const { test } = require('node:test')
+const assert = require('node:assert')
+const { tspl } = require('@matteo.collina/tspl')
 const { fetch } = require('../..')
 const { createServer } = require('http')
 
-test('do not leak', (t) => {
-  t.plan(1)
-
+test('do not leak', (t, done) => {
+  const { ok } = tspl(t, { plan: 1 })
   const server = createServer((req, res) => {
     res.end()
   })
-  t.teardown(server.close.bind(server))
+  t.after(server.close.bind(server))
 
   let url
-  let done = false
+  let isDone = false
   server.listen(0, function attack () {
-    if (done) {
+    if (isDone) {
       return
     }
     url ??= new URL(`http://127.0.0.1:${server.address().port}`)
@@ -29,16 +30,17 @@ test('do not leak', (t) => {
   let prev = Infinity
   let count = 0
   const interval = setInterval(() => {
-    done = true
+    isDone = true
     global.gc()
     const next = process.memoryUsage().heapUsed
     if (next <= prev) {
-      t.pass()
+      ok(true)
+      done()
     } else if (count++ > 20) {
-      t.fail()
+      assert.fail()
     } else {
       prev = next
     }
   }, 1e3)
-  t.teardown(() => clearInterval(interval))
+  t.after(() => clearInterval(interval))
 })
diff --git a/test/fetch/fetch-timeouts.js b/test/fetch/fetch-timeouts.js
index b659aaa08d6..6fe72be969c 100644
--- a/test/fetch/fetch-timeouts.js
+++ b/test/fetch/fetch-timeouts.js
@@ -1,25 +1,25 @@
 'use strict'
 
-const { test } = require('tap')
+const { test } = require('node:test')
+const { tspl } = require('@matteo.collina/tspl')
 
 const { fetch, Agent } = require('../..')
 const timers = require('../../lib/timers')
 const { createServer } = require('http')
 const FakeTimers = require('@sinonjs/fake-timers')
 
-test('Fetch very long request, timeout overridden so no error', (t) => {
+test('Fetch very long request, timeout overridden so no error', (t, done) => {
   const minutes = 6
   const msToDelay = 1000 * 60 * minutes
 
-  t.setTimeout(undefined)
-  t.plan(1)
+  const { strictEqual } = tspl(t, { plan: 1 })
 
   const clock = FakeTimers.install()
-  t.teardown(clock.uninstall.bind(clock))
+  t.after(clock.uninstall.bind(clock))
 
   const orgTimers = { ...timers }
   Object.assign(timers, { setTimeout, clearTimeout })
-  t.teardown(() => {
+  t.after(() => {
     Object.assign(timers, orgTimers)
   })
 
@@ -29,7 +29,7 @@ test('Fetch very long request, timeout overridden so no error', (t) => {
     }, msToDelay)
     clock.tick(msToDelay + 1)
   })
-  t.teardown(server.close.bind(server))
+  t.after(server.close.bind(server))
 
   server.listen(0, () => {
     fetch(`http://localhost:${server.address().port}`, {
@@ -43,12 +43,12 @@ test('Fetch very long request, timeout overridden so no error', (t) => {
     })
       .then((response) => response.text())
       .then((response) => {
-        t.equal('hello', response)
-        t.end()
+        strictEqual('hello', response)
+        done()
       })
       .catch((err) => {
         // This should not happen, a timeout error should not occur
-        t.error(err)
+        throw err
       })
 
     clock.tick(msToDelay - 1)
diff --git a/test/fetch/file.js b/test/fetch/file.js
index 59015413eb0..0e2cac2f10c 100644
--- a/test/fetch/file.js
+++ b/test/fetch/file.js
@@ -1,190 +1,177 @@
 'use strict'
 
 const { Blob } = require('buffer')
-const { test } = require('tap')
+const { test } = require('node:test')
+const assert = require('node:assert')
+const { tspl } = require('@matteo.collina/tspl')
 const { File, FileLike } = require('../../lib/fetch/file')
 
 test('args validation', (t) => {
-  t.plan(14)
+  const { throws, doesNotThrow, strictEqual } = tspl(t, { plan: 14 })
 
-  t.throws(() => {
+  throws(() => {
     File.prototype.name.toString()
   }, TypeError)
-  t.throws(() => {
+  throws(() => {
     File.prototype.lastModified.toString()
   }, TypeError)
-  t.doesNotThrow(() => {
+  doesNotThrow(() => {
     File.prototype[Symbol.toStringTag].charAt(0)
   }, TypeError)
 
-  t.throws(() => {
+  throws(() => {
     FileLike.prototype.stream.call(null)
   }, TypeError)
-  t.throws(() => {
+  throws(() => {
     FileLike.prototype.arrayBuffer.call(null)
   }, TypeError)
-  t.throws(() => {
+  throws(() => {
     FileLike.prototype.slice.call(null)
   }, TypeError)
-  t.throws(() => {
+  throws(() => {
     FileLike.prototype.text.call(null)
   }, TypeError)
-  t.throws(() => {
+  throws(() => {
     FileLike.prototype.size.toString()
   }, TypeError)
-  t.throws(() => {
+  throws(() => {
     FileLike.prototype.type.toString()
   }, TypeError)
-  t.throws(() => {
+  throws(() => {
     FileLike.prototype.name.toString()
   }, TypeError)
-  t.throws(() => {
+  throws(() => {
     FileLike.prototype.lastModified.toString()
   }, TypeError)
-  t.doesNotThrow(() => {
+  doesNotThrow(() => {
     FileLike.prototype[Symbol.toStringTag].charAt(0)
   }, TypeError)
 
-  t.equal(File.prototype[Symbol.toStringTag], 'File')
-  t.equal(FileLike.prototype[Symbol.toStringTag], 'File')
+  strictEqual(File.prototype[Symbol.toStringTag], 'File')
+  strictEqual(FileLike.prototype[Symbol.toStringTag], 'File')
 })
 
 test('return value of File.lastModified', (t) => {
-  t.plan(2)
+  const { ok } = tspl(t, { plan: 2 })
 
   const f = new File(['asd1'], 'filename123')
   const lastModified = f.lastModified
-  t.ok(typeof lastModified === typeof Date.now())
-  t.ok(lastModified >= 0 && lastModified <= Date.now())
+  ok(typeof lastModified === typeof Date.now())
+  ok(lastModified >= 0 && lastModified <= Date.now())
 })
 
 test('Symbol.toStringTag', (t) => {
-  t.plan(2)
-  t.equal(new File([], '')[Symbol.toStringTag], 'File')
-  t.equal(new FileLike()[Symbol.toStringTag], 'File')
+  const { strictEqual } = tspl(t, { plan: 2 })
+  strictEqual(new File([], '')[Symbol.toStringTag], 'File')
+  strictEqual(new FileLike()[Symbol.toStringTag], 'File')
 })
 
-test('arguments', (t) => {
-  t.throws(() => {
+test('arguments', () => {
+  assert.throws(() => {
     new File() // eslint-disable-line no-new
   }, TypeError)
 
-  t.throws(() => {
+  assert.throws(() => {
     new File([]) // eslint-disable-line no-new
   }, TypeError)
-
-  t.end()
 })
 
-test('lastModified', (t) => {
+test('lastModified', () => {
   const file = new File([], '')
   const lastModified = Date.now() - 69_000
 
-  t.notOk(file === 0)
+  assert.ok(file !== 0)
 
   const file1 = new File([], '', { lastModified })
-  t.equal(file1.lastModified, lastModified)
+  assert.strictEqual(file1.lastModified, lastModified)
 
-  t.equal(new File([], '', { lastModified: 0 }).lastModified, 0)
+  assert.strictEqual(new File([], '', { lastModified: 0 }).lastModified, 0)
 
-  t.equal(
+  assert.strictEqual(
     new File([], '', {
       lastModified: true
     }).lastModified,
     1
   )
-
-  t.end()
 })
 
 test('File.prototype.text', async (t) => {
-  t.test('With Blob', async (t) => {
+  await t.test('With Blob', async () => {
     const blob1 = new Blob(['hello'])
     const blob2 = new Blob([' '])
     const blob3 = new Blob(['world'])
 
     const file = new File([blob1, blob2, blob3], 'hello_world.txt')
 
-    t.equal(await file.text(), 'hello world')
-    t.end()
+    assert.strictEqual(await file.text(), 'hello world')
   })
 
   /* eslint-disable camelcase */
-  t.test('With TypedArray', async (t) => {
+  await t.test('With TypedArray', async () => {
     const uint8_1 = new Uint8Array(Buffer.from('hello'))
     const uint8_2 = new Uint8Array(Buffer.from(' '))
     const uint8_3 = new Uint8Array(Buffer.from('world'))
 
     const file = new File([uint8_1, uint8_2, uint8_3], 'hello_world.txt')
 
-    t.equal(await file.text(), 'hello world')
-    t.end()
+    assert.strictEqual(await file.text(), 'hello world')
   })
 
-  t.test('With TypedArray range', async (t) => {
+  await t.test('With TypedArray range', async () => {
     const uint8_1 = new Uint8Array(Buffer.from('hello world'))
     const uint8_2 = new Uint8Array(uint8_1.buffer, 1, 4)
 
     const file = new File([uint8_2], 'hello_world.txt')
 
-    t.equal(await file.text(), 'ello')
-    t.end()
+    assert.strictEqual(await file.text(), 'ello')
   })
   /* eslint-enable camelcase */
 
-  t.test('With ArrayBuffer', async (t) => {
+  await t.test('With ArrayBuffer', async () => {
     const uint8 = new Uint8Array([65, 66, 67])
     const ab = uint8.buffer
 
     const file = new File([ab], 'file.txt')
 
-    t.equal(await file.text(), 'ABC')
-    t.end()
+    assert.strictEqual(await file.text(), 'ABC')
   })
 
-  t.test('With string', async (t) => {
+  await t.test('With string', async () => {
     const string = 'hello world'
     const file = new File([string], 'hello_world.txt')
 
-    t.equal(await file.text(), 'hello world')
-    t.end()
+    assert.strictEqual(await file.text(), 'hello world')
   })
 
-  t.test('With Buffer', async (t) => {
+  await t.test('With Buffer', async () => {
     const buffer = Buffer.from('hello world')
 
     const file = new File([buffer], 'hello_world.txt')
 
-    t.equal(await file.text(), 'hello world')
-    t.end()
+    assert.strictEqual(await file.text(), 'hello world')
   })
 
-  t.test('Mixed', async (t) => {
+  await t.test('Mixed', async () => {
     const blob = new Blob(['Hello, '])
     const uint8 = new Uint8Array(Buffer.from('world! This'))
     const string = ' is a test! Hope it passes!'
 
     const file = new File([blob, uint8, string], 'mixed-messages.txt')
 
-    t.equal(
+    assert.strictEqual(
       await file.text(),
       'Hello, world! This is a test! Hope it passes!'
     )
-    t.end()
   })
-
-  t.end()
 })
 
-test('endings=native', async (t) => {
+test('endings=native', async () => {
   const file = new File(['Hello\nWorld'], 'text.txt', { endings: 'native' })
   const text = await file.text()
 
   if (process.platform === 'win32') {
-    t.equal(text, 'Hello\r\nWorld', 'on windows, LF is replace with CRLF')
+    assert.strictEqual(text, 'Hello\r\nWorld', 'on windows, LF is replace with CRLF')
   } else {
-    t.equal(text, 'Hello\nWorld', `on ${process.platform} LF stays LF`)
+    assert.strictEqual(text, 'Hello\nWorld', `on ${process.platform} LF stays LF`)
   }
-
-  t.end()
 })
diff --git a/test/fetch/formdata.js b/test/fetch/formdata.js
index fed95bfb3da..157b7a77868 100644
--- a/test/fetch/formdata.js
+++ b/test/fetch/formdata.js
@@ -1,236 +1,224 @@
 'use strict'
 
-const { test } = require('tap')
+const { test } = require('node:test')
+const assert = require('node:assert')
+const { tspl } = require('@matteo.collina/tspl')
 const { FormData, File, Response } = require('../../')
 const { Blob: ThirdPartyBlob } = require('formdata-node')
 const { Blob } = require('buffer')
 const { isFormDataLike } = require('../../lib/core/util')
 const ThirdPartyFormDataInvalid = require('form-data')
 
-test('arg validation', (t) => {
+test('arg validation', () => {
   const form = new FormData()
 
   // constructor
-  t.throws(() => {
+  assert.throws(() => {
     // eslint-disable-next-line
     new FormData('asd')
   }, TypeError)
 
   // append
-  t.throws(() => {
+  assert.throws(() => {
     FormData.prototype.append.call(null)
   }, TypeError)
-  t.throws(() => {
+  assert.throws(() => {
     form.append()
   }, TypeError)
-  t.throws(() => {
+  assert.throws(() => {
     form.append('k', 'not usv', '')
   }, TypeError)
 
   // delete
-  t.throws(() => {
+  assert.throws(() => {
     FormData.prototype.delete.call(null)
   }, TypeError)
-  t.throws(() => {
+  assert.throws(() => {
     form.delete()
   }, TypeError)
 
   // get
-  t.throws(() => {
+  assert.throws(() => {
     FormData.prototype.get.call(null)
   }, TypeError)
-  t.throws(() => {
+  assert.throws(() => {
     form.get()
   }, TypeError)
 
   // getAll
-  t.throws(() => {
+  assert.throws(() => {
     FormData.prototype.getAll.call(null)
   }, TypeError)
-  t.throws(() => {
+  assert.throws(() => {
     form.getAll()
   }, TypeError)
 
   // has
-  t.throws(() => {
+  assert.throws(() => {
     FormData.prototype.has.call(null)
   }, TypeError)
-  t.throws(() => {
+  assert.throws(() => {
     form.has()
   }, TypeError)
 
   // set
-  t.throws(() => {
+  assert.throws(() => {
     FormData.prototype.set.call(null)
   }, TypeError)
-  t.throws(() => {
+  assert.throws(() => {
     form.set('k')
   }, TypeError)
-  t.throws(() => {
+  assert.throws(() => {
     form.set('k', 'not usv', '')
   }, TypeError)
 
   // iterator
-  t.throws(() => {
+  assert.throws(() => {
     Reflect.apply(FormData.prototype[Symbol.iterator], null)
   }, TypeError)
 
   // toStringTag
-  t.doesNotThrow(() => {
+  assert.doesNotThrow(() => {
     FormData.prototype[Symbol.toStringTag].charAt(0)
   })
-
-  t.end()
 })
 
-test('append file', (t) => {
+test('append file', () => {
   const form = new FormData()
   form.set('asd', new File([], 'asd1', { type: 'text/plain' }), 'asd2')
   form.append('asd2', new File([], 'asd1'), 'asd2')
 
-  t.equal(form.has('asd'), true)
-  t.equal(form.has('asd2'), true)
-  t.equal(form.get('asd').name, 'asd2')
-  t.equal(form.get('asd2').name, 'asd2')
-  t.equal(form.get('asd').type, 'text/plain')
+  assert.strictEqual(form.has('asd'), true)
+  assert.strictEqual(form.has('asd2'), true)
+  assert.strictEqual(form.get('asd').name, 'asd2')
+  assert.strictEqual(form.get('asd2').name, 'asd2')
+  assert.strictEqual(form.get('asd').type, 'text/plain')
   form.delete('asd')
-  t.equal(form.get('asd'), null)
-  t.equal(form.has('asd2'), true)
-  t.equal(form.has('asd'), false)
-
-  t.end()
+  assert.strictEqual(form.get('asd'), null)
+  assert.strictEqual(form.has('asd2'), true)
+  assert.strictEqual(form.has('asd'), false)
 })
 
-test('append blob', async (t) => {
+test('append blob', async () => {
   const form = new FormData()
   form.set('asd', new Blob(['asd1'], { type: 'text/plain' }))
 
-  t.equal(form.has('asd'), true)
-  t.equal(form.get('asd').type, 'text/plain')
-  t.equal(await form.get('asd').text(), 'asd1')
+  assert.strictEqual(form.has('asd'), true)
+  assert.strictEqual(form.get('asd').type, 'text/plain')
+  assert.strictEqual(await form.get('asd').text(), 'asd1')
   form.delete('asd')
-  t.equal(form.get('asd'), null)
-
-  t.end()
+  assert.strictEqual(form.get('asd'), null)
 })
 
-test('append third-party blob', async (t) => {
+test('append third-party blob', async () => {
   const form = new FormData()
   form.set('asd', new ThirdPartyBlob(['asd1'], { type: 'text/plain' }))
 
-  t.equal(form.has('asd'), true)
-  t.equal(form.get('asd').type, 'text/plain')
-  t.equal(await form.get('asd').text(), 'asd1')
+  assert.strictEqual(form.has('asd'), true)
+  assert.strictEqual(form.get('asd').type, 'text/plain')
+  assert.strictEqual(await form.get('asd').text(), 'asd1')
   form.delete('asd')
-  t.equal(form.get('asd'), null)
-
-  t.end()
+  assert.strictEqual(form.get('asd'), null)
 })
 
-test('append string', (t) => {
+test('append string', () => {
   const form = new FormData()
   form.set('k1', 'v1')
   form.set('k2', 'v2')
-  t.same([...form], [['k1', 'v1'], ['k2', 'v2']])
-  t.equal(form.has('k1'), true)
-  t.equal(form.get('k1'), 'v1')
+  assert.deepStrictEqual([...form], [['k1', 'v1'], ['k2', 'v2']])
+  assert.strictEqual(form.has('k1'), true)
+  assert.strictEqual(form.get('k1'), 'v1')
   form.append('k1', 'v1+')
-  t.same(form.getAll('k1'), ['v1', 'v1+'])
+  assert.deepStrictEqual(form.getAll('k1'), ['v1', 'v1+'])
   form.set('k2', 'v1++')
-  t.equal(form.get('k2'), 'v1++')
+  assert.strictEqual(form.get('k2'), 'v1++')
   form.delete('asd')
-  t.equal(form.get('asd'), null)
-  t.end()
+  assert.strictEqual(form.get('asd'), null)
 })
 
-test('formData.entries', (t) => {
-  t.plan(2)
+test('formData.entries', async (t) => {
   const form = new FormData()
 
-  t.test('with 0 entries', (t) => {
-    t.plan(1)
+  await t.test('with 0 entries', (t) => {
+    const { deepStrictEqual } = tspl(t, { plan: 1 })
 
     const entries = [...form.entries()]
-    t.same(entries, [])
+    deepStrictEqual(entries, [])
   })
 
-  t.test('with 1+ entries', (t) => {
-    t.plan(2)
+  await t.test('with 1+ entries', (t) => {
+    const { deepStrictEqual } = tspl(t, { plan: 2 })
 
     form.set('k1', 'v1')
     form.set('k2', 'v2')
 
     const entries = [...form.entries()]
     const entries2 = [...form.entries()]
-    t.same(entries, [['k1', 'v1'], ['k2', 'v2']])
-    t.same(entries, entries2)
+    deepStrictEqual(entries, [['k1', 'v1'], ['k2', 'v2']])
+    deepStrictEqual(entries, entries2)
   })
 })
 
-test('formData.keys', (t) => {
-  t.plan(2)
+test('formData.keys', async (t) => {
   const form = new FormData()
 
-  t.test('with 0 keys', (t) => {
-    t.plan(1)
+  await t.test('with 0 keys', (t) => {
+    const { deepStrictEqual } = tspl(t, { plan: 1 })
 
     const keys = [...form.entries()]
-    t.same(keys, [])
+    deepStrictEqual(keys, [])
   })
 
-  t.test('with 1+ keys', (t) => {
-    t.plan(2)
+  await t.test('with 1+ keys', (t) => {
+    const { deepStrictEqual } = tspl(t, { plan: 2 })
 
     form.set('k1', 'v1')
     form.set('k2', 'v2')
 
     const keys = [...form.keys()]
     const keys2 = [...form.keys()]
-    t.same(keys, ['k1', 'k2'])
-    t.same(keys, keys2)
+    deepStrictEqual(keys, ['k1', 'k2'])
+    deepStrictEqual(keys, keys2)
   })
 })
 
-test('formData.values', (t) => {
-  t.plan(2)
+test('formData.values', async (t) => {
   const form = new FormData()
 
-  t.test('with 0 values', (t) => {
-    t.plan(1)
+  await t.test('with 0 values', (t) => {
+    const { deepStrictEqual } = tspl(t, { plan: 1 })
 
     const values = [...form.values()]
-    t.same(values, [])
+    deepStrictEqual(values, [])
   })
 
-  t.test('with 1+ values', (t) => {
-    t.plan(2)
+  await t.test('with 1+ values', (t) => {
+    const { deepStrictEqual } = tspl(t, { plan: 2 })
 
     form.set('k1', 'v1')
     form.set('k2', 'v2')
 
     const values = [...form.values()]
     const values2 = [...form.values()]
-    t.same(values, ['v1', 'v2'])
-    t.same(values, values2)
+    deepStrictEqual(values, ['v1', 'v2'])
+    deepStrictEqual(values, values2)
   })
 })
 
-test('formData forEach', (t) => {
-  t.test('invalid arguments', (t) => {
-    t.throws(() => {
+test('formData forEach', async (t) => {
+  await t.test('invalid arguments', () => {
+    assert.throws(() => {
       FormData.prototype.forEach.call({})
     }, TypeError('Illegal invocation'))
 
-    t.throws(() => {
+    assert.throws(() => {
       const fd = new FormData()
 
       fd.forEach({})
     }, TypeError)
-
-    t.end()
   })
 
-  t.test('with a callback', (t) => {
+  await t.test('with a callback', () => {
     const fd = new FormData()
 
     fd.set('a', 'b')
@@ -239,58 +227,48 @@ test('formData forEach', (t) => {
     let i = 0
     fd.forEach((value, key, self) => {
       if (i++ === 0) {
-        t.equal(value, 'b')
-        t.equal(key, 'a')
+        assert.strictEqual(value, 'b')
+        assert.strictEqual(key, 'a')
       } else {
-        t.equal(value, 'd')
-        t.equal(key, 'c')
+        assert.strictEqual(value, 'd')
+        assert.strictEqual(key, 'c')
       }
 
-      t.equal(fd, self)
+      assert.strictEqual(fd, self)
     })
-
-    t.end()
   })
 
-  t.test('with a thisArg', (t) => {
+  await t.test('with a thisArg', () => {
     const fd = new FormData()
     fd.set('b', 'a')
 
     fd.forEach(function (value, key, self) {
-      t.equal(this, globalThis)
-      t.equal(fd, self)
-      t.equal(key, 'b')
-      t.equal(value, 'a')
+      assert.strictEqual(this, globalThis)
+      assert.strictEqual(fd, self)
+      assert.strictEqual(key, 'b')
+      assert.strictEqual(value, 'a')
     })
 
     const thisArg = Symbol('thisArg')
     fd.forEach(function () {
-      t.equal(this, thisArg)
+      assert.strictEqual(this, thisArg)
     }, thisArg)
-
-    t.end()
   })
-
-  t.end()
 })
 
-test('formData toStringTag', (t) => {
+test('formData toStringTag', () => {
   const form = new FormData()
-  t.equal(form[Symbol.toStringTag], 'FormData')
-  t.equal(FormData.prototype[Symbol.toStringTag], 'FormData')
-  t.end()
+  assert.strictEqual(form[Symbol.toStringTag], 'FormData')
+  assert.strictEqual(FormData.prototype[Symbol.toStringTag], 'FormData')
 })
 
-test('formData.constructor.name', (t) => {
+test('formData.constructor.name', () => {
   const form = new FormData()
-  t.equal(form.constructor.name, 'FormData')
-  t.end()
+  assert.strictEqual(form.constructor.name, 'FormData')
 })
 
-test('formData should be an instance of FormData', (t) => {
-  t.plan(3)
-
-  t.test('Invalid class FormData', (t) => {
+test('formData should be an instance of FormData', async (t) => {
+  await t.test('Invalid class FormData', () => {
     class FormData {
       constructor () {
         this.data = []
@@ -306,11 +284,10 @@ test('formData should be an instance of FormData', (t) => {
     }
 
     const form = new FormData()
-    t.equal(isFormDataLike(form), false)
-    t.end()
+    assert.strictEqual(isFormDataLike(form), false)
   })
 
-  t.test('Invalid function FormData', (t) => {
+  await t.test('Invalid function FormData', () => {
     function FormData () {
       const data = []
       return {
@@ -324,25 +301,22 @@ test('formData should be an instance of FormData', (t) => {
     }
 
     const form = new FormData()
-    t.equal(isFormDataLike(form), false)
-    t.end()
+    assert.strictEqual(isFormDataLike(form), false)
   })
 
-  test('Invalid third-party FormData', (t) => {
+  await t.test('Invalid third-party FormData', () => {
     const form = new ThirdPartyFormDataInvalid()
-    t.equal(isFormDataLike(form), false)
-    t.end()
+    assert.strictEqual(isFormDataLike(form), false)
   })
 
-  t.test('Valid FormData', (t) => {
+  await t.test('Valid FormData', () => {
     const form = new FormData()
-    t.equal(isFormDataLike(form), true)
-    t.end()
+    assert.strictEqual(isFormDataLike(form), true)
   })
 })
 
 test('FormData should be compatible with third-party libraries', (t) => {
-  t.plan(1)
+  const { strictEqual } = tspl(t, { plan: 1 })
 
   class FormData {
     constructor () {
@@ -366,23 +340,21 @@ test('FormData should be compatible with third-party libraries', (t) => {
   }
 
   const form = new FormData()
-  t.equal(isFormDataLike(form), true)
+  strictEqual(isFormDataLike(form), true)
 })
 
-test('arguments', (t) => {
-  t.equal(FormData.constructor.length, 1)
-  t.equal(FormData.prototype.append.length, 2)
-  t.equal(FormData.prototype.delete.length, 1)
-  t.equal(FormData.prototype.get.length, 1)
-  t.equal(FormData.prototype.getAll.length, 1)
-  t.equal(FormData.prototype.has.length, 1)
-  t.equal(FormData.prototype.set.length, 2)
-
-  t.end()
+test('arguments', () => {
+  assert.strictEqual(FormData.constructor.length, 1)
+  assert.strictEqual(FormData.prototype.append.length, 2)
+  assert.strictEqual(FormData.prototype.delete.length, 1)
+  assert.strictEqual(FormData.prototype.get.length, 1)
+  assert.strictEqual(FormData.prototype.getAll.length, 1)
+  assert.strictEqual(FormData.prototype.has.length, 1)
+  assert.strictEqual(FormData.prototype.set.length, 2)
 })
 
 // https://github.com/nodejs/undici/pull/1814
-test('FormData returned from bodyMixin.formData is not a clone', async (t) => {
+test('FormData returned from bodyMixin.formData is not a clone', async () => {
   const fd = new FormData()
   fd.set('foo', 'bar')
 
@@ -391,11 +363,11 @@ test('FormData returned from bodyMixin.formData is not a clone', async (t) => {
 
   const fd2 = await res.formData()
 
-  t.equal(fd2.get('foo'), 'bar')
-  t.equal(fd.get('foo'), 'foo')
+  assert.strictEqual(fd2.get('foo'), 'bar')
+  assert.strictEqual(fd.get('foo'), 'foo')
 
   fd2.set('foo', 'baz')
 
-  t.equal(fd2.get('foo'), 'baz')
-  t.equal(fd.get('foo'), 'foo')
+  assert.strictEqual(fd2.get('foo'), 'baz')
+  assert.strictEqual(fd.get('foo'), 'foo')
 })
diff --git a/test/fetch/general.js b/test/fetch/general.js
index 0469875aa2d..e93e67ba794 100644
--- a/test/fetch/general.js
+++ b/test/fetch/general.js
@@ -1,6 +1,7 @@
 'use strict'
 
-const { test } = require('tap')
+const { test } = require('node:test')
+const assert = require('node:assert')
 const {
   File,
   FormData,
@@ -9,7 +10,7 @@ const {
   Response
 } = require('../../index')
 
-test('Symbol.toStringTag descriptor', (t) => {
+test('Symbol.toStringTag descriptor', () => {
   for (const cls of [
     File,
     FormData,
@@ -18,13 +19,11 @@ test('Symbol.toStringTag descriptor', (t) => {
     Response
   ]) {
     const desc = Object.getOwnPropertyDescriptor(cls.prototype, Symbol.toStringTag)
-    t.same(desc, {
+    assert.deepStrictEqual(desc, {
       value: cls.name,
       writable: false,
       enumerable: false,
       configurable: true
     })
   }
-
-  t.end()
 })
diff --git a/test/fetch/headers.js b/test/fetch/headers.js
index fcf6f5f4891..51218f12ee9 100644
--- a/test/fetch/headers.js
+++ b/test/fetch/headers.js
@@ -1,119 +1,115 @@
 'use strict'
 
-const tap = require('tap')
+const { test } = require('node:test')
+const assert = require('node:assert')
+const { tspl } = require('@matteo.collina/tspl')
 const { Headers, fill } = require('../../lib/fetch/headers')
 const { kGuard } = require('../../lib/fetch/symbols')
 const { once } = require('events')
 const { fetch } = require('../..')
 const { createServer } = require('http')
 
-tap.test('Headers initialization', t => {
-  t.plan(8)
+test('Headers initialization', async (t) => {
+  await t.test('allows undefined', () => {
+    const { doesNotThrow } = tspl(t, { plan: 1 })
 
-  t.test('allows undefined', t => {
-    t.plan(1)
-
-    t.doesNotThrow(() => new Headers())
+    doesNotThrow(() => new Headers())
   })
 
-  t.test('with array of header entries', t => {
-    t.plan(3)
-
-    t.test('fails on invalid array-based init', t => {
-      t.plan(3)
-      t.throws(
+  await t.test('with array of header entries', async (t) => {
+    await t.test('fails on invalid array-based init', (t) => {
+      const { throws } = tspl(t, { plan: 3 })
+      throws(
         () => new Headers([['undici', 'fetch'], ['fetch']]),
         TypeError('Headers constructor: expected name/value pair to be length 2, found 1.')
       )
-      t.throws(() => new Headers(['undici', 'fetch', 'fetch']), TypeError)
-      t.throws(
+      throws(() => new Headers(['undici', 'fetch', 'fetch']), TypeError)
+      throws(
         () => new Headers([0, 1, 2]),
         TypeError('Sequence: Value of type Number is not an Object.')
       )
     })
 
-    t.test('allows even length init', t => {
-      t.plan(1)
+    await t.test('allows even length init', (t) => {
+      const { doesNotThrow } = tspl(t, { plan: 1 })
       const init = [['undici', 'fetch'], ['fetch', 'undici']]
-      t.doesNotThrow(() => new Headers(init))
+      doesNotThrow(() => new Headers(init))
     })
 
-    t.test('fails for event flattened init', t => {
-      t.plan(1)
+    await t.test('fails for event flattened init', (t) => {
+      const { throws } = tspl(t, { plan: 1 })
       const init = ['undici', 'fetch', 'fetch', 'undici']
-      t.throws(
+      throws(
         () => new Headers(init),
         TypeError('Sequence: Value of type String is not an Object.')
       )
     })
   })
 
-  t.test('with object of header entries', t => {
-    t.plan(1)
+  await t.test('with object of header entries', (t) => {
+    const { doesNotThrow } = tspl(t, { plan: 1 })
     const init = {
       undici: 'fetch',
       fetch: 'undici'
     }
-    t.doesNotThrow(() => new Headers(init))
+    doesNotThrow(() => new Headers(init))
   })
 
-  t.test('fails silently if a boxed primitive object is passed', t => {
-    t.plan(3)
+  await t.test('fails silently if a boxed primitive object is passed', (t) => {
+    const { doesNotThrow } = tspl(t, { plan: 3 })
     /* eslint-disable no-new-wrappers */
-    t.doesNotThrow(() => new Headers(new Number()))
-    t.doesNotThrow(() => new Headers(new Boolean()))
-    t.doesNotThrow(() => new Headers(new String()))
+    doesNotThrow(() => new Headers(new Number()))
+    doesNotThrow(() => new Headers(new Boolean()))
+    doesNotThrow(() => new Headers(new String()))
     /* eslint-enable no-new-wrappers */
   })
 
-  t.test('fails if primitive is passed', t => {
-    t.plan(2)
+  await t.test('fails if primitive is passed', (t) => {
+    const { throws } = tspl(t, { plan: 2 })
     const expectedTypeError = TypeError
-    t.throws(() => new Headers(1), expectedTypeError)
-    t.throws(() => new Headers('1'), expectedTypeError)
+    throws(() => new Headers(1), expectedTypeError)
+    throws(() => new Headers('1'), expectedTypeError)
   })
 
-  t.test('allows some weird stuff (because of webidl)', t => {
-    t.doesNotThrow(() => {
+  await t.test('allows some weird stuff (because of webidl)', () => {
+    assert.doesNotThrow(() => {
       new Headers(function () {}) // eslint-disable-line no-new
     })
 
-    t.doesNotThrow(() => {
+    assert.doesNotThrow(() => {
       new Headers(Function) // eslint-disable-line no-new
     })
-
-    t.end()
   })
 
-  t.test('allows a myriad of header values to be passed', t => {
-    t.plan(4)
+  await t.test('allows a myriad of header values to be passed', (t) => {
+    const { doesNotThrow, throws } = tspl(t, { plan: 4 })
 
     // Headers constructor uses Headers.append
 
-    t.doesNotThrow(() => new Headers([
+    doesNotThrow(() => new Headers([
       ['a', ['b', 'c']],
       ['d', ['e', 'f']]
     ]), 'allows any array values')
-    t.doesNotThrow(() => new Headers([
+    doesNotThrow(() => new Headers([
       ['key', null]
     ]), 'allows null values')
-    t.throws(() => new Headers([
+    throws(() => new Headers([
       ['key']
     ]), 'throws when 2 arguments are not passed')
-    t.throws(() => new Headers([
+    throws(() => new Headers([
       ['key', 'value', 'value2']
     ]), 'throws when too many arguments are passed')
   })
 
-  t.test('accepts headers as objects with array values', t => {
-    t.plan(1)
+  await t.test('accepts headers as objects with array values', (t) => {
+    const { deepStrictEqual } = tspl(t, { plan: 1 })
     const headers = new Headers({
       c: '5',
       b: ['3', '4'],
       a: ['1', '2']
     })
 
-    t.same([...headers.entries()], [
+    deepStrictEqual([...headers.entries()], [
       ['a', '1,2'],
       ['b', '3,4'],
       ['c', '5']
@@ -121,21 +117,19 @@ tap.test('Headers initialization', t => {
   })
 })
 
-tap.test('Headers append', t => {
-  t.plan(3)
-
-  t.test('adds valid header entry to instance', t => {
-    t.plan(2)
+test('Headers append', async (t) => {
+  await t.test('adds valid header entry to instance', (t) => {
+    const { doesNotThrow, strictEqual } = tspl(t, { plan: 2 })
     const headers = new Headers()
 
     const name = 'undici'
     const value = 'fetch'
-    t.doesNotThrow(() => headers.append(name, value))
-    t.equal(headers.get(name), value)
+    doesNotThrow(() => headers.append(name, value))
+    strictEqual(headers.get(name), value)
   })
 
-  t.test('adds valid header to existing entry', t => {
-    t.plan(4)
+  await t.test('adds valid header to existing entry', (t) => {
+    const { strictEqual, doesNotThrow } = tspl(t, { plan: 4 })
     const headers = new Headers()
 
     const name = 'undici'
@@ -143,218 +137,200 @@ tap.test('Headers append', t => {
     const value2 = 'fetch2'
     const value3 = 'fetch3'
     headers.append(name, value1)
-    t.equal(headers.get(name), value1)
-    t.doesNotThrow(() => headers.append(name, value2))
-    t.doesNotThrow(() => headers.append(name, value3))
-    t.equal(headers.get(name), [value1, value2, value3].join(', '))
+    strictEqual(headers.get(name), value1)
+    doesNotThrow(() => headers.append(name, value2))
+    doesNotThrow(() => headers.append(name, value3))
+    strictEqual(headers.get(name), [value1, value2, value3].join(', '))
   })
 
-  t.test('throws on invalid entry', t => {
-    t.plan(3)
+  await t.test('throws on invalid entry', (t) => {
+    const { throws } = tspl(t, { plan: 3 })
     const headers = new Headers()
 
-    t.throws(() => headers.append(), 'throws on missing name and value')
-    t.throws(() => headers.append('undici'), 'throws on missing value')
-    t.throws(() => headers.append('invalid @ header ? name', 'valid value'), 'throws on invalid name')
+    throws(() => headers.append(), 'throws on missing name and value')
+    throws(() => headers.append('undici'), 'throws on missing value')
+    throws(() => headers.append('invalid @ header ? name', 'valid value'), 'throws on invalid name')
   })
 })
 
-tap.test('Headers delete', t => {
-  t.plan(4)
-
-  t.test('deletes valid header entry from instance', t => {
-    t.plan(3)
+test('Headers delete', async (t) => {
+  await t.test('deletes valid header entry from instance', (t) => {
+    const { strictEqual, doesNotThrow } = tspl(t, { plan: 3 })
     const headers = new Headers()
 
     const name = 'undici'
     const value = 'fetch'
     headers.append(name, value)
-    t.equal(headers.get(name), value)
-    t.doesNotThrow(() => headers.delete(name))
-    t.equal(headers.get(name), null)
+    strictEqual(headers.get(name), value)
+    doesNotThrow(() => headers.delete(name))
+    strictEqual(headers.get(name), null)
   })
 
-  t.test('does not mutate internal list when no match is found', t => {
-    t.plan(3)
+  await t.test('does not mutate internal list when no match is found', (t) => {
+    const { strictEqual, doesNotThrow } = tspl(t, { plan: 3 })
 
     const headers = new Headers()
     const name = 'undici'
     const value = 'fetch'
     headers.append(name, value)
-    t.equal(headers.get(name), value)
-    t.doesNotThrow(() => headers.delete('not-undici'))
-    t.equal(headers.get(name), value)
+    strictEqual(headers.get(name), value)
+    doesNotThrow(() => headers.delete('not-undici'))
+    strictEqual(headers.get(name), value)
   })
 
-  t.test('throws on invalid entry', t => {
-    t.plan(2)
+  await t.test('throws on invalid entry', (t) => {
+    const { throws } = tspl(t, { plan: 2 })
     const headers = new Headers()
 
-    t.throws(() => headers.delete(), 'throws on missing namee')
-    t.throws(() => headers.delete('invalid @ header ? name'), 'throws on invalid name')
+    throws(() => headers.delete(), 'throws on missing namee')
+    throws(() => headers.delete('invalid @ header ? name'), 'throws on invalid name')
   })
 
   // https://github.com/nodejs/undici/issues/2429
-  t.test('`Headers#delete` returns undefined', t => {
-    t.plan(2)
+  await t.test('`Headers#delete` returns undefined', (t) => {
+    const { strictEqual } = tspl(t, { plan: 2 })
     const headers = new Headers({ test: 'test' })
 
-    t.same(headers.delete('test'), undefined)
-    t.same(headers.delete('test2'), undefined)
+    strictEqual(headers.delete('test'), undefined)
+    strictEqual(headers.delete('test2'), undefined)
   })
 })
 
-tap.test('Headers get', t => {
-  t.plan(3)
-
-  t.test('returns null if not found in instance', t => {
-    t.plan(1)
+test('Headers get', async (t) => {
+  await t.test('returns null if not found in instance', (t) => {
+    const { strictEqual } = tspl(t, { plan: 1 })
     const headers = new Headers()
     headers.append('undici', 'fetch')
 
-    t.equal(headers.get('not-undici'), null)
+    strictEqual(headers.get('not-undici'), null)
   })
 
-  t.test('returns header values from valid header name', t => {
-    t.plan(2)
+  await t.test('returns header values from valid header name', (t) => {
+    const { strictEqual } = tspl(t, { plan: 2 })
     const headers = new Headers()
 
     const name = 'undici'; const value1 = 'fetch1'; const value2 = 'fetch2'
     headers.append(name, value1)
-    t.equal(headers.get(name), value1)
+    strictEqual(headers.get(name), value1)
     headers.append(name, value2)
-    t.equal(headers.get(name), [value1, value2].join(', '))
+    strictEqual(headers.get(name), [value1, value2].join(', '))
   })
 
-  t.test('throws on invalid entry', t => {
-    t.plan(2)
+  await t.test('throws on invalid entry', (t) => {
+    const { throws } = tspl(t, { plan: 2 })
     const headers = new Headers()
 
-    t.throws(() => headers.get(), 'throws on missing name')
-    t.throws(() => headers.get('invalid @ header ? name'), 'throws on invalid name')
+    throws(() => headers.get(), 'throws on missing name')
+    throws(() => headers.get('invalid @ header ? name'), 'throws on invalid name')
   })
 })
 
-tap.test('Headers has', t => {
-  t.plan(2)
-
-  t.test('returns boolean existence for a header name', t => {
-    t.plan(2)
+test('Headers has', async (t) => {
+  await t.test('returns boolean existence for a header name', (t) => {
+    const { strictEqual } = tspl(t, { plan: 2 })
     const headers = new Headers()
 
     const name = 'undici'
     headers.append('not-undici', 'fetch')
-    t.equal(headers.has(name), false)
+    strictEqual(headers.has(name), false)
     headers.append(name, 'fetch')
-    t.equal(headers.has(name), true)
+    strictEqual(headers.has(name), true)
   })
 
-  t.test('throws on invalid entry', t => {
-    t.plan(2)
+  await t.test('throws on invalid entry', (t) => {
+    const { throws } = tspl(t, { plan: 2 })
     const headers = new Headers()
 
-    t.throws(() => headers.has(), 'throws on missing name')
-    t.throws(() => headers.has('invalid @ header ? name'), 'throws on invalid name')
+    throws(() => headers.has(), 'throws on missing name')
+    throws(() => headers.has('invalid @ header ? name'), 'throws on invalid name')
   })
 })
 
-tap.test('Headers set', t => {
-  t.plan(5)
-
-  t.test('sets valid header entry to instance', t => {
-    t.plan(2)
+test('Headers set', async (t) => {
+  await t.test('sets valid header entry to instance', (t) => {
+    const { doesNotThrow, strictEqual } = tspl(t, { plan: 2 })
     const headers = new Headers()
 
     const name = 'undici'
     const value = 'fetch'
     headers.append('not-undici', 'fetch')
-    t.doesNotThrow(() => headers.set(name, value))
-    t.equal(headers.get(name), value)
+    doesNotThrow(() => headers.set(name, value))
+    strictEqual(headers.get(name), value)
   })
 
-  t.test('overwrites existing entry', t => {
-    t.plan(4)
+  await t.test('overwrites existing entry', (t) => {
+    const { doesNotThrow, strictEqual } = tspl(t, { plan: 4 })
     const headers = new Headers()
 
     const name = 'undici'
     const value1 = 'fetch1'
     const value2 = 'fetch2'
-    t.doesNotThrow(() => headers.set(name, value1))
-    t.equal(headers.get(name), value1)
-    t.doesNotThrow(() => headers.set(name, value2))
-    t.equal(headers.get(name), value2)
+    doesNotThrow(() => headers.set(name, value1))
+    strictEqual(headers.get(name), value1)
+    doesNotThrow(() => headers.set(name, value2))
+    strictEqual(headers.get(name), value2)
   })
 
-  t.test('allows setting a myriad of values', t => {
-    t.plan(4)
+  await t.test('allows setting a myriad of values', (t) => {
+    const { doesNotThrow, throws } = tspl(t, { plan: 4 })
     const headers = new Headers()
 
-    t.doesNotThrow(() => headers.set('a', ['b', 'c']), 'sets array values properly')
-    t.doesNotThrow(() => headers.set('b', null), 'allows setting null values')
-    t.throws(() => headers.set('c'), 'throws when 2 arguments are not passed')
-    t.doesNotThrow(() => headers.set('c', 'd', 'e'), 'ignores extra arguments')
+    doesNotThrow(() => headers.set('a', ['b', 'c']), 'sets array values properly')
+    doesNotThrow(() => headers.set('b', null), 'allows setting null values')
+    throws(() => headers.set('c'), 'throws when 2 arguments are not passed')
+    doesNotThrow(() => headers.set('c', 'd', 'e'), 'ignores extra arguments')
   })
 
-  t.test('throws on invalid entry', t => {
-    t.plan(3)
+  await t.test('throws on invalid entry', (t) => {
+    const { throws } = tspl(t, { plan: 3 })
     const headers = new Headers()
 
-    t.throws(() => headers.set(), 'throws on missing name and value')
-    t.throws(() => headers.set('undici'), 'throws on missing value')
-    t.throws(() => headers.set('invalid @ header ? name', 'valid value'), 'throws on invalid name')
+    throws(() => headers.set(), 'throws on missing name and value')
+    throws(() => headers.set('undici'), 'throws on missing value')
+    throws(() => headers.set('invalid @ header ? name', 'valid value'), 'throws on invalid name')
   })
 
   // https://github.com/nodejs/undici/issues/2431
-  t.test('`Headers#set` returns undefined', t => {
-    t.plan(2)
+  await t.test('`Headers#set` returns undefined', (t) => {
+    const { strictEqual, ok } = tspl(t, { plan: 2 })
     const headers = new Headers()
 
-    t.same(headers.set('a', 'b'), undefined)
+    strictEqual(headers.set('a', 'b'), undefined)
 
-    t.notOk(headers.set('c', 'd') instanceof Map)
+    ok(!(headers.set('c', 'd') instanceof Map))
   })
 })
 
-tap.test('Headers forEach', t => {
+test('Headers forEach', async (t) => {
   const headers = new Headers([['a', 'b'], ['c', 'd']])
 
-  t.test('standard', t => {
-    t.equal(typeof headers.forEach, 'function')
+  await t.test('standard', () => {
+    assert.strictEqual(typeof headers.forEach, 'function')
 
     headers.forEach((value, key, headerInstance) => {
-      t.ok(value === 'b' || value === 'd')
-      t.ok(key === 'a' || key === 'c')
-      t.equal(headers, headerInstance)
+      assert.ok(value === 'b' || value === 'd')
+      assert.ok(key === 'a' || key === 'c')
+      assert.strictEqual(headers, headerInstance)
     })
-
-    t.end()
   })
 
-  t.test('when no thisArg is set, it is globalThis', (t) => {
+  await t.test('when no thisArg is set, it is globalThis', () => {
     headers.forEach(function () {
-      t.equal(this, globalThis)
+      assert.strictEqual(this, globalThis)
     })
-
-    t.end()
   })
 
-  t.test('with thisArg', t => {
+  await t.test('with thisArg', () => {
     const thisArg = { a: Math.random() }
     headers.forEach(function () {
-      t.equal(this, thisArg)
+      assert.strictEqual(this, thisArg)
     }, thisArg)
-
-    t.end()
   })
-
-  t.end()
 })
 
-tap.test('Headers as Iterable', t => {
-  t.plan(7)
-
-  t.test('should freeze values while iterating', t => {
-    t.plan(1)
+test('Headers as Iterable', async (t) => {
+  await t.test('should freeze values while iterating', (t) => {
+    const { deepStrictEqual } = tspl(t, { plan: 1 })
     const init = [
       ['foo', '123'],
       ['bar', '456']
@@ -368,11 +344,11 @@ tap.test('Headers as Iterable', t => {
       headers.delete(key)
       headers.set(`x-${key}`, val)
     }
-    t.strictSame([...headers], expected)
+    deepStrictEqual([...headers], expected)
   })
 
-  t.test('returns combined and sorted entries using .forEach()', t => {
-    t.plan(8)
+  await t.test('returns combined and sorted entries using .forEach()', (t) => {
+    const { deepStrictEqual, strictEqual } = tspl(t, { plan: 8 })
     const init = [
       ['a', '1'],
       ['b', '2'],
@@ -390,13 +366,13 @@ tap.test('Headers as Iterable', t => {
     const that = {}
     let i = 0
     headers.forEach(function (value, key, _headers) {
-      t.strictSame(expected[i++], [key, value])
-      t.equal(this, that)
+      deepStrictEqual(expected[i++], [key, value])
+      strictEqual(this, that)
     }, that)
   })
 
-  t.test('returns combined and sorted entries using .entries()', t => {
-    t.plan(4)
+  await t.test('returns combined and sorted entries using .entries()', (t) => {
+    const { deepStrictEqual } = tspl(t, { plan: 4 })
     const init = [
       ['a', '1'],
       ['b', '2'],
@@ -413,12 +389,12 @@ tap.test('Headers as Iterable', t => {
     const headers = new Headers(init)
     let i = 0
     for (const header of headers.entries()) {
-      t.strictSame(header, expected[i++])
+      deepStrictEqual(header, expected[i++])
     }
   })
 
-  t.test('returns combined and sorted keys using .keys()', t => {
-    t.plan(4)
+  await t.test('returns combined and sorted keys using .keys()', (t) => {
+    const { strictEqual } = tspl(t, { plan: 4 })
     const init = [
       ['a', '1'],
       ['b', '2'],
@@ -430,12 +406,12 @@ tap.test('Headers as Iterable', t => {
     const headers = new Headers(init)
     let i = 0
     for (const key of headers.keys()) {
-      t.strictSame(key, expected[i++])
+      strictEqual(key, expected[i++])
     }
   })
 
-  t.test('returns combined and sorted values using .values()', t => {
-    t.plan(4)
+  await t.test('returns combined and sorted values using .values()', (t) => {
+    const { strictEqual } = tspl(t, { plan: 4 })
     const init = [
       ['a', '1'],
       ['b', '2'],
@@ -447,12 +423,12 @@ tap.test('Headers as Iterable', t => {
     const headers = new Headers(init)
     let i = 0
     for (const value of headers.values()) {
-      t.strictSame(value, expected[i++])
+      strictEqual(value, expected[i++])
     }
   })
 
-  t.test('returns combined and sorted entries using for...of loop', t => {
-    t.plan(5)
+  await t.test('returns combined and sorted entries using for...of loop', (t) => {
+    const { deepStrictEqual } = tspl(t, { plan: 5 })
     const init = [
       ['a', '1'],
       ['b', '2'],
@@ -470,12 +446,12 @@ tap.test('Headers as Iterable', t => {
     ]
     let i = 0
     for (const header of new Headers(init)) {
-      t.strictSame(header, expected[i++])
+      deepStrictEqual(header, expected[i++])
     }
   })
 
-  t.test('validate append ordering', t => {
-    t.plan(1)
+  await t.test('validate append ordering', (t) => {
+    const { deepStrictEqual } = tspl(t, { plan: 1 })
     const headers = new Headers([['b', '2'], ['c', '3'], ['e', '5']])
     headers.append('d', '4')
     headers.append('a', '1')
@@ -493,180 +469,164 @@ tap.test('Headers as Iterable', t => {
       ['f', '6']
     ])]
 
-    t.same([...headers], expected)
+    deepStrictEqual([...headers], expected)
   })
 })
 
-tap.test('arg validation', (t) => {
+test('arg validation', () => {
   // fill
-  t.throws(() => {
+  assert.throws(() => {
     fill({}, 0)
   }, TypeError)
 
   const headers = new Headers()
 
   // constructor
-  t.throws(() => {
+  assert.throws(() => {
     // eslint-disable-next-line
     new Headers(0)
   }, TypeError)
 
   // get [Symbol.toStringTag]
-  t.doesNotThrow(() => {
+  assert.doesNotThrow(() => {
     Object.prototype.toString.call(Headers.prototype)
   })
 
   // toString
-  t.doesNotThrow(() => {
+  assert.doesNotThrow(() => {
     Headers.prototype.toString.call(null)
   })
 
   // append
-  t.throws(() => {
+  assert.throws(() => {
     Headers.prototype.append.call(null)
   }, TypeError)
-  t.throws(() => {
+  assert.throws(() => {
     headers.append()
   }, TypeError)
 
   // delete
-  t.throws(() => {
+  assert.throws(() => {
     Headers.prototype.delete.call(null)
   }, TypeError)
-  t.throws(() => {
+  assert.throws(() => {
     headers.delete()
   }, TypeError)
 
   // get
-  t.throws(() => {
+  assert.throws(() => {
     Headers.prototype.get.call(null)
   }, TypeError)
-  t.throws(() => {
+  assert.throws(() => {
     headers.get()
   }, TypeError)
 
   // has
-  t.throws(() => {
+  assert.throws(() => {
     Headers.prototype.has.call(null)
   }, TypeError)
-  t.throws(() => {
+  assert.throws(() => {
     headers.has()
   }, TypeError)
 
   // set
-  t.throws(() => {
+  assert.throws(() => {
     Headers.prototype.set.call(null)
   }, TypeError)
-  t.throws(() => {
+  assert.throws(() => {
     headers.set()
   }, TypeError)
 
   // forEach
-  t.throws(() => {
+  assert.throws(() => {
     Headers.prototype.forEach.call(null)
   }, TypeError)
-  t.throws(() => {
+  assert.throws(() => {
     headers.forEach()
   }, TypeError)
-  t.throws(() => {
+  assert.throws(() => {
     headers.forEach(1)
   }, TypeError)
 
   // inspect
-  t.throws(() => {
+  assert.throws(() => {
     Headers.prototype[Symbol.for('nodejs.util.inspect.custom')].call(null)
   }, TypeError)
-
-  t.end()
 })
 
-tap.test('function signature verification', (t) => {
-  t.test('function length', (t) => {
-    t.equal(Headers.prototype.append.length, 2)
-    t.equal(Headers.prototype.constructor.length, 0)
-    t.equal(Headers.prototype.delete.length, 1)
-    t.equal(Headers.prototype.entries.length, 0)
-    t.equal(Headers.prototype.forEach.length, 1)
-    t.equal(Headers.prototype.get.length, 1)
-    t.equal(Headers.prototype.has.length, 1)
-    t.equal(Headers.prototype.keys.length, 0)
-    t.equal(Headers.prototype.set.length, 2)
-    t.equal(Headers.prototype.values.length, 0)
-    t.equal(Headers.prototype[Symbol.iterator].length, 0)
-    t.equal(Headers.prototype.toString.length, 0)
-
-    t.end()
+test('function signature verification', async (t) => {
+  await t.test('function length', () => {
+    assert.strictEqual(Headers.prototype.append.length, 2)
+    assert.strictEqual(Headers.prototype.constructor.length, 0)
+    assert.strictEqual(Headers.prototype.delete.length, 1)
+    assert.strictEqual(Headers.prototype.entries.length, 0)
+    assert.strictEqual(Headers.prototype.forEach.length, 1)
+    assert.strictEqual(Headers.prototype.get.length, 1)
+    assert.strictEqual(Headers.prototype.has.length, 1)
+    assert.strictEqual(Headers.prototype.keys.length, 0)
+    assert.strictEqual(Headers.prototype.set.length, 2)
+    assert.strictEqual(Headers.prototype.values.length, 0)
+    assert.strictEqual(Headers.prototype[Symbol.iterator].length, 0)
+    assert.strictEqual(Headers.prototype.toString.length, 0)
   })
 
-  t.test('function equality', (t) => {
-    t.equal(Headers.prototype.entries, Headers.prototype[Symbol.iterator])
-    t.equal(Headers.prototype.toString, Object.prototype.toString)
-
-    t.end()
+  await t.test('function equality', () => {
+    assert.strictEqual(Headers.prototype.entries, Headers.prototype[Symbol.iterator])
+    assert.strictEqual(Headers.prototype.toString, Object.prototype.toString)
   })
 
-  t.test('toString and Symbol.toStringTag', (t) => {
-    t.equal(Object.prototype.toString.call(Headers.prototype), '[object Headers]')
-    t.equal(Headers.prototype[Symbol.toStringTag], 'Headers')
-    t.equal(Headers.prototype.toString.call(null), '[object Null]')
-
-    t.end()
+  await t.test('toString and Symbol.toStringTag', () => {
+    assert.strictEqual(Object.prototype.toString.call(Headers.prototype), '[object Headers]')
+    assert.strictEqual(Headers.prototype[Symbol.toStringTag], 'Headers')
+    assert.strictEqual(Headers.prototype.toString.call(null), '[object Null]')
   })
-
-  t.end()
 })
 
-tap.test('various init paths of Headers', (t) => {
+test('various init paths of Headers', () => {
   const h1 = new Headers()
   const h2 = new Headers({})
   const h3 = new Headers(undefined)
-  t.equal([...h1.entries()].length, 0)
-  t.equal([...h2.entries()].length, 0)
-  t.equal([...h3.entries()].length, 0)
-
-  t.end()
+  assert.strictEqual([...h1.entries()].length, 0)
+  assert.strictEqual([...h2.entries()].length, 0)
+  assert.strictEqual([...h3.entries()].length, 0)
 })
 
-tap.test('immutable guard', (t) => {
+test('immutable guard', () => {
   const headers = new Headers()
   headers.set('key', 'val')
   headers[kGuard] = 'immutable'
 
-  t.throws(() => {
+  assert.throws(() => {
     headers.set('asd', 'asd')
   })
-  t.throws(() => {
+  assert.throws(() => {
     headers.append('asd', 'asd')
   })
-  t.throws(() => {
+  assert.throws(() => {
     headers.delete('asd')
   })
-  t.equal(headers.get('key'), 'val')
-  t.equal(headers.has('key'), true)
-
-  t.end()
+  assert.strictEqual(headers.get('key'), 'val')
+  assert.strictEqual(headers.has('key'), true)
 })
 
-tap.test('request-no-cors guard', (t) => {
+test('request-no-cors guard', () => {
   const headers = new Headers()
   headers[kGuard] = 'request-no-cors'
-  t.doesNotThrow(() => { headers.set('key', 'val') })
-  t.doesNotThrow(() => { headers.append('key', 'val') })
-  t.doesNotThrow(() => { headers.delete('key') })
-  t.end()
+  assert.doesNotThrow(() => { headers.set('key', 'val') })
+  assert.doesNotThrow(() => { headers.append('key', 'val') })
 })
 
-tap.test('invalid headers', (t) => {
-  t.doesNotThrow(() => new Headers({ "abcdefghijklmnopqrstuvwxyz0123456789!#$%&'*+-.^_`|~": 'test' }))
+test('invalid headers', () => {
+  assert.doesNotThrow(() => new Headers({ "abcdefghijklmnopqrstuvwxyz0123456789!#$%&'*+-.^_`|~": 'test' }))
 
   const chars = '"(),/:;<=>?@[\\]{}'.split('')
 
   for (const char of chars) {
-    t.throws(() => new Headers({ [char]: 'test' }), TypeError, `The string "${char}" should throw an error.`)
+    assert.throws(() => new Headers({ [char]: 'test' }), TypeError, `The string "${char}" should throw an error.`)
   }
 
   for (const byte of ['\r', '\n', '\t', ' ', String.fromCharCode(128), '']) {
-    t.throws(() => {
+    assert.throws(() => {
       new Headers().set(byte, 'test')
     }, TypeError, 'invalid header name')
   }
@@ -676,39 +636,35 @@ tap.test('invalid headers', (t) => {
     '\r',
     '\n'
   ]) {
-    t.throws(() => {
+    assert.throws(() => {
       new Headers().set('a', `a${byte}b`)
     }, TypeError, 'not allowed at all in header value')
   }
 
-  t.doesNotThrow(() => {
+  assert.doesNotThrow(() => {
     new Headers().set('a', '\r')
   })
 
-  t.doesNotThrow(() => {
+  assert.doesNotThrow(() => {
     new Headers().set('a', '\n')
   })
 
-  t.throws(() => {
+  assert.throws(() => {
     new Headers().set('a', Symbol('symbol'))
   }, TypeError, 'symbols should throw')
-
-  t.end()
 })
 
-tap.test('headers that might cause a ReDoS', (t) => {
-  t.doesNotThrow(() => {
+test('headers that might cause a ReDoS', () => {
+  assert.doesNotThrow(() => {
     // This test will time out if the ReDoS attack is successful.
     const headers = new Headers()
     const attack = 'a' + '\t'.repeat(500_000) + '\ta'
     headers.append('fhqwhgads', attack)
   })
-
-  t.end()
 })
 
-tap.test('Headers.prototype.getSetCookie', (t) => {
-  t.test('Mutating the returned list does not affect the set-cookie list', (t) => {
+test('Headers.prototype.getSetCookie', async (t) => {
+  await t.test('Mutating the returned list does not affect the set-cookie list', () => {
     const h = new Headers([
       ['set-cookie', 'a=b'],
       ['set-cookie', 'c=d']
@@ -718,35 +674,32 @@ tap.test('Headers.prototype.getSetCookie', (t) => {
     h.getSetCookie().push('oh=no')
     const now = h.getSetCookie()
 
-    t.same(old, now)
-    t.end()
+    assert.deepStrictEqual(old, now)
   })
 
   // https://github.com/nodejs/undici/issues/1935
-  t.test('When Headers are cloned, so are the cookies', async (t) => {
+  await t.test('When Headers are cloned, so are the cookies', async (t) => {
     const server = createServer((req, res) => {
       res.setHeader('Set-Cookie', 'test=onetwo')
       res.end('Hello World!')
     }).listen(0)
 
     await once(server, 'listening')
-    t.teardown(server.close.bind(server))
+    t.after(server.close.bind(server))
 
     const res = await fetch(`http://localhost:${server.address().port}`)
     const entries = Object.fromEntries(res.headers.entries())
 
-    t.same(res.headers.getSetCookie(), ['test=onetwo'])
-    t.ok('set-cookie' in entries)
+    assert.deepStrictEqual(res.headers.getSetCookie(), ['test=onetwo'])
+    assert.ok('set-cookie' in entries)
   })
-
-  t.end()
 })
 
-tap.test('When the value is updated, update the cache', (t) => {
-  t.plan(2)
+test('When the value is updated, update the cache', (t) => {
+  const { deepStrictEqual } = tspl(t, { plan: 2 })
   const expected = [['a', 'a'], ['b', 'b'], ['c', 'c']]
   const headers = new Headers(expected)
-  t.same([...headers], expected)
+  deepStrictEqual([...headers], expected)
   headers.append('d', 'd')
-  t.same([...headers], [...expected, ['d', 'd']])
+  deepStrictEqual([...headers], [...expected, ['d', 'd']])
 })
diff --git a/test/fetch/http2.js b/test/fetch/http2.js
index 2168e54a171..16aea7a5ec2 100644
--- a/test/fetch/http2.js
+++ b/test/fetch/http2.js
@@ -6,16 +6,15 @@ const { once } = require('node:events')
 const { Blob } = require('node:buffer')
 const { Readable } = require('node:stream')
 
-const { test, plan } = require('tap')
+const { test } = require('node:test')
+const { tspl } = require('@matteo.collina/tspl')
 const pem = require('https-pem')
 
 const { Client, fetch, Headers } = require('../..')
 
 const nodeVersion = Number(process.version.split('v')[1].split('.')[0])
 
-plan(8)
-
-test('[Fetch] Issue#2311', async t => {
+test('[Fetch] Issue#2311', async (t) => {
   const expectedBody = 'hello from client!'
 
   const server = createSecureServer(pem, async (req, res) => {
@@ -35,7 +34,7 @@ test('[Fetch] Issue#2311', async t => {
     res.end(body)
   })
 
-  t.plan(1)
+  const { strictEqual } = tspl(t, { plan: 1 })
 
   server.listen()
   await once(server, 'listening')
@@ -63,13 +62,13 @@ test('[Fetch] Issue#2311', async t => {
 
   const responseBody = await response.text()
 
-  t.teardown(server.close.bind(server))
-  t.teardown(client.close.bind(client))
+  t.after(server.close.bind(server))
+  t.after(() => client.close())
 
-  t.equal(responseBody, expectedBody)
+  strictEqual(responseBody, expectedBody)
 })
 
-test('[Fetch] Simple GET with h2', async t => {
+test('[Fetch] Simple GET with h2', async (t) => {
   const server = createSecureServer(pem)
   const expectedRequestBody = 'hello h2!'
 
@@ -84,7 +83,7 @@ test('[Fetch] Simple GET with h2', async t => {
     stream.end(expectedRequestBody)
   })
 
-  t.plan(5)
+  const { strictEqual, throws } = tspl(t, { plan: 5 })
 
   server.listen()
   await once(server, 'listening')
@@ -111,22 +110,22 @@ test('[Fetch] Simple GET with h2', async t => {
 
   const responseBody = await response.text()
 
-  t.teardown(server.close.bind(server))
-  t.teardown(client.close.bind(client))
+  t.after(server.close.bind(server))
+  t.after(() => client.close())
 
-  t.equal(responseBody, expectedRequestBody)
-  t.equal(response.headers.get('x-method'), 'GET')
-  t.equal(response.headers.get('x-custom-h2'), 'foo')
+  strictEqual(responseBody, expectedRequestBody)
+  strictEqual(response.headers.get('x-method'), 'GET')
+  strictEqual(response.headers.get('x-custom-h2'), 'foo')
   // https://github.com/nodejs/undici/issues/2415
-  t.throws(() => {
+  throws(() => {
     response.headers.get(':status')
   }, TypeError)
 
   // See https://fetch.spec.whatwg.org/#concept-response-status-message
-  t.equal(response.statusText, '')
+  strictEqual(response.statusText, '')
 })
 
-test('[Fetch] Should handle h2 request with body (string or buffer)', async t => {
+test('[Fetch] Should handle h2 request with body (string or buffer)', async (t) => {
   const server = createSecureServer(pem)
   const expectedBody = 'hello from client!'
   const expectedRequestBody = 'hello h2!'
@@ -144,7 +143,7 @@ test('[Fetch] Should handle h2 request with body (string or buffer)', async t =>
     stream.end(expectedRequestBody)
   })
 
-  t.plan(2)
+  const { strictEqual } = tspl(t, { plan: 2 })
 
   server.listen()
   await once(server, 'listening')
@@ -172,27 +171,29 @@ test('[Fetch] Should handle h2 request with body (string or buffer)', async t =>
 
   const responseBody = await response.text()
 
-  t.teardown(server.close.bind(server))
-  t.teardown(client.close.bind(client))
+  t.after(server.close.bind(server))
+  t.after(() => client.close())
 
-  t.equal(Buffer.concat(requestBody).toString('utf-8'), expectedBody)
-  t.equal(responseBody, expectedRequestBody)
+  strictEqual(Buffer.concat(requestBody).toString('utf-8'), expectedBody)
+  strictEqual(responseBody, expectedRequestBody)
 })
 
 // Skipping for now, there is something odd in the way the body is handled
 test(
   '[Fetch] Should handle h2 request with body (stream)',
   { skip: nodeVersion === 16 },
-  async t => {
+  async (t) => {
     const server = createSecureServer(pem)
     const expectedBody = readFileSync(__filename, 'utf-8')
     const stream = createReadStream(__filename)
     const requestChunks = []
 
+    const { strictEqual } = tspl(t, { plan: 8 })
+
     server.on('stream', async (stream, headers) => {
-      t.equal(headers[':method'], 'PUT')
-      t.equal(headers[':path'], '/')
-      t.equal(headers[':scheme'], 'https')
+      strictEqual(headers[':method'], 'PUT')
+      strictEqual(headers[':path'], '/')
+      strictEqual(headers[':scheme'], 'https')
 
       stream.respond({
         'content-type': 'text/plain; charset=utf-8',
@@ -207,8 +208,6 @@ test(
       stream.end('hello h2!')
     })
 
-    t.plan(8)
-
     server.listen(0)
     await once(server, 'listening')
 
@@ -219,8 +218,8 @@ test(
       allowH2: true
     })
 
-    t.teardown(server.close.bind(server))
-    t.teardown(client.close.bind(client))
+    t.after(server.close.bind(server))
+    t.after(() => client.close())
 
     const response = await fetch(
       `https://localhost:${server.address().port}/`,
@@ -239,14 +238,14 @@ test(
 
     const responseBody = await response.text()
 
-    t.equal(response.status, 200)
-    t.equal(response.headers.get('content-type'), 'text/plain; charset=utf-8')
-    t.equal(response.headers.get('x-custom-h2'), 'foo')
-    t.equal(responseBody, 'hello h2!')
-    t.equal(Buffer.concat(requestChunks).toString('utf-8'), expectedBody)
+    strictEqual(response.status, 200)
+    strictEqual(response.headers.get('content-type'), 'text/plain; charset=utf-8')
+    strictEqual(response.headers.get('x-custom-h2'), 'foo')
+    strictEqual(responseBody, 'hello h2!')
+    strictEqual(Buffer.concat(requestChunks).toString('utf-8'), expectedBody)
   }
 )
-test('Should handle h2 request with body (Blob)', { skip: !Blob }, async t => {
+test('Should handle h2 request with body (Blob)', { skip: !Blob }, async (t) => {
   const server = createSecureServer(pem)
   const expectedBody = 'asd'
   const requestChunks = []
@@ -254,10 +253,12 @@ test('Should handle h2 request with body (Blob)', { skip: !Blob }, async t => {
     type: 'text/plain'
   })
 
+  const { strictEqual } = tspl(t, { plan: 8 })
+
   server.on('stream', async (stream, headers) => {
-    t.equal(headers[':method'], 'POST')
-    t.equal(headers[':path'], '/')
-    t.equal(headers[':scheme'], 'https')
+    strictEqual(headers[':method'], 'POST')
+    strictEqual(headers[':path'], '/')
+    strictEqual(headers[':scheme'], 'https')
 
     stream.on('data', chunk => requestChunks.push(chunk))
 
@@ -270,8 +271,6 @@ test('Should handle h2 request with body (Blob)', { skip: !Blob }, async t => {
     stream.end('hello h2!')
   })
 
-  t.plan(8)
-
   server.listen(0)
   await once(server, 'listening')
 
@@ -282,8 +281,8 @@ test('Should handle h2 request with body (Blob)', { skip: !Blob }, async t => {
     allowH2: true
   })
 
-  t.teardown(server.close.bind(server))
-  t.teardown(client.close.bind(client))
+  t.after(server.close.bind(server))
+  t.after(() => client.close())
 
   const response = await fetch(
     `https://localhost:${server.address().port}/`,
@@ -301,17 +300,17 @@ test('Should handle h2 request with body (Blob)', { skip: !Blob }, async t => {
 
   const responseBody = await response.arrayBuffer()
 
-  t.equal(response.status, 200)
-  t.equal(response.headers.get('content-type'), 'text/plain; charset=utf-8')
-  t.equal(response.headers.get('x-custom-h2'), 'foo')
-  t.same(new TextDecoder().decode(responseBody).toString(), 'hello h2!')
-  t.equal(Buffer.concat(requestChunks).toString('utf-8'), expectedBody)
+  strictEqual(response.status, 200)
+  strictEqual(response.headers.get('content-type'), 'text/plain; charset=utf-8')
+  strictEqual(response.headers.get('x-custom-h2'), 'foo')
+  strictEqual(new TextDecoder().decode(responseBody).toString(), 'hello h2!')
+  strictEqual(Buffer.concat(requestChunks).toString('utf-8'), expectedBody)
 })
 
 test(
   'Should handle h2 request with body (Blob:ArrayBuffer)',
   { skip: !Blob },
-  async t => {
+  async (t) => {
     const server = createSecureServer(pem)
     const expectedBody = 'hello'
     const requestChunks = []
@@ -321,10 +320,12 @@ test(
 
     buf.copy(new Uint8Array(body))
 
+    const { strictEqual, deepStrictEqual } = tspl(t, { plan: 8 })
+
     server.on('stream', async (stream, headers) => {
-      t.equal(headers[':method'], 'PUT')
-      t.equal(headers[':path'], '/')
-      t.equal(headers[':scheme'], 'https')
+      strictEqual(headers[':method'], 'PUT')
+      strictEqual(headers[':path'], '/')
+      strictEqual(headers[':scheme'], 'https')
 
       stream.on('data', chunk => requestChunks.push(chunk))
 
@@ -337,8 +338,6 @@ test(
       stream.end(JSON.stringify(expectedResponseBody))
     })
 
-    t.plan(8)
-
     server.listen(0)
     await once(server, 'listening')
 
@@ -349,8 +348,8 @@ test(
       allowH2: true
     })
 
-    t.teardown(server.close.bind(server))
-    t.teardown(client.close.bind(client))
+    t.after(server.close.bind(server))
+    t.after(() => client.close())
 
     const response = await fetch(
       `https://localhost:${server.address().port}/`,
@@ -368,16 +367,16 @@ test(
 
     const responseBody = await response.json()
 
-    t.equal(response.status, 200)
-    t.equal(response.headers.get('content-type'), 'application/json')
-    t.equal(response.headers.get('x-custom-h2'), 'foo')
-    t.same(responseBody, expectedResponseBody)
-    t.equal(Buffer.concat(requestChunks).toString('utf-8'), expectedBody)
+    strictEqual(response.status, 200)
+    strictEqual(response.headers.get('content-type'), 'application/json')
+    strictEqual(response.headers.get('x-custom-h2'), 'foo')
+    deepStrictEqual(responseBody, expectedResponseBody)
+    strictEqual(Buffer.concat(requestChunks).toString('utf-8'), expectedBody)
   }
 )
 
 test('Issue#2415', async (t) => {
-  t.plan(1)
+  const { doesNotThrow } = tspl(t, { plan: 1 })
   const server = createSecureServer(pem)
 
   server.on('stream', async (stream, headers) => {
@@ -408,13 +407,13 @@ test('Issue#2415', async (t) => {
 
   await response.text()
 
-  t.teardown(server.close.bind(server))
-  t.teardown(client.close.bind(client))
+  t.after(server.close.bind(server))
+  t.after(() => client.close())
 
-  t.doesNotThrow(() => new Headers(response.headers))
+  doesNotThrow(() => new Headers(response.headers))
 })
 
-test('Issue #2386', async t => {
+test('Issue #2386', async (t) => {
   const server = createSecureServer(pem)
   const body = Buffer.from('hello')
   const requestChunks = []
@@ -422,10 +421,12 @@ test('Issue #2386', async t => {
   const controller = new AbortController()
   const signal = controller.signal
 
+  const { strictEqual, ok } = tspl(t, { plan: 4 })
+
   server.on('stream', async (stream, headers) => {
-    t.equal(headers[':method'], 'PUT')
-    t.equal(headers[':path'], '/')
-    t.equal(headers[':scheme'], 'https')
+    strictEqual(headers[':method'], 'PUT')
+    strictEqual(headers[':path'], '/')
+    strictEqual(headers[':scheme'], 'https')
 
     stream.on('data', chunk => requestChunks.push(chunk))
 
@@ -438,8 +439,6 @@ test('Issue #2386', async t => {
     stream.end(JSON.stringify(expectedResponseBody))
   })
 
-  t.plan(4)
-
   server.listen(0)
   await once(server, 'listening')
 
@@ -450,28 +449,24 @@ test('Issue #2386', async t => {
     allowH2: true
   })
 
-  t.teardown(server.close.bind(server))
-  t.teardown(client.close.bind(client))
+  t.after(server.close.bind(server))
+  t.after(() => client.close())
 
-  try {
-    await fetch(
-      `https://localhost:${server.address().port}/`,
-      // Needs to be passed to disable the reject unauthorized
-      {
-        body,
-        signal,
-        method: 'PUT',
-        dispatcher: client,
-        headers: {
-          'x-my-header': 'foo',
-          'content-type': 'text-plain'
-        }
+  await fetch(
+    `https://localhost:${server.address().port}/`,
+    // Needs to be passed to disable the reject unauthorized
+    {
+      body,
+      signal,
+      method: 'PUT',
+      dispatcher: client,
+      headers: {
+        'x-my-header': 'foo',
+        'content-type': 'text-plain'
       }
-    )
+    }
+  )
 
-    controller.abort()
-    t.pass()
-  } catch (error) {
-    t.error(error)
-  }
+  controller.abort()
+  ok(true)
 })

From 8060c4f693004cf1df39afe693a99c506ba5a40e Mon Sep 17 00:00:00 2001
From: Anurag Roy <anurag@2pirad.com>
Date: Wed, 3 Jan 2024 02:37:56 +0530
Subject: [PATCH 2/3] Use borp's `--expose-gc` flag

---
 package.json | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/package.json b/package.json
index e94e7371ab5..765fe5db56c 100644
--- a/package.json
+++ b/package.json
@@ -78,7 +78,7 @@
     "test": "node scripts/generate-pem && npm run test:tap && npm run test:node-fetch && npm run test:fetch && npm run test:cookies && npm run test:wpt && npm run test:websocket && npm run test:jest && npm run test:typescript",
     "test:cookies": "borp --coverage -p \"test/cookie/*.js\"",
     "test:node-fetch": "mocha --exit test/node-fetch",
-    "test:fetch": "npm run build:node && node --expose-gc --test test/fetch/*.js && borp --coverage -p \"test/webidl/*.js\"",
+    "test:fetch": "npm run build:node && borp --expose-gc --coverage -p \"test/fetch/*.js\" && borp --coverage -p \"test/webidl/*.js\"",
     "test:jest": "jest",
     "test:tap": "tap test/*.js test/diagnostics-channel/*.js",
     "test:tdd": "tap test/*.js test/diagnostics-channel/*.js -w",
@@ -101,7 +101,7 @@
     "@types/node": "^18.0.3",
     "abort-controller": "^3.0.0",
     "atomic-sleep": "^1.0.0",
-    "borp": "^0.4.2",
+    "borp": "^0.5.0",
     "chai": "^4.3.4",
     "chai-as-promised": "^7.1.1",
     "chai-iterator": "^3.0.2",

From e1f7a696ed02fa99c3726a638d3d674a8b65667a Mon Sep 17 00:00:00 2001
From: Anurag Roy <anurag@2pirad.com>
Date: Wed, 3 Jan 2024 02:39:45 +0530
Subject: [PATCH 3/3] Port `issue-2171.js`

---
 test/fetch/issue-2171.js | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/test/fetch/issue-2171.js b/test/fetch/issue-2171.js
index 72770f3713f..cd4368b9d86 100644
--- a/test/fetch/issue-2171.js
+++ b/test/fetch/issue-2171.js
@@ -3,16 +3,17 @@
 const { fetch } = require('../..')
 const { once } = require('events')
 const { createServer } = require('http')
-const { test } = require('tap')
+const { test } = require('node:test')
+const assert = require('node:assert')
 
 test('error reason is forwarded - issue #2171', { skip: !AbortSignal.timeout }, async (t) => {
   const server = createServer(() => {}).listen(0)
 
-  t.teardown(server.close.bind(server))
+  t.after(server.close.bind(server))
   await once(server, 'listening')
 
   const timeout = AbortSignal.timeout(100)
-  await t.rejects(
+  await assert.rejects(
     fetch(`http://localhost:${server.address().port}`, {
       signal: timeout
     }),
