kequapp
Version:
DEPRECATED: renamed to @kequtech/arbor
209 lines (208 loc) • 6.01 kB
JavaScript
/**
* 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;
}
}