add more header tests

This commit is contained in:
Randall Schmidt 2021-06-11 15:25:53 -04:00
parent 901e32fa09
commit 7f2dba84a3
4 changed files with 148 additions and 76 deletions

@ -4,19 +4,21 @@ class Headers {
} }
entries() { entries() {
return Object.entries(this.rawHeaders); return Object.entries(this.rawHeaders)
.sort((e1, e2) => e1[0].localeCompare(e2[0]))
.map(([key, val]) => [key, val[0]]);
} }
keys() { keys() {
return Object.keys(this.rawHeaders); return this.entries().map((e) => e[0]);
} }
values() { values() {
return Object.values(this.rawHeaders); return this.entries().map((e) => e[1]);
} }
get(name) { get(name) {
return this.rawHeaders[name.toLowerCase()] || null; return (this.rawHeaders[name.toLowerCase()] || [])[0];
} }
has(name) { has(name) {

@ -73,16 +73,13 @@ function getCacheKey(requestArguments) {
async function createRawResponse(fetchRes) { async function createRawResponse(fetchRes) {
const buffer = await fetchRes.buffer(); const buffer = await fetchRes.buffer();
const rawHeaders = Array.from(fetchRes.headers.entries())
.reduce((aggregate, entry) => ({ ...aggregate, [entry[0]]: entry[1] }), {});
return { return {
status: fetchRes.status, status: fetchRes.status,
statusText: fetchRes.statusText, statusText: fetchRes.statusText,
type: fetchRes.type, type: fetchRes.type,
url: fetchRes.url, url: fetchRes.url,
ok: fetchRes.ok, ok: fetchRes.ok,
headers: rawHeaders, headers: fetchRes.headers.raw(),
redirected: fetchRes.redirected, redirected: fetchRes.redirected,
bodyBuffer: buffer, bodyBuffer: buffer,
}; };

@ -5,7 +5,7 @@
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"test": "mocha --timeout 10000 --exit", "test": "mocha --timeout 10000 --exit",
"coverage": "nyc --reporter=lcov npm test", "coverage": "nyc --reporter=lcov --reporter=text npm test",
"lint": "./node_modules/.bin/eslint .", "lint": "./node_modules/.bin/eslint .",
"lintfix": "./node_modules/.bin/eslint . --fix" "lintfix": "./node_modules/.bin/eslint . --fix"
}, },

@ -3,8 +3,9 @@ const FormData = require('form-data');
const assert = require('assert'); const assert = require('assert');
const rimraf = require('rimraf'); const rimraf = require('rimraf');
const path = require('path'); const path = require('path');
const FetchCache = require('../index.js');
const { URLSearchParams } = require('url'); const { URLSearchParams } = require('url');
const standardFetch = require('node-fetch');
const FetchCache = require('../index.js');
const MemoryCache = require('../classes/caching/memory_cache.js'); const MemoryCache = require('../classes/caching/memory_cache.js');
const CACHE_PATH = path.join(__dirname, '..', '.cache'); const CACHE_PATH = path.join(__dirname, '..', '.cache');
@ -19,98 +20,170 @@ const PNG_BODY_URL = 'https://httpbin.org/image/png';
const TEXT_BODY_EXPECTED = 'User-agent: *\nDisallow: /deny\n'; const TEXT_BODY_EXPECTED = 'User-agent: *\nDisallow: /deny\n';
let fetch; let cachedFetch;
let res;
let body; let body;
function post(body) { function post(body) {
return { method: 'POST', body }; return { method: 'POST', body };
} }
function removeDates(arrOrObj) {
if (arrOrObj.date) {
const copy = { ...arrOrObj };
delete copy.date;
return copy;
}
if (Array.isArray(arrOrObj)) {
return [...arrOrObj].filter(e => !Date.parse(e));
}
return arrOrObj;
}
async function dualFetch(...args) {
const [cachedFetchResponse, standardFetchResponse] = await Promise.all([
cachedFetch(...args),
standardFetch(...args),
]);
return { cachedFetchResponse, standardFetchResponse };
}
beforeEach(async function() { beforeEach(async function() {
rimraf.sync(CACHE_PATH); rimraf.sync(CACHE_PATH);
fetch = FetchCache.withCache(new MemoryCache()); cachedFetch = FetchCache.withCache(new MemoryCache());
}); });
describe('Basic property tests', function() { describe('Basic property tests', function() {
it('Has a status property', async function() { it('Has a status property', async function() {
res = await fetch(TWO_HUNDRED_URL); let { cachedFetchResponse, standardFetchResponse } = await dualFetch(TWO_HUNDRED_URL);
assert.strictEqual(res.status, 200); assert.strictEqual(cachedFetchResponse.status, standardFetchResponse.status);
res = await fetch(TWO_HUNDRED_URL); cachedFetchResponse = await cachedFetch(TWO_HUNDRED_URL);
assert.strictEqual(res.status, 200); assert.strictEqual(cachedFetchResponse.status, standardFetchResponse.status);
}); });
it('Has a statusText property', async function() { it('Has a statusText property', async function() {
res = await fetch(TWO_HUNDRED_URL); let { cachedFetchResponse, standardFetchResponse } = await dualFetch(TWO_HUNDRED_URL);
assert.strictEqual(res.statusText, 'OK'); assert.strictEqual(cachedFetchResponse.statusText, standardFetchResponse.statusText);
res = await fetch(TWO_HUNDRED_URL); cachedFetchResponse = await cachedFetch(TWO_HUNDRED_URL);
assert.strictEqual(res.statusText, 'OK'); assert.strictEqual(cachedFetchResponse.statusText, standardFetchResponse.statusText);
}); });
it('Has a url property', async function() { it('Has a url property', async function() {
res = await fetch(TWO_HUNDRED_URL); let { cachedFetchResponse, standardFetchResponse } = await dualFetch(TWO_HUNDRED_URL);
assert.strictEqual(res.url, TWO_HUNDRED_URL); assert.strictEqual(cachedFetchResponse.url, standardFetchResponse.url);
res = await fetch(TWO_HUNDRED_URL); cachedFetchResponse = await cachedFetch(TWO_HUNDRED_URL);
assert.strictEqual(res.url, TWO_HUNDRED_URL); assert.strictEqual(cachedFetchResponse.url, standardFetchResponse.url);
}); });
it('Has an ok property', async function() { it('Has an ok property', async function() {
res = await fetch(FOUR_HUNDRED_URL); let { cachedFetchResponse, standardFetchResponse } = await dualFetch(FOUR_HUNDRED_URL);
assert.strictEqual(res.ok, false); assert.strictEqual(cachedFetchResponse.ok, standardFetchResponse.ok);
assert.strictEqual(res.status, 400); assert.strictEqual(cachedFetchResponse.status, standardFetchResponse.status);
res = await fetch(FOUR_HUNDRED_URL); cachedFetchResponse = await cachedFetch(FOUR_HUNDRED_URL);
assert.strictEqual(res.ok, false); assert.strictEqual(cachedFetchResponse.ok, standardFetchResponse.ok);
assert.strictEqual(res.status, 400); assert.strictEqual(cachedFetchResponse.status, standardFetchResponse.status);
});
it('Has a headers property', async function() {
res = await fetch(TWO_HUNDRED_URL);
assert.notStrictEqual(res.headers, undefined);
res = await fetch(TWO_HUNDRED_URL);
assert.notStrictEqual(res.headers, undefined);
}); });
it('Has a redirected property', async function() { it('Has a redirected property', async function() {
res = await fetch(THREE_HUNDRED_TWO_URL); let { cachedFetchResponse, standardFetchResponse } = await dualFetch(THREE_HUNDRED_TWO_URL);
assert.strictEqual(res.redirected, true); assert.strictEqual(cachedFetchResponse.redirected, standardFetchResponse.redirected);
res = await fetch(THREE_HUNDRED_TWO_URL); cachedFetchResponse = await cachedFetch(THREE_HUNDRED_TWO_URL);
assert.strictEqual(res.redirected, true); assert.strictEqual(cachedFetchResponse.redirected, standardFetchResponse.redirected);
}); });
}).timeout(10000); }).timeout(10000);
describe('Header tests', function() {
it('Gets correct raw headers', async function() {
let { cachedFetchResponse, standardFetchResponse } = await dualFetch(TWO_HUNDRED_URL);
assert.deepStrictEqual(
removeDates(cachedFetchResponse.headers.raw()),
removeDates(standardFetchResponse.headers.raw()),
);
cachedFetchResponse = await cachedFetch(TWO_HUNDRED_URL);
assert.deepStrictEqual(
removeDates(cachedFetchResponse.headers.raw()),
removeDates(standardFetchResponse.headers.raw()),
);
});
it('Gets correct header keys', async function() {
let { cachedFetchResponse, standardFetchResponse } = await dualFetch(TWO_HUNDRED_URL);
assert.deepStrictEqual(cachedFetchResponse.headers.keys(), [...standardFetchResponse.headers.keys()]);
cachedFetchResponse = await cachedFetch(TWO_HUNDRED_URL);
assert.deepStrictEqual(cachedFetchResponse.headers.keys(), [...standardFetchResponse.headers.keys()]);
});
it('Gets correct header values', async function() {
let { cachedFetchResponse, standardFetchResponse } = await dualFetch(TWO_HUNDRED_URL);
assert.deepStrictEqual(cachedFetchResponse.headers.values(), [...standardFetchResponse.headers.values()]);
cachedFetchResponse = await cachedFetch(TWO_HUNDRED_URL);
assert.deepStrictEqual(cachedFetchResponse.headers.values(), [...standardFetchResponse.headers.values()]);
});
it('Gets correct header entries', async function() {
let { cachedFetchResponse, standardFetchResponse } = await dualFetch(TWO_HUNDRED_URL);
assert.deepStrictEqual(cachedFetchResponse.headers.entries(), [...standardFetchResponse.headers.entries()]);
cachedFetchResponse = await cachedFetch(TWO_HUNDRED_URL);
assert.deepStrictEqual(cachedFetchResponse.headers.entries(), [...standardFetchResponse.headers.entries()]);
});
it('Can get a header by value', async function() {
let { cachedFetchResponse, standardFetchResponse } = await dualFetch(TWO_HUNDRED_URL);
assert(standardFetchResponse.headers.get('content-length'));
assert.deepStrictEqual(cachedFetchResponse.headers.get('content-length'), standardFetchResponse.headers.get('content-length'));
cachedFetchResponse = await cachedFetch(TWO_HUNDRED_URL);
assert.deepStrictEqual(cachedFetchResponse.headers.get('content-length'), standardFetchResponse.headers.get('content-length'));
});
it('Can get whether a header is present', async function() {
let { cachedFetchResponse, standardFetchResponse } = await dualFetch(TWO_HUNDRED_URL);
assert(standardFetchResponse.headers.has('content-length'));
assert.deepStrictEqual(cachedFetchResponse.headers.has('content-length'), standardFetchResponse.headers.has('content-length'));
cachedFetchResponse = await cachedFetch(TWO_HUNDRED_URL);
assert.deepStrictEqual(cachedFetchResponse.headers.has('content-length'), standardFetchResponse.headers.has('content-length'));
});
});
describe('Cache tests', function() { describe('Cache tests', function() {
it('Uses cache', async function() { it('Uses cache', async function() {
res = await fetch(TWO_HUNDRED_URL); res = await cachedFetch(TWO_HUNDRED_URL);
assert.strictEqual(res.fromCache, false); assert.strictEqual(res.fromCache, false);
res = await fetch(TWO_HUNDRED_URL); res = await cachedFetch(TWO_HUNDRED_URL);
assert.strictEqual(res.fromCache, true); assert.strictEqual(res.fromCache, true);
}); });
it('Can eject from cache', async function() { it('Can eject from cache', async function() {
res = await fetch(TWO_HUNDRED_URL); res = await cachedFetch(TWO_HUNDRED_URL);
assert.strictEqual(res.fromCache, false); assert.strictEqual(res.fromCache, false);
res = await fetch(TWO_HUNDRED_URL); res = await cachedFetch(TWO_HUNDRED_URL);
assert.strictEqual(res.fromCache, true); assert.strictEqual(res.fromCache, true);
await res.ejectFromCache(); await res.ejectFromCache();
res = await fetch(TWO_HUNDRED_URL); res = await cachedFetch(TWO_HUNDRED_URL);
assert.strictEqual(res.fromCache, false); assert.strictEqual(res.fromCache, false);
res = await fetch(TWO_HUNDRED_URL); res = await cachedFetch(TWO_HUNDRED_URL);
assert.strictEqual(res.fromCache, true); assert.strictEqual(res.fromCache, true);
}); });
it('Does not error if rejecting from cache twice', async function() { it('Does not error if ejecting from cache twice', async function() {
res = await fetch(TWO_HUNDRED_URL); res = await cachedFetch(TWO_HUNDRED_URL);
assert.strictEqual(res.fromCache, false); assert.strictEqual(res.fromCache, false);
await res.ejectFromCache(); await res.ejectFromCache();
@ -118,34 +191,34 @@ describe('Cache tests', function() {
}); });
it('Gives different string bodies different cache keys', async function() { it('Gives different string bodies different cache keys', async function() {
res = await fetch(TWO_HUNDRED_URL, post('a')); res = await cachedFetch(TWO_HUNDRED_URL, post('a'));
assert.strictEqual(res.fromCache, false); assert.strictEqual(res.fromCache, false);
res = await fetch(TWO_HUNDRED_URL, post('b')); res = await cachedFetch(TWO_HUNDRED_URL, post('b'));
assert.strictEqual(res.fromCache, false); assert.strictEqual(res.fromCache, false);
}); });
it('Gives same string bodies same cache keys', async function() { it('Gives same string bodies same cache keys', async function() {
res = await fetch(TWO_HUNDRED_URL, post('a')); res = await cachedFetch(TWO_HUNDRED_URL, post('a'));
assert.strictEqual(res.fromCache, false); assert.strictEqual(res.fromCache, false);
res = await fetch(TWO_HUNDRED_URL, post('a')); res = await cachedFetch(TWO_HUNDRED_URL, post('a'));
assert.strictEqual(res.fromCache, true); assert.strictEqual(res.fromCache, true);
}); });
it('Gives different URLSearchParams different cache keys', async function() { it('Gives different URLSearchParams different cache keys', async function() {
res = await fetch(TWO_HUNDRED_URL, post(new URLSearchParams('a=a'))); res = await cachedFetch(TWO_HUNDRED_URL, post(new URLSearchParams('a=a')));
assert.strictEqual(res.fromCache, false); assert.strictEqual(res.fromCache, false);
res = await fetch(TWO_HUNDRED_URL, post(new URLSearchParams('a=b'))); res = await cachedFetch(TWO_HUNDRED_URL, post(new URLSearchParams('a=b')));
assert.strictEqual(res.fromCache, false); assert.strictEqual(res.fromCache, false);
}); });
it('Gives same URLSearchParams same cache keys', async function() { it('Gives same URLSearchParams same cache keys', async function() {
res = await fetch(TWO_HUNDRED_URL, post(new URLSearchParams('a=a'))); res = await cachedFetch(TWO_HUNDRED_URL, post(new URLSearchParams('a=a')));
assert.strictEqual(res.fromCache, false); assert.strictEqual(res.fromCache, false);
res = await fetch(TWO_HUNDRED_URL, post(new URLSearchParams('a=a'))); res = await cachedFetch(TWO_HUNDRED_URL, post(new URLSearchParams('a=a')));
assert.strictEqual(res.fromCache, true); assert.strictEqual(res.fromCache, true);
}); });
@ -153,20 +226,20 @@ describe('Cache tests', function() {
const s1 = fs.createReadStream(__filename); const s1 = fs.createReadStream(__filename);
const s2 = fs.createReadStream(path.join(__dirname, '..', 'index.js')); const s2 = fs.createReadStream(path.join(__dirname, '..', 'index.js'));
res = await fetch(TWO_HUNDRED_URL, post(s1)); res = await cachedFetch(TWO_HUNDRED_URL, post(s1));
assert.strictEqual(res.fromCache, false); assert.strictEqual(res.fromCache, false);
res = await fetch(TWO_HUNDRED_URL, post(s2)); res = await cachedFetch(TWO_HUNDRED_URL, post(s2));
assert.strictEqual(res.fromCache, false); assert.strictEqual(res.fromCache, false);
}); });
it('Gives the same read streams the same cache key', async function() { it('Gives the same read streams the same cache key', async function() {
const s1 = fs.createReadStream(__filename); const s1 = fs.createReadStream(__filename);
res = await fetch(TWO_HUNDRED_URL, post(s1)); res = await cachedFetch(TWO_HUNDRED_URL, post(s1));
assert.strictEqual(res.fromCache, false); assert.strictEqual(res.fromCache, false);
res = await fetch(TWO_HUNDRED_URL, post(s1)); res = await cachedFetch(TWO_HUNDRED_URL, post(s1));
assert.strictEqual(res.fromCache, true); assert.strictEqual(res.fromCache, true);
}); });
@ -177,10 +250,10 @@ describe('Cache tests', function() {
const data2 = new FormData(); const data2 = new FormData();
data2.append('b', 'b'); data2.append('b', 'b');
res = await fetch(TWO_HUNDRED_URL, post(data1)); res = await cachedFetch(TWO_HUNDRED_URL, post(data1));
assert.strictEqual(res.fromCache, false); assert.strictEqual(res.fromCache, false);
res = await fetch(TWO_HUNDRED_URL, post(data2)); res = await cachedFetch(TWO_HUNDRED_URL, post(data2));
assert.strictEqual(res.fromCache, false); assert.strictEqual(res.fromCache, false);
}); });
@ -191,17 +264,17 @@ describe('Cache tests', function() {
const data2 = new FormData(); const data2 = new FormData();
data2.append('a', 'a'); data2.append('a', 'a');
res = await fetch(TWO_HUNDRED_URL, post(data1)); res = await cachedFetch(TWO_HUNDRED_URL, post(data1));
assert.strictEqual(res.fromCache, false); assert.strictEqual(res.fromCache, false);
res = await fetch(TWO_HUNDRED_URL, post(data2)); res = await cachedFetch(TWO_HUNDRED_URL, post(data2));
assert.strictEqual(res.fromCache, true); assert.strictEqual(res.fromCache, true);
}); });
}).timeout(10000); }).timeout(10000);
describe('Data tests', function() { describe('Data tests', function() {
it('Refuses to consume body twice', async function() { it('Refuses to consume body twice', async function() {
res = await fetch(TEXT_BODY_URL); res = await cachedFetch(TEXT_BODY_URL);
await res.text(); await res.text();
try { try {
@ -213,43 +286,43 @@ describe('Data tests', function() {
}); });
it('Can get text body', async function() { it('Can get text body', async function() {
res = await fetch(TEXT_BODY_URL); res = await cachedFetch(TEXT_BODY_URL);
body = await res.text(); body = await res.text();
assert.strictEqual(body, TEXT_BODY_EXPECTED); assert.strictEqual(body, TEXT_BODY_EXPECTED);
assert.strictEqual(res.fromCache, false); assert.strictEqual(res.fromCache, false);
res = await fetch(TEXT_BODY_URL); res = await cachedFetch(TEXT_BODY_URL);
body = await res.text(); body = await res.text();
assert.strictEqual(body, TEXT_BODY_EXPECTED); assert.strictEqual(body, TEXT_BODY_EXPECTED);
assert.strictEqual(res.fromCache, true); assert.strictEqual(res.fromCache, true);
}); });
it('Can get JSON body', async function() { it('Can get JSON body', async function() {
res = await fetch(JSON_BODY_URL); res = await cachedFetch(JSON_BODY_URL);
body = await res.json(); body = await res.json();
assert(body.slideshow); assert(body.slideshow);
assert.strictEqual(res.fromCache, false); assert.strictEqual(res.fromCache, false);
res = await fetch(JSON_BODY_URL); res = await cachedFetch(JSON_BODY_URL);
body = await res.json(); body = await res.json();
assert(body.slideshow); assert(body.slideshow);
assert.strictEqual(res.fromCache, true); assert.strictEqual(res.fromCache, true);
}); });
it('Can get PNG buffer body', async function() { it('Can get PNG buffer body', async function() {
res = await fetch(PNG_BODY_URL); res = await cachedFetch(PNG_BODY_URL);
body = await res.buffer(); body = await res.buffer();
assert.strictEqual(expectedPngBuffer.equals(body), true); assert.strictEqual(expectedPngBuffer.equals(body), true);
assert.strictEqual(res.fromCache, false); assert.strictEqual(res.fromCache, false);
res = await fetch(PNG_BODY_URL); res = await cachedFetch(PNG_BODY_URL);
body = await res.buffer(); body = await res.buffer();
assert.strictEqual(expectedPngBuffer.equals(body), true); assert.strictEqual(expectedPngBuffer.equals(body), true);
assert.strictEqual(res.fromCache, true); assert.strictEqual(res.fromCache, true);
}); });
it('Can stream a body', async function() { it('Can stream a body', async function() {
res = await fetch(TEXT_BODY_URL); res = await cachedFetch(TEXT_BODY_URL);
body = ''; body = '';
for await (const chunk of res.body) { for await (const chunk of res.body) {
@ -259,7 +332,7 @@ describe('Data tests', function() {
assert.strictEqual(TEXT_BODY_EXPECTED, body); assert.strictEqual(TEXT_BODY_EXPECTED, body);
assert.strictEqual(res.fromCache, false); assert.strictEqual(res.fromCache, false);
res = await fetch(TEXT_BODY_URL); res = await cachedFetch(TEXT_BODY_URL);
body = ''; body = '';
for await (const chunk of res.body) { for await (const chunk of res.body) {