Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: fieldKeyParser: make clearer assertions about url manipulation #1291

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
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
37 changes: 25 additions & 12 deletions test/unit/http/middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,14 @@ const Option = require(appRoot + '/lib/util/option');
describe('middleware', () => {
describe('versionParser', () => {
const { versionParser } = middleware;

const createPreVersionParserRequest = ({ url, originalUrl, ...props }) => {
if (originalUrl) throw new Error('Should not specify originalUrl');
return createRequest({ url, originalUrl: url, ...props });
};

it('should fallthrough to the error handler if no version is present', (done) => {
const request = createRequest({ url: '/hello/test/v1' });
const request = createPreVersionParserRequest({ url: '/hello/test/v1' });
versionParser(request, null, (error) => {
error.isProblem.should.equal(true);
error.problemCode.should.equal(404.2);
Expand All @@ -18,7 +24,7 @@ describe('middleware', () => {
});

it('should fallthrough to the error handler if the version is wrong', (done) => {
const request = createRequest({ url: '/v4/users' });
const request = createPreVersionParserRequest({ url: '/v4/users' });
versionParser(request, null, (error) => {
error.isProblem.should.equal(true);
error.problemCode.should.equal(404.3);
Expand All @@ -28,15 +34,15 @@ describe('middleware', () => {
});

it('should strip off the version before calling next', (done) => {
const request = createRequest({ url: '/v1/users/23' });
const request = createPreVersionParserRequest({ url: '/v1/users/23' });
versionParser(request, null, () => {
request.url.should.equal('/users/23');
done();
});
});

it('should supply a numeric representation of the received number under request', (done) => {
const request = createRequest({ url: '/v1/forms/testform/submissions' });
const request = createPreVersionParserRequest({ url: '/v1/forms/testform/submissions' });
versionParser(request, null, () => {
request.apiVersion.should.equal(1);
done();
Expand All @@ -58,7 +64,8 @@ describe('middleware', () => {
const request = createRequest({ url: '/key/12|45/users/23' });
fieldKeyParser(request, null, () => {
request.fieldKey.should.eql(Option.of('12|45'));
request.url.should.equal('/users/23');
request.url .should.equal('/users/23'); // eslint-disable-line no-multi-spaces, no-whitespace-before-property
request.originalUrl.should.equal('/v1/key/12|45/users/23');
done();
});
});
Expand All @@ -67,7 +74,8 @@ describe('middleware', () => {
const request = createRequest({ url: '/key/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/users/23' });
fieldKeyParser(request, null, () => {
request.fieldKey.should.eql(Option.of('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'));
request.url.should.equal('/users/23');
request.url .should.equal('/users/23'); // eslint-disable-line no-multi-spaces, no-whitespace-before-property
request.originalUrl.should.equal('/v1/key/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/users/23');
done();
});
});
Expand All @@ -76,54 +84,59 @@ describe('middleware', () => {
const request = createRequest({ url: '/key/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%24aa!aaaaaaaaaaaaaaaaaa/users/23' });
fieldKeyParser(request, null, () => {
request.fieldKey.should.eql(Option.of('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa$aa!aaaaaaaaaaaaaaaaaa'));
request.url.should.equal('/users/23');
request.url .should.equal('/users/23'); // eslint-disable-line no-multi-spaces, no-whitespace-before-property
request.originalUrl.should.equal('/v1/key/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%24aa!aaaaaaaaaaaaaaaaaa/users/23');
done();
});
});

it('should pass through any query key content', (done) => {
const request = createRequest({ url: '/v1/users/23?st=inva|id' });
const request = createRequest({ url: '/users/23?st=inva|id' });
request.query.st.should.equal('inva|id');

fieldKeyParser(request, null, () => {
request.fieldKey.should.eql(Option.of('inva|id'));
request.url .should.equal('/users/23?st=inva|id'); // eslint-disable-line no-multi-spaces, no-whitespace-before-property
request.originalUrl.should.equal('/v1/key/inva|id/users/23?st=inva|id');
should(request.query.st).be.undefined();
done();
});
});

it('should escape slashes in the rewritten path prefix', (done) => {
const request = createRequest({ url: '/v1/users/23?st=in$va/i/d' });
const request = createRequest({ url: '/users/23?st=in$va/i/d' });
request.query.st.should.equal('in$va/i/d');

fieldKeyParser(request, null, () => {
request.fieldKey.should.eql(Option.of('in$va/i/d'));
request.url .should.equal('/users/23?st=in$va/i/d'); // eslint-disable-line no-multi-spaces, no-whitespace-before-property
request.originalUrl.should.equal('/v1/key/in$va%2Fi%2Fd/users/23?st=in$va/i/d');
should(request.query.st).be.undefined();
done();
});
});

it('should set Some(fk) and rewrite URL if a query key is found', (done) => {
const request = createRequest({ url: '/v1/users/23?st=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' });
const request = createRequest({ url: '/users/23?st=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' });
request.query.st.should.equal('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa');

fieldKeyParser(request, null, () => {
request.fieldKey.should.eql(Option.of('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'));
request.url .should.equal('/users/23?st=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'); // eslint-disable-line no-multi-spaces, no-whitespace-before-property
request.originalUrl.should.equal('/v1/key/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/users/23?st=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa');
should(request.query.st).be.undefined();
done();
});
});

it('should decode percent-encoded query keys', (done) => {
const request = createRequest({ url: '/v1/users/23?st=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%24aa!aaaaaaaaaaaaaaaaaa' });
const request = createRequest({ url: '/users/23?st=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%24aa!aaaaaaaaaaaaaaaaaa' });
request.query.st.should.equal('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa$aa!aaaaaaaaaaaaaaaaaa');

fieldKeyParser(request, null, () => {
request.fieldKey.should.eql(Option.of('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa$aa!aaaaaaaaaaaaaaaaaa'));
should(request.query.st).be.undefined();
request.url .should.equal('/users/23?st=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%24aa!aaaaaaaaaaaaaaaaaa'); // eslint-disable-line no-multi-spaces, no-whitespace-before-property
request.originalUrl.should.equal('/v1/key/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa$aa!aaaaaaaaaaaaaaaaaa/users/23?st=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%24aa!aaaaaaaaaaaaaaaaaa');
done();
});
});
Expand Down
22 changes: 16 additions & 6 deletions test/util/node-mocks-http.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,27 @@ const qs = (() => {
}
})();

const createRequest = options => {
if (!options?.url) return wrapped.createRequest(options);
const createRequest = ({ url, originalUrl, ...other }={}) => {
if (!url) return wrapped.createRequest({ originalUrl, ...other });

if (!originalUrl) {
if (url.startsWith('/v1')) {
throw new Error(
'URL should not start with /v1 when accessed after versionParser middleware. ' +
'If this is deliberate, set originalUrl explicitly.',
);
}
originalUrl = '/v1' + url; // eslint-disable-line no-param-reassign
}

const { search } = new URL(options.url, 'http://example.test');
const { query } = options;
const { search } = new URL(url, 'http://example.test');
const { query } = other;

if (!search) return wrapped.createRequest(options);
if (!search) return wrapped.createRequest({ url, originalUrl, ...other });

if (query != null) throw new Error('Unsupported: .query option and query string in .url simultaneously.');

return wrapped.createRequest({ ...options, query: qs.parse(search.substr(1)) });
return wrapped.createRequest({ url, originalUrl, ...other, query: qs.parse(search.substr(1)) });
};

const createResponse = options => wrapped.createResponse({ eventEmitter: EventEmitter, ...options });
Expand Down