UNPKG

with-simple-caching

Version:

A wrapper that makes it simple to add caching to any function

253 lines 14.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const createExampleCache_1 = require("../../__test_assets__/createExampleCache"); const BadRequestError_1 = require("../../utils/errors/BadRequestError"); const withExtendableCaching_1 = require("./withExtendableCaching"); describe('withExtendableCaching', () => { describe('execute', () => { it('should be able to execute logic', () => { // define an example fn const apiCalls = []; const callApi = (0, withExtendableCaching_1.withExtendableCaching)(() => { apiCalls.push(1); return Math.random(); }, { cache: (0, createExampleCache_1.createExampleSyncCache)().cache }); // call the fn a few times const result1 = callApi.execute(); const result2 = callApi.execute(); const result3 = callApi.execute(); // check that the response is the same each time expect(result1).toEqual(result2); expect(result2).toEqual(result3); // check that "api" was only called once expect(apiCalls.length).toEqual(1); }); }); describe('invalidate', () => { it('should be able to invalidate a cached value by input', () => { // define an example fn const apiCalls = []; const callApi = (0, withExtendableCaching_1.withExtendableCaching)(({ galaxy }) => { apiCalls.push(galaxy); return Math.random(); }, { cache: (0, createExampleCache_1.createExampleSyncCache)().cache }); // call the fn a few times const result1 = callApi.execute({ galaxy: 'andromeda' }); const result2 = callApi.execute({ galaxy: 'pegasus' }); const result3 = callApi.execute({ galaxy: 'andromeda' }); const result4 = callApi.execute({ galaxy: 'pegasus' }); // check that the response is the same each time the input is the same expect(result1).toEqual(result3); expect(result2).toEqual(result4); // check that "api" was only called twice expect(apiCalls.length).toEqual(2); // invalidate the cached value for one of the inputs callApi.invalidate({ forInput: [{ galaxy: 'andromeda' }] }); // now call the cache again for that invalidated value, and prove it called the api again const result5 = callApi.execute({ galaxy: 'andromeda' }); expect(result5).not.toEqual(result1); expect(apiCalls.length).toEqual(3); }); it('should be able to invalidate a cached value by key', () => { // define an example fn const apiCalls = []; const callApi = (0, withExtendableCaching_1.withExtendableCaching)(({ galaxy }) => { apiCalls.push(galaxy); return Math.random(); }, { cache: (0, createExampleCache_1.createExampleSyncCache)().cache }); // call the fn a few times const result1 = callApi.execute({ galaxy: 'andromeda' }); const result2 = callApi.execute({ galaxy: 'pegasus' }); const result3 = callApi.execute({ galaxy: 'andromeda' }); const result4 = callApi.execute({ galaxy: 'pegasus' }); // check that the response is the same each time the input is the same expect(result1).toEqual(result3); expect(result2).toEqual(result4); // check that "api" was only called twice expect(apiCalls.length).toEqual(2); // invalidate the cached value for one of the inputs callApi.invalidate({ forKey: JSON.stringify([{ galaxy: 'andromeda' }]) }); // now call the cache again for that invalidated value, and prove it called the api again const result5 = callApi.execute({ galaxy: 'andromeda' }); expect(result5).not.toEqual(result1); expect(apiCalls.length).toEqual(3); }); it('should be able to invalidate a cached value by key when the cache is to be defined at runtime from inputs', () => { // define an example fn const { cache } = (0, createExampleCache_1.createExampleSyncCache)(); const apiCalls = []; const callApi = (0, withExtendableCaching_1.withExtendableCaching)(({ galaxy }, _) => { apiCalls.push(galaxy); return Math.random(); }, { cache: ({ fromInput }) => fromInput[1].cache, serialize: { key: ({ forInput }) => forInput[0].galaxy, // dont include cache as part of the key + simplify the key to just the galaxy }, }); // call the fn a few times const result1 = callApi.execute({ galaxy: 'andromeda' }, { cache }); const result2 = callApi.execute({ galaxy: 'pegasus' }, { cache }); const result3 = callApi.execute({ galaxy: 'andromeda' }, { cache }); const result4 = callApi.execute({ galaxy: 'pegasus' }, { cache }); // check that the response is the same each time the input is the same expect(result1).toEqual(result3); expect(result2).toEqual(result4); // check that "api" was only called twice expect(apiCalls.length).toEqual(2); // prove that it will throw a helpful error if we dont explicitly pass in the cache in this case try { callApi.invalidate({ forKey: 'andromeda' }); throw new Error('should not reach here'); } catch (error) { expect(error).toBeInstanceOf(BadRequestError_1.BadRequestError); if (!(error instanceof BadRequestError_1.BadRequestError)) throw new Error('error should have been instance of BadRequestError'); // satisfy typescript defs expect(error.message).toContain('could not find the cache to invalidate'); expect(error.message).toMatchSnapshot(); } // invalidate the cached value for one of the inputs callApi.invalidate({ forKey: 'andromeda', cache }); // now call the cache again for that invalidated value, and prove it called the api again const result5 = callApi.execute({ galaxy: 'andromeda' }, { cache }); expect(result5).not.toEqual(result1); expect(apiCalls.length).toEqual(3); }); }); describe('update', () => { it('should be able to update a cached value by input', () => { // define an example fn const apiCalls = []; const callApi = (0, withExtendableCaching_1.withExtendableCaching)(({ galaxy }) => { apiCalls.push(galaxy); return Math.random(); }, { cache: (0, createExampleCache_1.createExampleSyncCache)().cache }); // call the fn a few times const result1 = callApi.execute({ galaxy: 'andromeda' }); const result2 = callApi.execute({ galaxy: 'pegasus' }); const result3 = callApi.execute({ galaxy: 'andromeda' }); const result4 = callApi.execute({ galaxy: 'pegasus' }); // check that the response is the same each time the input is the same expect(result1).toEqual(result3); expect(result2).toEqual(result4); // check that "api" was only called twice expect(apiCalls.length).toEqual(2); // update the cached value for one of the inputs callApi.update({ forInput: [{ galaxy: 'andromeda' }], toValue: 821 }); // now call the cache again const result5 = callApi.execute({ galaxy: 'andromeda' }); // and prove that the value changed to what we wanted to update it to expect(result5).toEqual(821); expect(result5).not.toEqual(result1); // and prove that we didn't call the api again expect(apiCalls.length).toEqual(2); }); it('should be able to update a cached value by key', () => { // define an example fn const apiCalls = []; const callApi = (0, withExtendableCaching_1.withExtendableCaching)(({ galaxy }) => { apiCalls.push(galaxy); return Math.random(); }, { cache: (0, createExampleCache_1.createExampleSyncCache)().cache }); // call the fn a few times const result1 = callApi.execute({ galaxy: 'andromeda' }); const result2 = callApi.execute({ galaxy: 'pegasus' }); const result3 = callApi.execute({ galaxy: 'andromeda' }); const result4 = callApi.execute({ galaxy: 'pegasus' }); // check that the response is the same each time the input is the same expect(result1).toEqual(result3); expect(result2).toEqual(result4); // check that "api" was only called twice expect(apiCalls.length).toEqual(2); // update the cached value for one of the inputs callApi.update({ forKey: JSON.stringify([{ galaxy: 'andromeda' }]), toValue: 821, }); // now call the cache again const result5 = callApi.execute({ galaxy: 'andromeda' }); // and prove that the value changed to what we wanted to update it to expect(result5).toEqual(821); expect(result5).not.toEqual(result1); // and prove that we didn't call the api again expect(apiCalls.length).toEqual(2); }); it('should be able to update a cached value by key when the cache is to be defined at runtime from inputs', () => { // define an example fn const { cache } = (0, createExampleCache_1.createExampleSyncCache)(); const apiCalls = []; const callApi = (0, withExtendableCaching_1.withExtendableCaching)(({ galaxy }, _) => { apiCalls.push(galaxy); return Math.random(); }, { cache: ({ fromInput }) => fromInput[1].cache, serialize: { key: ({ forInput }) => forInput[0].galaxy, // dont include cache as part of the key + simplify the key to just the galaxy }, }); // call the fn a few times const result1 = callApi.execute({ galaxy: 'andromeda' }, { cache }); const result2 = callApi.execute({ galaxy: 'pegasus' }, { cache }); const result3 = callApi.execute({ galaxy: 'andromeda' }, { cache }); const result4 = callApi.execute({ galaxy: 'pegasus' }, { cache }); // check that the response is the same each time the input is the same expect(result1).toEqual(result3); expect(result2).toEqual(result4); // check that "api" was only called twice expect(apiCalls.length).toEqual(2); // prove that it will throw a helpful error if we dont explicitly pass in the cache in this case try { callApi.update({ forKey: 'andromeda', toValue: 821 }); throw new Error('should not reach here'); } catch (error) { expect(error).toBeInstanceOf(BadRequestError_1.BadRequestError); if (!(error instanceof BadRequestError_1.BadRequestError)) throw new Error('error should have been instance of BadRequestError'); // satisfy typescript defs expect(error.message).toContain('could not find the cache to update'); expect(error.message).toMatchSnapshot(); } // invalidate the cached value for one of the inputs callApi.update({ forKey: 'andromeda', toValue: 821, cache }); // now call the cache again const result5 = callApi.execute({ galaxy: 'andromeda' }, { cache }); // and prove that the value changed to what we wanted to update it to expect(result5).toEqual(821); expect(result5).not.toEqual(result1); // and prove that we didn't call the api again expect(apiCalls.length).toEqual(2); }); it('should be able to update a cached value by input to a value based on prior value', () => { // define an example fn const apiCalls = []; const callApi = (0, withExtendableCaching_1.withExtendableCaching)(({ galaxy }) => { apiCalls.push(galaxy); return Math.random(); }, { cache: (0, createExampleCache_1.createExampleSyncCache)().cache }); // call the fn a few times const result1 = callApi.execute({ galaxy: 'andromeda' }); const result2 = callApi.execute({ galaxy: 'pegasus' }); const result3 = callApi.execute({ galaxy: 'andromeda' }); const result4 = callApi.execute({ galaxy: 'pegasus' }); // check that the response is the same each time the input is the same expect(result1).toEqual(result3); expect(result2).toEqual(result4); // check that "api" was only called twice expect(apiCalls.length).toEqual(2); // update the cached value for one of the inputs callApi.update({ forInput: [{ galaxy: 'andromeda' }], toValue: ({ fromCachedOutput }) => (fromCachedOutput !== null && fromCachedOutput !== void 0 ? fromCachedOutput : 0) * 2, }); // now call the cache again const result5 = callApi.execute({ galaxy: 'andromeda' }); // and prove that the value changed to what we wanted to update it to expect(result5).not.toEqual(result1); expect(result5).toEqual(result1 * 2); // and prove that we didn't call the api again expect(apiCalls.length).toEqual(2); }); }); }); //# sourceMappingURL=withExtendableCaching.test.js.map