112 lines
4.3 KiB
TypeScript
112 lines
4.3 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import test from 'node:test';
|
|
|
|
import { buildScreenshotRequest, ValidationError } from './validation.js';
|
|
|
|
test('buildScreenshotRequest accepts supported screenshot parameters', () => {
|
|
const result = buildScreenshotRequest({
|
|
lat: '51.5074',
|
|
lon: '-0.1278',
|
|
zoom: '12.5',
|
|
tab: 'properties',
|
|
og: '1',
|
|
path: '/invite/abc123',
|
|
filter: ['Last known price:100000:500000', 'Total floor area (sqm):50:150'],
|
|
school: 'primary:good:2:1:10',
|
|
crime: ['Burglary (avg/yr):0:5', 'Vehicle crime (avg/yr):0:10'],
|
|
voteShare: ['% Labour:30:55', '% Conservative:10:35'],
|
|
ethnicity: ['% White:10:80', '% South Asian:5:35'],
|
|
amenityDistance: [
|
|
'Distance%20to%20nearest%20amenity%20(Park)%20(km):0:0.4',
|
|
'Distance%20to%20nearest%20amenity%20(Caf%C3%A9)%20(km):0:1.5',
|
|
],
|
|
transportDistance: 'Distance%20to%20nearest%20amenity%20(Bus%20stop)%20(km):0:0.3',
|
|
amenityCount2km: 'Number%20of%20amenities%20(Cafe)%20within%202km:2:8',
|
|
amenityCount5km: 'Number%20of%20amenities%20(Park)%20within%205km:1:20',
|
|
poi: 'supermarket',
|
|
tt: 'transit:kings-cross:Kings Cross:b:0:30',
|
|
share: 'abc123',
|
|
pc: 'SW1A 1AA',
|
|
});
|
|
|
|
assert.equal(result.pagePath, '/invite/abc123');
|
|
assert.equal(result.qs.get('lat'), '51.5074');
|
|
assert.equal(result.qs.get('lon'), '-0.1278');
|
|
assert.equal(result.qs.get('zoom'), '12.5');
|
|
assert.equal(result.qs.get('tab'), 'properties');
|
|
assert.deepEqual(result.qs.getAll('filter'), [
|
|
'Last known price:100000:500000',
|
|
'Total floor area (sqm):50:150',
|
|
]);
|
|
assert.deepEqual(result.qs.getAll('school'), ['primary:good:2:1:10']);
|
|
assert.deepEqual(result.qs.getAll('crime'), [
|
|
'Burglary (avg/yr):0:5',
|
|
'Vehicle crime (avg/yr):0:10',
|
|
]);
|
|
assert.deepEqual(result.qs.getAll('voteShare'), ['% Labour:30:55', '% Conservative:10:35']);
|
|
assert.deepEqual(result.qs.getAll('ethnicity'), ['% White:10:80', '% South Asian:5:35']);
|
|
assert.deepEqual(result.qs.getAll('amenityDistance'), [
|
|
'Distance%20to%20nearest%20amenity%20(Park)%20(km):0:0.4',
|
|
'Distance%20to%20nearest%20amenity%20(Caf%C3%A9)%20(km):0:1.5',
|
|
]);
|
|
assert.deepEqual(result.qs.getAll('transportDistance'), [
|
|
'Distance%20to%20nearest%20amenity%20(Bus%20stop)%20(km):0:0.3',
|
|
]);
|
|
assert.deepEqual(result.qs.getAll('amenityCount2km'), [
|
|
'Number%20of%20amenities%20(Cafe)%20within%202km:2:8',
|
|
]);
|
|
assert.deepEqual(result.qs.getAll('amenityCount5km'), [
|
|
'Number%20of%20amenities%20(Park)%20within%205km:1:20',
|
|
]);
|
|
assert.equal(result.qs.get('share'), 'abc123');
|
|
assert.equal(result.qs.get('pc'), 'SW1A 1AA');
|
|
});
|
|
|
|
test('buildScreenshotRequest safely passes through future dashboard parameters', () => {
|
|
const result = buildScreenshotRequest({
|
|
futureFilter: ['alpha:1:2', 'beta:3:4'],
|
|
viewMode_2: 'postcode',
|
|
});
|
|
|
|
assert.deepEqual(result.qs.getAll('futureFilter'), ['alpha:1:2', 'beta:3:4']);
|
|
assert.equal(result.qs.get('viewMode_2'), 'postcode');
|
|
});
|
|
|
|
test('buildScreenshotRequest rejects invalid numeric values', () => {
|
|
assert.throws(
|
|
() => buildScreenshotRequest({ lat: '91', lon: '-0.1', zoom: '12' }),
|
|
ValidationError,
|
|
);
|
|
assert.throws(
|
|
() => buildScreenshotRequest({ lat: '51abc', lon: '-0.1', zoom: '12' }),
|
|
ValidationError,
|
|
);
|
|
});
|
|
|
|
test('buildScreenshotRequest rejects unsafe paths', () => {
|
|
assert.throws(() => buildScreenshotRequest({ path: '//example.com' }), ValidationError);
|
|
assert.throws(() => buildScreenshotRequest({ path: '/../../etc/passwd' }), ValidationError);
|
|
});
|
|
|
|
test('buildScreenshotRequest limits repeated parameters', () => {
|
|
assert.throws(
|
|
() =>
|
|
buildScreenshotRequest({
|
|
filter: Array.from({ length: 41 }, (_, index) => `Feature ${index}:0:1`),
|
|
}),
|
|
ValidationError,
|
|
);
|
|
});
|
|
|
|
test('buildScreenshotRequest rejects control characters', () => {
|
|
assert.throws(() => buildScreenshotRequest({ filter: 'Feature:\u0000:1' }), ValidationError);
|
|
});
|
|
|
|
test('buildScreenshotRequest rejects reserved screenshot service parameters', () => {
|
|
assert.throws(() => buildScreenshotRequest({ screenshot: '0' }), ValidationError);
|
|
assert.throws(() => buildScreenshotRequest({ _auth: '1' }), ValidationError);
|
|
});
|
|
|
|
test('buildScreenshotRequest rejects unsafe passthrough parameter names', () => {
|
|
assert.throws(() => buildScreenshotRequest({ 'filter[]': 'Feature:0:1' }), ValidationError);
|
|
});
|