CoolFace
Apppublic

strong-tie/inbound-calls

sourceHugging Faceupdated 2y agoView on Hugging Face
0likes
README.md262 linesDownload Raw Back to light-my-request
1# Light my Request2 3[![CI](https://github.com/fastify/light-my-request/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/fastify/light-my-request/actions/workflows/ci.yml)4[![NPM version](https://img.shields.io/npm/v/light-my-request.svg?style=flat)](https://www.npmjs.com/package/light-my-request)5[![neostandard javascript style](https://img.shields.io/badge/code_style-neostandard-brightgreen?style=flat)](https://github.com/neostandard/neostandard)6 7Injects a fake HTTP request/response into a node HTTP server for simulating server logic, writing tests, or debugging.8Does not use a socket connection so can be run against an inactive server (server not in listen mode).9 10## Example11 12```javascript13const http = require('node:http')14const inject = require('light-my-request')15 16const dispatch = function (req, res) {17  const reply = 'Hello World'18  res.writeHead(200, { 'Content-Type': 'text/plain', 'Content-Length': reply.length })19  res.end(reply)20}21 22const server = http.createServer(dispatch)23 24inject(dispatch, { method: 'get', url: '/' }, (err, res) => {25  console.log(res.payload)26})27```28Note how `server.listen` is never called.29 30Async await and promises are supported as well!31```javascript32// promises33inject(dispatch, { method: 'get', url: '/' })34  .then(res => console.log(res.payload))35  .catch(console.log)36 37// async-await38try {39  const res = await inject(dispatch, { method: 'get', url: '/' })40  console.log(res.payload)41} catch (err) {42  console.log(err)43}44```45 46You can also use chaining methods if you do not pass the callback function. Check [here](#method-chaining) for details.47 48```js49// chaining methods50inject(dispatch)51  .get('/')                   // set the request method to GET, and request URL to '/'52  .headers({ foo: 'bar' })    // set the request headers53  .query({ foo: 'bar' })      // set the query parameters54  .end((err, res) => {55    console.log(res.payload)56  })57 58inject(dispatch)59  .post('/')                  // set the request method to POST, and request URL to '/'60  .payload('request payload') // set the request payload61  .body('request body')       // alias for payload62  .end((err, res) => {63    console.log(res.payload)64  })65 66// async-await is also supported67try {68  const chain = inject(dispatch).get('/')69  const res = await chain.end()70  console.log(res.payload)71} catch (err) {72  console.log(err)73}74```75 76File uploads (`multipart/form-data`) or form submit (`x-www-form-urlencoded`) can be achieved by using [form-auto-content](https://github.com/Eomm/form-auto-content) package as shown below:77 78```js79const formAutoContent = require('form-auto-content')80const fs = require('node:fs')81 82try {83  const form = formAutoContent({84    myField: 'hello',85    myFile: fs.createReadStream(`./path/to/file`)86  })87 88  const res = await inject(dispatch, {89    method: 'post',90    url: '/upload',91    ...form92  })93  console.log(res.payload)94} catch (err) {95  console.log(err)96}97```98 99This module ships with a handwritten TypeScript declaration file for TS support. The declaration exports a single namespace `LightMyRequest`. You can import it one of two ways:100```typescript101import * as LightMyRequest from 'light-my-request'102 103const dispatch: LightMyRequest.DispatchFunc = function (req, res) {104  const reply = 'Hello World'105  res.writeHead(200, { 'Content-Type': 'text/plain', 'Content-Length': reply.length })106  res.end(reply)107}108 109LightMyRequest.inject(dispatch, { method: 'get', url: '/' }, (err, res) => {110  console.log(res.payload)111})112 113// or114import { inject, DispatchFunc } from 'light-my-request'115 116const dispatch: DispatchFunc = function (req, res) {117  const reply = 'Hello World'118  res.writeHead(200, { 'Content-Type': 'text/plain', 'Content-Length': reply.length })119  res.end(reply)120}121 122inject(dispatch, { method: 'get', url: '/' }, (err, res) => {123  console.log(res.payload)124})125```126The declaration file exports types for the following parts of the API:127- `inject` - standard light-my-request `inject` method128- `DispatchFunc` - the fake HTTP dispatch function129- `InjectPayload` - a union type for valid payload types130- `isInjection` - standard light-my-request `isInjection` method131- `InjectOptions` - options object for `inject` method132- `Request` - custom light-my-request `request` object interface. Extends133  Node.js `stream.Readable` type by default. This behavior can be changed by134  setting the `Request` option in the `inject` method's options135- `Response` - custom light-my-request `response` object interface. Extends Node.js `http.ServerResponse` type136 137## API138 139#### `inject(dispatchFunc[, options, callback])`140 141Injects a fake request into an HTTP server.142 143- `dispatchFunc` - listener function. The same as you would pass to `Http.createServer` when making a node HTTP server. Has the signature `function (req, res)` where:144    - `req` - a simulated request object. Inherits from `Stream.Readable` by145      default. Optionally inherits from another class, set in146      `options.Request`147    - `res` - a simulated response object. Inherits from node's `Http.ServerResponse`.148- `options` - request options object where:149  - `url` | `path` - a string specifying the request URL.150  - `method` - a string specifying the HTTP request method, defaulting to `'GET'`.151  - `authority` - a string specifying the HTTP HOST header value to be used if no header is provided, and the `url`152    does not include an authority component. Defaults to `'localhost'`.153  - `headers` - an optional object containing request headers.154  - `cookies` - an optional object containing key-value pairs that will be encoded and added to `cookie` header. If the header is already set, the data will be appended.155  - `remoteAddress` - an optional string specifying the client remote address. Defaults to `'127.0.0.1'`.156  - `payload` - an optional request payload. Can be a string, Buffer, Stream, or object. If the payload is string, Buffer or Stream is used as is as the request payload. Otherwise, it is serialized with `JSON.stringify` forcing the request to have the `Content-type` equal to `application/json`157  - `query` - an optional object or string containing query parameters.158  - `body` - alias for payload.159  - `simulate` - an object containing flags to simulate various conditions:160    - `end` - indicates whether the request will fire an `end` event. Defaults to `undefined`, meaning an `end` event will fire.161    - `split` - indicates whether the request payload will be split into chunks. Defaults to `undefined`, meaning payload will not be chunked.162    - `error` - whether the request will emit an `error` event. Defaults to `undefined`, meaning no `error` event will be emitted. If set to `true`, the emitted error will have a message of `'Simulated'`.163    - `close` - whether the request will emit a `close` event. Defaults to `undefined`, meaning no `close` event will be emitted.164  - `validate` - Optional flag to validate this options object. Defaults to `true`.165  - `server` - Optional http server. It is used for binding the `dispatchFunc`.166  - `autoStart` - Automatically start the request as soon as the method167    is called. It is only valid when not passing a callback. Defaults to `true`.168  - `signal` - An `AbortSignal` that may be used to abort an ongoing request. Requires Node v16+.169  - `Request` - Optional type from which the `request` object should inherit170    instead of `stream.Readable`171  - `payloadAsStream` - if set to `true`, the response will be streamed and not accumulated; in this case `res.payload`, `res.rawPayload` will be undefined.172- `callback` - the callback function using the signature `function (err, res)` where:173  - `err` - error object174  - `res` - a response object where:175    - `raw` - an object containing the raw request and response objects where:176      - `req` - the simulated request object.177      - `res` - the simulated response object.178    - `headers` - an object containing the response headers.179    - `statusCode` - the HTTP status code.180    - `statusMessage` - the HTTP status message.181    - `payload` - the payload as a UTF-8 encoded string.182    - `body` - alias for payload.183    - `rawPayload` - the raw payload as a Buffer.184    - `trailers` - an object containing the response trailers.185    - `json` - a function that parses a json response payload and returns an object.186    - `stream` - a function that provides a `Readable` stream of the response payload.187    - `cookies` - a getter that parses the `set-cookie` response header and returns an array with all the cookies and their metadata.188 189Notes:190 191- You can also pass a string in place of the `options` object as a shorthand192  for `{url: string, method: 'GET'}`.193- Beware when using the `Request` option. That might make _light-my-request_194  slower. Sample benchmark result run on an i5-8600K CPU with `Request` set to195  `http.IncomingMessage`:196 197```198Request x 155,018 ops/sec ±0.47% (94 runs sampled)199Custom Request x 30,373 ops/sec ±0.64% (90 runs sampled)200Request With Cookies x 125,696 ops/sec ±0.29% (96 runs sampled)201Request With Cookies n payload x 114,391 ops/sec ±0.33% (97 runs sampled)202ParseUrl x 255,790 ops/sec ±0.23% (99 runs sampled)203ParseUrl and query x 194,479 ops/sec ±0.16% (99 runs sampled)204```205 206#### `inject.isInjection(obj)`207 208Checks if given object `obj` is a *light-my-request* `Request` object.209 210#### Method chaining211 212The following methods can be used in chaining:213- `delete`, `get`, `head`, `options`, `patch`, `post`, `put`, `trace`. They will set the HTTP request method and the request URL.214- `body`, `headers`, `payload`, `query`, `cookies`. They can be used to set the request options object.215 216And finally, you need to call `end`. It has the signature `function (callback)`.217If you invoke `end` without a callback function, the method will return a promise, thus you can:218 219```js220const chain = inject(dispatch).get('/')221 222try {223  const res = await chain.end()224  console.log(res.payload)225} catch (err) {226  // handle error227}228 229// or230chain.end()231  .then(res => {232    console.log(res.payload)233  })234  .catch(err => {235    // handle error236  })237```238 239By the way, you can also use promises without calling `end`!240 241```js242inject(dispatch)243  .get('/')244  .then(res => {245    console.log(res.payload)246  })247  .catch(err => {248    // handle error249  })250```251 252Note: The application would not respond multiple times. If you try to invoke any method after the application has responded, the application would throw an error.253 254## Acknowledgments255This project has been forked from [`hapi/shot`](https://github.com/hapijs/shot) because we wanted to support *Node ≥ v4* and not only *Node ≥ v8*.256All credits prior to commit [00a2a82](https://github.com/fastify/light-my-request/commit/00a2a82eb773b765003b6085788cc3564cd08326) go to the `hapi/shot` project [contributors](https://github.com/hapijs/shot/graphs/contributors).257Since commit [db8bced](https://github.com/fastify/light-my-request/commit/db8bced10b4367731688c8738621d42f39680efc) the project will be maintained by the Fastify team.258 259## License260 261Licensed under [BSD-3-Clause](./LICENSE).262