obscure authorization header value when logging
[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('obscureAuthorizationHeader', function () {
145 it('obscures basic data', function () {
146 const authHeader = 'Basic Zm9vOmJhcg==';
147 const expected = 'Basic ************';
148 const result = common.obscureAuthorizationHeader(authHeader);
149 assert.strictEqual(result, expected);
150 });
151 it('obscures all of other types', function () {
152 const authHeader = 'someWeirdAuth';
153 const expected = '*************';
154 const result = common.obscureAuthorizationHeader(authHeader);
155 assert.strictEqual(result, expected);
156 });
157 it('does nothing when empty', function () {
158 const authHeader = undefined;
159 const expected = undefined;
160 const result = common.obscureAuthorizationHeader(authHeader);
161 assert.strictEqual(result, expected);
162 });
163 }); // obscureAuthorizationHeader
164
165 describe('scrubHeaderObject', function () {
166 it('', function () {
167 const data = {
168 headers: {
169 'foo': 'bar',
170 'authorization': 'Basic Zm9vOmJhcg==',
171 },
172 };
173 const expected = {
174 headers: {
175 'foo': 'bar',
176 'authorization': 'Basic ************',
177 },
178 };
179 common.scrubHeaderObject(data);
180 assert.deepStrictEqual(data, expected);
181 });
182 }); // scrubHeaderObject
183
184 describe('responseLogData', function () {
185 it('gives data', function () {
186 const res = {
187 getHeaders: () => ({}),
188 statusCode: 200,
189 blah: 'blah',
190 };
191 const result = common.responseLogData(res);
192 assert.deepStrictEqual(result, {
193 headers: {},
194 statusCode: 200,
195 });
196 });
197 }); // responseLogData
198
199 describe('setOptions', function () {
200 it('sets options', function () {
201 const expected = {
202 keyOne: 'default',
203 keyTwo: 'option',
204 keyThree: 'default',
205 };
206 const target = {};
207 const defaultOptions = {
208 keyOne: 'default',
209 keyTwo: 'default',
210 keyThree: 'default',
211 };
212 const options = {
213 keyTwo: 'option',
214 keyFour: 'option',
215 };
216 common.setOptions(target, defaultOptions, options);
217 assert.deepStrictEqual(target, expected);
218 });
219 }); // setOptions
220
221 describe('splitFirst', function () {
222 it('splits', function () {
223 const expected = ['foo', 'awoo'];
224 const src = 'foo?awoo';
225 const delim = '?';
226 const fill = 'fill';
227 const result = common.splitFirst(src, delim, fill);
228 assert.deepStrictEqual(result, expected);
229 });
230 it('fills', function () {
231 const expected = ['foo', 'fill'];
232 const src = 'foo';
233 const delim = '?';
234 const fill = 'fill';
235 const result = common.splitFirst(src, delim, fill);
236 assert.deepStrictEqual(result, expected);
237 });
238 }); // splitFirst
239
240 describe('mergeEnum', function () {
241 it('merges enums', function () {
242 const origEnum = {
243 ContentType: {
244 TextHTML: 'text/html',
245 },
246 ErrorResponse: {
247 BadRequest: {
248 statusCode: 400,
249 errorMessage: 'Bad Request',
250 },
251 },
252 };
253 const newEnum = {
254 ContentType: {
255 TextPlain: 'text/plain',
256 },
257 ErrorResponse: {
258 BadResponse: {
259 statusCode: 401,
260 errorMessage: 'Unauthorized',
261 },
262 },
263 Header: {
264 Accept: 'accept',
265 },
266 Value: 'value',
267 };
268 const mergedEnum = {
269 ContentType: {
270 TextHTML: 'text/html',
271 TextPlain: 'text/plain',
272 },
273 ErrorResponse: {
274 BadRequest: {
275 statusCode: 400,
276 errorMessage: 'Bad Request',
277 },
278 BadResponse: {
279 statusCode: 401,
280 errorMessage: 'Unauthorized',
281 },
282 },
283 Header: {
284 Accept: 'accept',
285 },
286 Value: 'value',
287 };
288 const result = common.mergeEnum(origEnum, newEnum);
289 assert.deepStrictEqual(result, mergedEnum);
290 });
291 }); // mergeEnum
292
293 describe('requestId', function () {
294 it('returns a string', function () {
295 const id = common.requestId();
296 assert.strictEqual(typeof id, 'string');
297 assert(id.length > 0);
298 });
299 }); // requestId
300
301 describe('ensureLoggerLevels', function () {
302 it('adds missing levels', function () {
303 const result = common.ensureLoggerLevels();
304 assert.deepStrictEqual(result, common.nullLogger);
305 });
306 }); // ensureLoggerLevels
307
308 describe('httpStatusCodeClass', function () {
309 it('works', function () {
310 for (const [statusCode, statusClassExpected] of Object.entries({
311 200: 2,
312 304: 3,
313 404: 4,
314 500: 5,
315 })) {
316 const statusClass = common.httpStatusCodeClass(statusCode);
317 assert.strictEqual(statusClass, statusClassExpected);
318 }
319 });
320 }); // ensureLoggerLevels
321
322 describe('mergeDeep', function () {
323 it('simple merge', function () {
324 const o1 = {
325 foo1: 'one',
326 };
327 const o2 = {
328 foo2: 'two',
329 };
330 const o3 = {
331 foo3: 'three',
332 };
333 const expected = {
334 foo1: 'one',
335 foo2: 'two',
336 foo3: 'three',
337 };
338 const result = common.mergeDeep(o1, o2, o3);
339 assert.deepStrictEqual(result, expected);
340 });
341 it('deep merge', function () {
342 const o1 = {
343 foo: {
344 quux: 1,
345 },
346 bar: 'baz',
347 };
348 const o2 = {
349 foo: {
350 myList: ['fancy'],
351 gleep: {
352 fraz: 2,
353 },
354 },
355 blah: 'frop',
356 };
357 const o3 = {
358 foo: {
359 myList: ['pants'],
360 gleep: {
361 troz: 'poit',
362 },
363 narf: 3,
364 },
365 bar: 'yup',
366 };
367 const expected = {
368 foo: {
369 myList: ['fancy', 'pants'],
370 gleep: {
371 fraz: 2,
372 troz: 'poit',
373 },
374 narf: 3,
375 quux: 1,
376 },
377 bar: 'yup',
378 blah: 'frop',
379 };
380 const result = common.mergeDeep(o1, o2, o3);
381 assert.deepStrictEqual(result, expected);
382 });
383 it('merged object is distinct', function () {
384 const o1 = {
385 foo: {
386 bar: 3,
387 },
388 };
389 const o2 = {
390 baz: {
391 quux: 4,
392 },
393 };
394 const expected = {
395 foo: {
396 bar: 3,
397 },
398 baz: {
399 quux: 4,
400 },
401 };
402 const result = common.mergeDeep(o1, o2);
403 assert.deepStrictEqual(result, expected);
404 const expected2 = {
405 foo: {
406 bar: 5,
407 },
408 baz: {
409 quux: 5,
410 },
411 };
412 result.foo.bar = 5;
413 result.baz.quux = 5;
414 assert.deepStrictEqual(result, expected2);
415 assert.strictEqual(o1.foo.bar, 3);
416 assert.strictEqual(o2.baz.quux, 4);
417 });
418 }); // mergeDeep
419
420 describe('unfoldHeaderLines', function () {
421 it('folds', function () {
422 const lines = [
423 'Normal-Header: some header data',
424 'Folded-Header: more data',
425 ' second line of data',
426 ' third line of data',
427 ];
428 const expected = [
429 'Normal-Header: some header data',
430 'Folded-Header: more data second line of data third line of data',
431 ];
432 const result = common.unfoldHeaderLines(lines);
433 assert.deepStrictEqual(result, expected);
434 });
435 it('covers no input', function () {
436 const lines = undefined;
437 const result = common.unfoldHeaderLines();
438 assert.deepStrictEqual(result, lines);
439 });
440 }); // unfoldHeaderLines
441
442 });