UNPKG

kequapp

Version:

DEPRECATED: renamed to @kequtech/arbor

209 lines (208 loc) 6.01 kB
/** * Unit test fakes for IncomingMessage / ServerResponse usage. * * These are intentionally minimal helpers for unit tests — they do not fully * implement node:http internals. They provide: * - header helpers * - buffering of written response bytes via `_getBuffer()`/`_getString()` * - a small set of flags (`headersSent`, `finished`, `writableEnded`, ...) * * Known deviations: * - `FakeReq` accepts `body` as a string or Buffer only. If `body` is * omitted or undefined it will be consumed and the stream closed. * - Setting `body: null` keeps the request stream open as opposed to having * to explicitly close it. * - These fakes intentionally avoid re-implementing the full Node stream * state machine or exact semantics. */ import { STATUS_CODES } from 'node:http'; import { Transform } from 'node:stream'; export class FakeReq extends Transform { method; url; headers; rawHeaders; aborted; complete; socket; connection; trailers; rawTrailers; headersDistinct; trailersDistinct; constructor(options) { super(); this.aborted = false; this.complete = false; this.socket = undefined; this.connection = undefined; this.trailers = {}; this.rawTrailers = []; this.headersDistinct = undefined; this.trailersDistinct = undefined; for (const key of Object.keys(options)) { this[key] = options[key]; } this.method = options.method ?? 'GET'; this.url = options.url ?? ''; this.headers = {}; this.rawHeaders = []; if (options.headers) { for (const key of Object.keys(options.headers)) { if (options.headers[key] === undefined) continue; const value = options.headers[key]; this.headers[key.toLowerCase()] = value; this.rawHeaders.push(key, value); } } // Special case: only auto-close the request stream when body was // not explicitly set to null. Tests sometimes pass `body: null` to // keep the stream open for manual writes. if (options.body !== null) { this.end(options.body); } this.on('end', () => { this.complete = true; }); } setTimeout(ms, cb) { if (typeof cb === 'function') setTimeout(cb, ms); return this; } pause() { try { Transform.prototype.pause?.call(this); } catch (_) { } return this; } resume() { try { Transform.prototype.resume?.call(this); } catch (_) { } return this; } destroy(err) { if (err) this.emit('error', err); try { Transform.prototype.destroy?.call(this, err); } catch (_) { } return this; } _transform(chunk, _enc, done) { if (typeof chunk === 'string' || Buffer.isBuffer(chunk)) { this.push(chunk); } else { this.push(JSON.stringify(chunk)); } done(); } } export class FakeRes extends Transform { statusCode; statusMessage; headersSent; finished; writableEnded; writableFinished; _headers; _responseData; constructor() { super(); this.statusCode = 200; this.statusMessage = STATUS_CODES[this.statusCode] ?? 'OK'; this._headers = {}; this._responseData = []; this.headersSent = false; this.finished = false; this.writableEnded = false; this.writableFinished = false; this.on('finish', () => { this.finished = true; this.writableFinished = true; this.writableEnded = true; }); } _transform(chunk, _enc, done) { this.push(chunk); this._responseData.push(chunk); if (!this.headersSent) this.headersSent = true; done(); } setHeader(name, value) { this._headers[name.toLowerCase()] = value; } getHeader(name) { return this._headers[name.toLowerCase()]; } getHeaders() { return this._headers; } getHeaderNames() { return Object.keys(this._headers); } hasHeader(name) { return Object.prototype.hasOwnProperty.call(this._headers, name.toLowerCase()); } removeHeader(name) { delete this._headers[name.toLowerCase()]; } addTrailers(headers) { for (const k of Object.keys(headers)) { this._headers[k.toLowerCase()] = headers[k]; } } flushHeaders() { this.headersSent = true; } writeHead(statusCode, statusMessage, headers) { if (statusMessage !== undefined && typeof statusMessage !== 'string') { headers = statusMessage; statusMessage = undefined; } this.statusCode = statusCode; this.statusMessage = statusMessage ?? STATUS_CODES[statusCode] ?? 'unknown'; if (headers) { for (const name of Object.keys(headers)) { this.setHeader(name, headers[name]); } } this.headersSent = true; } writeContinue() { } writeProcessing() { } assignSocket(_socket) { } detachSocket() { } _getBuffer() { return Buffer.concat(this._responseData); } _getString() { return this._getBuffer().toString(); } _getJSON() { return JSON.parse(this._getString()); } end(...args) { this.finished = true; this.headersSent = true; try { this.writableEnded = true; this.writableFinished = true; } catch (_) { } super.end(...args); return this; } setTimeout(ms, cb) { if (typeof cb === 'function') setTimeout(cb, ms); return this; } }