summaryrefslogtreecommitdiffstats
path: root/test/utils
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-21 20:56:19 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-21 20:56:19 +0000
commit0b6210cd37b68b94252cb798598b12974a20e1c1 (patch)
treee371686554a877842d95aa94f100bee552ff2a8e /test/utils
parentInitial commit. (diff)
downloadnode-undici-upstream.tar.xz
node-undici-upstream.zip
Adding upstream version 5.28.2+dfsg1+~cs23.11.12.3.upstream/5.28.2+dfsg1+_cs23.11.12.3upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'test/utils')
-rw-r--r--test/utils/async-iterators.js25
-rw-r--r--test/utils/esm-wrapper.mjs102
-rw-r--r--test/utils/formdata.js49
-rw-r--r--test/utils/redirecting-servers.js265
-rw-r--r--test/utils/stream.js48
5 files changed, 489 insertions, 0 deletions
diff --git a/test/utils/async-iterators.js b/test/utils/async-iterators.js
new file mode 100644
index 0000000..da7e0a8
--- /dev/null
+++ b/test/utils/async-iterators.js
@@ -0,0 +1,25 @@
+'use strict'
+
+async function * wrapWithAsyncIterable (asyncIterable, indefinite = false) {
+ for await (const chunk of asyncIterable) {
+ yield chunk
+ }
+ if (indefinite) {
+ await new Promise(() => {})
+ }
+}
+
+const STREAM = 'stream'
+const ASYNC_ITERATOR = 'async-iterator'
+function maybeWrapStream (stream, type) {
+ if (type === STREAM) {
+ return stream
+ }
+ if (type === ASYNC_ITERATOR) {
+ return wrapWithAsyncIterable(stream)
+ }
+
+ throw new Error(`bad input ${type} should be ${STREAM} or ${ASYNC_ITERATOR}`)
+}
+
+module.exports = { wrapWithAsyncIterable, maybeWrapStream, consts: { STREAM, ASYNC_ITERATOR } }
diff --git a/test/utils/esm-wrapper.mjs b/test/utils/esm-wrapper.mjs
new file mode 100644
index 0000000..51f8572
--- /dev/null
+++ b/test/utils/esm-wrapper.mjs
@@ -0,0 +1,102 @@
+import { createServer } from 'http'
+import tap from 'tap'
+import {
+ Agent,
+ Client,
+ errors,
+ pipeline,
+ Pool,
+ request,
+ connect,
+ upgrade,
+ setGlobalDispatcher,
+ getGlobalDispatcher,
+ stream
+} from '../../index.js'
+
+const { test } = tap
+
+test('imported Client works with basic GET', (t) => {
+ t.plan(10)
+
+ const server = createServer((req, res) => {
+ t.equal('/', req.url)
+ t.equal('GET', req.method)
+ t.equal(`localhost:${server.address().port}`, req.headers.host)
+ t.equal(undefined, req.headers.foo)
+ t.equal('bar', req.headers.bar)
+ t.equal(undefined, req.headers['content-length'])
+ res.setHeader('Content-Type', 'text/plain')
+ res.end('hello')
+ })
+
+ t.teardown(server.close.bind(server))
+
+ const reqHeaders = {
+ foo: undefined,
+ bar: 'bar'
+ }
+
+ server.listen(0, () => {
+ const client = new Client(`http://localhost:${server.address().port}`)
+ t.teardown(client.close.bind(client))
+
+ client.request({
+ path: '/',
+ method: 'GET',
+ headers: reqHeaders
+ }, (err, data) => {
+ t.error(err)
+ const { statusCode, headers, body } = data
+ t.equal(statusCode, 200)
+ t.equal(headers['content-type'], 'text/plain')
+ const bufs = []
+ body.on('data', (buf) => {
+ bufs.push(buf)
+ })
+ body.on('end', () => {
+ t.equal('hello', Buffer.concat(bufs).toString('utf8'))
+ })
+ })
+ })
+})
+
+test('imported errors work with request args validation', (t) => {
+ t.plan(2)
+
+ const client = new Client('http://localhost:5000')
+
+ client.request(null, (err) => {
+ t.type(err, errors.InvalidArgumentError)
+ })
+
+ try {
+ client.request(null, 'asd')
+ } catch (err) {
+ t.type(err, errors.InvalidArgumentError)
+ }
+})
+
+test('imported errors work with request args validation promise', (t) => {
+ t.plan(1)
+
+ const client = new Client('http://localhost:5000')
+
+ client.request(null).catch((err) => {
+ t.type(err, errors.InvalidArgumentError)
+ })
+})
+
+test('named exports', (t) => {
+ t.equal(typeof Client, 'function')
+ t.equal(typeof Pool, 'function')
+ t.equal(typeof Agent, 'function')
+ t.equal(typeof request, 'function')
+ t.equal(typeof stream, 'function')
+ t.equal(typeof pipeline, 'function')
+ t.equal(typeof connect, 'function')
+ t.equal(typeof upgrade, 'function')
+ t.equal(typeof setGlobalDispatcher, 'function')
+ t.equal(typeof getGlobalDispatcher, 'function')
+ t.end()
+})
diff --git a/test/utils/formdata.js b/test/utils/formdata.js
new file mode 100644
index 0000000..edd8854
--- /dev/null
+++ b/test/utils/formdata.js
@@ -0,0 +1,49 @@
+const Busboy = require('@fastify/busboy')
+
+function parseFormDataString (
+ body,
+ contentType
+) {
+ const cache = {
+ fileMap: new Map(),
+ fields: []
+ }
+
+ const bb = new Busboy({
+ headers: {
+ 'content-type': contentType
+ }
+ })
+
+ return new Promise((resolve, reject) => {
+ bb.on('file', (name, file, filename, encoding, mimeType) => {
+ cache.fileMap.set(name, { data: [], info: { filename, encoding, mimeType } })
+
+ file.on('data', (data) => {
+ const old = cache.fileMap.get(name)
+
+ cache.fileMap.set(name, {
+ data: [...old.data, data],
+ info: old.info
+ })
+ }).on('end', () => {
+ const old = cache.fileMap.get(name)
+
+ cache.fileMap.set(name, {
+ data: Buffer.concat(old.data),
+ info: old.info
+ })
+ })
+ })
+
+ bb.on('field', (key, value) => cache.fields.push({ key, value }))
+ bb.on('finish', () => resolve(cache))
+ bb.on('error', (e) => reject(e))
+
+ bb.end(body)
+ })
+}
+
+module.exports = {
+ parseFormDataString
+}
diff --git a/test/utils/redirecting-servers.js b/test/utils/redirecting-servers.js
new file mode 100644
index 0000000..ad8aa58
--- /dev/null
+++ b/test/utils/redirecting-servers.js
@@ -0,0 +1,265 @@
+'use strict'
+
+const { createServer } = require('http')
+
+const isNode20 = process.version.startsWith('v20.')
+
+function close (server) {
+ return function () {
+ return new Promise(resolve => {
+ if (isNode20) {
+ server.closeAllConnections()
+ }
+ server.close(resolve)
+ })
+ }
+}
+
+function startServer (t, handler) {
+ return new Promise(resolve => {
+ const server = createServer(handler)
+
+ server.listen(0, () => {
+ resolve(`localhost:${server.address().port}`)
+ })
+
+ t.teardown(close(server))
+ })
+}
+
+async function startRedirectingServer (t) {
+ const server = await startServer(t, (req, res) => {
+ // Parse the path and normalize arguments
+ let [code, redirections, query] = req.url
+ .slice(1)
+ .split(/[/?]/)
+
+ if (req.url.indexOf('?') !== -1 && !query) {
+ query = redirections
+ redirections = 0
+ }
+
+ code = parseInt(code, 10)
+ redirections = parseInt(redirections, 10)
+
+ if (isNaN(code) || code < 0) {
+ code = 302
+ } else if (code < 300) {
+ res.statusCode = code
+ redirections = 5
+ }
+
+ if (isNaN(redirections) || redirections < 0) {
+ redirections = 0
+ }
+
+ // On 303, the method must be GET or HEAD after the first redirect
+ if (code === 303 && redirections > 0 && req.method !== 'GET' && req.method !== 'HEAD') {
+ res.statusCode = 400
+ res.setHeader('Connection', 'close')
+ res.end('Did not switch to GET')
+ return
+ }
+
+ // End the chain at some point
+ if (redirections === 5) {
+ res.setHeader('Connection', 'close')
+ res.write(
+ `${req.method} /${redirections}${query ? ` ${query}` : ''} :: ${Object.entries(req.headers)
+ .map(([k, v]) => `${k}@${v}`)
+ .join(' ')}`
+ )
+
+ if (parseInt(req.headers['content-length']) > 0) {
+ res.write(' :: ')
+ req.pipe(res)
+ } else {
+ res.end('')
+ }
+
+ return
+ }
+
+ // Redirect by default
+ res.statusCode = code
+ res.setHeader('Connection', 'close')
+ res.setHeader('Location', `http://${server}/${code}/${++redirections}${query ? `?${query}` : ''}`)
+ res.end('')
+ })
+
+ return server
+}
+
+async function startRedirectingWithBodyServer (t) {
+ const server = await startServer(t, (req, res) => {
+ if (req.url === '/') {
+ res.statusCode = 301
+ res.setHeader('Connection', 'close')
+ res.setHeader('Location', `http://${server}/end`)
+ res.end('REDIRECT')
+ return
+ }
+
+ res.setHeader('Connection', 'close')
+ res.end('FINAL')
+ })
+
+ return server
+}
+
+function startRedirectingWithoutLocationServer (t) {
+ return startServer(t, (req, res) => {
+ // Parse the path and normalize arguments
+ let [code] = req.url
+ .slice(1)
+ .split('/')
+ .map(r => parseInt(r, 10))
+
+ if (isNaN(code) || code < 0) {
+ code = 302
+ }
+
+ res.statusCode = code
+ res.setHeader('Connection', 'close')
+ res.end('')
+ })
+}
+
+async function startRedirectingChainServers (t) {
+ const server1 = await startServer(t, (req, res) => {
+ if (req.url === '/') {
+ res.statusCode = 301
+ res.setHeader('Connection', 'close')
+ res.setHeader('Location', `http://${server2}/`)
+ res.end('')
+ return
+ }
+
+ res.setHeader('Connection', 'close')
+ res.end(req.method)
+ })
+
+ const server2 = await startServer(t, (req, res) => {
+ res.statusCode = 301
+ res.setHeader('Connection', 'close')
+
+ if (req.url === '/') {
+ res.setHeader('Location', `http://${server3}/`)
+ } else {
+ res.setHeader('Location', `http://${server3}/end`)
+ }
+
+ res.end('')
+ })
+
+ const server3 = await startServer(t, (req, res) => {
+ res.statusCode = 301
+ res.setHeader('Connection', 'close')
+
+ if (req.url === '/') {
+ res.setHeader('Location', `http://${server2}/end`)
+ } else {
+ res.setHeader('Location', `http://${server1}/end`)
+ }
+
+ res.end('')
+ })
+
+ return [server1, server2, server3]
+}
+
+async function startRedirectingWithAuthorization (t, authorization) {
+ const server1 = await startServer(t, (req, res) => {
+ if (req.headers.authorization !== authorization) {
+ res.statusCode = 403
+ res.setHeader('Connection', 'close')
+ res.end('')
+ return
+ }
+
+ res.statusCode = 301
+ res.setHeader('Connection', 'close')
+
+ res.setHeader('Location', `http://${server2}`)
+ res.end('')
+ })
+
+ const server2 = await startServer(t, (req, res) => {
+ res.end(req.headers.authorization || '')
+ })
+
+ return [server1, server2]
+}
+
+async function startRedirectingWithCookie (t, cookie) {
+ const server1 = await startServer(t, (req, res) => {
+ if (req.headers.cookie !== cookie) {
+ res.statusCode = 403
+ res.setHeader('Connection', 'close')
+ res.end('')
+ return
+ }
+
+ res.statusCode = 301
+ res.setHeader('Connection', 'close')
+
+ res.setHeader('Location', `http://${server2}`)
+ res.end('')
+ })
+
+ const server2 = await startServer(t, (req, res) => {
+ res.end(req.headers.cookie || '')
+ })
+
+ return [server1, server2]
+}
+
+async function startRedirectingWithRelativePath (t) {
+ const server = await startServer(t, (req, res) => {
+ res.setHeader('Connection', 'close')
+
+ if (req.url === '/') {
+ res.statusCode = 301
+ res.setHeader('Location', '/absolute/a')
+ res.end('')
+ } else if (req.url === '/absolute/a') {
+ res.statusCode = 301
+ res.setHeader('Location', 'b')
+ res.end('')
+ } else {
+ res.statusCode = 200
+ res.end(req.url)
+ }
+ })
+
+ return server
+}
+
+async function startRedirectingWithQueryParams (t) {
+ const server = await startServer(t, (req, res) => {
+ if (req.url === '/?param1=first') {
+ res.statusCode = 301
+ res.setHeader('Connection', 'close')
+ res.setHeader('Location', `http://${server}/?param2=second`)
+ res.end('REDIRECT')
+ return
+ }
+
+ res.setHeader('Connection', 'close')
+ res.end('')
+ })
+
+ return server
+}
+
+module.exports = {
+ startServer,
+ startRedirectingServer,
+ startRedirectingWithBodyServer,
+ startRedirectingWithoutLocationServer,
+ startRedirectingChainServers,
+ startRedirectingWithAuthorization,
+ startRedirectingWithCookie,
+ startRedirectingWithRelativePath,
+ startRedirectingWithQueryParams
+}
diff --git a/test/utils/stream.js b/test/utils/stream.js
new file mode 100644
index 0000000..b78ff5c
--- /dev/null
+++ b/test/utils/stream.js
@@ -0,0 +1,48 @@
+'use strict'
+
+const { Readable, Writable } = require('stream')
+
+let ReadableStream
+
+function createReadable (data) {
+ return new Readable({
+ read () {
+ this.push(Buffer.from(data))
+ this.push(null)
+ }
+ })
+}
+
+function createWritable (target) {
+ return new Writable({
+ write (chunk, _, callback) {
+ target.push(chunk.toString())
+ callback()
+ },
+ final (callback) {
+ callback()
+ }
+ })
+}
+
+class Source {
+ constructor (data) {
+ this.data = data
+ }
+
+ async start (controller) {
+ this.controller = controller
+ }
+
+ async pull (controller) {
+ controller.enqueue(this.data)
+ controller.close()
+ }
+}
+
+function createReadableStream (data) {
+ ReadableStream = require('stream/web').ReadableStream
+ return new ReadableStream(new Source(data))
+}
+
+module.exports = { createReadableStream, createReadable, createWritable }