Compare commits
55 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
185b47c8a6 | ||
![]() |
b0b454de85 | ||
![]() |
7b0835fcf1 | ||
![]() |
34c8887e51 | ||
![]() |
3d7b27ceb5 | ||
![]() |
49f1c0cdc1 | ||
![]() |
2e7c35a34c | ||
![]() |
3896ae2832 | ||
![]() |
2f5409c25c | ||
![]() |
7f2dba84a3 | ||
![]() |
901e32fa09 | ||
![]() |
ef5ddf69e4 | ||
![]() |
6bc5de6581 | ||
![]() |
f2a6b048bd | ||
![]() |
2fb975d9a8 | ||
![]() |
41045996ae | ||
![]() |
ed25ae32ab | ||
![]() |
7779114055 | ||
![]() |
831440152a | ||
![]() |
ad256800f8 | ||
![]() |
421c68909e | ||
![]() |
1f9b533c66 | ||
![]() |
489da016fd | ||
![]() |
1e0030f5a3 | ||
![]() |
fe51a289e1 | ||
![]() |
1311282f5a | ||
![]() |
32a50f757a | ||
![]() |
664e2afc59 | ||
![]() |
ba9df959db | ||
![]() |
c46d08d76a | ||
![]() |
28348f511f | ||
![]() |
d676db7e88 | ||
![]() |
ac437a89ed | ||
![]() |
ff09dd8521 | ||
![]() |
9c9d2b88e1 | ||
![]() |
f92bd99968 | ||
![]() |
c3071e3059 | ||
![]() |
bf4c295e48 | ||
![]() |
9241b74dde | ||
![]() |
3b5adecd7f | ||
![]() |
8a338f5f5d | ||
![]() |
a7fc94c3e9 | ||
![]() |
facda80789 | ||
![]() |
8e1b57382a | ||
![]() |
558d63fd40 | ||
![]() |
02653e04c4 | ||
![]() |
371d20e933 | ||
![]() |
c452c8d860 | ||
![]() |
bc92aa6865 | ||
![]() |
01bc48594e | ||
![]() |
f854cccd23 | ||
![]() |
dea019c42c | ||
![]() |
eda9f98e3b | ||
![]() |
6d1369b50f | ||
![]() |
d97cb85f97 |
@ -2,3 +2,7 @@
|
|||||||
test
|
test
|
||||||
.cache
|
.cache
|
||||||
.nyc_output
|
.nyc_output
|
||||||
|
.github
|
||||||
|
.eslintignore
|
||||||
|
.vscode
|
||||||
|
coverage
|
||||||
|
24
README.md
24
README.md
@ -28,21 +28,27 @@ This module aims to expose the same API as `node-fetch` does for the most common
|
|||||||
|
|
||||||
Load the module.
|
Load the module.
|
||||||
|
|
||||||
### async fetch(resource [, init])
|
### await fetch(resource [, init])
|
||||||
|
|
||||||
Same arguments as [node-fetch](https://www.npmjs.com/package/node-fetch).
|
Same arguments as [node-fetch](https://www.npmjs.com/package/node-fetch).
|
||||||
|
|
||||||
Returns a **CachedResponse**.
|
Returns a **CachedResponse**.
|
||||||
|
|
||||||
### async CachedResponse.text()
|
### await CachedResponse.ejectFromCache()
|
||||||
|
|
||||||
|
Eject the response from the cache, so that the next request will perform a true HTTP request rather than returning a cached response.
|
||||||
|
|
||||||
|
Keep in mind that this module caches **all** responses, even if they return errors. You might want to use this function in certain cases like receiving a 5xx response status, so that you can retry requests.
|
||||||
|
|
||||||
|
### await CachedResponse.text()
|
||||||
|
|
||||||
Returns the body as a string, same as [node-fetch](https://www.npmjs.com/package/node-fetch).
|
Returns the body as a string, same as [node-fetch](https://www.npmjs.com/package/node-fetch).
|
||||||
|
|
||||||
### async CachedResponse.json()
|
### await CachedResponse.json()
|
||||||
|
|
||||||
Returns the body as a JavaScript object, parsed from JSON, same as [node-fetch](https://www.npmjs.com/package/node-fetch).
|
Returns the body as a JavaScript object, parsed from JSON, same as [node-fetch](https://www.npmjs.com/package/node-fetch).
|
||||||
|
|
||||||
### async CachedResponse.buffer()
|
### await CachedResponse.buffer()
|
||||||
|
|
||||||
Returns the body as a Buffer, same as [node-fetch](https://www.npmjs.com/package/node-fetch).
|
Returns the body as a Buffer, same as [node-fetch](https://www.npmjs.com/package/node-fetch).
|
||||||
|
|
||||||
@ -66,12 +72,6 @@ Returns true if the request was redirected, false otherwise, same as [node-fetch
|
|||||||
|
|
||||||
Returns a **ResponseHeaders** object representing the headers of the response, same as [node-fetch](https://www.npmjs.com/package/node-fetch).
|
Returns a **ResponseHeaders** object representing the headers of the response, same as [node-fetch](https://www.npmjs.com/package/node-fetch).
|
||||||
|
|
||||||
### async CachedResponse.ejectFromCache()
|
|
||||||
|
|
||||||
Eject the response from the cache, so that the next request will perform a true HTTP request rather than returning a cached response.
|
|
||||||
|
|
||||||
Keep in mind that this module caches **all** responses, even if they return error status codes. You might want to use this function when `!response.ok`, so that you can retry requests.
|
|
||||||
|
|
||||||
### ResponseHeaders.entries()
|
### ResponseHeaders.entries()
|
||||||
|
|
||||||
Returns the raw headers as an array of `[key, value]` pairs, same as [node-fetch](https://www.npmjs.com/package/node-fetch).
|
Returns the raw headers as an array of `[key, value]` pairs, same as [node-fetch](https://www.npmjs.com/package/node-fetch).
|
||||||
@ -114,7 +114,7 @@ This is the default cache delegate. It caches responses in-process in a POJO.
|
|||||||
Usage:
|
Usage:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const fetchBuilder, { MemoryCache } = require('node-fetch-cache');
|
const { fetchBuilder, MemoryCache } = require('node-fetch-cache');
|
||||||
const fetch = fetchBuilder.withCache(new MemoryCache(options));
|
const fetch = fetchBuilder.withCache(new MemoryCache(options));
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -135,7 +135,7 @@ Cache to a directory on disk. This allows the cache to survive the process exiti
|
|||||||
Usage:
|
Usage:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const fetchBuilder, { FileSystemCache } = require('node-fetch-cache');
|
const { fetchBuilder, FileSystemCache } = require('node-fetch-cache');
|
||||||
const fetch = fetchBuilder.withCache(new FileSystemCache(options));
|
const fetch = fetchBuilder.withCache(new FileSystemCache(options));
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -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] || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
has(name) {
|
has(name) {
|
||||||
|
@ -27,15 +27,15 @@ class Response {
|
|||||||
return this.bodyBuffer;
|
return this.bodyBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
text() {
|
async text() {
|
||||||
return this.consumeBody().toString();
|
return this.consumeBody().toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
json() {
|
async json() {
|
||||||
return JSON.parse(this.consumeBody().toString());
|
return JSON.parse(this.consumeBody().toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer() {
|
async buffer() {
|
||||||
return this.consumeBody();
|
return this.consumeBody();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
27
index.js
27
index.js
@ -4,6 +4,7 @@ const { URLSearchParams } = require('url');
|
|||||||
const crypto = require('crypto');
|
const crypto = require('crypto');
|
||||||
const Response = require('./classes/response.js');
|
const Response = require('./classes/response.js');
|
||||||
const MemoryCache = require('./classes/caching/memory_cache.js');
|
const MemoryCache = require('./classes/caching/memory_cache.js');
|
||||||
|
const FileSystemCache = require('./classes/caching/file_system_cache.js');
|
||||||
|
|
||||||
const CACHE_VERSION = 2;
|
const CACHE_VERSION = 2;
|
||||||
|
|
||||||
@ -16,15 +17,11 @@ function md5(str) {
|
|||||||
// the cache key.
|
// the cache key.
|
||||||
function getFormDataCacheKey(formData) {
|
function getFormDataCacheKey(formData) {
|
||||||
const cacheKey = { ...formData };
|
const cacheKey = { ...formData };
|
||||||
|
|
||||||
if (typeof formData.getBoundary === 'function') {
|
|
||||||
const boundary = formData.getBoundary();
|
const boundary = formData.getBoundary();
|
||||||
|
|
||||||
// eslint-disable-next-line no-underscore-dangle
|
// eslint-disable-next-line no-underscore-dangle
|
||||||
delete cacheKey._boundary;
|
delete cacheKey._boundary;
|
||||||
|
|
||||||
// eslint-disable-next-line no-underscore-dangle
|
|
||||||
if (Array.isArray(cacheKey._streams)) {
|
|
||||||
const boundaryReplaceRegex = new RegExp(boundary, 'g');
|
const boundaryReplaceRegex = new RegExp(boundary, 'g');
|
||||||
|
|
||||||
// eslint-disable-next-line no-underscore-dangle
|
// eslint-disable-next-line no-underscore-dangle
|
||||||
@ -35,8 +32,6 @@ function getFormDataCacheKey(formData) {
|
|||||||
|
|
||||||
return s;
|
return s;
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return cacheKey;
|
return cacheKey;
|
||||||
}
|
}
|
||||||
@ -54,14 +49,18 @@ function getBodyCacheKeyJson(body) {
|
|||||||
return getFormDataCacheKey(body);
|
return getFormDataCacheKey(body);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error('Unsupported body type');
|
throw new Error('Unsupported body type. Supported body types are: string, number, undefined, null, url.URLSearchParams, fs.ReadStream, FormData');
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCacheKey(requestArguments) {
|
function getCacheKey(requestArguments) {
|
||||||
const resource = requestArguments[0];
|
const resource = requestArguments[0];
|
||||||
const init = requestArguments[1] || {};
|
const init = requestArguments[1] || {};
|
||||||
|
|
||||||
const resourceCacheKeyJson = typeof resource === 'string' ? { url: resource } : { ...resource };
|
if (typeof resource !== 'string') {
|
||||||
|
throw new Error('The first argument must be a string (fetch.Request is not supported).');
|
||||||
|
}
|
||||||
|
|
||||||
|
const resourceCacheKeyJson = { url: resource };
|
||||||
const initCacheKeyJson = { ...init };
|
const initCacheKeyJson = { ...init };
|
||||||
|
|
||||||
resourceCacheKeyJson.body = getBodyCacheKeyJson(resourceCacheKeyJson.body);
|
resourceCacheKeyJson.body = getBodyCacheKeyJson(resourceCacheKeyJson.body);
|
||||||
@ -73,16 +72,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,
|
||||||
};
|
};
|
||||||
@ -111,4 +107,9 @@ function createFetchWithCache(cache) {
|
|||||||
return fetchCache;
|
return fetchCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = createFetchWithCache(new MemoryCache());
|
const defaultFetch = createFetchWithCache(new MemoryCache());
|
||||||
|
|
||||||
|
module.exports = defaultFetch;
|
||||||
|
module.exports.fetchBuilder = defaultFetch;
|
||||||
|
module.exports.MemoryCache = MemoryCache;
|
||||||
|
module.exports.FileSystemCache = FileSystemCache;
|
||||||
|
2
package-lock.json
generated
2
package-lock.json
generated
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "node-fetch-cache",
|
"name": "node-fetch-cache",
|
||||||
"version": "1.0.6",
|
"version": "2.0.0",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "node-fetch-cache",
|
"name": "node-fetch-cache",
|
||||||
"version": "1.0.6",
|
"version": "2.0.0",
|
||||||
"description": "node-fetch with a persistent cache.",
|
"description": "node-fetch with a persistent cache.",
|
||||||
"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"
|
||||||
},
|
},
|
||||||
|
302
test/tests.js
302
test/tests.js
@ -3,9 +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 MemoryCache = require('../classes/caching/memory_cache.js');
|
const standardFetch = require('node-fetch');
|
||||||
|
const FetchCache = require('../index.js');
|
||||||
|
|
||||||
const CACHE_PATH = path.join(__dirname, '..', '.cache');
|
const CACHE_PATH = path.join(__dirname, '..', '.cache');
|
||||||
const expectedPngBuffer = fs.readFileSync(path.join(__dirname, 'expected_png.png'));
|
const expectedPngBuffer = fs.readFileSync(path.join(__dirname, 'expected_png.png'));
|
||||||
@ -19,98 +19,200 @@ 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)) {
|
||||||
|
if (Array.isArray(arrOrObj[0])) {
|
||||||
|
return arrOrObj.filter(e => e[0] !== 'date');
|
||||||
|
}
|
||||||
|
|
||||||
|
return arrOrObj.filter(e => !Date.parse(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
return arrOrObj;
|
||||||
|
}
|
||||||
|
|
||||||
|
function wait(ms) {
|
||||||
|
return new Promise((fulfill) => setTimeout(fulfill, ms));
|
||||||
|
}
|
||||||
|
|
||||||
|
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 FetchCache.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);
|
||||||
|
|
||||||
|
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(
|
||||||
|
removeDates(cachedFetchResponse.headers.values()),
|
||||||
|
removeDates([...standardFetchResponse.headers.values()]),
|
||||||
|
);
|
||||||
|
|
||||||
|
cachedFetchResponse = await cachedFetch(TWO_HUNDRED_URL);
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
removeDates(cachedFetchResponse.headers.values()),
|
||||||
|
removeDates([...standardFetchResponse.headers.values()]),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Gets correct header entries', async function() {
|
||||||
|
let { cachedFetchResponse, standardFetchResponse } = await dualFetch(TWO_HUNDRED_URL);
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
removeDates(cachedFetchResponse.headers.entries()),
|
||||||
|
removeDates([...standardFetchResponse.headers.entries()]),
|
||||||
|
);
|
||||||
|
|
||||||
|
cachedFetchResponse = await cachedFetch(TWO_HUNDRED_URL);
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
removeDates(cachedFetchResponse.headers.entries()),
|
||||||
|
removeDates([...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('Returns undefined for non-existent header', async function() {
|
||||||
|
const headerName = 'zzzz';
|
||||||
|
let { cachedFetchResponse, standardFetchResponse } = await dualFetch(TWO_HUNDRED_URL);
|
||||||
|
assert(!standardFetchResponse.headers.get(headerName));
|
||||||
|
assert.deepStrictEqual(cachedFetchResponse.headers.get(headerName), standardFetchResponse.headers.get(headerName));
|
||||||
|
|
||||||
|
cachedFetchResponse = await cachedFetch(TWO_HUNDRED_URL);
|
||||||
|
assert.deepStrictEqual(cachedFetchResponse.headers.get(headerName), standardFetchResponse.headers.get(headerName));
|
||||||
|
});
|
||||||
|
|
||||||
|
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'));
|
||||||
});
|
});
|
||||||
}).timeout(10000);
|
}).timeout(10000);
|
||||||
|
|
||||||
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 +220,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 +255,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 +279,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,65 +293,75 @@ 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('Does not support Request objects', async function() {
|
||||||
|
try {
|
||||||
|
const request = new standardFetch.Request('https://google.com');
|
||||||
|
await cachedFetch(request);
|
||||||
|
throw new Error('The above line should have thrown.');
|
||||||
|
} catch (err) {
|
||||||
|
assert(err.message.includes('The first argument must be a string (fetch.Request is not supported).'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
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 {
|
||||||
await res.text();
|
await res.text();
|
||||||
throw new Error('The above line should have thrown.');
|
throw new Error('The above line should have thrown.');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// It threw
|
assert(err.message.includes('Error: body used already'));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
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 +371,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) {
|
||||||
@ -269,4 +381,56 @@ describe('Data tests', function() {
|
|||||||
assert.strictEqual(TEXT_BODY_EXPECTED, body);
|
assert.strictEqual(TEXT_BODY_EXPECTED, body);
|
||||||
assert.strictEqual(res.fromCache, true);
|
assert.strictEqual(res.fromCache, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Errors if the body type is not supported', async function() {
|
||||||
|
try {
|
||||||
|
await cachedFetch(TEXT_BODY_URL, { body: {} });
|
||||||
|
throw new Error('It was supposed to throw');
|
||||||
|
} catch (err) {
|
||||||
|
assert(err.message.includes('Unsupported body type'));
|
||||||
|
}
|
||||||
|
});
|
||||||
}).timeout(10000);
|
}).timeout(10000);
|
||||||
|
|
||||||
|
describe('Memory cache tests', function() {
|
||||||
|
it('Supports TTL', async function() {
|
||||||
|
cachedFetch = FetchCache.withCache(new FetchCache.MemoryCache({ ttl: 100 }));
|
||||||
|
let res = await cachedFetch(TWO_HUNDRED_URL);
|
||||||
|
assert.strictEqual(res.fromCache, false);
|
||||||
|
res = await cachedFetch(TWO_HUNDRED_URL);
|
||||||
|
assert.strictEqual(res.fromCache, true);
|
||||||
|
|
||||||
|
await wait(200);
|
||||||
|
|
||||||
|
res = await cachedFetch(TWO_HUNDRED_URL);
|
||||||
|
assert.strictEqual(res.fromCache, false);
|
||||||
|
});
|
||||||
|
}).timeout(10000);
|
||||||
|
|
||||||
|
describe('File system cache tests', function() {
|
||||||
|
it('Supports TTL', async function() {
|
||||||
|
cachedFetch = FetchCache.withCache(new FetchCache.FileSystemCache({ ttl: 100 }));
|
||||||
|
let res = await cachedFetch(TWO_HUNDRED_URL);
|
||||||
|
assert.strictEqual(res.fromCache, false);
|
||||||
|
res = await cachedFetch(TWO_HUNDRED_URL);
|
||||||
|
assert.strictEqual(res.fromCache, true);
|
||||||
|
|
||||||
|
await wait(200);
|
||||||
|
|
||||||
|
res = await cachedFetch(TWO_HUNDRED_URL);
|
||||||
|
assert.strictEqual(res.fromCache, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Can get PNG buffer body', async function() {
|
||||||
|
cachedFetch = FetchCache.withCache(new FetchCache.FileSystemCache());
|
||||||
|
res = await cachedFetch(PNG_BODY_URL);
|
||||||
|
body = await res.buffer();
|
||||||
|
assert.strictEqual(expectedPngBuffer.equals(body), true);
|
||||||
|
assert.strictEqual(res.fromCache, false);
|
||||||
|
|
||||||
|
res = await cachedFetch(PNG_BODY_URL);
|
||||||
|
body = await res.buffer();
|
||||||
|
assert.strictEqual(expectedPngBuffer.equals(body), true);
|
||||||
|
assert.strictEqual(res.fromCache, true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
Reference in New Issue
Block a user