Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions API.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,10 @@ Injects a fake request into an HTTP server.
- `remoteAddress` - an optional string specifying the client remote address. Defaults to `'127.0.0.1'`.
- `payload` - an optional request payload. Can be a string, Buffer, Stream or object.
- `simulate` - an object containing flags to simulate various conditions:
- `end` - indicates whether the request will fire an `end` event. Defaults to `undefined`, meaning an `end` event will fire.
- `end` - indicates whether the request will fire an `end` event. Defaults to `true`, meaning an `end` event will fire.
- `split` - indicates whether the request payload will be split into chunks. Defaults to `undefined`, meaning payload will not be chunked.
- `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'`.
- `close` - whether the request will emit a `close` event. Defaults to `undefined`, meaning no `close` event will be emitted.
- `close` - whether the request will emit a `close` event. Defaults to `true`, meaning a `close` event will be emitted.
- `validate` - Optional flag to validate this options object. Defaults to `true`.

Returns a response object where:
Expand Down
19 changes: 3 additions & 16 deletions lib/request.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ exports = module.exports = internals.Request = class extends Stream.Readable {
constructor(options) {

super({
emitClose: !!(options.simulate?.close),
emitClose: options.simulate?.close !== false,
autoDestroy: true // This is the default in node 14+
});

Expand Down Expand Up @@ -84,7 +84,6 @@ exports = module.exports = internals.Request = class extends Stream.Readable {

this._shot = {
payload,
isDone: false,
simulate: options.simulate ?? {}
};

Expand Down Expand Up @@ -114,18 +113,6 @@ exports = module.exports = internals.Request = class extends Stream.Readable {

setImmediate(() => {

if (this._shot.isDone) {
/* $lab:coverage:off$ */
if (this._shot.simulate.end !== false) { // 'end' defaults to true
this.push(null);
}
/* $lab:coverage:on$ */

return;
}

this._shot.isDone = true;

if (this._shot.payload) {
if (this._shot.simulate.split) {
this.push(this._shot.payload.slice(0, 1));
Expand All @@ -142,8 +129,8 @@ exports = module.exports = internals.Request = class extends Stream.Readable {
else if (this._shot.simulate.end !== false) { // 'end' defaults to true
this.push(null);
}
else if (this._shot.simulate.close) { // manually close (out of spec)
this.emit('close');
else {
this.destroy();
}
});
}
Expand Down
1 change: 1 addition & 0 deletions lib/response.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ exports = module.exports = internals.Response = class extends Http.ServerRespons

this.removeListener('close', abort);

req.destroy();
process.nextTick(() => onEnd(res));
};

Expand Down
28 changes: 22 additions & 6 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -725,7 +725,7 @@ describe('_read()', () => {

req.on('end', () => {

res.writeHead(200, { 'Content-Length': 0 });
res.writeHead(200, { 'Content-Length': buffer.length });
res.end(buffer);
req.destroy();
});
Expand All @@ -734,9 +734,9 @@ describe('_read()', () => {
};

const body = 'something special just for you';
const res = await Shot.inject(dispatch, { method: 'get', url: '/', payload: body });
const res = await Shot.inject(dispatch, { method: 'post', url: '/', payload: body });
expect(res.payload).to.equal(body);
expect(events).to.equal(['end']);
expect(events).to.equal(['end', 'close']);
});

it('supports async iteration', async () => {
Expand All @@ -758,6 +758,22 @@ describe('_read()', () => {
expect(res.payload).to.equal(body);
});

it('emits req "close" on "finish"', async () => {

const events = [];
const dispatch = function (req, res) {

res.end('ok');

internals.trackStreamLifetime(req, events, 'req');
internals.trackStreamLifetime(res, events, 'res');
};

const res = await Shot.inject(dispatch, { method: 'get', url: '/' });
expect(res.payload).to.equal('ok');
expect(events).to.equal(['close (req)']);
});

it('simulates split', async () => {

const events = [];
Expand All @@ -782,7 +798,7 @@ describe('_read()', () => {
const body = 'something special just for you';
const res = await Shot.inject(dispatch, { method: 'get', url: '/', payload: body, simulate: { split: true } });
expect(res.payload).to.equal(body);
expect(events).to.equal(['end']);
expect(events).to.equal(['end', 'close']);
});

it('simulates error (close = false)', async () => {
Expand Down Expand Up @@ -841,7 +857,7 @@ describe('_read()', () => {
internals.trackStreamLifetime(req, events);
};

Shot.inject(dispatch, { method: 'get', url: '/', simulate: { end: false } }); // Stuck
Shot.inject(dispatch, { method: 'get', url: '/', simulate: { close: false, end: false } }); // Stuck
await internals.wait(10);
expect(events).to.equal([]);
});
Expand All @@ -855,7 +871,7 @@ describe('_read()', () => {
internals.trackStreamLifetime(req, events);
};

Shot.inject(dispatch, { method: 'get', url: '/', payload: '1234567', simulate: { end: false } }); // Stuck
Shot.inject(dispatch, { method: 'get', url: '/', payload: '1234567', simulate: { close: false, end: false } }); // Stuck
await internals.wait(10);
expect(events).to.equal([]);
});
Expand Down
Loading