mirror of
https://github.com/arthurfiorette/axios-cache-interceptor.git
synced 2025-12-08 17:36:16 +00:00
* feat(WIP): etag and if-modified-since support * test: fixed test name * fix: merge response headers * test: add etag / last-modified tests * test: add must-revalidate tests * fix: handle expirationTime 0 as true. * tests: refactored some tests * test: added keepIfStale tests * fix: remove axios error for 304 requests * fix: possible infinite loop on validateStatus function * tests: ignore code that is hard test from coverage * fix: use Last-Modified header for modifiedSince If-Modified-Since is never sent by a server, it's a client only header. However Last-Modified is sent by the server to indicate the last time the returned version was modified (it might not be the last version depending on cache configuration on intermediate servers) * test: use validateStatus in mock This more closely match default axios adapter. * fix: validateStatus handling all cases * refactor: use cache.createdAt if the last-modified header isn't present * test: etag with predefined value * test: added more test cases * fix: fixed logic in some tests * docs: initial documentation * fix: manual values work out of the box This removes header requirement from server. * docs: add details to etag and modifiedSince features * fix: delete custom headers from response * feat: accept all configurations from global axios Merging global into local configuration enable user to use global configuration for all options and remove the need to check local and global values every time. * fix: preserve original types from AxiosInstance The only value axios needs is a URL, and in the second definition of the method, there is already a URL parameter, so it can be undefined. * Fix: defaults modifiedSince back to false. Avoids breaking changes. * docs: fix etag examples * docs: document internal headers * refactor: ternary operator :) * style: prettified code * test: remove modifiedSince: false in tests since this is the default * docs: fix headers examples * docs: fixed example formatting * tests: split tests into multiple files to test them faster * docs: correct jsdoc empty object Co-authored-by: Charly Koza <cka@f4-group.com>
139 lines
4.1 KiB
TypeScript
139 lines
4.1 KiB
TypeScript
import { CacheRequestInterceptor } from '../../src/interceptors/request';
|
|
import { mockAxios } from '../mocks/axios';
|
|
import { sleep } from '../utils';
|
|
|
|
describe('test request interceptor', () => {
|
|
it('tests against specified methods', async () => {
|
|
const axios = mockAxios({
|
|
// only cache post methods
|
|
methods: ['post']
|
|
});
|
|
|
|
const response = await axios.get('');
|
|
const cacheKey = await axios.generateKey(response.config);
|
|
const cache = await axios.storage.get(cacheKey);
|
|
|
|
expect(cache.state).toBe('empty');
|
|
});
|
|
|
|
it('tests specified methods', async () => {
|
|
const axios = mockAxios({
|
|
// only cache get methods
|
|
methods: ['get']
|
|
});
|
|
|
|
const response = await axios.get('');
|
|
const cacheKey = await axios.generateKey(response.config);
|
|
const cache = await axios.storage.get(cacheKey);
|
|
|
|
expect(cache.state).toBe('cached');
|
|
});
|
|
|
|
it('tests concurrent requests', async () => {
|
|
const axios = mockAxios();
|
|
|
|
const [resp1, resp2] = await Promise.all([axios.get(''), axios.get('')]);
|
|
|
|
expect(resp1.cached).toBe(false);
|
|
expect(resp2.cached).toBe(true);
|
|
});
|
|
|
|
it('tests concurrent requests with cache: false', async () => {
|
|
const axios = mockAxios();
|
|
|
|
const results = await Promise.all([
|
|
axios.get('', { cache: false }),
|
|
axios.get(''),
|
|
axios.get('', { cache: false })
|
|
]);
|
|
for (const result of results) {
|
|
expect(result.cached).toBe(false);
|
|
}
|
|
});
|
|
|
|
/**
|
|
* This is to test when two requests are made simultaneously. With
|
|
* that, the second response waits the deferred from the first one.
|
|
* Because the first request is not cached, the second should not be
|
|
* waiting forever for the deferred to be resolved with a cached response.
|
|
*/
|
|
it('tests concurrent requests with uncached responses', async () => {
|
|
const axios = mockAxios();
|
|
|
|
const [, resp2] = await Promise.all([
|
|
axios.get('', {
|
|
// Simple predicate to ignore cache in the response step.
|
|
cache: { cachePredicate: () => false }
|
|
}),
|
|
axios.get('')
|
|
]);
|
|
|
|
expect(resp2.cached).toBe(false);
|
|
});
|
|
|
|
it('tests response.cached', async () => {
|
|
const axios = mockAxios();
|
|
|
|
const response = await axios.get('');
|
|
expect(response.cached).toBe(false);
|
|
|
|
const response2 = await axios.get('');
|
|
expect(response2.cached).toBe(true);
|
|
|
|
const response3 = await axios.get('', { id: 'random-id' });
|
|
expect(response3.cached).toBe(false);
|
|
|
|
const response4 = await axios.get('', { id: 'random-id' });
|
|
expect(response4.cached).toBe(true);
|
|
});
|
|
|
|
it('test cache expiration', async () => {
|
|
const axios = mockAxios({}, { 'cache-control': 'max-age=1' });
|
|
|
|
await axios.get('', { cache: { interpretHeader: true } });
|
|
|
|
const resultCache = await axios.get('');
|
|
expect(resultCache.cached).toBe(true);
|
|
|
|
// Sleep entire max age time.
|
|
await sleep(1000);
|
|
|
|
const response2 = await axios.get('');
|
|
expect(response2.cached).toBe(false);
|
|
});
|
|
|
|
it('tests "must revalidate" handling without any headers to do so', async () => {
|
|
const axios = mockAxios({}, { 'cache-control': 'must-revalidate' });
|
|
const config = { cache: { interpretHeader: true } };
|
|
await axios.get('', config);
|
|
|
|
// 0ms cache
|
|
await sleep(1);
|
|
|
|
const response = await axios.get('', config);
|
|
// nothing to use for revalidation
|
|
expect(response.cached).toBe(false);
|
|
});
|
|
|
|
it('tests validate-status function', async () => {
|
|
const { createValidateStatus } = CacheRequestInterceptor;
|
|
|
|
const def = createValidateStatus();
|
|
expect(def(200)).toBe(true);
|
|
expect(def(345)).toBe(false);
|
|
expect(def(304)).toBe(true);
|
|
|
|
const only200 = createValidateStatus((s) => s >= 200 && s < 300);
|
|
expect(only200(200)).toBe(true);
|
|
expect(only200(299)).toBe(true);
|
|
expect(only200(304)).toBe(true);
|
|
expect(only200(345)).toBe(false);
|
|
|
|
const randomValue = createValidateStatus((s) => s >= 405 && s <= 410);
|
|
expect(randomValue(200)).toBe(false);
|
|
expect(randomValue(404)).toBe(false);
|
|
expect(randomValue(405)).toBe(true);
|
|
expect(randomValue(304)).toBe(true);
|
|
});
|
|
});
|