potentially serve static files with static headers
[squeep-api-dingus] / test / lib / common.js
1 /* eslint-disable capitalized-comments */
2 /* eslint-env mocha */
3 'use strict';
4
5 const assert = require('assert');
6 const sinon = require('sinon'); // eslint-disable-line node/no-unpublished-require
7 const common = require('../../lib/common');
8
9
10 describe('common', function () {
11
12 describe('fileScope', function () {
13 it('names a file path', function () {
14 const filename = 'lib/foo/bar.js';
15 const result = common.fileScope(filename)('baz');
16 assert.strictEqual(result, 'bar:baz');
17 });
18 it('names an index path', function () {
19 const filename = 'lib/foo/index.js';
20 const result = common.fileScope(filename)('baz');
21 assert.strictEqual(result, 'foo:baz');
22 });
23 }); // fileScope
24
25 describe('generateETag', function () {
26 it('generates a tag from data', function () {
27 const expected = '"RHUvNyculE/SyROjU0LqzN0arxibrlBnazAashP8UGE"';
28 const data = 'example data';
29 const result = common.generateETag(undefined, undefined, data);
30 assert.strictEqual(result, expected);
31 });
32 it('generates a tag from data and stats', function () {
33 const expected = '"mtI0qCyqXsZVfX0Pgi+G6mQM10y9yVyi1NZejXSYttk"';
34 const stat = {
35 mtimeMs: 1600113038270.2375,
36 };
37 const data = 'example data';
38 const result = common.generateETag(undefined, stat, data);
39 assert.strictEqual(result, expected);
40 });
41 }); // generateETag
42
43 describe('get', function () {
44 const def = 'default';
45 it('covers missing obj', function () {
46 const result = common.get(undefined, undefined, def);
47 assert.strictEqual(result, def);
48 });
49 it('covers missing prop', function () {
50 const result = common.get({}, undefined, def);
51 assert.strictEqual(result, def);
52 });
53 it('covers default', function () {
54 const result = common.get({ k: 'v' }, 'x', def);
55 assert.strictEqual(result, def);
56 });
57 it('covers prop', function () {
58 const result = common.get({ k: 'v' }, 'k', def);
59 assert.strictEqual(result, 'v');
60 });
61 }); // get
62
63 describe('isClientCached', function () {
64 let req, modifiedTimeMs, eTag;
65 const blueMoon = 'Sat, 31 Oct 2020 07:51:00 GMT';
66 const blueMoonMs = 1604130660000;
67 const oneDayMs = 86400000;
68 const anotherETag = '"RHUvNyculE/SyROjU0LqzN0arxibrlBnazAashP8UGE"';
69 beforeEach(function () {
70 req = {
71 getHeader: sinon.stub(),
72 };
73 modifiedTimeMs = 0;
74 eTag = '"mtI0qCyqXsZVfX0Pgi+G6mQM10y9yVyi1NZejXSYttk"';
75 });
76 it('no headers, not cached', function () {
77 const result = common.isClientCached(req, modifiedTimeMs, eTag);
78 assert.strictEqual(result, false);
79 });
80 it('modified header, not cached', function () {
81 req.getHeader.onCall(0).returns(blueMoon);
82 modifiedTimeMs = blueMoonMs + oneDayMs;
83 const result = common.isClientCached(req, modifiedTimeMs, eTag);
84 assert.strictEqual(result, false);
85 });
86 it('modified header, cached', function () {
87 req.getHeader.onCall(0).returns(blueMoon);
88 modifiedTimeMs = blueMoonMs - oneDayMs;
89 const result = common.isClientCached(req, modifiedTimeMs, eTag);
90 assert.strictEqual(result, true);
91 });
92 it('match header, not matched', function () {
93 req.getHeader.onCall(1).returns(anotherETag);
94 const result = common.isClientCached(req, modifiedTimeMs, eTag);
95 assert.strictEqual(result, false);
96 });
97 it('match header, matched', function () {
98 req.getHeader.onCall(1).returns(`${anotherETag}, ${eTag}, ${anotherETag}`);
99 const result = common.isClientCached(req, modifiedTimeMs, eTag);
100 assert.strictEqual(result, true);
101 });
102 it('match header any, matched', function () {
103 req.getHeader.onCall(1).returns('*');
104 const result = common.isClientCached(req, modifiedTimeMs, eTag);
105 assert.strictEqual(result, true);
106 });
107 it('modified header cached, match header not matched, not cached', function () {
108 req.getHeader.onCall(0).returns(blueMoon);
109 modifiedTimeMs = blueMoonMs - oneDayMs;
110 req.getHeader.onCall(1).returns(`${anotherETag}, ${anotherETag}`);
111 const result = common.isClientCached(req, modifiedTimeMs, eTag);
112 assert.strictEqual(result, false);
113 });
114 }); // iscClientCached
115
116 describe('pick', function () {
117 it('picks', function () {
118 const srcObj = {
119 a: 1,
120 b: 2,
121 c: 3,
122 };
123 const result = common.pick(srcObj, ['a', 'c', 'd']);
124 assert.deepStrictEqual(result, {
125 'a': 1,
126 'c': 3,
127 });
128 });
129 }); // pick
130
131 describe('requestLogData', function () {
132 it('gives data', function () {
133 const req = {
134 method: 'GET',
135 somethingElse: 'blah',
136 };
137 const result = common.requestLogData(req);
138 assert.deepStrictEqual(result, {
139 method: 'GET',
140 });
141 });
142 }); // requestLogData
143
144 describe('responseLogData', function () {
145 it('gives data', function () {
146 const res = {
147 getHeaders: () => ({}),
148 statusCode: 200,
149 blah: 'blah',
150 };
151 const result = common.responseLogData(res);
152 assert.deepStrictEqual(result, {
153 headers: {},
154 statusCode: 200,
155 });
156 });
157 }); // responseLogData
158
159 describe('setOptions', function () {
160 it('sets options', function () {
161 const expected = {
162 keyOne: 'default',
163 keyTwo: 'option',
164 keyThree: 'default',
165 };
166 const target = {};
167 const defaultOptions = {
168 keyOne: 'default',
169 keyTwo: 'default',
170 keyThree: 'default',
171 };
172 const options = {
173 keyTwo: 'option',
174 keyFour: 'option',
175 };
176 common.setOptions(target, defaultOptions, options);
177 assert.deepStrictEqual(target, expected);
178 });
179 }); // setOptions
180
181 describe('splitFirst', function () {
182 it('splits', function () {
183 const expected = ['foo', 'awoo'];
184 const src = 'foo?awoo';
185 const delim = '?';
186 const fill = 'fill';
187 const result = common.splitFirst(src, delim, fill);
188 assert.deepStrictEqual(result, expected);
189 });
190 it('fills', function () {
191 const expected = ['foo', 'fill'];
192 const src = 'foo';
193 const delim = '?';
194 const fill = 'fill';
195 const result = common.splitFirst(src, delim, fill);
196 assert.deepStrictEqual(result, expected);
197 });
198 }); // splitFirst
199
200 describe('mergeEnum', function () {
201 it('merges enums', function () {
202 const origEnum = {
203 ContentType: {
204 TextHTML: 'text/html',
205 },
206 ErrorResponse: {
207 BadRequest: {
208 statusCode: 400,
209 errorMessage: 'Bad Request',
210 },
211 },
212 };
213 const newEnum = {
214 ContentType: {
215 TextPlain: 'text/plain',
216 },
217 ErrorResponse: {
218 BadResponse: {
219 statusCode: 401,
220 errorMessage: 'Unauthorized',
221 },
222 },
223 Header: {
224 Accept: 'accept',
225 },
226 Value: 'value',
227 };
228 const mergedEnum = {
229 ContentType: {
230 TextHTML: 'text/html',
231 TextPlain: 'text/plain',
232 },
233 ErrorResponse: {
234 BadRequest: {
235 statusCode: 400,
236 errorMessage: 'Bad Request',
237 },
238 BadResponse: {
239 statusCode: 401,
240 errorMessage: 'Unauthorized',
241 },
242 },
243 Header: {
244 Accept: 'accept',
245 },
246 Value: 'value',
247 };
248 const result = common.mergeEnum(origEnum, newEnum);
249 assert.deepStrictEqual(result, mergedEnum);
250 });
251 }); // mergeEnum
252
253 describe('requestId', function () {
254 it('returns a string', function () {
255 const id = common.requestId();
256 assert.strictEqual(typeof id, 'string');
257 assert(id.length > 0);
258 });
259 }); // requestId
260
261 describe('ensureLoggerLevels', function () {
262 it('adds missing levels', function () {
263 const result = common.ensureLoggerLevels();
264 assert.deepStrictEqual(result, common.nullLogger);
265 });
266 }); // ensureLoggerLevels
267
268 describe('httpStatusCodeClass', function () {
269 it('works', function () {
270 for (const [statusCode, statusClassExpected] of Object.entries({
271 200: 2,
272 304: 3,
273 404: 4,
274 500: 5,
275 })) {
276 const statusClass = common.httpStatusCodeClass(statusCode);
277 assert.strictEqual(statusClass, statusClassExpected);
278 }
279 });
280 }); // ensureLoggerLevels
281
282 describe('mergeDeep', function () {
283 it('simple merge', function () {
284 const o1 = {
285 foo1: 'one',
286 };
287 const o2 = {
288 foo2: 'two',
289 };
290 const o3 = {
291 foo3: 'three',
292 };
293 const expected = {
294 foo1: 'one',
295 foo2: 'two',
296 foo3: 'three',
297 };
298 const result = common.mergeDeep(o1, o2, o3);
299 assert.deepStrictEqual(result, expected);
300 });
301 it('deep merge', function () {
302 const o1 = {
303 foo: {
304 quux: 1,
305 },
306 bar: 'baz',
307 };
308 const o2 = {
309 foo: {
310 myList: ['fancy'],
311 gleep: {
312 fraz: 2,
313 },
314 },
315 blah: 'frop',
316 };
317 const o3 = {
318 foo: {
319 myList: ['pants'],
320 gleep: {
321 troz: 'poit',
322 },
323 narf: 3,
324 },
325 bar: 'yup',
326 };
327 const expected = {
328 foo: {
329 myList: ['fancy', 'pants'],
330 gleep: {
331 fraz: 2,
332 troz: 'poit',
333 },
334 narf: 3,
335 quux: 1,
336 },
337 bar: 'yup',
338 blah: 'frop',
339 };
340 const result = common.mergeDeep(o1, o2, o3);
341 assert.deepStrictEqual(result, expected);
342 });
343 it('merged object is distinct', function () {
344 const o1 = {
345 foo: {
346 bar: 3,
347 },
348 };
349 const o2 = {
350 baz: {
351 quux: 4,
352 },
353 };
354 const expected = {
355 foo: {
356 bar: 3,
357 },
358 baz: {
359 quux: 4,
360 },
361 };
362 const result = common.mergeDeep(o1, o2);
363 assert.deepStrictEqual(result, expected);
364 const expected2 = {
365 foo: {
366 bar: 5,
367 },
368 baz: {
369 quux: 5,
370 },
371 };
372 result.foo.bar = 5;
373 result.baz.quux = 5;
374 assert.deepStrictEqual(result, expected2);
375 assert.strictEqual(o1.foo.bar, 3);
376 assert.strictEqual(o2.baz.quux, 4);
377 });
378 }); // mergeDeep
379
380 describe('unfoldHeaderLines', function () {
381 it('folds', function () {
382 const lines = [
383 'Normal-Header: some header data',
384 'Folded-Header: more data',
385 ' second line of data',
386 ' third line of data',
387 ];
388 const expected = [
389 'Normal-Header: some header data',
390 'Folded-Header: more data second line of data third line of data',
391 ];
392 const result = common.unfoldHeaderLines(lines);
393 assert.deepStrictEqual(result, expected);
394 });
395 it('covers no input', function () {
396 const lines = undefined;
397 const result = common.unfoldHeaderLines();
398 assert.deepStrictEqual(result, lines);
399 });
400 }); // unfoldHeaderLines
401
402 });