add tests, fix flawed logic

This commit is contained in:
Randall Schmidt 2020-11-27 22:26:31 -05:00
parent bc92aa6865
commit c452c8d860
4 changed files with 126 additions and 29 deletions

@ -7,10 +7,48 @@ function md5(str) {
return crypto.createHash('md5').update(str).digest('hex'); return crypto.createHash('md5').update(str).digest('hex');
} }
class Headers {
constructor(rawHeaders) {
this.rawHeaders = rawHeaders;
}
* entries() {
for (let entry of Object.entries(this.rawHeaders)) {
yield entry;
}
}
* keys() {
for (let key of Object.keys(this.rawHeaders)) {
yield key;
}
}
* values() {
for (let value of Object.values(this.rawHeaders)) {
yield value;
}
}
get(name) {
return this.rawHeaders[name.toLowerCase()] || null;
}
has(name) {
return !!this.get(name);
}
}
class Response { class Response {
constructor(raw, cacheFilePath) { constructor(raw, cacheFilePath, fromCache) {
Object.assign(this, raw); Object.assign(this, raw);
this.cacheFilePath = cacheFilePath; this.cacheFilePath = cacheFilePath;
this.headers = new Headers(raw.headers);
this.fromCache = fromCache;
if (this.bodyBuffer.type === 'Buffer') {
this.bodyBuffer = Buffer.from(this.bodyBuffer);
}
} }
text() { text() {
@ -33,14 +71,16 @@ class Response {
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,
useFinalURL: fetchRes.useFinalURL,
ok: fetchRes.ok, ok: fetchRes.ok,
headers: fetchRes.headers, headers: rawHeaders,
redirected: fetchRes.redirected, redirected: fetchRes.redirected,
bodyBuffer: buffer, bodyBuffer: buffer,
}; };
@ -52,17 +92,17 @@ async function getResponse(cacheDirPath, requestArguments) {
? ({ ...requestInit, body: typeof requestInit.body === 'object' ? requestInit.body.toString() : requestInit.body }) ? ({ ...requestInit, body: typeof requestInit.body === 'object' ? requestInit.body.toString() : requestInit.body })
: requestInit; : requestInit;
const cacheHash = md5(JSON.stringify([url, requestParams, ...rest]) + bodyFunctionName); const cacheHash = md5(JSON.stringify([url, requestParams, ...rest]));
const cachedFilePath = path.join(cacheDirPath, `${cacheHash}.json`); const cachedFilePath = path.join(cacheDirPath, `${cacheHash}.json`);
try { try {
const rawResponse = JSON.parse(await fs.promises.readFile(cachedFilePath)); const rawResponse = JSON.parse(await fs.promises.readFile(cachedFilePath));
return new Response(rawResponse); return new Response(rawResponse, cachedFilePath, true);
} catch (err) { } catch (err) {
const fetchResponse = await fetch(...requestArguments); const fetchResponse = await fetch(...requestArguments);
const rawResponse = createRawResponse(fetchResponse); const rawResponse = await createRawResponse(fetchResponse);
await fs.promises.writeFile(cachedFilePath, JSON.stringify(rawResponse)); await fs.promises.writeFile(cachedFilePath, JSON.stringify(rawResponse));
return new Response(rawResponse); return new Response(rawResponse, cachedFilePath, false);
} }
} }

17
package-lock.json generated

@ -750,6 +750,17 @@
"flatted": "^2.0.0", "flatted": "^2.0.0",
"rimraf": "2.6.3", "rimraf": "2.6.3",
"write": "1.0.3" "write": "1.0.3"
},
"dependencies": {
"rimraf": {
"version": "2.6.3",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
"integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
"dev": true,
"requires": {
"glob": "^7.1.3"
}
}
} }
}, },
"flatted": { "flatted": {
@ -1730,9 +1741,9 @@
} }
}, },
"rimraf": { "rimraf": {
"version": "2.6.3", "version": "3.0.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
"integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
"dev": true, "dev": true,
"requires": { "requires": {
"glob": "^7.1.3" "glob": "^7.1.3"

@ -29,7 +29,8 @@
"eslint": "^6.8.0", "eslint": "^6.8.0",
"eslint-config-airbnb-base": "^14.1.0", "eslint-config-airbnb-base": "^14.1.0",
"eslint-plugin-import": "^2.20.2", "eslint-plugin-import": "^2.20.2",
"mocha": "^8.2.1" "mocha": "^8.2.1",
"rimraf": "^3.0.2"
}, },
"dependencies": { "dependencies": {
"node-fetch": "*" "node-fetch": "*"

@ -1,50 +1,95 @@
const assert = require('assert'); const assert = require('assert');
const rimraf = require('rimraf');
const path = require('path'); const path = require('path');
const fetch = require('../index.js')(path.join(__dirname, '..', '.cache')); const FetchCache = require('../index.js');
const CACHE_PATH = path.join(__dirname, '..', '.cache');
const TWO_HUNDRED_URL = 'https://httpbin.org/status/200'; const TWO_HUNDRED_URL = 'https://httpbin.org/status/200';
const FOUR_HUNDRED_URL = 'https://httpbin.org/status/400'; const FOUR_HUNDRED_URL = 'https://httpbin.org/status/400';
const THREE_HUNDRED_TWO_URL = 'https://httpbin.org/status/302'; const THREE_HUNDRED_TWO_URL = 'https://httpbin.org/status/302';
const TEXT_BODY_URL = 'https://httpbin.org/robots.txt';
const TEXT_BODY_EXPECTED = 'User-agent: *\nDisallow: /deny\n';
let fetch;
let res;
let body;
beforeEach(async function() {
rimraf.sync(CACHE_PATH);
fetch = FetchCache(CACHE_PATH);
});
describe('Basic property tests', function() { describe('Basic property tests', function() {
it('Has a status property', async function() { it('Has a status property', async function() {
const res = await fetch(TWO_HUNDRED_URL); res = await fetch(TWO_HUNDRED_URL);
assert.strictEqual(res.status, 200);
res = await fetch(TWO_HUNDRED_URL);
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
}); });
it('Has a statusText property', async function() { it('Has a statusText property', async function() {
const res = await fetch(TWO_HUNDRED_URL); res = await fetch(TWO_HUNDRED_URL);
assert.strictEqual(res.statusText, 'OK');
res = await fetch(TWO_HUNDRED_URL);
assert.strictEqual(res.statusText, 'OK'); assert.strictEqual(res.statusText, 'OK');
}); });
it('Has a type property', async function() {
const res = await fetch(TWO_HUNDRED_URL);
assert.strictEqual(res.type, 'basic');
});
it('Has a url property', async function() { it('Has a url property', async function() {
const res = await fetch(TWO_HUNDRED_URL); res = await fetch(TWO_HUNDRED_URL);
assert.strictEqual(res.url, TWO_HUNDRED_URL);
res = await fetch(TWO_HUNDRED_URL);
assert.strictEqual(res.url, TWO_HUNDRED_URL); assert.strictEqual(res.url, TWO_HUNDRED_URL);
}); });
it('Has a useFinalURL property', async function() {
const res = await fetch(TWO_HUNDRED_URL);
assert.strictEqual(res.useFinalURL, true);
});
it('Has an ok property', async function() { it('Has an ok property', async function() {
const res = await fetch(FOUR_HUNDRED_URL); res = await fetch(FOUR_HUNDRED_URL);
assert.strictEqual(res.ok, false);
assert.strictEqual(res.status, 400);
res = await fetch(FOUR_HUNDRED_URL);
assert.strictEqual(res.ok, false); assert.strictEqual(res.ok, false);
assert.strictEqual(res.status, 400); assert.strictEqual(res.status, 400);
}); });
it('Has a headers property', async function() { it('Has a headers property', async function() {
const res = await fetch(TWO_HUNDRED_URL); res = await fetch(TWO_HUNDRED_URL);
assert.notStrictEqual(res.headers, undefined);
res = await fetch(TWO_HUNDRED_URL);
assert.notStrictEqual(res.headers, undefined); assert.notStrictEqual(res.headers, undefined);
}); });
it('Has a redirected property', async function() { it('Has a redirected property', async function() {
const res = await fetch(THREE_HUNDRED_TWO_URL); res = await fetch(THREE_HUNDRED_TWO_URL);
assert.strictEqual(res.redirected, true);
res = await fetch(THREE_HUNDRED_TWO_URL);
assert.strictEqual(res.redirected, true); assert.strictEqual(res.redirected, true);
}); });
}).timeout(10000);
describe('Cache tests', function() {
it('Uses cache', async function() {
res = await fetch(TWO_HUNDRED_URL);
assert.strictEqual(res.fromCache, false);
res = await fetch(TWO_HUNDRED_URL);
assert.strictEqual(res.fromCache, true);
}); });
}).timeout(10000);
describe('Data tests', function() {
it('Can get text body', async function() {
res = await fetch(TEXT_BODY_URL);
body = await res.text();
assert.strictEqual(body, TEXT_BODY_EXPECTED);
res = await fetch(TEXT_BODY_URL);
body = await res.text();
assert.strictEqual(body, TEXT_BODY_EXPECTED);
});
}).timeout(10000);