| | import type { |
| | ServerResponse, |
| | OutgoingHttpHeaders, |
| | OutgoingHttpHeader, |
| | IncomingMessage, |
| | IncomingHttpHeaders, |
| | } from 'http' |
| | import type { Socket } from 'net' |
| | import type { TLSSocket } from 'tls' |
| |
|
| | import Stream from 'stream' |
| | import { |
| | fromNodeOutgoingHttpHeaders, |
| | toNodeOutgoingHttpHeaders, |
| | } from '../web/utils' |
| |
|
| | interface MockedRequestOptions { |
| | url: string |
| | headers: IncomingHttpHeaders |
| | method: string |
| | readable?: Stream.Readable |
| | socket?: Socket | null |
| | } |
| |
|
| | export class MockedRequest extends Stream.Readable implements IncomingMessage { |
| | public url: string |
| | public readonly statusCode?: number | undefined |
| | public readonly statusMessage?: string | undefined |
| | public readonly headers: IncomingHttpHeaders |
| | public readonly method: string |
| |
|
| | |
| | public readonly httpVersion = '1.0' |
| | public readonly httpVersionMajor = 1 |
| | public readonly httpVersionMinor = 0 |
| |
|
| | private bodyReadable?: Stream.Readable |
| |
|
| | |
| | |
| | |
| | public socket: Socket = new Proxy<TLSSocket>({} as TLSSocket, { |
| | get: (_target, prop) => { |
| | if (prop !== 'encrypted' && prop !== 'remoteAddress') { |
| | throw new Error('Method not implemented') |
| | } |
| |
|
| | if (prop === 'remoteAddress') return undefined |
| | |
| | |
| | return false |
| | }, |
| | }) |
| |
|
| | constructor({ |
| | url, |
| | headers, |
| | method, |
| | socket = null, |
| | readable, |
| | }: MockedRequestOptions) { |
| | super() |
| |
|
| | this.url = url |
| | this.headers = headers |
| | this.method = method |
| |
|
| | if (readable) { |
| | this.bodyReadable = readable |
| | this.bodyReadable.on('end', () => this.emit('end')) |
| | this.bodyReadable.on('close', () => this.emit('close')) |
| | } |
| |
|
| | if (socket) { |
| | this.socket = socket |
| | } |
| | } |
| |
|
| | public get headersDistinct(): NodeJS.Dict<string[]> { |
| | const headers: NodeJS.Dict<string[]> = {} |
| | for (const [key, value] of Object.entries(this.headers)) { |
| | if (!value) continue |
| |
|
| | headers[key] = Array.isArray(value) ? value : [value] |
| | } |
| |
|
| | return headers |
| | } |
| |
|
| | public _read(size: number): void { |
| | if (this.bodyReadable) { |
| | return this.bodyReadable._read(size) |
| | } else { |
| | this.emit('end') |
| | this.emit('close') |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | public get connection(): Socket { |
| | return this.socket |
| | } |
| |
|
| | |
| | |
| |
|
| | public get aborted(): boolean { |
| | throw new Error('Method not implemented') |
| | } |
| |
|
| | public get complete(): boolean { |
| | throw new Error('Method not implemented') |
| | } |
| |
|
| | public get trailers(): NodeJS.Dict<string> { |
| | throw new Error('Method not implemented') |
| | } |
| |
|
| | public get trailersDistinct(): NodeJS.Dict<string[]> { |
| | throw new Error('Method not implemented') |
| | } |
| |
|
| | public get rawTrailers(): string[] { |
| | throw new Error('Method not implemented') |
| | } |
| |
|
| | public get rawHeaders(): string[] { |
| | throw new Error('Method not implemented.') |
| | } |
| |
|
| | public setTimeout(): this { |
| | throw new Error('Method not implemented.') |
| | } |
| | } |
| |
|
| | export interface MockedResponseOptions { |
| | statusCode?: number |
| | socket?: Socket | null |
| | headers?: OutgoingHttpHeaders |
| | resWriter?: (chunk: Uint8Array | Buffer | string) => boolean |
| | } |
| |
|
| | export class MockedResponse extends Stream.Writable implements ServerResponse { |
| | public statusCode: number |
| | public statusMessage: string = '' |
| | public finished = false |
| | public headersSent = false |
| | public readonly socket: Socket | null |
| |
|
| | |
| | |
| | |
| | |
| | |
| | public readonly hasStreamed: Promise<boolean> |
| |
|
| | |
| | |
| | |
| | |
| | |
| | public readonly buffers: Buffer[] = [] |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | public readonly headers: Headers |
| |
|
| | private resWriter: MockedResponseOptions['resWriter'] |
| |
|
| | public readonly headPromise: Promise<void> |
| | private headPromiseResolve?: () => void |
| |
|
| | constructor(res: MockedResponseOptions = {}) { |
| | super() |
| |
|
| | this.statusCode = res.statusCode ?? 200 |
| | this.socket = res.socket ?? null |
| | this.headers = res.headers |
| | ? fromNodeOutgoingHttpHeaders(res.headers) |
| | : new Headers() |
| |
|
| | this.headPromise = new Promise<void>((resolve) => { |
| | this.headPromiseResolve = resolve |
| | }) |
| |
|
| | |
| | |
| | this.hasStreamed = new Promise<boolean>((resolve, reject) => { |
| | this.on('finish', () => resolve(true)) |
| | this.on('end', () => resolve(true)) |
| | this.on('error', (err) => reject(err)) |
| | }).then((val) => { |
| | this.headPromiseResolve?.() |
| | return val |
| | }) |
| |
|
| | if (res.resWriter) { |
| | this.resWriter = res.resWriter |
| | } |
| | } |
| |
|
| | public appendHeader(name: string, value: string | string[]): this { |
| | const values = Array.isArray(value) ? value : [value] |
| | for (const v of values) { |
| | this.headers.append(name, v) |
| | } |
| |
|
| | return this |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | public get isSent() { |
| | return this.finished || this.headersSent |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | public get connection(): Socket | null { |
| | return this.socket |
| | } |
| |
|
| | public write(chunk: Uint8Array | Buffer | string) { |
| | if (this.resWriter) { |
| | return this.resWriter(chunk) |
| | } |
| | this.buffers.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)) |
| |
|
| | return true |
| | } |
| |
|
| | public end() { |
| | this.finished = true |
| | return super.end(...arguments) |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | public _implicitHeader() {} |
| |
|
| | public _write( |
| | chunk: Buffer | string, |
| | _encoding: string, |
| | callback: () => void |
| | ) { |
| | this.write(chunk) |
| |
|
| | |
| | |
| | |
| | |
| | |
| | callback() |
| | } |
| |
|
| | public writeHead( |
| | statusCode: number, |
| | statusMessage?: string | undefined, |
| | headers?: OutgoingHttpHeaders | OutgoingHttpHeader[] | undefined |
| | ): this |
| | public writeHead( |
| | statusCode: number, |
| | headers?: OutgoingHttpHeaders | OutgoingHttpHeader[] | undefined |
| | ): this |
| | public writeHead( |
| | statusCode: number, |
| | statusMessage?: |
| | | string |
| | | OutgoingHttpHeaders |
| | | OutgoingHttpHeader[] |
| | | undefined, |
| | headers?: OutgoingHttpHeaders | OutgoingHttpHeader[] | undefined |
| | ): this { |
| | if (!headers && typeof statusMessage !== 'string') { |
| | headers = statusMessage |
| | } else if (typeof statusMessage === 'string' && statusMessage.length > 0) { |
| | this.statusMessage = statusMessage |
| | } |
| |
|
| | if (headers) { |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | if (Array.isArray(headers)) { |
| | |
| | |
| | |
| | |
| | for (let i = 0; i < headers.length; i += 2) { |
| | |
| | this.setHeader(headers[i] as string, headers[i + 1]) |
| | } |
| | } else { |
| | for (const [key, value] of Object.entries(headers)) { |
| | |
| | if (typeof value === 'undefined') continue |
| |
|
| | this.setHeader(key, value) |
| | } |
| | } |
| | } |
| |
|
| | this.statusCode = statusCode |
| | this.headersSent = true |
| | this.headPromiseResolve?.() |
| |
|
| | return this |
| | } |
| |
|
| | public hasHeader(name: string): boolean { |
| | return this.headers.has(name) |
| | } |
| |
|
| | public getHeader(name: string): string | undefined { |
| | return this.headers.get(name) ?? undefined |
| | } |
| |
|
| | public getHeaders(): OutgoingHttpHeaders { |
| | return toNodeOutgoingHttpHeaders(this.headers) |
| | } |
| |
|
| | public getHeaderNames(): string[] { |
| | return Array.from(this.headers.keys()) |
| | } |
| |
|
| | public setHeader(name: string, value: OutgoingHttpHeader) { |
| | if (Array.isArray(value)) { |
| | |
| | |
| | this.headers.delete(name) |
| |
|
| | for (const v of value) { |
| | this.headers.append(name, v) |
| | } |
| | } else if (typeof value === 'number') { |
| | this.headers.set(name, value.toString()) |
| | } else { |
| | this.headers.set(name, value) |
| | } |
| |
|
| | return this |
| | } |
| |
|
| | public removeHeader(name: string): void { |
| | this.headers.delete(name) |
| | } |
| |
|
| | public flushHeaders(): void { |
| | |
| | |
| | } |
| |
|
| | |
| | |
| |
|
| | public get strictContentLength(): boolean { |
| | throw new Error('Method not implemented.') |
| | } |
| |
|
| | public writeEarlyHints() { |
| | throw new Error('Method not implemented.') |
| | } |
| |
|
| | public get req(): IncomingMessage { |
| | throw new Error('Method not implemented.') |
| | } |
| |
|
| | public assignSocket() { |
| | throw new Error('Method not implemented.') |
| | } |
| |
|
| | public detachSocket(): void { |
| | throw new Error('Method not implemented.') |
| | } |
| |
|
| | public writeContinue(): void { |
| | throw new Error('Method not implemented.') |
| | } |
| |
|
| | public writeProcessing(): void { |
| | throw new Error('Method not implemented.') |
| | } |
| |
|
| | public get upgrading(): boolean { |
| | throw new Error('Method not implemented.') |
| | } |
| |
|
| | public get chunkedEncoding(): boolean { |
| | throw new Error('Method not implemented.') |
| | } |
| |
|
| | public get shouldKeepAlive(): boolean { |
| | throw new Error('Method not implemented.') |
| | } |
| |
|
| | public get useChunkedEncodingByDefault(): boolean { |
| | throw new Error('Method not implemented.') |
| | } |
| |
|
| | public get sendDate(): boolean { |
| | throw new Error('Method not implemented.') |
| | } |
| |
|
| | public setTimeout(): this { |
| | throw new Error('Method not implemented.') |
| | } |
| |
|
| | public addTrailers(): void { |
| | throw new Error('Method not implemented.') |
| | } |
| |
|
| | public setHeaders(): this { |
| | throw new Error('Method not implemented.') |
| | } |
| | } |
| |
|
| | interface RequestResponseMockerOptions { |
| | url: string |
| | headers?: IncomingHttpHeaders |
| | method?: string |
| | bodyReadable?: Stream.Readable |
| | resWriter?: (chunk: Uint8Array | Buffer | string) => boolean |
| | socket?: Socket | null |
| | } |
| |
|
| | export function createRequestResponseMocks({ |
| | url, |
| | headers = {}, |
| | method = 'GET', |
| | bodyReadable, |
| | resWriter, |
| | socket = null, |
| | }: RequestResponseMockerOptions) { |
| | return { |
| | req: new MockedRequest({ |
| | url, |
| | headers, |
| | method, |
| | socket, |
| | readable: bodyReadable, |
| | }), |
| | res: new MockedResponse({ socket, resWriter }), |
| | } |
| | } |
| |
|