diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-21 20:56:19 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-21 20:56:19 +0000 |
commit | 0b6210cd37b68b94252cb798598b12974a20e1c1 (patch) | |
tree | e371686554a877842d95aa94f100bee552ff2a8e /test/utils | |
parent | Initial commit. (diff) | |
download | node-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.js | 25 | ||||
-rw-r--r-- | test/utils/esm-wrapper.mjs | 102 | ||||
-rw-r--r-- | test/utils/formdata.js | 49 | ||||
-rw-r--r-- | test/utils/redirecting-servers.js | 265 | ||||
-rw-r--r-- | test/utils/stream.js | 48 |
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 } |