
Security News
Open Source Maintainers Demand Ability to Block Copilot-Generated Issues and PRs
Open source maintainers are urging GitHub to let them block Copilot from submitting AI-generated issues and pull requests to their repositories.
mock-web-server
Advanced tools
A lightweight simple helper utility to mock web-servers
Consider working on a API web client whose concerns are:
Consider the API server to be an expansive resource to access:
it could require API keys
it could be tethering your requests and fail builds
it risk resulting with monetary transactions
or whatever reason that drove you to decide to develop with a mock server
This utility helps you launch the simplest tiniest nodejs actual web-server, bind it to a port and configure it to play the role of the real API server.
But the real intention is to faciliate testing. The good part - is that you run it in-process to your test suite, and therefore can inspect progrmatically what requests it received (and perform asserts against them) and easily manipulate the response it emits.
This example uses a simple static response descriptor. Every time the server receives a request - it will:
You can replace the response descriptor using svr.reset(newResponseDescriptor)
.
This example uses mocha, however - it can be used with every any test runner.
const mockSvrFactory = require('mock-web-server');
const sut = require('../lib/my-web-client');
const Should = require('should');
const request = require('request');
describe('lib/my-web-client', () => {
let client;
before(() => client = sut({baseUrl: "http://localhost:3030"}));
it('should be a module object',
() => Should(sut).be.an.Object()
);
it('should have .get API', () => {
() => Should(sut).have.properties(['get'])
});
describe('when calling .get(id) with a valid id', () => {
let svr = mockSvrFactory({
status: 200,
headers: { 'content-type' : 'application/json' },
body: {
status: 'OK',
data: {
entity: { id: 333, note: 'it is what it is' }
}
}
});
before((done) => svr.listen(3030, done));
after(() => svr.close());
describe('and the server responds with valid response', () => {
let foundErr, foundRes;
beforeAll(() => {
svr.reset()
return client.get(333)
.then(entity => foundRes = entity)
.catch(err => foundErr = err)
});
it('should send content type header (app/json)', () => {
Should(svr.accepted[0])
.have.property('headers')
.have.property('content-type', 'application/json')
});
it('should hit the correct URL', () => {
Should(svr.accepted[0])
.have.properties({
method: 'GET',
url: 'http://localhost:3030/entity/333'
})
});
it('should resolve to the entity, unwrapped from protocol envelopes', () => {
Should(foundRes).eql({ id: 333, note: 'it is what it is' })
})
})
describe('and the server responds with a malformed response', () => {
let foundErr, foundRes;
beforeAll(() => {
//replace the response to a malformed response
svr.reset({status: 200, body: {}, headres: {} });
return client.get(333)
.then(entity => foundRes = entity)
.catch(err => foundErr = err)
})
it('should reject with a friendly error', () => {
Should(foundErr).have.property('message').match(/Bad response from backend service/)
Should(foundErr).have.property('id', 333);
Should(foundErr).have.property('innerError').be.an.Error();
})
})
describe('and the server does not respond at all', () => {
let foundErr, foundRes;
beforeAll(() => {
//replace the response to a malformed response
svr.close();
return client.get(333)
.then(entity => foundRes = entity)
.catch(err => foundErr = err)
})
it('should reject with a friendly error', () => {
Should(foundErr).have.property('message').match(/No response from backend service/)
Should(foundErr).have.property('id', 333);
Should(foundErr).have.property('innerError').be.an.Error();
})
})
})
})
(since: 0.9.2
)
In most cases, a static response to a single API is enough. However, sometimes you need your mock server to support few endpoints, and/or control for a non-idempotent API that accumulates state between responses.
For this, you can provide an preResponse
hook. The hook can map an accepted request to a response, or mutate the response object behind the server.
The server will keep collecting REPL-friendly views of the accepted requests as usual, so you can ask it what it heard from your tested client.
If you return a value from your preResponse hook - its props cascades their equivalents on the response descriptor the server holds.
In this example, the response descriptor the sever is initiated with defaults to a reply of Not-found. However, when the uri represents a supported entry - the returned object cascades the status: 200
and the body
for that entry.
const routes = {
'/api/user/1': { name: 'John Snow '},
'/api/user/2': { name: 'John Doe '},
};
const svr = mockSvrFactory({
status: 404,
headers: {
'Content-Type': 'application/json; charset: utf-8',
},
body: { err: 'not-found' },
preResponse: ({ uri }) => routes[uri] && ({ status: 200, body: this.routes[uri] }),
});
Using the hook as a mutator lets you manage a simple in-memory state on the response descriptor. If you don't want to manage it in a closure - you can use this object as a context object.
The response descriptor is both passed to the hook as a 2nd argument, and used as the context on which the hook is called, so you can use this
, or an arrow-function according to your preferences.
This example uses a queue of bodies on the response descriptor, and accesses it via the this
keyword.
const faker = require('faker');
const svr = mockSvrFactory({
queue: [{ one: 1 }, { two: 2 }, { three: 3 }],
preResponse: ({ method }, response) => {
const body = this.queue.unshift();
if (!body) return { status: 404, body: { status: 'empty' } };
return { status: 200, body };
},
});
This example uses a sum
counter on the response desriptor, and accesses it via the 2nd argument:
const svr = mockSvrFactory({
sum: 0,
preResponse: ({ body: { sum = 0 } }, response) => {
this.sum += sum;
return { status: 200, body: { requests: svr.accepted.length, sum: this.sum } };
},
});
mock-web-server
β should be a factory function that names 1 arguments - response (it has an optional 2nd param for options)
when provided only a response object
β should not fail and return an server instance
when provided a response object and the optional config object
β should not fail and return an server instance
a server instance obtained by the factory
supported API:
β method .listen(port, done) to start the server
β method .close(done) to close it
β attribute .response as the provided response
β attribute .accepted as array of accepted requests
β method .reset() to clear the accepted requests and optionally - reset the response
starting and closing the server should work
and the server should serve requests with
β the provided status code
β the provided headers
β the provided body
and server keeps a REPL-friendly view of the accepted requests that is cleared with .reset()
β found 3 requests
β views are serializable
β .reset() returns the interface and clears the accepted array
β .reset(response) returns the interface and sets the response
structure of a request view should contain
β httpVersion
β method
β url
β headers
β rawHeaders
β upgrade
β body
β trailers
β rawTrailers
when response object has a preResponse hook
and the response hook returns an object
the server should serve request with response returned by the hook
β the provided status code
β the provided headers
β the provided body
and the response hook mutates current response using `this`
the server should serve request with response mutated by the hook
β the provided status code
β the provided headers
β the provided body
closing the server with a callback
β should call the callback as well as closing the server
an error passed by body-parser
β should be collected to the request view as .parseError
~internals
.mapParsers(parsers)
β should be a function that names 1 argument - parsers
when called with an object element
β should not fail
when called with a function element
β should not fail
when called with a string element that is not a body-parser built-in
β should not fail and map it to an instance produced by the required module
when called with an object element who's first key is not a body-parser built-in
β should not fail
β should map it to an instance produced by the required module
β should pass it the arguments
39 passing (53ms)
0.9.2
FAQs
A lightweight simple helper utility to mock web-servers
We found that mock-web-server demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago.Β It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Open source maintainers are urging GitHub to let them block Copilot from submitting AI-generated issues and pull requests to their repositories.
Research
Security News
Malicious Koishi plugin silently exfiltrates messages with hex strings to a hardcoded QQ account, exposing secrets in chatbots across platforms.
Research
Security News
Malicious PyPI checkers validate stolen emails against TikTok and Instagram APIs, enabling targeted account attacks and dark web credential sales.