Task 1.1.
List up to five functions that you think are not tested sufficiently. Note: A function may be insufficiently tested if the tests do not fully exercise different scenarios that can occur in the function. As such, programmers are not able to verify all outcomes confidently. Note: Provide function's name and a line number per function. |
|
/** * Body.js * * Body interface provides common methods for Request and Response */ import Stream, {PassThrough} from 'node:stream'; import {types, deprecate, promisify} from 'node:util'; import {Buffer} from 'node:buffer'; import Blob from 'fetch-blob'; import {FormData, formDataToBlob} from 'formdata-polyfill/esm.min.js'; import {FetchError} from './errors/fetch-error.js'; import {FetchBaseError} from './errors/base.js'; import {isBlob, isURLSearchParameters} from './utils/is.js'; const pipeline = promisify(Stream.pipeline); const INTERNALS = Symbol('Body internals'); /** * Body mixin * * Ref: https://fetch.spec.whatwg.org/#body * * @param Stream body Readable stream * @param Object opts Response options * @return Void */ export default class Body { constructor(body, { size = 0 } = {}) { let boundary = null; if (body === null) { // Body is undefined or null body = null; } else if (isURLSearchParameters(body)) { // Body is a URLSearchParams body = Buffer.from(body.toString()); } else if (body instanceof FormData) { // Body is FormData body = formDataToBlob(body); boundary = body.type.split('=')[1]; } else { // None of the above // coerce to string then buffer body = Buffer.from(String(body)); } let stream = body; if (Buffer.isBuffer(body)) { stream = Stream.Readable.from(body); } else if (isBlob(body)) { stream = Stream.Readable.from(body.stream()); } if (body instanceof Stream) { body.on('error', error_ => { const error = error_ instanceof FetchBaseError ? error_ : new FetchError(`Invalid response body while trying to fetch ${this.url}: ${error_.message}`, 'system', error_); this[INTERNALS].error = error; }); } } get body() { return this[INTERNALS].stream; } get bodyUsed() { return this[INTERNALS].disturbed; } /** * Decode response as ArrayBuffer * * @return Promise */ async function arrayBuffer() { const {buffer, byteOffset, byteLength} = await consumeBody(this); return buffer.slice(byteOffset, byteOffset + byteLength); } async function formData() { const ct = this.headers.get('content-type'); if (ct.startsWith('application/x-www-form-urlencoded')) { const formData = new FormData(); const parameters = new URLSearchParams(await this.text()); for (const [name, value] of parameters) { formData.append(name, value); } return formData; } const {toFormData} = await import('./utils/multipart-parser.js'); return toFormData(this.body, ct); } /** * Return raw response as Blob * * @return Promise */ async function blob() { const ct = (this.headers && this.headers.get('content-type')) || (this[INTERNALS].body && this[INTERNALS].body.type) || ''; const buf = await this.arrayBuffer(); return new Blob([buf], { type: ct }); } /** * Decode response as json * * @return Promise */ async function json() { const buffer = await consumeBody(this); return JSON.parse(buffer.toString()); } /** * Decode response as text * * @return Promise */ async function text() { const buffer = await consumeBody(this); return new TextDecoder().decode(buffer); } /** * Decode response as buffer (non-spec api) * * @return Promise */ function buffer() { return consumeBody(this); } } Body.prototype.buffer = deprecate(Body.prototype.buffer, 'Please use \'response.arrayBuffer()\' instead of \'response.buffer()\'', 'node-fetch#buffer'); // In browsers, all properties are enumerable. Object.defineProperties(Body.prototype, { body: {enumerable: true}, bodyUsed: {enumerable: true}, arrayBuffer: {enumerable: true}, blob: {enumerable: true}, json: {enumerable: true}, text: {enumerable: true}, data: {get: deprecate(() => {}, 'data doesn\'t exist, use json(), text(), arrayBuffer(), or body instead', 'https://github.com/node-fetch/node-fetch/issues/1000 (response)')} }); /** * Consume and convert an entire Body to a Buffer. * * Ref: https://fetch.spec.whatwg.org/#concept-body-consume-body * * @return Promise */ async function consumeBody(data) { if (data[INTERNALS].disturbed) { throw new TypeError(`body used already for: ${data.url}`); } data[INTERNALS].disturbed = true; if (data[INTERNALS].error) { throw data[INTERNALS].error; } const {body} = data; // Body is null if (body === null) { return Buffer.alloc(0); } /* c8 ignore next 3 */ if (!(body instanceof Stream)) { return Buffer.alloc(0); } // Body is stream // get ready to actually consume the body const accum = []; let accumBytes = 0; try { for await (const chunk of body) { if (data.size > 0 && accumBytes + chunk.length > data.size) { const error = new FetchError(`content size at ${data.url} over limit: ${data.size}`, 'max-size'); body.destroy(error); throw error; } accumBytes += chunk.length; accum.push(chunk); } } catch (error) { const error_ = error instanceof FetchBaseError ? error : new FetchError(`Invalid response body while trying to fetch ${data.url}: ${error.message}`, 'system', error); throw error_; } if (body.readableEnded === true || body._readableState.ended === true) { try { if (accum.every(c => typeof c === 'string')) { return Buffer.from(accum.join('')); } return Buffer.concat(accum, accumBytes); } catch (error) { throw new FetchError(`Could not create Buffer from response body for ${data.url}: ${error.message}`, 'system', error); } } else { throw new FetchError(`Premature close of server response while trying to fetch ${data.url}`); } } /** * Clone body given Res/Req instance * * @param Mixed instance Response or Request instance * @param String highWaterMark highWaterMark for both PassThrough body streams * @return Mixed */ function clone(instance, highWaterMark) { let p1; let p2; let {body} = instance[INTERNALS]; // Don't allow cloning a used body if (instance.bodyUsed) { throw new Error('cannot clone body after it is used'); } // Check that body is a stream and not form-data object // note: we can't clone the form-data object without having it as a dependency if ((body instanceof Stream) && (typeof body.getBoundary !== 'function')) { // Tee instance body p1 = new PassThrough({highWaterMark}); p2 = new PassThrough({highWaterMark}); body.pipe(p1); body.pipe(p2); // Set instance body to teed body and return the other teed body instance[INTERNALS].stream = p1; body = p2; } return body; }; const getNonSpecFormDataBoundary = deprecate( body => body.getBoundary(), 'form-data doesn\'t follow the spec and requires special treatment. Use alternative package', 'https://github.com/node-fetch/node-fetch/issues/1167' ); /** * Performs the operation "extract a `Content-Type` value from |object|" as * specified in the specification: * https://fetch.spec.whatwg.org/#concept-bodyinit-extract * * This function assumes that instance.body is present. * * @param {any} body Any options.body input * @returns {string | null} */ function extractContentType(body, request) { // Body is null or undefined if (body === null) { return null; } // Body is string if (typeof body === 'string') { return 'text/plain;charset=UTF-8'; } // Body is blob if (isBlob(body)) { return body.type || null; } // Body is a Buffer (Buffer, ArrayBuffer or ArrayBufferView) if (Buffer.isBuffer(body) || types.isAnyArrayBuffer(body) || ArrayBuffer.isView(body)) { return null; } // Body is stream - can't really do much about this if (body instanceof Stream) { return null; } // Body constructor defaults other things to string return 'text/plain;charset=UTF-8'; }; /** * The Fetch Standard treats this as if "total bytes" is a property on the body. * For us, we have to explicitly get it with a function. * * ref: https://fetch.spec.whatwg.org/#concept-body-total-bytes * * @param {any} obj.body Body object from the Body instance. * @returns {number | null} */ function getTotalBytes(request) { const {body} = request[INTERNALS]; // Body is null or undefined if (body === null) { return 0; } // Body is Blob if (isBlob(body)) { return body.size; } // Body is Buffer if (Buffer.isBuffer(body)) { return body.length; } // Detect form data input from form-data module if (body && typeof body.getLengthSync === 'function') { return body.hasKnownLength && body.hasKnownLength() ? body.getLengthSync() : null; } // Body is stream return null; }; /** * Write a Body to a Node.js WritableStream (e.g. http.Request) object. * * @param {Stream.Writable} dest The stream to write to. * @param obj.body Body object from the Body instance. * @returns {Promise |