Skip to content

Commit

Permalink
use IncomingMessageMock for ResponseMock and SpecialPage (#1582)
Browse files Browse the repository at this point in the history
* use IncomingMessageMock for ResponseMock and SpecialPage

* fix tests

* renames
  • Loading branch information
miherlosev authored Apr 24, 2018
1 parent 0749bd4 commit c3ed48a
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 59 deletions.
26 changes: 26 additions & 0 deletions src/request-pipeline/incoming-message-mock.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Readable } from 'stream';

export default class IncomingMessageMock extends Readable {
constructor (init) {
super();

this.headers = init.headers;
this.trailers = init.trailers;
this.statusCode = init.statusCode;
this._body = this._getBody(init._body);
}

_read () {
this.push(this._body);
this._body = null;
}

_getBody (body) {
if (!body)
return new Buffer(0);

const bodyStr = typeof body === 'object' ? JSON.stringify(body) : String(body);

return Buffer.from(bodyStr);
}
}
15 changes: 3 additions & 12 deletions src/request-pipeline/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { fetchBody, respond404 } from '../utils/http';
import { inject as injectUpload } from '../upload';
import { respondOnWebSocket } from './websocket';
import { PassThrough } from 'stream';
import * as specialPage from './special-page';
import createSpecialPageResponse from './special-page';
import matchUrl from 'match-url-wildcard';
import * as requestEventInfo from '../session/events/info';
import REQUEST_EVENT_NAMES from '../session/events/names';
Expand All @@ -35,7 +35,7 @@ const stages = {

1: function sendDestinationRequest (ctx, next) {
if (ctx.isSpecialPage) {
ctx.destRes = specialPage.getResponse();
ctx.destRes = createSpecialPageResponse();
next();
}
else {
Expand Down Expand Up @@ -126,7 +126,7 @@ const stages = {
},

4: async function fetchContent (ctx, next) {
await getResponseBody(ctx);
ctx.destResBody = await fetchBody(ctx.destRes);

if (ctx.requestFilterRules.length)
ctx.saveNonProcessedDestResBody(ctx.destResBody);
Expand Down Expand Up @@ -293,15 +293,6 @@ function sendRequest (ctx, next) {
req.on('socketHangUp', () => ctx.req.socket.end());
}

async function getResponseBody (ctx) {
if (ctx.isSpecialPage)
ctx.destResBody = specialPage.getBody();
else if (ctx.mock)
ctx.destResBody = ctx.mock.getBody();
else
ctx.destResBody = await fetchBody(ctx.destRes);
}

// API
export function run (req, res, serverInfo, openSessions) {
const ctx = new RequestPipelineContext(req, res, serverInfo);
Expand Down
28 changes: 12 additions & 16 deletions src/request-pipeline/request-hooks/response-mock.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import IncomingMessageMock from '../incoming-message-mock';
import { JSON_MIME } from '../../utils/content-type';

const PAGE_CONTENT_TYPE = 'text/html; charset=utf-8';
Expand Down Expand Up @@ -76,28 +77,23 @@ export default class ResponseMock {
statusCode: this.statusCode || 200
};

if (this.headers)
response.headers = Object.assign(response.headers, this.headers);

if (this.body === void 0)
this.body = EMPTY_PAGE_HTML;
response._body = EMPTY_PAGE_HTML;
else if (typeof this.body === 'function') {
response.setBody = value => {
this.body = value;
response._body = value;
};
response = Object.assign(response, this.body(this.requestOptions, response));
delete response.setBody;
}

if (this.headers)
response.headers = Object.assign(response.headers, this.headers);

return response;
}

getBody () {
if (!this.body)
return new Uint8Array(0);
response = Object.assign(response, this.body(this.requestOptions, response));

const bodyStr = typeof this.body === 'object' ? JSON.stringify(this.body) : String(this.body);
delete response.setBody;
}
else
response._body = this.body;

return Buffer.from(bodyStr);
return new IncomingMessageMock(response);
}
}
12 changes: 6 additions & 6 deletions src/request-pipeline/special-page.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
export function getResponse () {
return {
import IncomingMessageMock from './incoming-message-mock';

export default function createSpecialPageResponse () {
return new IncomingMessageMock({
_body: new Buffer(0),
statusCode: 200,
trailers: {},
headers: {
'content-type': 'text/html',
'content-length': '0'
}
};
});
}

export function getBody () {
return new Buffer(0);
}
91 changes: 74 additions & 17 deletions test/server/proxy-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2125,29 +2125,86 @@ describe('Proxy', () => {
});
});

it('Should allow to use a response mock', () => {
const url = 'http://dummy_page.com';
const mock = new ResponseMock();
const rule = new RequestFilterRule(url);
const processedHtml = fs.readFileSync('test/server/data/empty-page/expected.html').toString();
describe('Response mock', () => {
it('Basic', () => {
const url = 'http://dummy_page.com';
const mock = new ResponseMock();
const rule = new RequestFilterRule(url);
const processedHtml = fs.readFileSync('test/server/data/empty-page/expected.html').toString();

session.addRequestEventListeners(rule, {
onRequest: e => e.setMock(mock)
session.addRequestEventListeners(rule, {
onRequest: e => e.setMock(mock)
});

const options = {
url: proxy.openSession(url, session),
headers: {
accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*!/!*;q=0.8'
}
};

return request(options)
.then(body => {
compareCode(body, processedHtml);

session.removeRequestEventListeners(rule);
});
});

const options = {
url: proxy.openSession(url, session),
headers: {
accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*!/!*;q=0.8'
}
};
it('Should allow to mock response without body (page)', () => {
const url = 'http://dummy_page.com';
const mock = new ResponseMock(null, 204);
const rule = new RequestFilterRule(url);

return request(options)
.then(body => {
compareCode(body, processedHtml);
session.addRequestEventListeners(rule, {
onRequest: e => e.setMock(mock)
});

session.removeRequestEventListeners(rule);
const options = {
url: proxy.openSession(url, session),
resolveWithFullResponse: true,
headers: {
accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*!/!*;q=0.8'
}
};

return request(options)
.then(res => {
const expected = fs.readFileSync('test/server/data/empty-page/expected.html').toString();

compareCode(res.body, expected);
expect(res.statusCode).eql(200);

session.removeRequestEventListeners(rule);
});
});

it('Should allow to mock a large response', () => {
const url = 'http://example.com/get';
const largeResponse = '1234567890'.repeat(1000000);
const mock = new ResponseMock(largeResponse);
const rule = new RequestFilterRule(url);

session.addRequestEventListeners(rule, {
onRequest: e => e.setMock(mock)
});

const options = {
url: proxy.openSession(url, session),
headers: {
accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*!/!*;q=0.8',
referer: proxy.openSession('http://example.com', session),
[XHR_HEADERS.requestMarker]: 'true'
}
};

return request(options)
.then(body => {
expect(body).eql(largeResponse);

session.removeRequestEventListeners(rule);
});
});
});

it('Should allow to set request options', () => {
Expand Down
14 changes: 6 additions & 8 deletions test/server/request-hook-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ describe('ResponseMock', () => {

expect(response.headers['content-type']).eql('application/json');
expect(response.statusCode).eql(200);
expect(mock.getBody().toString()).eql(JSON.stringify(data));
expect(response.read().toString()).eql(JSON.stringify(data));
});

it('HTML page', () => {
Expand All @@ -55,7 +55,7 @@ describe('ResponseMock', () => {

expect(response.headers['content-type']).to.include('text/html');
expect(response.statusCode).eql(200);
expect(mock.getBody().toString()).eql(html);
expect(response.read().toString()).eql(html);
});

it('Empty HTML page', () => {
Expand All @@ -64,18 +64,16 @@ describe('ResponseMock', () => {

expect(response.headers['content-type']).to.include('text/html');
expect(response.statusCode).eql(200);
expect(mock.getBody().toString()).eql('<html><body></body></html>');
expect(response.read().toString()).eql('<html><body></body></html>');
});

it('Custom status code', () => {
const mock = new ResponseMock(null, 204);
const response = mock.getResponse();
const body = mock.getBody();

expect(response.headers['content-type']).to.include('text/html');
expect(response.statusCode).eql(204);
expect(body).to.be.instanceof(Uint8Array);
expect(body.length).eql(0);
expect(response.read()).to.be.null;
});

it('Custom headers', () => {
Expand All @@ -85,7 +83,7 @@ describe('ResponseMock', () => {

expect(response.headers['content-type']).eql('application/javascript');
expect(response.statusCode).eql(200);
expect(mock.getBody().toString()).eql(script);
expect(response.read().toString()).eql(script);
});

it('Respond function', () => {
Expand All @@ -112,7 +110,7 @@ describe('ResponseMock', () => {
expect(response.headers['content-type']).to.include('text/html');
expect(response.statusCode).eql(555);
expect(response.headers['x-calculated-header-name']).eql('calculated-value');
expect(mock.getBody().toString()).eql('calculated body3');
expect(response.read().toString()).eql('calculated body3');
});
});
});
Expand Down

0 comments on commit c3ed48a

Please sign in to comment.